导出表中最重要的是三张表AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals,分别存储了函数地址,函数名称,函数序号,前文实现了按照函数名称查找以及按照序号查找

导出函数名称表的移动是一个难点,导出函数名称表中存储的导出函数名称偏移地址RVA,需要根据地址再进行访问函数名称。整体步骤如下:
AddressOfFunction(size:NumberOfFunctions * 4)到新增的节AddressOfNameOrdinals(size:NumberOfNames * 2)到新增的节中AddressOfNames(szie:NumberOfNames * 4)到新增的节中AddressOfNames表中对应的所有地址,及其函数名,移动字符串时也需要注意大小\0结尾(每复制一个函数名就要计算偏移RVA添加到函数名称表的地址中)AddressOfFunctions、AddressOfNameOrdinals、AddressOfNames地址,其他的参数不影响运行VirtualAddress指向新的导出表的地址
自己编写一个最简单的DLL文件
xxxxxxxxxx
int __stdcall Plus(int x, int y) { return x + y;}xxxxxxxxxxextern "C" _declspec(dllexport) int __stdcall Plus(int x,int y);生成DLL后进行测试
xxxxxxxxxxint main(){ LPSTR SRC = (LPSTR)"TestDLL.dll"; LPSTR DEST = (LPSTR)"TestDLL1.dll";
LPVOID SRCBuffer = NULL; DWORD size = ReadPEFile(SRC, &SRCBuffer); LPVOID DESTBuffer = NULL; DWORD newSize = MoveExportTableToNewSection(SRCBuffer,&DESTBuffer,size);
MemeryToFile(DESTBuffer, newSize, DEST);
typedef int(__stdcall* lpPlus)(int, int); lpPlus myPlus = NULL; // 测试两个DLL HINSTANCE hModule = LoadLibrary(L"TestDLL1.dll"); myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8"); if (!myPlus) { printf("出现错误\n"); return 0; } int a = 0; a = myPlus(10, 2); printf("10+2=%d\n", a); printf("导出函数可以使用,说明移动导出表成功\n"); getchar();
return 0;}发现新DLL可以正常运行

之前的文章中提到过新增节的内容,这里重新写一下相关的内容
定义一个新增节的函数,第一个参数是原文件指针;第二个参数是修改后的文件指针,一个LPVOID指针;第三个参数是原文件的大小,最后一个参数是新增节需要的大小
xxxxxxxxxxDWORD AddSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize);复制一份原来的文件
xxxxxxxxxxLPVOID pTempFileBuffer = malloc(dwFileBufferSize);if (!pTempFileBuffer){ return 0, 0, 0, 0;}memcpy(pTempFileBuffer, pFileBuffer, dwFileBufferSize);pFileBuffer = pTempFileBuffer;解析头部
xxxxxxxxxxPIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);拿到最后一节和新节的节表结构体指针
xxxxxxxxxxDWORD pNumberOfSections = pPEHeader->NumberOfSections; PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + pNumberOfSections;之前提到过只有节表剩余大小大于80字节才可以新增,因为一个节表大小为40,需要流出零一份全0节表来表示节表完全结束,因此一共需要80字节大小,当不足80字节可以整体上提PE头部
xxxxxxxxxxif (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader){ printf("没有足够的80字节插入新节表\n"); free(pTempFileBuffer); return 0;}构造新节表的结构体,节表的Name要求是8个字节长度
xxxxxxxxxxIMAGE_SECTION_HEADER newSectionHeader;memcpy(newSectionHeader.Name, ".newsec", 8);新节的对齐前大小按照内存对齐来对齐,认为所有部分都是有效内存
xxxxxxxxxxnewSectionHeader.Misc.VirtualSize = AlignLength(dwNewSectionSize, pOptionHeader->SectionAlignment);新节的VA是上一节的VA加上一节自身的大小(确保对齐)
xxxxxxxxxxnewSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + AlignLength(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);新节的SizeOFRawData按照文件对齐来对齐
xxxxxxxxxxnewSectionHeader.SizeOfRawData = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);新节的PointerToRawData按照上一节的PointerToRawData加SizeOfRawData对齐
xxxxxxxxxxnewSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;其他属性不重要
xxxxxxxxxxnewSectionHeader.PointerToRelocations = 0;newSectionHeader.PointerToLinenumbers = 0;newSectionHeader.NumberOfRelocations = 0;newSectionHeader.NumberOfLinenumbers = 0;newSectionHeader.Characteristics = 0x60000020;给新增节分配内存并初始化
xxxxxxxxxx*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData);刷新指针
xxxxxxxxxxpDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = pPEHeader->NumberOfSections;pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;pNewSectionHeader = pSectionHeader + pNumberOfSections;节的数量加一,且SizeOfImage参数增加新节按照内存对齐的大小
xxxxxxxxxxpPEHeader->NumberOfSections += 1;pOptionHeader->SizeOfImage += AlignLength(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);拷贝新节,返回新节大小
xxxxxxxxxxmemcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));free(pTempFileBuffer);return dwFileBufferSize + newSectionHeader.SizeOfRawData;
解析头部并拿到导出表的三个主要表
xxxxxxxxxxPIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));PDWORD pAddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfFunctions));PWORD pAddressOfNameOrdinals = (PWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNameOrdinals));PDWORD pAddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNames));需要计算新增节的大小,并传递到新增节函数进行新增
首先是函数地址表四字节,函数名和序号表分别是两和四字节
xxxxxxxxxxDWORD dwNewSectionSize = 0;dwNewSectionSize += pExportTable->NumberOfFunctions * 4; dwNewSectionSize += pExportTable->NumberOfNames * (2 + 4); 统计所有函数名字符串大小,注意需要多加1因为字符串以00结尾
xxxxxxxxxxfor (i = 0; i < pExportTable->NumberOfNames; i++){ LPCSTR lpszFuncName = (LPCSTR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pAddressOfNames[i])); dwNewSectionSize += strlen(lpszFuncName) + 1;}计算并加入导出表结构的大小,最后按照文件对齐来对齐,得到大小作为新增节参数
xxxxxxxxxxdwNewSectionSize += sizeof(_IMAGE_EXPORT_DIRECTORY);dwNewSectionSize = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);DWORD dwNewBufferSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileBufferSize, dwNewSectionSize);修改新增节属性为可读,含已初始化数据
xxxxxxxxxxpSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0x40000040;计算得到新文件Buffer的导出表地址
xxxxxxxxxxpExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
pAddressOfFunctions = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfFunctions));pAddressOfNameOrdinals = (PWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNameOrdinals));pAddressOfNames = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNames));复制三张子表信息到新节,并保留新子表的指针
xxxxxxxxxxLPVOID pInsert = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);memcpy(pInsert, pAddressOfFunctions, 4 * pExportTable->NumberOfFunctions);pAddressOfFunctions = (PDWORD)pInsert;pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfFunctions);memcpy(pInsert, pAddressOfNameOrdinals, 2 * pExportTable->NumberOfNames);pAddressOfNameOrdinals = (PWORD)pInsert;pInsert = (LPVOID)((DWORD)pInsert + 2 * pExportTable->NumberOfNames);memcpy(pInsert, pAddressOfNames, 4 * pExportTable->NumberOfNames);pAddressOfNames = (PDWORD)pInsert;pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfNames);遍历函数名称表,字符串地址转为FOA后获得,复制字符串长度+1 (00) 到新节中,新节指针偏移增加继续遍历
xxxxxxxxxxfor (i = 0; i < pExportTable->NumberOfNames; i++){ LPCSTR lpszFuncName = (LPCSTR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pAddressOfNames[i])); memcpy(pInsert, lpszFuncName, strlen(lpszFuncName) + 1); pAddressOfNames[i] = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer); pInsert = (LPVOID)((DWORD)pInsert + strlen(lpszFuncName) + 1);}复制节表,修改三张子表的RVA
xxxxxxxxxxmemcpy(pInsert, pExportTable, sizeof(_IMAGE_EXPORT_DIRECTORY));pExportTable = (PIMAGE_EXPORT_DIRECTORY)pInsert;pExportTable->AddressOfFunctions = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfFunctions - (DWORD)*pNewFileBuffer);pExportTable->AddressOfNameOrdinals = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNameOrdinals - (DWORD)*pNewFileBuffer);pExportTable->AddressOfNames = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNames - (DWORD)*pNewFileBuffer);修改数据目录的导出表指向新节的导出表
xxxxxxxxxxpOptionHeader->DataDirectory[0].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, (DWORD)pExportTable - (DWORD)*pNewFileBuffer);
xxxxxxxxxxDWORD AddSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize){ LPVOID pTempFileBuffer = malloc(dwFileBufferSize); if (!pTempFileBuffer) { return 0, 0, 0, 0; } memcpy(pTempFileBuffer, pFileBuffer, dwFileBufferSize); pFileBuffer = pTempFileBuffer;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
DWORD pNumberOfSections = pPEHeader->NumberOfSections; PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pNumberOfSections - 1; PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + pNumberOfSections; DWORD newFileBufferSize = 0;
if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader) { free(pTempFileBuffer); return 0; }
IMAGE_SECTION_HEADER newSectionHeader; memcpy(newSectionHeader.Name, ".newsec", 8); newSectionHeader.Misc.VirtualSize = AlignLength(dwNewSectionSize, pOptionHeader->SectionAlignment); newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + AlignLength(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment); newSectionHeader.SizeOfRawData = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment); newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData; newSectionHeader.PointerToRelocations = 0; newSectionHeader.PointerToLinenumbers = 0; newSectionHeader.NumberOfRelocations = 0; newSectionHeader.NumberOfLinenumbers = 0; newSectionHeader.Characteristics = 0x60000020;
*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData); memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize); memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData);
pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); pNumberOfSections = pPEHeader->NumberOfSections; pLastSectionHeader = pSectionHeader + pNumberOfSections - 1; pNewSectionHeader = pSectionHeader + pNumberOfSections; pPEHeader->NumberOfSections += 1; pOptionHeader->SizeOfImage += AlignLength(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment); memcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader)); free(pTempFileBuffer); return dwFileBufferSize + newSectionHeader.SizeOfRawData;}
DWORD MoveExportTableToNewSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4); PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress)); PDWORD pAddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfFunctions)); PWORD pAddressOfNameOrdinals = (PWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNameOrdinals)); PDWORD pAddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNames));
DWORD dwNewSectionSize = 0; dwNewSectionSize += pExportTable->NumberOfFunctions * 4; dwNewSectionSize += pExportTable->NumberOfNames * (2 + 4); for (DWORD i = 0; i < pExportTable->NumberOfNames; i++) { LPCSTR lpszFuncName = (LPCSTR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pAddressOfNames[i])); dwNewSectionSize += strlen(lpszFuncName) + 1; } dwNewSectionSize += sizeof(_IMAGE_EXPORT_DIRECTORY); dwNewSectionSize = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment); DWORD dwNewBufferSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileBufferSize, dwNewSectionSize);
pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0x40000040;
pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
pAddressOfFunctions = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfFunctions)); pAddressOfNameOrdinals = (PWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNameOrdinals)); pAddressOfNames = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNames));
LPVOID pInsert = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData); memcpy(pInsert, pAddressOfFunctions, 4 * pExportTable->NumberOfFunctions); pAddressOfFunctions = (PDWORD)pInsert; pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfFunctions); memcpy(pInsert, pAddressOfNameOrdinals, 2 * pExportTable->NumberOfNames); pAddressOfNameOrdinals = (PWORD)pInsert; pInsert = (LPVOID)((DWORD)pInsert + 2 * pExportTable->NumberOfNames); memcpy(pInsert, pAddressOfNames, 4 * pExportTable->NumberOfNames); pAddressOfNames = (PDWORD)pInsert; pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfNames); for (DWORD i = 0; i < pExportTable->NumberOfNames; i++) { LPCSTR lpszFuncName = (LPCSTR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pAddressOfNames[i])); memcpy(pInsert, lpszFuncName, strlen(lpszFuncName) + 1); pAddressOfNames[i] = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer); pInsert = (LPVOID)((DWORD)pInsert + strlen(lpszFuncName) + 1); } memcpy(pInsert, pExportTable, sizeof(_IMAGE_EXPORT_DIRECTORY)); pExportTable = (PIMAGE_EXPORT_DIRECTORY)pInsert; pExportTable->AddressOfFunctions = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfFunctions - (DWORD)*pNewFileBuffer); pExportTable->AddressOfNameOrdinals = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNameOrdinals - (DWORD)*pNewFileBuffer); pExportTable->AddressOfNames = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNames - (DWORD)*pNewFileBuffer); pOptionHeader->DataDirectory[0].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, (DWORD)pExportTable - (DWORD)*pNewFileBuffer); return dwNewBufferSize;}