文章

滴水-day26-导出表-重定位表

导出表

HINSTANCE hMoudle = LoadLibrary(L"Dll1.dll");
//这里hMoudle就是dll1.dll拉伸后在内存中的首地址。
pAdd padd = (pAdd)GetProcAddress(hMoudle, "_add@8");
//padd的结果是_add@8的地址,这个地址是jmp那个地址,不是直接到_add@8的地址。在由jmp跳转到真实函数地址

PS:其实hMoudle返回的就是文件拉伸后的内存首地址,也就是ImageBase

遍历导出表

bool PrintDataExport(LPCSTR filePath)
{

	if (!filePath)
	{
		perror("参数错误");
		return false;
	}
	// 读取文件到FileBuffer
	LPVOID fileBuffer = NULL;
	DWORD readFileResult = ReadFile(filePath, &fileBuffer);
	if (!readFileResult)
	{
		return false;
	}
	// 获取PE信息
	PEHeaders peheader;
	bool GetPEInfo = GetPeheadersInfo(fileBuffer, peheader);
	if (!GetPEInfo)
	{
		return false;
	}
	// 导出表位置
	// 通过PEHeaders获取导出表位置
	DWORD VA = 0;
	VA = peheader.dataHeaders->VirtualAddress;
	if (DEBUG)
	{
		std::cout << std::hex << "PEHeaders Data_Exports Address------>" << VA << std::endl;
	}
	// 通过OPTIONAL获取到导出表
	//std::cout << "Optional Data_Exports Address------->"  <<  peheader.optionalHeader->DataDirectory[0].VirtualAddress << std::endl;
	// RVA转FOV
	DWORD FOV = RvaToFov(VA, fileBuffer);
	if (DEBUG)
	{
		std::cout << std::hex << "FOV-----> " << FOV << std::endl;
	}
	// 导出表
	PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)fileBuffer + FOV);
	std::cout << "**********************Export Info*******************************" << std::endl;
	std::cout << "模块名称地址-------------> " << std::hex << std::uppercase << pExport->Name << std::endl;
	std::cout << "基址---------------------> " << pExport->Base << std::endl;
	std::cout << "函数数量-----------------> " << pExport->NumberOfFunctions << std::endl;
	std::cout << "函数名称数量-------------> " << pExport->NumberOfNames << std::endl;
	std::cout << "函数地址-----------------> " << std::hex << std::uppercase << pExport->AddressOfFunctions << std::endl;
	std::cout << "函数名称地址-------------> " << std::hex << std::uppercase << pExport->AddressOfNames << std::endl;
	std::cout << "函数名称序号地址---------> " << std::hex << std::uppercase << pExport->AddressOfNameOrdinals << std::endl;
	DWORD AddressNamesFov = RvaToFov(pExport->AddressOfNames, fileBuffer);
	DWORD NameFOV = RvaToFov(pExport->Name, fileBuffer);
	DWORD AddressFunction = RvaToFov(pExport->AddressOfFunctions, fileBuffer);
	DWORD AddressNameOrdinals = RvaToFov(pExport->AddressOfNameOrdinals, fileBuffer);
	std::cout << "名称:文件偏移----------> " << std::hex << std::uppercase<< NameFOV << std::endl;
	std::cout << "函数名称:文件偏移------> " << std::hex << std::uppercase << AddressNamesFov << std::endl;
	std::cout << "函数地址:文件偏移------> " << std::hex << std::uppercase << AddressFunction << std::endl;
	std::cout << "函数序号:文件偏移------> " << std::hex << std::uppercase << AddressNameOrdinals << std::endl;
	std::cout << "*****************************Function Info*************************************" << std::endl;

	printf("文件名称---->%s\\\\n", ((char*)fileBuffer + NameFOV));
	// 获取信息
	DWORD* pNameArray = (DWORD*)((BYTE*)fileBuffer + AddressNamesFov);
	DWORD* pFunctionAddress = (DWORD*)((BYTE*)fileBuffer + AddressFunction);
	WORD* pNameOrdinals = (WORD*)((BYTE*)fileBuffer + AddressNameOrdinals);
	for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
		std::cout << "********** Function:" << i << " ******************" << std::endl;
		DWORD functionNameFov = RvaToFov(pNameArray[i], fileBuffer);
		WORD functionOrdinal = pNameOrdinals[i];
		DWORD functionAddresss = pFunctionAddress[i];
		DWORD functionFOV = RvaToFov(pFunctionAddress[i], fileBuffer);
		std::cout << "Function FOV: " << std::hex << std::uppercase <<  functionFOV << std::endl;
		std::cout << "Function RVA: " << std::hex << std::uppercase << functionAddresss << std::endl;
		printf("Function Name: %s\\\\nFunction Ordinal: %d\\\\n", (char*)fileBuffer + functionNameFov, functionOrdinal);
	}
	return true;
}

注意事项:

  1. 通过PIMAGE_DATA_DIRECTORY获取到导出表位置

  2. 把RVA转为FOV后在文件中定位到,此时文件中保存的也是一个RVA,需要再次转为FOV才可以取到正确的值(函数地址,函数名称)

  3. AddressOfNameOrdinals 转为FOV后可以直接取值,这个地址保存的不在是RVA了,而是直接值

移动导出表

// 导出表信息
	DWORD exportAddressFOA = RvaToFov(newPehear.dataHeaders[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, newBuffer);
	PIMAGE_EXPORT_DIRECTORY pEXPORT = (PIMAGE_EXPORT_DIRECTORY)(newBuffer + exportAddressFOA);
	cout << "导出表位置:" <<  hex << exportAddressFOA << endl;

	// 新节起始位置
	DWORD* SectionAddress = (DWORD*)((BYTE*)newBuffer + readfileResu);
	cout << "节表起始位置FOA: " << hex << readfileResu << endl;

	// 复制 AddressOfFunctions 到新节
	memcpy(SectionAddress, newBuffer + RvaToFov(pEXPORT->AddressOfFunctions, newBuffer), pEXPORT->NumberOfFunctions * sizeof(DWORD));

	// 复制 AddressOfNameOrdinals 到新节
	WORD* Ordinals = (WORD*)((BYTE*)SectionAddress + pEXPORT->NumberOfFunctions * sizeof(DWORD));
	memcpy(Ordinals, newBuffer + RvaToFov(pEXPORT->AddressOfNameOrdinals, newBuffer), pEXPORT->NumberOfFunctions * sizeof(WORD));

	// 复制 AddressOfNames 到新节
	DWORD* NamesAddress = (DWORD*)((BYTE*)Ordinals + pEXPORT->NumberOfFunctions * sizeof(WORD));
	memcpy(NamesAddress, newBuffer + RvaToFov(pEXPORT->AddressOfNames, newBuffer), pEXPORT->NumberOfFunctions * sizeof(DWORD));

	// 复制名字到新节
	BYTE* nameData = (BYTE*)((BYTE*)NamesAddress + pEXPORT->NumberOfFunctions * sizeof(DWORD));
	for (size_t i = 0; i < pEXPORT->NumberOfFunctions; i++)
	{
		const char* functionName = (const char*)((BYTE*)newBuffer + RvaToFov(NamesAddress[i], newBuffer));
		size_t nameLength = strlen(functionName) + 1;
		memcpy(nameData, functionName, nameLength);
		// 直接更新NamesAddress地址
		NamesAddress[i] = FoaToRva((DWORD)(nameData - newBuffer), newBuffer);
		nameData += nameLength;
	}

	// 复制模块名称到新节中
	DWORD* Module = (DWORD*)(nameData + pEXPORT->NumberOfFunctions);
	memcpy(Module, newBuffer + RvaToFov(pEXPORT->Name, newBuffer), pEXPORT->NumberOfFunctions * sizeof(DWORD));

	// 复制导出表到新节
	PIMAGE_EXPORT_DIRECTORY NewExportAddress = (PIMAGE_EXPORT_DIRECTORY)(DWORD*)((BYTE*)nameData + pEXPORT->NumberOfFunctions * sizeof(DWORD));
	memcpy(NewExportAddress, pEXPORT, sizeof(IMAGE_EXPORT_DIRECTORY));

	// 更新 IMAGE_EXPORT+DIRECTORY 字段
	NewExportAddress->AddressOfFunctions = FoaToRva((DWORD)((BYTE*)SectionAddress - newBuffer), newBuffer);
	NewExportAddress->AddressOfNameOrdinals = FoaToRva((DWORD)((BYTE*)Ordinals - newBuffer), newBuffer);
	NewExportAddress->AddressOfNames = FoaToRva((DWORD)((BYTE*)NamesAddress - newBuffer), newBuffer);
	NewExportAddress->Name = FoaToRva((DWORD)((BYTE*)Module - newBuffer), newBuffer);

	// 更新 IMAGE_DIRECTORY_ENTRY_EXPORT 指向新表中的新结构
	newPehear.dataHeaders[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = newVirtualAddress + (DWORD)((BYTE*)NewExportAddress - (BYTE*)SectionAddress);

需要注意的就是先把名字字符串地址复制过去,再把名称字符串复制过来,复制完成一个就直接修改名称地址。

修改的是FOA转RVA,要把当前新的字符串名称地址复制给刚才复制过来的地方。

当前地址减去首地址就是FOA,在转为RVA就行了。

重定位表

// 计算需要修改的项数
DWORD numRelocations = (pRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
// 首地址
WORD* relocations = (WORD*)((BYTE*)pRelocation + sizeof(IMAGE_BASE_RELOCATION));

这两个计算正确的话剩下的就是遍历输出了。

移动重定位表

// 重定位表位置
PIMAGE_BASE_RELOCATION pBASE_RELOCATION = (PIMAGE_BASE_RELOCATION)&newPehear.optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
// 数据位置
PIMAGE_BASE_RELOCATION pBASE_DATA = (PIMAGE_BASE_RELOCATION)((BYTE*)newBuffer + RvaToFov(pBASE_RELOCATION->VirtualAddress, newBuffer));
DWORD* dataAddress = (DWORD*)((BYTE*)newBuffer + RvaToFov(pBASE_RELOCATION->VirtualAddress, newBuffer));
// 数量
DWORD DataNumer = 0;
while (pBASE_DATA->VirtualAddress != 0)
{
	DataNumer += pBASE_DATA->SizeOfBlock;
	pBASE_DATA = (PIMAGE_BASE_RELOCATION)((BYTE*)pBASE_DATA + pBASE_DATA->SizeOfBlock);
}
// 新节表位置
DWORD* newSectionAddress = (DWORD*)((BYTE*)newBuffer + readfileResu);
// 复制数据
memcpy(newSectionAddress, dataAddress, DataNumer);

// 修正目录项使其指向新的重定表位置
DWORD newAddress = FoaToRva(((BYTE*)newSectionAddress - newBuffer), newBuffer);
newPehear.optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = newAddress;
newPehear.optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = DataNumer;

这个没什么好说的,数据都是在一起的,复制的时候直接复制就完事了。

修改ImageBase,修正重定位表

    newPehear.optionalHeader->ImageBase += 0x1000;
	PIMAGE_BASE_RELOCATION pNewBASE_DATA = (PIMAGE_BASE_RELOCATION)((BYTE*)newBuffer + RvaToFov(pBASE_RELOCATION->VirtualAddress, newBuffer));

	while (pNewBASE_DATA->VirtualAddress != 0)
	{
		// 数据数量
		DWORD BaseNumer = (pNewBASE_DATA->SizeOfBlock - (sizeof(DWORD) * 2)) / sizeof(WORD);
		// 起始位置
		WORD* BaseAddress1 = (WORD*)((BYTE*)pNewBASE_DATA + sizeof(DWORD) * 2);

		for (size_t i = 0; i < BaseNumer; i++)
		{
			WORD typeOffset = *BaseAddress1;
			DWORD type = (typeOffset & 0xF000) >> 12;
			DWORD offset = typeOffset & 0x0FFF;

			if (type != IMAGE_REL_BASED_HIGHLOW)
			{
				break;
			}

			// 计算需要修正的相对虚拟地址
			DWORD rvaToPatch = pNewBASE_DATA->VirtualAddress + offset;

			// 将RVA转换为文件偏移(FOA)
			DWORD fileOffsetToPatch = RvaToFov(rvaToPatch, newBuffer);

			// 获取要修正的内存位置
			DWORD* patchLocation = (DWORD*)((BYTE*)newBuffer + fileOffsetToPatch);
			*patchLocation += 0x1000;
			BaseAddress1++;
		}

		// 移动到下一个重定位表
		pNewBASE_DATA = (PIMAGE_BASE_RELOCATION)((BYTE*)pNewBASE_DATA + pNewBASE_DATA->SizeOfBlock);
	}

和打印重定位表差不多

修改的是重定位表目录项RVA转为FOA后在文件中的位置,把这个位置的DWORD类型的地址加上新的ImageBase和旧的ImageBase的差值,就是修正后的地址了。

目录项本身的RVA和fileOffseet不需要修改。

License:  CC BY 4.0