PE逆向-移动重定向表

admin 2023年9月22日23:13:33评论10 views字数 8850阅读29分30秒阅读模式

移动重定向表的步骤:

* 在这里,我们要根据以下步骤移动

* 1.先复制导出表的结构

* 2.移动Functions表中的数据

* 3.移动序号表中的数据

* 4.移动name表中的数据       

* 5.修复name表中的数据和指向        

* 6.修复序号表和Functions的指向

   移动之前:

PE逆向-移动重定向表

PE逆向-移动重定向表

移动之后:

PE逆向-移动重定向表

PE逆向-移动重定向表

代码如下:

// 滴水三期3.26.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//
#include <iostream>#include <Windows.h>#include <cstring>
using namespace std;
//向上取值函数size_t AlignUp(size_t value, size_t alignment) { return (value + alignment - 1) & ~(alignment - 1);}//得到PE文件大小long GetSize(char* fileName) { FILE* file = NULL; errno_t err = fopen_s(&file, fileName, "rb"); if (err != 0) { cout << "文件打开失败!" << endl; fclose(file); return 0; } long fileSize = 0; fseek(file, 0, SEEK_END); fileSize = ftell(file); fseek(file, 0, SEEK_SET); fclose(file); return fileSize;}
//RVA转FOADWORD RVA_TO_FOA(char* fileBuffer, long long RVAaddress) {
PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew); if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) { cout << "PE的NT头错误!" << endl; return 0; } PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader); PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader); PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader); int sectionNum = 0; for (int i = 0; i < fileFileHeader->NumberOfSections; i++) { if (RVAaddress >= (fileSectionHeader + i)->VirtualAddress && RVAaddress < (fileSectionHeader + i)->VirtualAddress + (fileSectionHeader + i)->Misc.VirtualSize) { sectionNum = i; break; } } long long offsetSize = RVAaddress - (fileSectionHeader + sectionNum)->VirtualAddress; DWORD fileAdderss = (DWORD)((fileSectionHeader + sectionNum)->PointerToRawData + offsetSize); //cout << hex <<"RVAaddress:"<< RVAaddress<<"转换====》" << "文件地址:" << fileAdderss << endl; return fileAdderss;
}

//FOA转RVA//这里的FOA要减去fileBufferDWORD FOA_TO_RVA(char* fileBuffer, long long FOAaddress) {
PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew); if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) { cout << "PE的NT头错误X!" << endl; return 0; } PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader); PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader); PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader); int sectionNum = 0; for (int i = 0; i < fileFileHeader->NumberOfSections; i++) { if (FOAaddress >= (fileSectionHeader + i)->PointerToRawData && FOAaddress < (fileSectionHeader + i)->PointerToRawData + (fileSectionHeader + i)->SizeOfRawData) { sectionNum = i; break; } } long long offsetSize = FOAaddress - (fileSectionHeader + sectionNum)->PointerToRawData; DWORD fileAdderss = (DWORD)((fileSectionHeader + sectionNum)->VirtualAddress + offsetSize); //cout << hex <<"RVAaddress:"<< RVAaddress<<"转换====》" << "文件地址:" << fileAdderss << endl; return fileAdderss;
}
//要移动导出表,需要新增一个节,因此,我们需要先给dll文件新增加一个节void MoveExportToNewSection(char* fileName) { //先得到大小 long fileSize = GetSize(fileName); //得到大小后,再申请内存 这里为了简单,我就不重新申请了内存了,所以,这里就直接将后面需要扩大的节的大小写进来 char* fileBuffer = (char*)malloc(fileSize+0x1000); if (!fileBuffer) { cout << "RVA_TO_FOA---->fileBuffer内存申请失败!" << endl; free(fileBuffer); return; } //如果内存申请成功,那么就要读fileBuffer,读之前,先打开文件 FILE* file = NULL; errno_t err = fopen_s(&file, fileName, "rb"); if (err != 0) { cout << "文件打开失败!" << endl; free(fileBuffer); fclose(file); return; } //如果打开也没问题,那么就需要真正的开始读了 fread(fileBuffer, 1, fileSize, file); if ((*(PWORD)fileBuffer) != IMAGE_DOS_SIGNATURE) { cout << "PE文件格式错误!" << endl;; free(fileBuffer); fclose(file); return; } PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew); if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) { cout << "PE的NT头错误!" << endl; free(fileBuffer); fclose(file); return; } PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader); PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader); PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader);
//添加一个节,在这里,我们添加节就不复制添加了,根据实际大小来添加 DWORD exportTableSize = fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; cout << "节表数量:" << fileFileHeader->NumberOfSections << endl; //得到大小后,我们需要判断节后面的空间有没有给我们留下80个空白字节,如果没有,我们需要考虑上移NT头,或者扩大最后一个节。 //这个lastSection是我们需要添加的节,不要和文件本身最后一个节搞混了 //这里需要把转换成char*类型,因为char*类型+1才是真的加1 char* lastSection = (((char*)(fileSectionHeader + (fileFileHeader->NumberOfSections - 1))) + 40);
bool isZero = true; for (int i = 0; i < 80; i++) { if (*(lastSection + i) != 0x00) { isZero = false; break; } } //isZero还为真时,就说明有两个节表80个字节的空间留给我们 if (isZero) { cout << "可以添加一个节表!" << endl; //这里解释一下,因为是指针,所以根据指针的特性,只需要从第一个节复制到最后一个节,大小是40 memcpy(lastSection, fileSectionHeader, 40);
//复制成功后,修改节表的属性 //为了方便理解,这里直接设置一个指针变量指向添加的节表,也可以不设置,都以fileSectionHeader+fileFileHeader->NumberOfSections这种解释 PIMAGE_SECTION_HEADER llastSectionHeader = (PIMAGE_SECTION_HEADER)((char*)(fileSectionHeader + (fileFileHeader->NumberOfSections - 1)) + 40); PIMAGE_SECTION_HEADER lastSectionHeader = fileSectionHeader + (fileFileHeader->NumberOfSections - 1); //修改内存大小为适合的大小 llastSectionHeader->Misc.VirtualSize = exportTableSize; //修改对齐后的大小为适当的大小,需要满足对齐后的大小 llastSectionHeader->SizeOfRawData = AlignUp(exportTableSize, fileOptionHeader->SectionAlignment); cout << "lastSectionHeader->SizeOfRawData:" <<hex<< lastSectionHeader->SizeOfRawData<<"==="<<AlignUp(lastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment) << endl;
//修改VirtualAddress的大小,要满足内存对齐 llastSectionHeader->VirtualAddress = lastSectionHeader->VirtualAddress + AlignUp(lastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment);


//修改VirtualAddress的大小,要满足磁盘对齐 llastSectionHeader->PointerToRawData = lastSectionHeader->PointerToRawData + lastSectionHeader->SizeOfRawData;

//节表的其他属性不需要修改,现在再修改节表数量 fileFileHeader->NumberOfSections = fileFileHeader->NumberOfSections + 1;
//修改SizeOfImage,SizeOfImage等于原来的sizeOfImage加上新添加的节的对齐后的大小,满足内存对齐 fileOptionHeader->SizeOfImage = AlignUp(fileOptionHeader->SizeOfImage + llastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment); //修改节表数量 fileFileHeader->NumberOfSections = fileFileHeader->NumberOfSections + 1;
//节表修改完毕,可以开始移动
/* * 在这里,我们要根据以下步骤移动 * 1.先复制导出表的结构 * 2.移动Functions表中的数据 * 3.移动序号表中的数据 * 4.移动name表中的数据 * 5.修复name表中的数据和指向 * 6.修复序号表和Functions的指向 */
//在之前,先把添加的节内容设置为0 memset(llastSectionHeader->PointerToRawData + fileBuffer, 0, 0x1000);
//1.先复制导出表的结构 /* * */ PDWORD copyBegin = (PDWORD)(llastSectionHeader->PointerToRawData + fileBuffer); DWORD exportTableFOA = RVA_TO_FOA(fileBuffer, fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)(exportTableFOA + fileBuffer); memcpy(copyBegin, exportTable, sizeof(IMAGE_EXPORT_DIRECTORY)); fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = llastSectionHeader->VirtualAddress; /* * 2.移动Functions表中的数据 */ DWORD addressOfFunctionsFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfFunctions); PDWORD addressOfFunctions = (PDWORD)(addressOfFunctionsFOA + fileBuffer); PDWORD copyFunctionsBegin = copyBegin + sizeof(IMAGE_EXPORT_DIRECTORY); memcpy(copyFunctionsBegin, addressOfFunctions, exportTable->NumberOfFunctions * 4);
/* * 3. 移动序号表中的数据 */ DWORD addressOfNameOrdinalsFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfNameOrdinals); PDWORD addressOfNameOrdinals = (PDWORD)(addressOfNameOrdinalsFOA + fileBuffer); PDWORD copyOrdinalsBegin = copyFunctionsBegin + exportTable->NumberOfFunctions * 4; memcpy(copyOrdinalsBegin, addressOfNameOrdinals, exportTable->NumberOfFunctions * 2);
/* * 4.移动name表中的数据 */ DWORD addressOfNamesFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfNames); PDWORD addressOfNames = (PDWORD)(addressOfNamesFOA + fileBuffer); PDWORD copyNamesBegin = copyOrdinalsBegin + exportTable->NumberOfFunctions * 2; memcpy(copyNamesBegin, addressOfNames, exportTable->NumberOfFunctions * 4);
/* * 5.修复name表中的数据和指向 * 先修复RVA的指向 */ PIMAGE_EXPORT_DIRECTORY newExportTable = (PIMAGE_EXPORT_DIRECTORY)copyBegin; newExportTable->AddressOfNames = FOA_TO_RVA(fileBuffer, (long long)copyNamesBegin - (long long)fileBuffer); PDWORD copyNamesEnd = copyNamesBegin + exportTable->NumberOfFunctions * 4; //修复完毕之后,开始复制名字,然后修改addressOfNames中的数据 int tmp = 0; for (int i = 0; i < exportTable->NumberOfNames; i++) { PDWORD newExportTalbe = (PDWORD)(FOA_TO_RVA(fileBuffer, newExportTable->AddressOfNames) + fileBuffer); char* name = (char* )(RVA_TO_FOA(fileBuffer,*(addressOfNames + i)) + fileBuffer); //这里不要忘了,c++字符串是结尾,所以要+1,而为了进行内存对齐,所以要看看他占几个4字节。 int result = (strlen(name) + 4) / 4; PDWORD copyRNameEnd = copyNamesEnd + tmp; memcpy(copyRNameEnd, name, result * 4); tmp = result + tmp; DWORD copyRNameEndRVA = FOA_TO_RVA(fileBuffer, (long long)copyRNameEnd - (long long)fileBuffer); *(copyNamesBegin+i) = copyRNameEndRVA;
}

//名称复制好了之后,开始存盘 FILE* newFile = NULL; errno_t newErr = fopen_s(&newFile, "C:\Users\zyjsuper\Desktop\test\777.dll", "wb"); if (newErr != 0) { std::cout << "newFile创建失败!" << std::endl; fclose(newFile); fclose(file); free(fileBuffer); return; } fwrite(fileBuffer, 1, fileSize + 0x200, newFile); std::cout << "写入成功!" << std::endl; fclose(newFile);
}


free(fileBuffer); fclose(file);
}
int main(){ char fileName[] = "C:\Users\zyjsuper\Desktop\test\MyDll.dll"; MoveExportToNewSection(fileName);}


原文始发于微信公众号(loochSec):PE逆向-移动重定向表

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月22日23:13:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PE逆向-移动重定向表https://cn-sec.com/archives/2061126.html

发表评论

匿名网友 填写信息