紧接着上一篇的千里追踪输入表,现在终于可以开始看输入表里面的神奇东东了。。。
输入表结构
回顾一下,在 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
成员介绍:
OriginalFirstThunk
它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function name 的地址。
TimeDateStamp
该字段可以忽略。如果那里有绑定的话它包含时间/数据戳(time/data stamp)。如果它是0,就没有绑定在被导入的DLL中发生。在最近,它被设置为0xFFFFFFFF以表示绑定发生。
ForwarderChain
一般情况下我们也可以忽略该字段。在老版的绑定中,它引用API的第一个forwarder chain(传递器链表)。它可被设置为0xFFFFFFFF以代表没有forwarder。
Name
它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称,如:KERNEL32.DLL)。
FirstThunk
它包含由IMAGE_THUNK_DATA定义的 first thunk数组的虚地址,通过loader用函数虚地址初始化thunk。在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。
这个OriginalFirstThunk 和 FirstThunk明显是亲家,两家伙首先名字就差不多哈。那他们有什么不可告人的秘密呢?来,我们看下面一张图(画的很辛苦,大家仔细看哈):
输入地址表(IAT)
PIMAGE_THUNK_DATA SrcImageThunkData = (PIMAGE_THUNK_DATA) PINT;
if ( ! (SrcImageThunkData[i].u1.Ordinal & 0x80000000))
注意一下这里的 0x80000000是十六进制的,0x8转换成二进制就是1000 这样最高位就是1了
通过这个if条件的判断,就可以知道联合体的成员是ForwarderString 还是Ordinal
好吧,下面把我自己写的代码贴上来,虽然说有点乱,只是当做自己的笔记。大家可以不看的。因为代码写的很不工整。。。。
#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;
}