回顾一下,在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的。而输入表是以一个 IMAGE_IMPORT_DESCRIPTOR(简称IID) 的数组开始。每个被 PE文件链接进来的 DLL文件都分别对应一个 IID数组结构。在这个 IID数组中,并没有指出有多少个项(就是没有明确指明有多少个链接文件),但它最后是以一个全为NULL(0) 的 IID 作为结束的标志。
IMAGE_IMPORT_DESCRIPTOR 结构定义如下:
IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics DWORD ?
OriginalFirstThunk DWORD ?
ends
TimeDateStamp DWORD ?
ForwarderChain DWORD ?
Name DWORD ?
FirstThunk DWORD ?
IMAGE_IMPORT_DESCRIPTOR ENDS
它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function name 的地址。
该字段可以忽略。如果那里有绑定的话它包含时间/数据戳(time/data stamp)。如果它是0,就没有绑定在被导入的DLL中发生。在最近,它被设置为0xFFFFFFFF以表示绑定发生。
一般情况下我们也可以忽略该字段。在老版的绑定中,它引用API的第一个forwarder chain(传递器链表)。它可被设置为0xFFFFFFFF以代表没有forwarder。
它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称,如:KERNEL32.DLL)。
它包含由IMAGE_THUNK_DATA定义的 first thunk数组的虚地址,通过loader用函数虚地址初始化thunk。在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。
这个OriginalFirstThunk 和 FirstThunk明显是亲家,两家伙首先名字就差不多哈。那他们有什么不可告人的秘密呢?来,我们看下面一张图(画的很辛苦,大家仔细看哈):
PIMAGE_THUNK_DATA SrcImageThunkData = (PIMAGE_THUNK_DATA) PINT; if ( ! (SrcImageThunkData[i].u1.Ordinal & 0x80000000))
#include <stdio.h> #include <windows.h> #include <tchar.h> #include <winnt.h> const TCHAR* FileName = L"d:\\PING.exe"; //文件目录 const TCHAR* NewFileName = L"d:\\PING2.exe"; //复制的文件目录 HANDLE hFile; //文件句柄 BYTE* pBase = NULL; //基地址 int NumberOfSections; //区块表的个数 DWORD IMPORT_RVA = NULL; //输入表的RVA DWORD IMPORT_OFFSET = NULL; //输入表的offset DWORD IN_IMPORT_RVA = NULL; //输入表所在的区块的起始RVA DWORD IN_IMPORT_OFFSET = NULL; //输入表所在区块的物理起始地址 BYTE* fnReadFile(); //读取文件 int fnReadPE (BYTE *pBuffer); //读取PE文件信息 BYTE* fnReadDosHeader(BYTE* pBuffer); //读取dos文件头信息 int fnReadNTHeader(BYTE* pBuffer); //读取 PE文件头信息 int fnReadFileHeader(BYTE* ImageFileHeader); //读取PE文件头中的FileHeader结构的信息 int fnReadOptionalHeader(BYTE* ImageOptionalHeader); //读取PE文件头中的OptionalHeader结构的信息 int fnReadSectionHeader(BYTE* ImageSectionHeader); //读取区块表 int fnReadImportTable(BYTE* ImportTable); //读取输入表 int fnReadDllName(BYTE* PDllName); //读取DLL名称 int fnReadINT(BYTE* PINT); //读取INT int fnReadFunctionName(BYTE* PFunctionName); //读取函数名称 //int fnAddSectionTable(LONG offset); //添加PE区块表 int Copy(); //复制文件,防止文件被破坏 IMAGE_SECTION_HEADER Own_Section_Header; /* Own_Section_Header.Name[Own_Section_Header] = ".carl"; Own_Section_Header.VirtualAddress = 9A010000 ; // 节区的 RVA 地址 Own_Section_Header.SizeOfRawData =00100000 ; // 在文件中对齐后的尺寸 Own_Section_Header.PointerToRawData = 00020000; // 在文件中的偏移量 Own_Section_Header.PointerToRelocations = 00040000; // 在OBJ文件中使用,重定位的偏移 Own_Section_Header.PointerToLinenumbers = 00000000; // 行号表的偏移(供调试使用地) Own_Section_Header.NumberOfRelocations =0000; // 在OBJ文件中使用,重定位项数目 Own_Section_Header.NumberOfLinenumbers = 0000; // 行号表中行号的数目 Own_Section_Header.Characteristics = 20000060; // 节属性如可读,可写,可执行等 */ int main() { Copy(); BYTE* pBuffer = fnReadFile(); fnReadPE (pBuffer); return 0; } BYTE* fnReadFile() { printf("映射文件...\n"); hFile = CreateFile(NewFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //读取文件 if (hFile == INVALID_HANDLE_VALUE) { printf("打开文件出错\n"); return 0; } DWORD dwFileSize = GetFileSize(hFile, NULL); //获得文件大小 BYTE* pBuffer = new BYTE[dwFileSize]; //new相当于malloc DWORD dwSizeOfRead; if(!ReadFile(hFile, pBuffer, dwFileSize, &dwSizeOfRead, NULL)) { printf("读取文件出错\n"); pBase = NULL; return 0; } pBase = pBuffer; return pBase; //基地址 } int fnReadPE(BYTE *pBuffer) { printf("读取PE信息...\n"); BYTE* pBaseNTHeader = fnReadDosHeader(pBuffer); //调用fnReadDosHeader函数获取Dos头信息,并且获得PE文件头的基地址 if (!pBaseNTHeader) { return 1; } if (!fnReadNTHeader(pBaseNTHeader)) //读取PE文件头信息 { return 1; } return 0; } BYTE* fnReadDosHeader(BYTE* pBuffer) //读取dos文件头信息 { printf("读取DOS文件头...\n"); PIMAGE_DOS_HEADER SrcImageDosHeader = (PIMAGE_DOS_HEADER)pBuffer; if (SrcImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return 0; } else { printf("IMAGE_DOS_HEADER结构\n"); printf("e_magic :%04x DOS可执行文件标记\n",SrcImageDosHeader->e_magic); printf("e_cblp :%04x Bytes on last page of file\n",SrcImageDosHeader->e_cblp); printf("e_cp :%04x Page in file\n", SrcImageDosHeader->e_cp); printf("e_crlc :%04x Relocations\n", SrcImageDosHeader->e_crlc); printf("e_cparhdr :%04x Size of header in paragraphs\n", SrcImageDosHeader->e_cparhdr); printf("e_minalloc:%04x Minimum extra paragraphs needed\n", SrcImageDosHeader->e_minalloc); printf("e_maxalloc:%04x Maximum extra paragraphs needed\n", SrcImageDosHeader->e_maxalloc); printf("e_ss :%04x Initial (relative) SS value DOS代码的初始化堆栈SS\n", SrcImageDosHeader->e_ss); printf("e_sp :%04x Initial SP value DOS代码的初始化堆栈指针SP\n", SrcImageDosHeader->e_sp); printf("e_csum :%04x Checksum\n", SrcImageDosHeader->e_csum); printf("e_ip :%04x Initial IP value DOS代码的初始化指针入口\n", SrcImageDosHeader->e_ip); printf("e_cs :%04x Initial (relative) CS value DOS代码的初始堆栈入口\n", SrcImageDosHeader->e_cs); printf("e_lfarlc :%04x File address of elocation table\n", SrcImageDosHeader->e_lfarlc); printf("e_ovno :%04x Overlay number\n", SrcImageDosHeader->e_ovno); printf("e_res[4] :%04x, %04x, %04x, %04x Reserved words\n", SrcImageDosHeader->e_res[0],SrcImageDosHeader->e_res[1],SrcImageDosHeader->e_res[2],SrcImageDosHeader->e_res[3]); printf("e_oemid :%04x OEM identifier (for e_oeminfo)\n", SrcImageDosHeader->e_oemid); printf("e_oeminfo :%04x OEM information; e_oemid specific\n", SrcImageDosHeader->e_oeminfo); printf("e_res2[10]:%04x,%04x,%04x,%04x,%04x,%04x,%04x,%04x,%04x,%04x, Reserved words\n", SrcImageDosHeader->e_res2[0],SrcImageDosHeader->e_res2[1],SrcImageDosHeader->e_res2[2],SrcImageDosHeader->e_res2[3],SrcImageDosHeader->e_res2[4],SrcImageDosHeader->e_res2[5],SrcImageDosHeader->e_res2[6],SrcImageDosHeader->e_res2[7],SrcImageDosHeader->e_res2[8],SrcImageDosHeader->e_res2[9]); printf("e_lfanew :%04x File address of new exe header 指向PE文件头\n", SrcImageDosHeader->e_lfanew); } LONG test = (LONG) (SrcImageDosHeader->e_lfanew); //fnAddSectionTable(test);//PE文件头的开始位置 return (BYTE*) (pBuffer + SrcImageDosHeader->e_lfanew); } int fnReadNTHeader(BYTE* pBaseNTBuffer) { printf("读取PE文件头...\n"); PIMAGE_NT_HEADERS SrcImageNTHeader = (PIMAGE_NT_HEADERS)(pBaseNTBuffer); if (SrcImageNTHeader->Signature != IMAGE_NT_SIGNATURE) { return 1; } else { printf("IMAGE_NT_HEADERS结构\n"); printf("Signature :%08x\n", SrcImageNTHeader->Signature); fnReadFileHeader((BYTE*)&SrcImageNTHeader->FileHeader); //读取FileHeader结构的信息 fnReadOptionalHeader((BYTE*) &SrcImageNTHeader->OptionalHeader); //读取OptionalHeader结构的信息 fnReadSectionHeader((BYTE*)IMAGE_FIRST_SECTION(pBaseNTBuffer)); //读取区块表 fnReadImportTable((BYTE*) (pBase+IMPORT_OFFSET)); } return 0; } int fnReadFileHeader(BYTE* ImageFileHeader) { PIMAGE_FILE_HEADER SrcImageFileHeader = (PIMAGE_FILE_HEADER)ImageFileHeader; printf("IMAGE_FILE_HEADERS结构\n"); printf("Machine: %04x\n", SrcImageFileHeader->Machine); printf("NumberOfSections: %04x\n", SrcImageFileHeader->NumberOfSections); printf("TimeDateStamp: %08x\n", SrcImageFileHeader->TimeDateStamp); printf("PointerToSymbolTable: %08x\n", SrcImageFileHeader->PointerToSymbolTable); printf("NumberOfSymbols: %08x\n", SrcImageFileHeader->NumberOfSymbols); printf("SizeOfOptionalHeader: %04x\n", SrcImageFileHeader->SizeOfOptionalHeader); printf("Characteristics: %04x\n", SrcImageFileHeader->Characteristics); NumberOfSections = SrcImageFileHeader->NumberOfSections; return 0; } int fnReadOptionalHeader(BYTE* ImageOptionalHeader) { PIMAGE_OPTIONAL_HEADER SrcImageOptional_Header = (PIMAGE_OPTIONAL_HEADER)ImageOptionalHeader; printf("IMAGE_OPTIONAL_HEADER结构\n"); // Standard fields. printf("Magic: %04x\n", SrcImageOptional_Header->Magic); printf("MajorLinkerVersion: %02x\n", SrcImageOptional_Header->MajorLinkerVersion); printf("MinorLinkerVersion: %02x\n", SrcImageOptional_Header->MinorLinkerVersion); printf("SizeOfCode: %08x\n", SrcImageOptional_Header->SizeOfCode); printf("SizeOfInitializedData: %08x\n", SrcImageOptional_Header->SizeOfInitializedData); printf("SizeOfUninitializedData: %08x\n", SrcImageOptional_Header->SizeOfUninitializedData); printf("AddressOfEntryPoint: %08x\n", SrcImageOptional_Header->AddressOfEntryPoint); printf("BaseOfCode: %08x\n", SrcImageOptional_Header->BaseOfCode); printf("BaseOfData: %08x\n", SrcImageOptional_Header->BaseOfData); // NT additional fields. printf("ImageBase: %08x\n", SrcImageOptional_Header->ImageBase); printf("SectionAlignment: %08x\n", SrcImageOptional_Header->SectionAlignment); printf("FileAlignmen: %08x\n", SrcImageOptional_Header->FileAlignment); printf("MajorOperatingSystemVersion:%04x\n", SrcImageOptional_Header->MajorOperatingSystemVersion); printf("MinorOperatingSystemVersion:%04x\n", SrcImageOptional_Header->MinorOperatingSystemVersion); printf("MajorImageVersion; %04x\n", SrcImageOptional_Header->MajorImageVersion); printf("MinorImageVersion: %04x\n", SrcImageOptional_Header->MinorImageVersion); printf("MajorSubsystemVersion: %04x\n", SrcImageOptional_Header->MajorSubsystemVersion); printf("MinorSubsystemVersion: %04x\n", SrcImageOptional_Header->MinorSubsystemVersion); printf("Win32VersionValue: %08x\n", SrcImageOptional_Header->Win32VersionValue); printf("SizeOfImage: %08x\n", SrcImageOptional_Header->SizeOfImage); printf("SizeOfHeaders: %08x\n", SrcImageOptional_Header->SizeOfHeaders); printf("CheckSum: %08x\n", SrcImageOptional_Header->CheckSum); printf("Subsystem: %04x\n", SrcImageOptional_Header->Subsystem); printf("DllCharacteristics: %04x\n", SrcImageOptional_Header->DllCharacteristics); printf("SizeOfStackReserve: %08x\n", SrcImageOptional_Header->SizeOfStackReserve); printf("SizeOfStackCommit: %08x\n", SrcImageOptional_Header->SizeOfStackCommit); printf("SizeOfHeapReserve: %08x\n", SrcImageOptional_Header->SizeOfHeapCommit); printf("SizeOfHeapCommit: %08x\n", SrcImageOptional_Header->SizeOfHeapCommit); printf("LoaderFlags: %08x\n", SrcImageOptional_Header->LoaderFlags); printf("NumberOfRvaAndSizes: %08x\n", SrcImageOptional_Header->NumberOfRvaAndSizes); //获得数据目录表中输入表的大小和RVA地址 printf("IMAGE_DIRECTORY_ENTRY_EXPOET Size: %08x\n", SrcImageOptional_Header->DataDirectory[1].Size); printf("IMAGE_DIRECTORY_ENTRY_EXPOET VirtualAddress(RVA): %08x\n", SrcImageOptional_Header->DataDirectory[1].VirtualAddress); IMPORT_RVA = SrcImageOptional_Header->DataDirectory[1].VirtualAddress; return 0; } int fnReadSectionHeader(BYTE* ImageSectionHeader){ int i = 0; int NUMBER_OF_RVA = NULL; //获得输入表所在的区块位置 PIMAGE_SECTION_HEADER SrcImageSectionHeader = (PIMAGE_SECTION_HEADER) ImageSectionHeader; //数组名可以看成是指针,所以下面可以用数组的方式来表示相当于 (i+1) *sizeof(PIMAGE_SECTION_HEADER) printf("IMAGE_SECTION_HEADER结构\n"); for (i = 0; i < NumberOfSections; i++) { char SectionName[9]; memset(SectionName, 0, sizeof(SectionName)); memcpy(SectionName, SrcImageSectionHeader[i].Name, 8); printf("name:%s\n", SectionName); printf("Misc: %08x\n", SrcImageSectionHeader[i].Misc); printf("VirtualAddres: %08x\n", SrcImageSectionHeader[i].VirtualAddress); printf("SizeOfRawData: %08x\n", SrcImageSectionHeader[i].SizeOfRawData); printf("PointerToRawData: %08x\n", SrcImageSectionHeader[i].PointerToRawData); printf("PointerToRelocations:%08x\n", SrcImageSectionHeader[i].PointerToRelocations); printf("PointerToLinenumbers:%08x\n", SrcImageSectionHeader[i].PointerToLinenumbers); printf("NumberOfRelocations: %04x\n", SrcImageSectionHeader[i].NumberOfRelocations); printf("NumberOfLinenumbers: %04x\n", SrcImageSectionHeader[i].NumberOfLinenumbers); printf("Characteristics: %08x\n", SrcImageSectionHeader[i].Characteristics); if (i < NumberOfSections - 1) if (IMPORT_RVA >= SrcImageSectionHeader[i].VirtualAddress && IMPORT_RVA < SrcImageSectionHeader[i+1].VirtualAddress) NUMBER_OF_RVA = i; } printf("输入表所在的区块是在第%d个区块\n", NUMBER_OF_RVA+1); IMPORT_OFFSET = IMPORT_RVA - (SrcImageSectionHeader[NUMBER_OF_RVA].VirtualAddress - SrcImageSectionHeader[NUMBER_OF_RVA].PointerToRawData); //printf("%08x\n", SrcImageSectionHeader[NUMBER_OF_RVA].VirtualAddress); //printf("%08x\n", SrcImageSectionHeader[NUMBER_OF_RVA].PointerToRawData); IN_IMPORT_RVA = SrcImageSectionHeader[NUMBER_OF_RVA].VirtualAddress; //输入表所在的区块的起始RVA IN_IMPORT_OFFSET = SrcImageSectionHeader[NUMBER_OF_RVA].PointerToRawData; //输入表所在区块的物理起始地址 //printf("输入表的RVA是%08x\n", IMPORT_RVA); //printf("输入表的offset是%08x\n", IMPORT_OFFSET); return 0; } int fnReadImportTable(BYTE* ImportTable) { PIMAGE_IMPORT_DESCRIPTOR SrcImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImportTable; printf("输入表内容.....\n"); for (int i = 0; SrcImageImportDescriptor[i].OriginalFirstThunk != 0; i++) //IID最后一个结构如果全部为0标志着结束 { printf("OriginalFirstThunk is %08x\n",SrcImageImportDescriptor[i].OriginalFirstThunk); printf("TimeDataStamp is %08x\n", SrcImageImportDescriptor[i].TimeDateStamp); printf("ForwarderChain is %08x\n",SrcImageImportDescriptor[i].ForwarderChain); printf("Name is %08x\n", SrcImageImportDescriptor[i].Name); printf("FirstChunk is %08x\n ",SrcImageImportDescriptor[i].FirstThunk); printf("\n"); fnReadDllName((BYTE*)(SrcImageImportDescriptor[i].Name - IN_IMPORT_RVA + IN_IMPORT_OFFSET + pBase)); fnReadINT((BYTE*) (SrcImageImportDescriptor[i].OriginalFirstThunk - IN_IMPORT_RVA + IN_IMPORT_OFFSET + pBase) ); //BYTE * SrcImageThunkData = SrcImageImportDescriptor[i].OriginalFirstThunk; //PIMAGE_THUNK_DATA SrcImageThunkData = } return 0; } int fnReadDllName(BYTE* PDllName) { char* Dll; Dll = (char*)PDllName; printf("Dll name is %s\n", Dll); printf("\n"); return 0; } int fnReadINT(BYTE* PINT) { PIMAGE_THUNK_DATA SrcImageThunkData = (PIMAGE_THUNK_DATA) PINT; for (int i = 0; SrcImageThunkData[i].u1.AddressOfData != 0; i++) { //printf("IMAGE_THUNK_DATA INT is %08x\n", SrcImageThunkData[i].u1); PIMAGE_IMPORT_BY_NAME SrcImageImportByName = (PIMAGE_IMPORT_BY_NAME)(SrcImageThunkData[i].u1.AddressOfData - IN_IMPORT_RVA + IN_IMPORT_OFFSET + pBase); if ( ! (SrcImageThunkData[i].u1.Ordinal & 0x80000000)) //这里的8是16进制的,转换为2进制就是1000 { char* FunctionName; FunctionName = (char*)(SrcImageImportByName) + sizeof(char) * 2; printf("Function name is %s\n", FunctionName); printf("\n"); } /* else { printf("%08x\n",SrcImageThunkData[i].u1.Ordinal & 0x80000000); } */ } //fnReadFunctionName((BYTE*) (SrcImageThunkData->u1.AddressOfData - IN_IMPORT_RVA + IN_IMPORT_OFFSET + pBase) ); return 0; } int fnReadFunctionName(BYTE* PFunctionName) { char* FunctionName; FunctionName = (char*)PFunctionName; printf("Function name is %s\n", FunctionName); printf("\n"); return 0; } /* int fnAddSectionTable(LONG offset) { DWORD dwSizeOfWrite; DWORD add = 0x6C726163; LPDWORD lpNumberOfBytesWritten = NULL; PIMAGE_SECTION_HEADER p = &Own_Section_Header; //BYTE* ChangeAddress = ImageSectionTable - 2 *sizeof(BYTE); //BYTE* lpWriteBuffer = ImageSectionTable - 8 * sizeof(BYTE); //Own_Section_Header.Name[Own_Section_Header] = ".carl"; //Own_Section_Header.VirtualAddress = 9A010000 ; // 节区的 RVA 地址 Own_Section_Header.SizeOfRawData =00100000 ; // 在文件中对齐后的尺寸 Own_Section_Header.PointerToRawData = 00020000; // 在文件中的偏移量 Own_Section_Header.PointerToRelocations = 00040000; // 在OBJ文件中使用,重定位的偏移 Own_Section_Header.PointerToLinenumbers = 00000000; // 行号表的偏移(供调试使用地) Own_Section_Header.NumberOfRelocations =0000; // 在OBJ文件中使用,重定位项数目 Own_Section_Header.NumberOfLinenumbers = 0000; // 行号表中行号的数目 Own_Section_Header.Characteristics = 20000060; // 节属性如可读,可写,可执行等 SetFilePointer( hFile, offset, NULL, FILE_BEGIN ); if (WriteFile(hFile, p, 1 * sizeof(Own_Section_Header), &dwSizeOfWrite, NULL )) printf("正确写入文件\n"); else printf("写入文件错误\n"); return 0; } */ int Copy() { BOOL Flag = TRUE; if(CopyFile(FileName, NewFileName, Flag)) printf("成功复制文件\n"); else printf("复制文件失败\n"); return 0; }