菜鸟在此,大牛飞过~~~~~~~
以上仅为个人的一点小小的心得,有什么不对的地方还请各位指出。
大家好,我是F8。继续的,我来水贴了,顺便骗分骗经验。啦啦啦,求别打我。
上次我简单的介绍了DOS头,因为DOS头并不算重要,所以以后可能不会再提及了,有兴趣的请自行翻一下我前面的帖子(这不是打广告~~~)。
那么,下面正式开始:
----------------------------------请叫我分割线-------------------------------------分割线-----------------------------------
下面我们来玩一下PE头,不过在这之前,得先介绍一下相对偏移与基址的概念。
首先,来看一个长16,宽3的字符数组的定义:
char Text[3][0x10]; //定义3个长0x10的字符串
然后我要求在OD中手动定位这3个字符串,并写一个程序,动态修改它的内容。不难,对吧。好的,那么我们来找一下它的位置吧。
于是,我用了下面的代码来显示他们的位置。
cout<<" VA\tRVA"<<endl;
char Text[3][0x10]; //定义3个长0x10的字符串
for(int i = 0; i < 3; i++) {
cout<<hex<<(DWORD)Text<<"\t+"<<hex<<(DWORD)(Text-Text[0])<<endl; //显示字符的内存地址与相对偏移
}
4次运行结果:
奇怪了?明明是同一段代码,但是一运行起来,在内存中的地址却不一样,这是为啥?
啧啧,OD中载入看一下原因。
点开内存窗口,快捷键ALT+M,查一下模块的地址:
晕了,晕了,开了3次,每一次的程序装载的地址都不一样。。。
其实,程序被装载到内存的地址叫做基址,像上面,1的0x1140000,2的00190000,3的00380000就被叫做基址了。而每次程序打开,都会被转载到不同的地址,这技术就叫做动态基址。
这就难办了,这字符串的地址都是会变化的,那么我要怎么去定位它呢?不对,再仔细看看,上面的运行图中,有几个数是不变的,就是那个叫做RVA的东西啦。那是字符串的地址间相减的结果,不变也正常。等等,既然是这样,如果用字符串的地址减去程序的基址,得到的结果还是一样的吗?我把程序做一点小小的修改:
cout<<" VA\tRVA"<<endl;
HMODULE hModuleBase =GetModuleHandle(NULL);
for(int i = 0; i < 3; i++) {
cout<<hex<<(DWORD)Text<<"\t+"<<hex<<(DWORD)(Text-(DWORD)hModuleBase<<endl;
}
DEBUG RELEASE:
结果出来了,和我预想中的一样,字符串的地址减基址大小是不变的。这个便是RVA:相对偏移地址的概念。用来指出两个地方之间的地址差值。
上面我分别用来Debug版本的和Release版本的做了演示,所以你会看到有两组RVA。
啰嗦了这么久,总算可以真正的开始了。上一次说到了DOS头,接下来我们就要从DOS头引入到PE头了。先复习一下:
图中,DOS尾部的最后4个字节的数据,IMAGE_DOS_HEADER.lfanew的数据指出了PE头的相对偏移。也就是说,对于文件来说,PE头的开始地址便是 00000000+000000C8 = 000000C8,如果加载到了内存中,基地址是00400000,那么PE头的开始地址就是004000C8。好了,直接跳到C8这个地方看一下吧。
这里便是PE头的一个大概了。具体的解析在下一遍文章介绍。现在先来简要的认识几个重要的参数:
最外层的是一个IMAGE_NT_HEADERS,位于0xC8处,它定义如下:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //PE标志”PE\0\0”
IMAGE_FILE_HEADER FileHeader; //基本的文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展文件头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
所以,它的第一个参数是IMAGE_NT_HEADERS.signature,固定为 “PE\0\0”, 0x50, 0x45, 0x00, 0x00。呵呵,就是PE头的标识了,因此想快速找PE头,认准这个参数就可以。。
然后第二个参数, IMAGE_FILE_HEADER,是标准文件头,长0x14,与signature加起来为0x18,恰好占1行半,请记住这个。这里我特别的标志了它的倒数第二个参数,IMAGE_FILE_HEADER.SizeOfOptionalHeader。正如名字一样,是标志出扩展头的大小的。因为扩展头的长度是可变的,所以。。。这里的值为 00E0,于是,扩展头的大小也为0xE0,挺好看的吧。
最后是第三个参数IMAGE_OPTIONAL_HEADER,俗称扩展头,关于PE文件的大部分信息都可以在这里找到,可以说,这个部分是最为重要的。起始位置为PE头+0x18的地方,请记住了。
啰啰嗦嗦地写了这么多,就暂且打住吧,准备下节开始才正式的讲解PE头,感谢大家的支持啊,啦啦啦。
第一篇传送门