模拟Loader装载输入函数的RVA地址至FirstThunk

这次模拟了系统在加载程序到内存时,程序的导入表中的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;
}

猜你喜欢

转载自blog.csdn.net/qq_40890756/article/details/90056537