一丶可选头结构以及作用
typedef struct_IMAGE_OPTIONAL_HEADER {
WORD Magic;/*机器型号,判断是PE是32位还是64位*/BYTE MajorLinkerVersion;/*连接器版本号高版本*/BYTE MinorLinkerVersion;/*连接器版本号低版本,组合起来就是 5.12 其中5是高版本,C是低版本*/DWORD SizeOfCode;/*代码节的总大小(512为一个磁盘扇区)*/DWORD SizeOfInitializedData;/*初始化数据的节的总大小,也就是.data*/DWORD SizeOfUninitializedData;/*未初始化数据的节的大小,也就是 .data ?*/DWORD AddressOfEntryPoint;/*程序执行入口(OEP) RVA(相对偏移)*/DWORD BaseOfCode;/*代码的节的起始RVA(相对偏移)也就是代码区的偏移,偏移+模块首地址定位代码区*/DWORD BaseOfData;/*数据结的起始偏移(RVA),同上*/DWORD ImageBase;/*程序的建议模块基址(意思就是说作参考用的,模块地址在哪里)*/DWORD SectionAlignment;/*内存中的节对齐*/DWORD FileAlignment;/*文件中的节对齐*/WORD MajorOperatingSystemVersion;/*操作系统版本号高位*/WORD MinorOperatingSystemVersion;/*操作系统版本号低位*/WORD MajorImageVersion;/*PE版本号高位*/WORD MinorImageVersion;/*PE版本号低位*/WORD MajorSubsystemVersion;/*子系统版本号高位*/WORD MinorSubsystemVersion;/*子系统版本号低位*/DWORD Win32VersionValue;/*32位系统版本号值,注意只能修改为4 5 6表示操作系统支持nt4.0 以上,5的话依次类推*/DWORD SizeOfImage;/*整个程序在内存中占用的空间(PE映尺寸)*/DWORD SizeOfHeaders;/*所有头(头的结构体大小)+节表的大小*/DWORD CheckSum;/*校验和,对于驱动程序,可能会使用*/WORD Subsystem;/*文件的子系统 :重要*/WORD DllCharacteristics;/*DLL文件属性,也可以成为特性,可能DLL文件可以当做驱动程序使用*/DWORD SizeOfStackReserve;/*预留的栈的大小*/DWORD SizeOfStackCommit;/*立即申请的栈的大小(分页为单位)*/DWORD SizeOfHeapReserve;/*预留的堆空间大小*/DWORD SizeOfHeapCommit;/*立即申请的堆的空间的大小*/DWORD LoaderFlags;/*与调试有关*/DWORD NumberOfRvaAndSizes;/*下面的成员,数据目录结构的项目数量*/IMAGE_DATA_DIRECTORY DataDirectory[16];/*数据目录,默认16个,16是宏,这里方便直接写成16*/} IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
1.PE类型
这个有宏定义了
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b /*32位PE*/
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b /*64位PE*/
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 /*其它,单片机
2丶.OEP,程序执行入口位置.
我们利用昨天写的程序,可以完成一个反调试.
思路:
1.修改OEP偏移,置为0位置处(也就是MZ的位置)
2.在MZ位置后面添加我们自己的代码
3.添加完成之后,继续跳到以前OEP的位置.
首先,看PE文件的值,OEP的偏移位置是00001008偏移,那么OD调试,看下位置在哪里.
我们知道了入口偏移是00401008位置,那么我们就知道了模块首地址是00400000
公式 00401008 - 1008 = 00400000 因为我们知道1008是相对于模块地址来的所以可以求出模块地址,我们跳转过去
可以看出,前边正好是4D5A,那么我们可以修改一下,添加自己的代码,首先4D5A正好是汇编代码
那么我们可以去平栈,然后跳转到我们以前的OEP位置.
修改成下边那样
首先,我们以前讲DOS头的时候说过,如果这个EXE文件运行在32位系统下,那么DOS头中就地一个和最后一个成员有用,那么后面我们随便修改.
上面代码很简单,首先栈平衡,然后跳转到我们以前代码执行位置.
文件中(PE)我们把后面的二进制都修改为我们的代码
入后偏移(RVA)修改为0000000
运行我们的程序,和调试我们的程序
运行程序:
可以正常运行
调试程序:
二丶数据目录
数据目录,主要是存放各种表格的,看下
typedef struct_IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; 虚拟地址(表格位置)
DWORD Size; 大小
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
关于表格,这里有很多宏定义.
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 //Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 //Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 //Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 //Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 //Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 //Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 //Debug Directory
//IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7//(X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 //Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 //RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 //TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 //Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 //Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 //Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 //Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 //COM Runtime descriptor
它是按照位来计算的.
三丶节表
在NT头下面,紧跟着的是节表
节表是什么意思? 可以理解为分区,就是几个区
那么意思就是保存了区
那么我们猜想一下,都需要什么成员
地址
地址大小
文件中的地址
文件大小
等等....
看下节表的信息吧
typedef struct_IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];/*节区的名字*/union {
DWORD PhysicalAddress;
DWORD VirtualSize;/*节区的尺寸*/} Misc;
DWORD VirtualAddress;/*虚拟地址 节区的RVA地址(偏移)*/DWORD SizeOfRawData;/*在文件中对齐的尺寸*/DWORD PointerToRawData;/*在文件中的偏移*/DWORD PointerToRelocations;/*在OBJ文件中使用*/DWORD PointerToLinenumbers;/*行号表位置,调试使用*/WORD NumberOfRelocations;/*在OBJ文件中使用*/WORD NumberOfLinenumbers;/*行号表的数量*/DWORD Characteristics;/*节的属性*/} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;
重要成员
1.节的尺寸
2.虚拟地址,RVA(偏移)
3.文件中的大小
4.文件中的偏移
5.节的属性
其中节的属性有很多,(表达这个分区是一个什么样的分区,代码区,常量区等等)
看下宏定义(按位来的,可以看下第二讲的最后关于文件属性的讲解,其中讲解了这个怎么按位来)
#define IMAGE_SCN_CNT_CODE 0x00000020 //Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 //Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 //Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER 0x00000100 //Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200 //Section contains comments or some other type of information.
//IMAGE_SCN_TYPE_OVER 0x00000400//Reserved.
#define IMAGE_SCN_LNK_REMOVE 0x00000800 //Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000 //Section contents comdat.
//0x00002000//Reserved.//IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 //Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000 //Section content can be accessed relative to GP
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 //Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 //Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 //Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 //Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 //Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 //Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 //Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 //Section is writeable.