滴水-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;
}
注意事项:
通过PIMAGE_DATA_DIRECTORY获取到导出表位置
把RVA转为FOV后在文件中定位到,此时文件中保存的也是一个RVA,需要再次转为FOV才可以取到正确的值(函数地址,函数名称)
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