这次模拟了系统在加载程序到内存时,程序的导入表中的FirstThunk被替换的过程。这里只演示了ImportByName的情形,仅供自娱自乐,233333。
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)
#pragma warning(disable: 4018)
DWORD CalcFileOffset(PIMAGE_SECTION_HEADER pSectionHeader, DWORD dwVirtualAddr);
BOOL IsPeFormat(UCHAR *data);
bool FindCorrespondingDll(UCHAR *pImportTable, UCHAR *data);
BOOL FindCorrespondingDllFunction(PIMAGE_IMPORT_DESCRIPTOR pImageDescriptor, UCHAR *data);
DWORD FindCorrespondingImportFunctionByName(char *importedFunctionName, char * DllName);
UCHAR *GetFileData(char *fileName);
DWORD GetExportTableRVA(UCHAR *data);
DWORD numberOfSections = 0;//区块表数目
BOOL IsPeFormat(UCHAR *data)//check 是否是pe文件格式
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)data;
PIMAGE_NT_HEADERS pFileHeader = NULL;
if (pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader+pDosHeader->e_lfanew);
if (pFileHeader->Signature == IMAGE_NT_SIGNATURE)//若这两个签名都满足 说明是PE格式文件
return TRUE;
}
return FALSE;
}
bool FindCorrespondingDll(UCHAR *pImportTable, UCHAR *data)//找到给定pe文件的依赖dll,即导入的各项的dll,然后再分发给子函数
{
PIMAGE_IMPORT_DESCRIPTOR pImageDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)pImportTable;//输入表以一个全0的PIMAGE_IMPORT_DESCRIPTOR作为结束条件
UCHAR *zeroBuf = (UCHAR *)malloc(sizeof(IMAGE_IMPORT_DESCRIPTOR));
memset(zeroBuf, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));
while (memcmp(pImageDescriptor, zeroBuf, sizeof(IMAGE_IMPORT_DESCRIPTOR)) != 0) {//当输入表描述符不为全0时
//每个输入表描述符描述一个DLL的信息,送入相应的处理函数处理每一个DLL
FindCorrespondingDllFunction(pImageDescriptor, data);//送入一个DLL描述符
pImageDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImageDescriptor + sizeof(IMAGE_IMPORT_DESCRIPTOR));//获取下一个输入表描述符
}
return TRUE;
}
BOOL FindCorrespondingDllFunction(PIMAGE_IMPORT_DESCRIPTOR pImageDescriptor, UCHAR *data)//用于获得每个DLL的相应的函数的函数名用于二进制比较 函数名都是ASCII字符串
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)data;
PIMAGE_NT_HEADERS pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)data + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pHeader = (PIMAGE_FILE_HEADER)(&pFileHeader->FileHeader);//获取fileHeader
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)(&pFileHeader->OptionalHeader);//获得可选头地址
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pFileHeader);//获取区块表地址
DWORD offsetOfDllName = CalcFileOffset(pSectionHeader, pImageDescriptor->Name);//计算Name的相应RVA
DWORD offsetOfFunctionName;
PIMAGE_THUNK_DATA pThunkAddr = (PIMAGE_THUNK_DATA) ((DWORD)data + CalcFileOffset(pSectionHeader, pImageDescriptor->OriginalFirstThunk));
PIMAGE_IMPORT_BY_NAME nameOffset;
UCHAR *zeroBuf = (UCHAR *)malloc(sizeof(IMAGE_THUNK_DATA));
DWORD MSB = 0;
printf("Import DLL Name :%s\n", data + offsetOfDllName);//打印DLL名
printf("ImportFunctionName\tFunctionExportedFromThisDll\tFunctionOrdinalInExportTable\tExportedFunctionAddress\n");
memset(zeroBuf, 0, sizeof(IMAGE_THUNK_DATA));
while (memcmp(zeroBuf, pThunkAddr, sizeof(IMAGE_THUNK_DATA)) != 0)
{
MSB = pThunkAddr->u1.AddressOfData & 0x80000000;
if (MSB) {
printf(" the funtion is imported by ordinals!\n");
printf(" Original_Ordinal : %x\t ordinal : %x\n", pThunkAddr->u1.AddressOfData, pThunkAddr->u1.Ordinal & 0x7FFFFFFF);
}
else {
offsetOfFunctionName = CalcFileOffset(pSectionHeader, pThunkAddr->u1.AddressOfData);
nameOffset = (PIMAGE_IMPORT_BY_NAME)((DWORD)data + offsetOfFunctionName);//获得IMAGE_IMPORT_BY_NAME结构
printf("%-18s\t", &nameOffset->Name);//name的RVA
FindCorrespondingImportFunctionByName((char *)&nameOffset->Name, (char *)(data + offsetOfDllName));//传入Name的RVA
}
pThunkAddr = (PIMAGE_THUNK_DATA)((DWORD)pThunkAddr + sizeof(IMAGE_THUNK_DATA));
}
return TRUE;
}
DWORD GetExportTableRVA(UCHAR *data)//返回导出表文件偏移
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)data;
PIMAGE_NT_HEADERS pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)data + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pHeader = (PIMAGE_FILE_HEADER)(&pFileHeader->FileHeader);//获取fileHeader
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)(&pFileHeader->OptionalHeader);//获得可选头地址
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pFileHeader);//获取区块表地址
DWORD dwExportTable = (DWORD)(pOptionalHeader->DataDirectory[0].VirtualAddress);//获得输出表RVA偏移
//printf("\nExportTableRVA:%x\n", dwExportTable);
DWORD dwFileOffsetOfExportTable = CalcFileOffset(pSectionHeader, dwExportTable);//获得导出表的文件偏移
return dwFileOffsetOfExportTable;
}
DWORD GetNameOrdinalInExportTable(UCHAR *data, DWORD dwFileOffset, char *importedFunctionName)//返回名字在AddressOfNames的位置
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)data;
PIMAGE_NT_HEADERS pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)data + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pFileHeader);//获取区块表地址
PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)data + dwFileOffset);//获得输出表
DWORD eachExportedFunctionAddr = ((DWORD)data + CalcFileOffset(pSectionHeader, pExportTable->AddressOfNames));//获得了AddressOfNames对应的文件地址
char *functionName = NULL;
int i;
for (i = 0; i < pExportTable->NumberOfNames; i++) {//pExportTable->NumberOfNames
functionName = (char *)((DWORD)data + CalcFileOffset(pSectionHeader, *(DWORD *)eachExportedFunctionAddr));
if (strlen(functionName) == strlen(importedFunctionName)) {
if (memcmp(functionName, importedFunctionName, strlen(importedFunctionName)) == 0) {
printf("%-27s\t", functionName);
return (DWORD)i;//返回序数值
}
}
eachExportedFunctionAddr += 4;//指向下一个RVA地址
}
return 0;
}
DWORD FindCorrespondingImportFunctionByName(char *importedFunctionName, char * dllName)//这里与相应的DLL的输出表进行关联 传入Name或者Ordinal 返回对应的输出表中的地址
{
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pFileHeader;
PIMAGE_SECTION_HEADER pSectionHeader;
UCHAR *data = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
DWORD dwAddrOfExportFunction= 0;
WORD dwIndexOfOrdinal;
DWORD dwIndexOfNames;
DWORD dwFileOffsetOfExportTable = 0;
DWORD nameOrdinalAddr = 0;
DWORD ExportedAddressTable = 0;
PIMAGE_EXPORT_DIRECTORY pExportTable;//输出表描述符
int i;
char *fileName = (char *)malloc(1024 * sizeof(UCHAR));
sprintf(fileName, "F:\\桌面\\pdf包\\加密与解密第四版随书源码\\PEDIY_BOOK4\\chap11\\11.1~11.6 实例\\PE32\\%s", dllName);
//printf("fileName : %s\n", fileName);
data = GetFileData(fileName);//获取输出表DLL
pDosHeader = (PIMAGE_DOS_HEADER)data;
pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)data + pDosHeader->e_lfanew);
pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pFileHeader);//获取区块表地址
dwFileOffsetOfExportTable = GetExportTableRVA(data);
dwIndexOfNames = GetNameOrdinalInExportTable(data, dwFileOffsetOfExportTable,importedFunctionName);//找到Name对应的序数值
//从NameOrdinal中找到Name映射的EAT的index
pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)data + dwFileOffsetOfExportTable);//获取输出表地址
//printf(" dwIndexOfNames : %d\tpExportTable->AddressOfNameOrdinals : %x\n", dwIndexOfNames, pExportTable->AddressOfNameOrdinals);
nameOrdinalAddr = ((DWORD)data + CalcFileOffset(pSectionHeader, pExportTable->AddressOfNameOrdinals));//获得了NameOrdinals的地址
dwIndexOfOrdinal = *(WORD *)(nameOrdinalAddr + dwIndexOfNames * sizeof(WORD));//获取了在EAT中的index
printf("0x%-28X\t", dwIndexOfOrdinal + pExportTable->Base);//注意 index这里为WORD大小
//找到EAT对应的地址
ExportedAddressTable = ((DWORD)data + CalcFileOffset(pSectionHeader, pExportTable->AddressOfFunctions));//获得了EAT的地址
dwAddrOfExportFunction = *(DWORD *)(ExportedAddressTable + dwIndexOfOrdinal * sizeof(DWORD));//获得了对应的函数RVA
printf("0x%-23X\n", dwAddrOfExportFunction);
return dwAddrOfExportFunction;
}
DWORD CalcFileOffset(PIMAGE_SECTION_HEADER pSectionHeader, DWORD dwVirtualAddr)//传入一个要求的RVA地址和节区表地址
{
DWORD offset = 0;//最终返回的文件偏移
DWORD indexOfSection = 0;//所在的第几个块
DWORD min = 1 << 31;//记录最接近的块
int i;
//printf("Section Table :\n");
for (i = 0; i < numberOfSections; i++) {//遍历找到index
//printf("VirtualAddress: %x\tdwVirtualAddr:%x\tRawData:%x\n", pSectionHeader->VirtualAddress, dwVirtualAddr, pSectionHeader->PointerToRawData);
if (pSectionHeader->VirtualAddress < dwVirtualAddr && (dwVirtualAddr - pSectionHeader->VirtualAddress) < min) {
min = dwVirtualAddr - pSectionHeader->VirtualAddress;//更新偏移
indexOfSection = i;//获取所在块位置
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + sizeof(IMAGE_SECTION_HEADER));
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader - (sizeof(IMAGE_SECTION_HEADER) * numberOfSections) + (sizeof(IMAGE_SECTION_HEADER) * indexOfSection));
offset = dwVirtualAddr - (pSectionHeader->VirtualAddress - pSectionHeader->PointerToRawData);
//printf("offset: %x\n", offset);
return offset;
}
DWORD FindImportTableRVA(UCHAR *data)//返回导出表的地址
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)data;
PIMAGE_NT_HEADERS pFileHeader = (PIMAGE_NT_HEADERS)((DWORD)data + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pHeader = (PIMAGE_FILE_HEADER)(&pFileHeader->FileHeader);//获取fileHeader
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)(&pFileHeader->OptionalHeader);//获得可选头地址
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pFileHeader);//获取区块表地址
DWORD dwImportTable = (DWORD)(pOptionalHeader->DataDirectory[1].VirtualAddress);//获得输入表RVA偏移
DWORD dwFileOffsetOfImportTable = 0;
numberOfSections = pHeader->NumberOfSections;//获取区块数
//接下来需要获取区块表信息以计算文件偏移
dwFileOffsetOfImportTable = CalcFileOffset(pSectionHeader, dwImportTable);
if (FindCorrespondingDll( (UCHAR *)data + dwFileOffsetOfImportTable, data)) {
printf("\nsucceed in finding DLL\n");
}
else {
printf("Something Wrong in Finding DLL");
exit(0);
}
return 0;
}
UCHAR *GetFileData(char *fileName)
{
FILE *fp = NULL;
DWORD fileLen = 0;
UCHAR *data = NULL;
DWORD numOfItem = 0;
fp = fopen(fileName, "rb");
if (NULL == fp) {
printf("error in open file! %d \n", GetLastError());
exit(0);
}
fseek(fp, 0, SEEK_END);//定位到文件尾
fileLen = ftell(fp) + 1;//获得长度
fseek(fp, 0, SEEK_SET);
data = (UCHAR *)malloc(sizeof(UCHAR) * fileLen);
memset(data, 0, fileLen * sizeof(UCHAR));
if ((numOfItem = fread(data, fileLen * sizeof(UCHAR) - 1, 1, fp)) != 1) {
printf("Error in read file! %d %d \n", GetLastError(), numOfItem);
fclose(fp);
exit(0);
}
fclose(fp);
return data;
}
int main()
{
UCHAR *data = NULL;//接收数据的缓冲区
char * fileName = "F:\\桌面\\pdf包\\加密与解密第四版随书源码\\PEDIY_BOOK4\\chap11\\11.1~11.6 实例\\PE32\\PE.exe";
data = GetFileData(fileName);//获取文件名
if (IsPeFormat(data)) {
FindImportTableRVA(data);
}
else {
printf("Not PE format!\n");
}
return 0;
}