首先介绍
IMAGE_OPTIONAL_HEADER32 ,导入表的最后一个成员是数据目录项数组,该数组长度 由NumberOfRvaAndSizes
字段决定
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; 32 位 64位 rom
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; 代码大小
DWORD SizeOfInitializedData; 已初始化大小
DWORD SizeOfUninitializedData;未初始化大小
DWORD AddressOfEntryPoint; 程序入口点
DWORD BaseOfCode; 代码基址
DWORD BaseOfData; 数据基址
DWORD ImageBase; 映像基址 建议装载地址 不能轻易修改,否则存在重定位问题 必须是分页首地址
DWORD SectionAlignment; 内存对齐 0x1000的倍数 按操做系统特性修改
DWORD FileAlignment; 文件对齐
WORD MajorOperatingSystemVersion;cc
WORD MinorOperatingSystemVersion;cc
WORD MajorImageVersion;cc
WORD MinorImageVersion;cc
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;cc
DWORD Win32VersionValue; win cc
DWORD SizeOfImage; 可执行文件在内存中的大小
DWORD SizeOfHeaders;可执行文件的头部大小
DWORD CheckSum; 校验和
WORD Subsystem; 控制台 窗口 内核
WORD DllCharacteristics;
DWORD SizeOfStackReserve; 栈空间大小
DWORD SizeOfStackCommit; 提交大小
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags; cc
DWORD NumberOfRvaAndSizes; 数据目录数组的总个数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
数据目录项每一个下标的意义,可知下标为1的地方决定了导入表的RVA
#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
以下以一个简短的程序,弹出hello 的messagebox为例,得知导入表RVA=0x2010
查询节表得到,该导入表处在第二个节内,且节内偏移为0x10 ,所以FOA=0x610
ps:以下所有RVA自动转为FOA
2:导入表结构
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME; 函数RVA表 INT表
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; 依赖的dll名 RVA
DWORD FirstThunk; IAT 表获得的函数指针RVA表
} IMAGE_IMPORT_DESCRIPTOR; 以全0结尾,不以数据目录项中的size
2.1:操作系统首先检查IAT项取内容是否为0,若为0则该导入表项无效,自动跳到下一个项
该导入表IAT项FOA=600 [600]=0x205c 不等于0 则该项有效,
首先 loadlibrary dllName FOA=kernel32
INT表 FOA=0x64c ,则INT表第一项为0x0000205c ,检查最高位是否为0,
本程序为字符串导出
若为0则是字符串导出
则将该项当作字符串RVA,FOA= 0x65c ExitProcess
为1则是序号导出
若是序号导出,则将表的第一项取低俩字节 为序号进行getprodess
最后将获得的函数地址填入RVA=0x2000处,
查找下一个INT表项,若为DWORD 0则说明没有下一个函数 否则继续进行GetProaddress
本程序只引用了一个函数,所以此时直接检查下一个导入表项是否为全0结尾
若不为结尾,则进行如上操作
3.OD bug
由于OD没有判断pe结构中的导入表项是否有效,而且在采用存储dllName时用的是定长数组,且最大长度为MAX_PATH
所以,当某项导入表为无效项,且dllname超过了MAx_PATH,由于操作系统认定该项为无效项,所以不会检查dll名长度,不会报错
而OD没有检查,且使用了定长数组,造成了溢出漏洞,轻则崩溃
4.构造无效项,
增加一个导入表项,选择修改数据目录项对应下标的RVA,重新填写导入表,避免了大量修改
此处选择在最后一个节存放dllname
OD调试挂
CFF 挂