原文链接:逆向工具之移动导出表
0x01 移动表目的
1、PE结构里许多表是编译器自动生成的,里面存储很多非常重要的信息,比如这次要移动的导出表记录了函数的地址,序号,函数名称,函数数量等,通俗来说,导出表相当于一张函数使用说明书,提供给函数使用。
2、在程序启动的时候,系统会根据这些表来做初始化的工作:比如将用到的DLL中的函数地址存储到IAT表中,修复IAT表。
3、很多时候,为了保护我们的程序,可以对程序的二进制代码进行加密操作,但存在的问题是:各种表的信息与客户字节的代码和数据都混在一起,如果全部进行加密,那系统在初始化程序的时候会出问题,无法加载这个程序,那加密失去意义。
0x02 导出表结构
上图中的导出表中的AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals的值都是内存相对偏移
RVA(内存相对偏移),FOA(文件相对偏移)
0x03 移动导出表
1、将原来的dll加载进内存中,堆区新开辟一块内存,大小为原来dll大小与新增节的大小之和(新增节大小直接给了0x1000,可以通过计算来判断大小),将原来dll的数据全部拷贝到申请的内存中去,再新增一个节表,并返回新开辟内存的首地址。
2、复制AddressOfFunctions指向的函数地址表,需要将内存相对偏移转化为文件相对偏移,长度:4*NumberOfFunctions。
3、复制AddressOfNameOrdinals指向的函数序号表,需要将内存相对偏移转化为文件相对偏移,长度:2*AddressOfNames。
4、复制AddressOfNames指向的函数名称地址表,需要将内存相对偏移转化为文件相对偏移,长度:4*AddressOfNames。
5、复制所有的函数名,长度需要通过strlen()函数进行计算,复制时直接修复函数名称地址表中的地址。
6、复制IMAGEEXPORTDIRECTORY结构(导出表)。
7、修复IMAGEEXPORTDIRECTORY结构中的AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals,需要将FOA转化为RVA,并且指向IMAGEEXPORTDIRECTORY结构的指针要进行更新,否则修改的还是原来位置的导出表。
8.修复可选PE头中的导出表目录的地址值(RVA),指向新的IMAGEEXPORTDIRECTORY。
0x04 实现函数
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <windows.h>
- #define SECTIONLENGTH 0x1000
- #define EVERYSECTIONTABLELENGTH 0x28
- #define SECTIONNAME ".export"
- #define INPUTFILENAME "D:/dynamicdll.dll"
- #define INPUTMODE "rb"
- #define OUTPUTFILENAME "D:/test.dll"
- #define OUTPUTMODE "wb"
- /*************************************************
复制代码
函数功能:读取一个文件,将文件内容写入到内存中;
函数参数:无
函数返回值:pFileBuffer(LPVOID)
- **************************************************/
- LPVOID FileBuffer() {
- FILE* pFile = NULL; //文件指针
- LPVOID pFileBuffer = NULL; //存放数据内存的首地址
- DWORD filesize = 0;//记录文件大小
- size_t result = 0;//记录写入的返回结果
- //打开一个文件
- pFile = fopen(INPUTFILENAME, INPUTMODE);
- if (pFile == NULL) {
- printf("打开文件失败!\n");
- return NULL;
- }
- //统计文件的大小
- fseek(pFile, 0, SEEK_END);
- filesize = ftell(pFile);
- fseek(pFile, 0, SEEK_SET);
- //申请一块内存
- pFileBuffer = malloc(filesize);
- if (pFileBuffer == NULL) {
- printf("申请内存失败!\n");
- fclose(pFile);
- return NULL;
- }
- //将文件数据写入内存
- result = fread(pFileBuffer, 1, filesize, pFile);
- if (result != filesize) {
- printf("文件写入内存失败!\n");
- fclose(pFile);
- free(pFileBuffer);
- return NULL;
- }
- fclose(pFile);
- return pFileBuffer;
- }
- /*************************************************
复制代码
函数功能:计算一个文件大小;
函数参数:filename(文件的绝对路径,LPSTR),mode(读取文件的形式,LPSTR)
函数返回值:filesize(int)
- **************************************************/
- DWORD CountFileSize(LPSTR filename, LPSTR mode) {
- FILE* pFile = NULL;
- LPSTR pFileBuffer = NULL;
- DWORD filesize = 0;
- //打开文件
- pFile = fopen(filename, mode);
- if (pFile == NULL) {
- printf("打开文件失败!\n");
- return NULL;
- }
- //统计文件大小
- fseek(pFile, 0, SEEK_END);
- filesize = ftell(pFile);
- fseek(pFile, 0, SEEK_SET);
- fclose(pFile);
- return filesize;
- }
- /*************************************************
复制代码
函数功能:计算一个filebuffer的大小;
函数参数:pFileBuffer(LPVOID)
函数返回值:filesize(int)
- **************************************************/
- DWORD CountFileBufferSize(LPVOID pFileBuffer) {
- PIMAGE_DOS_HEADER pDosHeader = NULL;
- PIMAGE_NT_HEADERS pNTHeader = NULL;
- PIMAGE_FILE_HEADER pPEHeader = NULL;
- PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
- PIMAGE_SECTION_HEADER pSectionHeader = NULL;
- DWORD filesize = 0;//记录文件大小
- if (pFileBuffer == NULL) {
- printf("文件写入内存失败!\n");
- return NULL;
- }
- //判读是否具有MZ标志
- if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) {
- printf("不具有MZ标志!\n");
- free(pFileBuffer);
- return 0;
- }
- pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
- //判断是否具有PE标志
- if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) {
- printf("不具有PE标志\n");
- free(pFileBuffer);
- return 0;
- }
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- //最后一个节头地址 + 最后一节大小
- filesize = (DWORD)((pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData + (pSectionHeader + pPEHeader->NumberOfSections - 1)->SizeOfRawData);
- return filesize;
- }
- /*************************************************
复制代码
函数功能:PE文件尾部新增一个节;
函数参数:pFileBuffer(LPVOID)
函数返回值:pNewFileBuffer(LPVOID)
- **************************************************/
- LPVOID AddLastSection(LPVOID pFileBuffer) {
- LPVOID pNewFileBuffer = NULL;
- PIMAGE_DOS_HEADER pDosHeader = NULL;
- PIMAGE_NT_HEADERS pNTHeader = NULL;
- PIMAGE_FILE_HEADER pPEHeader = NULL;
- PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
- PIMAGE_SECTION_HEADER pSectionHeader = NULL;
- if (pFileBuffer == NULL) {
- printf("文件写入内存失败!\n");
- return NULL;
- }
- pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- //判断节表空间是否足够,节表后需要预留一个节表空间的位置
- if (pSectionHeader->PointerToRawData - (DWORD)(pSectionHeader + pPEHeader->NumberOfSections) < 0x50) {
- printf("节表空间不够!\n");
- free(pFileBuffer);
- return NULL;
- }
- //开辟新的内存
- DWORD filesize = CountFileBufferSize(pFileBuffer);
- pNewFileBuffer = malloc(filesize + SECTIONLENGTH);
- if (pNewFileBuffer == NULL) {
- printf("申请内存失败!\n");
- free(pFileBuffer);
- return NULL;
- }
- //新内存初始化为0
- memset(pNewFileBuffer, 0, filesize + SECTIONLENGTH);
- //拷贝原来的文件内容到新内存并释放旧文件内存
- memcpy(pNewFileBuffer, pFileBuffer, filesize);
- free(pFileBuffer);
- //结构体指针再次初始化
- pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- //修改sizeofimage
- pOptionHeader->SizeOfImage += SECTIONLENGTH;
- //copy第一个节表内容
- memcpy(pSectionHeader + pPEHeader->NumberOfSections, pSectionHeader, EVERYSECTIONTABLELENGTH);
- //修改新增的节表的项目
- PIMAGE_SECTION_HEADER changeSection1 = pSectionHeader + pPEHeader->NumberOfSections;//新增节表首地址
- PIMAGE_SECTION_HEADER changeSection2 = pSectionHeader + pPEHeader->NumberOfSections - 1;//新增节表的前一个节表首地址
- changeSection1->Misc.VirtualSize = SECTIONLENGTH;//修改内存中的尺寸
- changeSection1->SizeOfRawData = SECTIONLENGTH;//修改文件中的尺寸
- memcpy(changeSection1, SECTIONNAME, 0x8);//修改名字
- changeSection1->PointerToRawData = changeSection2->PointerToRawData + changeSection2->SizeOfRawData;
- if (changeSection2->SizeOfRawData > changeSection2->Misc.VirtualSize){
- changeSection1->VirtualAddress = changeSection2->VirtualAddress + changeSection2->SizeOfRawData;
- }
- else{
- changeSection1->VirtualAddress = changeSection2->VirtualAddress + changeSection2->Misc.VirtualSize;
- }
- //修改NumberOfSections
- pPEHeader->NumberOfSections += 1;
- return pNewFileBuffer;
- }
- /*************************************************
复制代码
函数功能:将RVA的值转换成FOA;
函数参数:pFileBuffer(LPVOID),virtualAddress(LPSTR)
函数返回值:fileAddress(LPVOID)
- **************************************************/
- LPVOID RvaToFoa(LPVOID pFileBuffer, LPSTR virtualAddress) {
- LPSTR sectionAddress = NULL;//记录距离节头的距离
- LPSTR fileAddress = NULL;//记录文件中的偏移
- PIMAGE_DOS_HEADER pDosHeader = NULL;
- PIMAGE_NT_HEADERS pNTHeader = NULL;
- PIMAGE_FILE_HEADER pPEHeader = NULL;
- PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
- PIMAGE_SECTION_HEADER pSectionHeader = NULL;
- if (pFileBuffer == NULL) {
- printf("文件写入内存失败!\n");
- return NULL;
- }
- pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- if ((DWORD)virtualAddress <= pOptionHeader->SizeOfHeaders){
- return virtualAddress;
- }
- for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
- if ((DWORD)virtualAddress < pSectionHeader->VirtualAddress) {
- pSectionHeader--;
- break;
- }
- else if (i == pPEHeader->NumberOfSections){
- break;
- }
- else{
- pSectionHeader++;
- }
- }
- //距离该节头的距离
- sectionAddress = virtualAddress - pSectionHeader->VirtualAddress;
- fileAddress = pSectionHeader->PointerToRawData + sectionAddress;
- return (LPVOID)fileAddress;
- }
- /*************************************************
复制代码
函数功能:将FOA的值转换成RVA;
函数参数:pFileBuffer(LPVOID),virtualAddress(LPSTR)
函数返回值:fileAddress(LPVOID)
- **************************************************/
- LPVOID FoaToRva(LPVOID pFileBuffer, LPSTR fileaddress) {
- LPSTR sectionAddress = NULL;//记录距离节头的距离
- LPSTR virtualaddress = NULL;//记录内存中的偏移
- PIMAGE_DOS_HEADER pDosHeader = NULL;
- PIMAGE_NT_HEADERS pNTHeader = NULL;
- PIMAGE_FILE_HEADER pPEHeader = NULL;
- PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
- PIMAGE_SECTION_HEADER pSectionHeader = NULL;
- if (pFileBuffer == NULL) {
- printf("文件写入内存失败!\n");
- return NULL;
- }
- pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- if ((DWORD)fileaddress <= pOptionHeader->SizeOfHeaders){
- return fileaddress;
- }
- for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
- if ((DWORD)fileaddress < pSectionHeader->PointerToRawData) {
- pSectionHeader--;
- break;
- }
- else if (i == pPEHeader->NumberOfSections){
- break;
- }
- else{
- pSectionHeader++;
- }
- }
- //距离该节头的距离
- sectionAddress = fileaddress - pSectionHeader->PointerToRawData;
- virtualaddress = pSectionHeader->VirtualAddress + sectionAddress;
- return (LPVOID)virtualaddress;
- }
- /*************************************************
复制代码
函数功能:移动导出表;
函数参数:pFileBuffer(LPVOID)
函数返回值:无
- **************************************************/
- void MoveExportTable(LPVOID pFileBuffer) {
- PIMAGE_DOS_HEADER pDosHeader = NULL;
- PIMAGE_NT_HEADERS pNTHeader = NULL;
- PIMAGE_FILE_HEADER pPEHeader = NULL;
- PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
- PIMAGE_SECTION_HEADER pSectionHeader = NULL;
- PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;//定位目录
- PIMAGE_EXPORT_DIRECTORY exportTableAddress = NULL;//定位导出表的真正位置
- PDWORD addressOfFunction = NULL;//定位函数地址表的真正位置
- PDWORD addressOfName = NULL;//定位函数名称地址表的真正位置
- PWORD addressNameOrdinals = NULL;//定位函数序号表的真正位置
- LPVOID returnAddress = NULL;//记录RVAtoFOA的返回值
- if (pFileBuffer == NULL) {
- printf("文件写入内存失败!\n");
- return;
- }
- //新增一个节
- LPVOID pNewFileBuffer = AddLastSection(pFileBuffer);
- if (pNewFileBuffer == NULL) {
- printf("新增节失败!\n");
- return;
- }
- pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;
- pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewFileBuffer + pDosHeader->e_lfanew);
- pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
- pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
- pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
- //定位导出表位置
- returnAddress = RvaToFoa(pNewFileBuffer, (LPSTR)(pDataDirectory->VirtualAddress));
- exportTableAddress = (PIMAGE_EXPORT_DIRECTORY)((DWORD)returnAddress + (DWORD)pNewFileBuffer);
- //定位函数地址表
- returnAddress = RvaToFoa(pNewFileBuffer, (LPSTR)(exportTableAddress->AddressOfFunctions));
- addressOfFunction = (PDWORD)((DWORD)(returnAddress)+(DWORD)pNewFileBuffer);
- PDWORD pNewSection = (PDWORD)((pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData + (DWORD)pNewFileBuffer);
- //复制AddressOfFunctions
- PDWORD repairAddressOfFunction = (PDWORD)((DWORD)pNewSection - (DWORD)pNewFileBuffer);
- memcpy(pNewSection, addressOfFunction, (exportTableAddress->NumberOfFunctions) * 4);
- //定位函数序号表
- returnAddress = RvaToFoa(pNewFileBuffer, (LPSTR)(exportTableAddress->AddressOfNameOrdinals));
- addressNameOrdinals = (PWORD)((DWORD)(returnAddress)+(DWORD)pNewFileBuffer);
- pNewSection = (PDWORD)((DWORD)pNewSection + ((exportTableAddress->NumberOfFunctions) * 4));
- //复制AddressOfNameOrdinals
- PWORD repairAddressNameOrdinals = (PWORD)((DWORD)pNewSection - (DWORD)pNewFileBuffer);
- memcpy(pNewSection, addressNameOrdinals, (exportTableAddress->NumberOfNames) * 2);
- //定位函数名称地址表
- returnAddress = RvaToFoa(pNewFileBuffer, (LPSTR)(exportTableAddress->AddressOfNames));
- addressOfName = (PDWORD)((DWORD)(returnAddress)+(DWORD)pNewFileBuffer);
- pNewSection = (PDWORD)((DWORD)pNewSection + ((exportTableAddress->NumberOfNames) * 2));
- //复制AddressOfNames
- PDWORD repairAddressOfName = (PDWORD)((DWORD)pNewSection - (DWORD)pNewFileBuffer);
- memcpy(pNewSection, addressOfName, (exportTableAddress->NumberOfNames) * 4);
- //循环复制函数名并修复函数名地址
- DWORD singleFunctionNamelen = 0;//记录函数名称长度
- DWORD allFuntionNamelen = 0; //记录函数名称总长度
- PDWORD repairAddressName = (PDWORD)pNewSection;//用于修复函数名地址
- PDWORD functionNamePtr = (PDWORD)((DWORD)pNewSection + (exportTableAddress->NumberOfNames) * 4);//用于拷贝函数名称进行偏移
- PDWORD namePtr = NULL;//存储函数名的FOA
- for (DWORD i = 1; i <= exportTableAddress->NumberOfNames; i++) {
- //将函数名称地址表的RVA转化成FOA
- returnAddress = (PBYTE)RvaToFoa(pNewFileBuffer, LPSTR(*repairAddressName));
- namePtr = (PDWORD)((DWORD)returnAddress + (DWORD)pNewFileBuffer);
- //尾部的0
- singleFunctionNamelen = strlen((const char*)namePtr) + 1;
- memcpy(functionNamePtr, (const void*)namePtr, singleFunctionNamelen);
- allFuntionNamelen += singleFunctionNamelen;
- *repairAddressName = (DWORD)(FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)functionNamePtr - (DWORD)pNewFileBuffer)));
- functionNamePtr = (PDWORD)((DWORD)functionNamePtr + singleFunctionNamelen);
- repairAddressName++;
- }
- pNewSection = (PDWORD)((DWORD)pNewSection + (exportTableAddress->NumberOfNames) * 4 + (DWORD)allFuntionNamelen);
- PBYTE repairExportTable = (PBYTE)((DWORD)pNewSection - (DWORD)pNewFileBuffer);//用于修复目录表
- //复制IMAGE_EXPORT_DIRECTORY结构
- memcpy(pNewSection, exportTableAddress, 0x28);
- //修复IMAGE_EXPORT_DIRECTORY结构中的AddressOfFunctions、AddressOfNameOrdinals、AddressOfNames FOA→RVA
- exportTableAddress = (PIMAGE_EXPORT_DIRECTORY)pNewSection;
- exportTableAddress->AddressOfFunctions = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)repairAddressOfFunction);
- exportTableAddress->AddressOfNameOrdinals = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)repairAddressNameOrdinals);
- exportTableAddress->AddressOfNames = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)repairAddressOfName);
- //修复目录项中的值,指向新的IMAGE_EXPORT_DIRECTORY
- pDataDirectory->VirtualAddress = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)repairExportTable);
- //存盘
- FILE* pFile = NULL;
- DWORD newfilesize = 0;
- size_t newresult = 0;
- pFile = fopen(OUTPUTFILENAME, OUTPUTMODE);
- if (pFile == NULL) {
- printf("打开文件失败!\n");
- free(pNewFileBuffer);
- return;
- }
- newfilesize = CountFileSize(INPUTFILENAME, INPUTMODE) + SECTIONLENGTH;
- newresult = fwrite(pNewFileBuffer, 1, newfilesize, pFile);
- if (newresult != newfilesize) {
- printf("写入文件失败!\n");
- fclose(pFile);
- free(pNewFileBuffer);
- return;
- }
- printf("存盘成功!\n");
- fclose(pFile);
- free(pNewFileBuffer);
- }
- int main(){
- LPVOID pFileBuffer = FileBuffer();
- MoveExportTable(pFileBuffer);
- return 0;
- }
复制代码
0x05 结果展示
原来的dll
移动后的dll
|