打印导出表

    科技2022-08-27  97

    文章目录

    一、流程二、演示三、完整代码四 、勿在浮沙筑高台

    一、流程

    1、创建一个RVA转FOA的函数 2、创建一个打印导出表的函数 3、File-> FileBuffer 4、获取头信息 5、获取结构体数组的信息 6、获取导出表的地址Rva 7、获取导出表的地址Foa 8、导出表在文件中的地址 9、打印导出表的成员属性 10、将导出表Name、AddressOfNames、AddressOfFunctions、AddressOfNameOrdinals的Rva转换成Foa 11、 打印导出表文件名 12、获取导出函数地址表、序号表、名称表文件中的位置 13、判断是以函数导出还是序号导出

    二、演示

    1、创建一个RVA转FOA的函数

    DWORD RvaToFoa(IN LPVOID pFileBuffer, IN DWORD dwRva) { 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; //DOS头 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //NT头地址 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); //标准PE头地址 pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4); //可选PE头地址 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //第一个节表地址 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); for(int i=0 ; i < pPEHeader->NumberOfSections ; i++) { //节在内存中的位置RVA DWORD dwSectionBeginRva = pSectionHeader[i].VirtualAddress; //节在内存中相对于文件中结束的位置RVA DWORD dwSectionEndRva = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData; //判断RVA是否在当前节中 if(dwRva >= dwSectionBeginRva && dwRva <= dwSectionEndRva) { //FOA = RVA - 节在内存中的位置 + 节在文件中的偏移 DWORD dwFoa = dwRva - dwSectionBeginRva + pSectionHeader[i].PointerToRawData; return dwFoa; } } }

    2、创建一个打印导出表的函数

    VOID TestPrintExPort() { LPVOID pFileBuffer = NULL; DWORD Size = 0; PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; }

    3、File-> FileBuffer

    //File-> FileBuffer Size = ReadPEFile(FILEPATH_IN,&pFileBuffer); //调用函数读取文件数据 if(!pFileBuffer || !Size) { printf("File-> FileBuffer失败"); return; }

    4、获取头信息

    //获取头信息 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 0x4 + IMAGE_SIZEOF_FILE_HEADER);

    5、获取结构体数组的信息

    //指向结构体数组 PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionHeader->DataDirectory;

    6、获取导出表的地址Rva

    //导出表的地址Rva DWORD dwExProtTableRva = pDataDirectory[0].VirtualAddress;

    7、获取导出表的地址Foa

    //导出表的地址Foa DWORD dwExProtTableFoa = RvaToFoa(pFileBuffer,dwExProtTableRva);

    8、导出表在文件中的地址

    //导出表在文件中的地址 PIMAGE_EXPORT_DIRECTORY PExProtTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + dwExProtTableFoa);

    9、打印导出表的成员属性

    printf("导出表name:0xX\n",PExProtTable->Name); printf("导出表Base:0xX\n",PExProtTable->Base); printf("导出表NumberOfFunctions:0xX\n",PExProtTable->NumberOfFunctions); printf("导出表NumberOfNames:0xX\n",PExProtTable->NumberOfNames); printf("导出表AddressOfFunctions:0xX\n",PExProtTable->AddressOfFunctions); printf("导出表AddressOfNames:0xX\n",PExProtTable->AddressOfNames); printf("导出表AddressOfNameOrdinals:0xX\n",PExProtTable->AddressOfNameOrdinals);

    10、将导出表Name、AddressOfNames、AddressOfFunctions、AddressOfNameOrdinals的Rva转换成Foa

    //将导出表Name、AddressOfNames、AddressOfFunctions、AddressOfNameOrdinals的Rva转换成Foa DWORD dwNameFoa = RvaToFoa(pFileBuffer,PExProtTable->Name); DWORD dwAddressOfNamesFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfNames); DWORD dwAddressOfFunctionsFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfFunctions); DWORD dwAddressOfNameOrdinalsFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfNameOrdinals);

    11、 打印导出表文件名

    //导出表的名字 PBYTE pDllName = (PBYTE)((DWORD)pFileBuffer + dwNameFoa); printf("导出表名%s\n",pDllName);

    12、获取导出函数地址表、序号表、名称表文件中的位置

    //导出函数地址表文件中的位置 PDWORD pAddressTable = (PDWORD)((DWORD)pFileBuffer + dwAddressOfFunctionsFoa); //导出函数序号表文件中的位置 PWORD pOrdinaTable = (PWORD)((DWORD)pFileBuffer + dwAddressOfNameOrdinalsFoa); //导出函数名称表文件中的位置 PDWORD pNameTable = (PDWORD)((DWORD)pFileBuffer + dwAddressOfNamesFoa);

    13、判断是以函数导出还是序号导出

    //判断是以序号导出还是以文件导出 BOOL bIndexIsExIst = FALSE; for(DWORD i=0 ; i<PExProtTable->NumberOfFunctions ; i++) { //获取导出函数地址 printf("RVA的地址:X",pAddressTable[i]); bIndexIsExIst = FALSE; for(DWORD nNameIndex = 0 ; nNameIndex < PExProtTable->NumberOfNames ; ++nNameIndex) { //根据导出函数地址的索引,去序号表中找是否存在 if(i == pOrdinaTable[nNameIndex]) { bIndexIsExIst = TRUE; break; } } if(bIndexIsExIst == TRUE) { //序号表中存在,根据序号表的索引,去名称表中找函数名称 DWORD dwNameRva = pNameTable[nNameIndex]; PBYTE pFunName = (PBYTE)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer,dwNameRva)); printf(" 函数名[%s]\t",pFunName); //获取导出序号 printf(" 序号[%d]",i + PExProtTable->Base); } else { //序号表中不存在,判断是否存在这个函数 if(pAddressTable[i] != 0) { //隐藏函数名 printf(" 函数名:[-]\t"); printf(" 序号[%d]",i + PExProtTable->Base); } } printf("\n"); } return;

    三、完整代码

    #include "stdafx.h" #include <windows.h> #include "stdlib.h" #define FILEPATH_IN "C:/testdll.dll" DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer ) { FILE *pFile = NULL; DWORD fileSize = 0; //文件大小 LPVOID pTempFileBuffer = NULL; //缓冲区首地址 pFile = fopen(lpszFile,"rb"); //打开文件 if(!pFile) { printf("打开文件失败"); return NULL; } //读取文件大小 fseek(pFile,0,SEEK_END); //将指针从开始的位置移动到末尾 fileSize = ftell(pFile); //获取数据大小 //分配缓冲区(申请内存) pTempFileBuffer = malloc(fileSize); if(!pTempFileBuffer) { printf("分配空间失败"); fclose(pFile); return NULL; } //将文件数据读取到缓冲区 fseek(pFile,0,SEEK_SET); //将指针指向开始 size_t n = fread(pTempFileBuffer,fileSize,1,pFile); //将数据读取到缓冲区中 if(!n) { printf("读取数据失败"); free(pTempFileBuffer); //释放内存 fclose(pFile); //关闭文件 return NULL; } //关闭文件 *pFileBuffer = pTempFileBuffer; pTempFileBuffer = NULL; fclose(pFile); //关闭文件 return fileSize; } //********************************************************************** DWORD RvaToFoa(IN LPVOID pFileBuffer, IN DWORD dwRva) { 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; //DOS头 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //NT头地址 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); //标准PE头地址 pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4); //可选PE头地址 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //第一个节表地址 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); for(int i=0 ; i < pPEHeader->NumberOfSections ; i++) { //节在内存中的位置RVA DWORD dwSectionBeginRva = pSectionHeader[i].VirtualAddress; //节在内存中相对于文件中结束的位置RVA DWORD dwSectionEndRva = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData; //判断RVA是否在当前节中 if(dwRva >= dwSectionBeginRva && dwRva <= dwSectionEndRva) { //FOA = RVA - 节在内存中的位置 + 节在文件中的偏移 DWORD dwFoa = dwRva - dwSectionBeginRva + pSectionHeader[i].PointerToRawData; return dwFoa; } } } //********************************************************************** VOID TestPrintExPort() { LPVOID pFileBuffer = NULL; DWORD Size = 0; PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; //File-> FileBuffer Size = ReadPEFile(FILEPATH_IN,&pFileBuffer); //调用函数读取文件数据 if(!pFileBuffer || !Size) { printf("File-> FileBuffer失败"); return; } //获取头信息 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 0x4 + IMAGE_SIZEOF_FILE_HEADER); //指向数组 PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionHeader->DataDirectory; //导出表的地址Rva DWORD dwExProtTableRva = pDataDirectory[0].VirtualAddress; //导出表的地址Foa DWORD dwExProtTableFoa = RvaToFoa(pFileBuffer,dwExProtTableRva); //导出表在文件中的地址 PIMAGE_EXPORT_DIRECTORY PExProtTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + dwExProtTableFoa); printf("导出表name:0xX\n",PExProtTable->Name); printf("导出表Base:0xX\n",PExProtTable->Base); printf("导出表NumberOfFunctions:0xX\n",PExProtTable->NumberOfFunctions); printf("导出表NumberOfNames:0xX\n",PExProtTable->NumberOfNames); printf("导出表AddressOfFunctions:0xX\n",PExProtTable->AddressOfFunctions); printf("导出表AddressOfNames:0xX\n",PExProtTable->AddressOfNames); printf("导出表AddressOfNameOrdinals:0xX\n",PExProtTable->AddressOfNameOrdinals); //将导出表Name、AddressOfNames、AddressOfFunctions、AddressOfNameOrdinals的Rva转换成Foa DWORD dwNameFoa = RvaToFoa(pFileBuffer,PExProtTable->Name); DWORD dwAddressOfNamesFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfNames); DWORD dwAddressOfFunctionsFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfFunctions); DWORD dwAddressOfNameOrdinalsFoa = RvaToFoa(pFileBuffer,PExProtTable->AddressOfNameOrdinals); //导出表的名字 PBYTE pDllName = (PBYTE)((DWORD)pFileBuffer + dwNameFoa); printf("导出表名%s\n",pDllName); //导出函数地址表文件中的位置 PDWORD pAddressTable = (PDWORD)((DWORD)pFileBuffer + dwAddressOfFunctionsFoa); //导出函数序号表文件中的位置 PWORD pOrdinaTable = (PWORD)((DWORD)pFileBuffer + dwAddressOfNameOrdinalsFoa); //导出函数名称表文件中的位置 PDWORD pNameTable = (PDWORD)((DWORD)pFileBuffer + dwAddressOfNamesFoa); //判断是以序号导出还是以文件导出 BOOL bIndexIsExIst = FALSE; for(DWORD i=0 ; i<PExProtTable->NumberOfFunctions ; i++) { //获取导出函数地址 printf("RVA的地址:X",pAddressTable[i]); bIndexIsExIst = FALSE; for(DWORD nNameIndex = 0 ; nNameIndex < PExProtTable->NumberOfNames ; ++nNameIndex) { //根据导出函数地址的索引,去序号表中找是否存在 if(i == pOrdinaTable[nNameIndex]) { bIndexIsExIst = TRUE; break; } } if(bIndexIsExIst == TRUE) { //序号表中存在,根据序号表的索引,去名称表中找函数名称 DWORD dwNameRva = pNameTable[nNameIndex]; PBYTE pFunName = (PBYTE)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer,dwNameRva)); printf(" 函数名[%s]\t",pFunName); //获取导出序号 printf(" 序号[%d]",i + PExProtTable->Base); } else { //序号表中不存在,判断是否存在这个函数 if(pAddressTable[i] != 0) { //隐藏函数名 printf(" 函数名:[-]\t"); printf(" 序号[%d]",i + PExProtTable->Base); } } printf("\n"); } return; } //********************************************************************** int main(int argc, char* argv[]) { TestPrintExPort(); getchar(); return 0; }

    四 、勿在浮沙筑高台

    Processed: 0.043, SQL: 9