一个PE文件中有很多节,每个节都存储不同的数据。而PE文件中的各种表也都分散存储在这些节当中。此时各种表的信息与程序的代码和数据相互混合在一起,如果我们直接对整个程序进行加密,那系统在初始化程序时就会出问题。比如:这些表的数据被加密时,系统就无法根据这些表将用到的.DLL中的函数地址存储到IAT表中,这时候程序就无法正常启动
所以对程序加密或破解之前,需要移动各种表到程序新增的节当中,再对剩下的数据进行加密
注意:DOS头、NT头等所有头和节表是操作系统判断该文件是否为Windows可执行程序的依据,因此这些数据不可以加密。
学会移动各种表,是对程序加密/破解的基础
步骤说明:
在FileBuffer中进行操作,现结合图示来理解移动的步骤
在PE文件FileBuffer中新增一个节,并获取该节的FOA。该表用来存储导出表,节大小按照表的的大小对齐以后计算,粗略估计0x1000字节大小,
新增节时需要修改的数据:
1.修改PE头中的字段:NumberOfSections、SizeOfImage
2.重新开辟一个FileBuffer,并新增节表
3.修改节表中的字段:Name、Misc.VirtualSize、VirtualAddress、SizeOfRawData、PointerToRawData、Characteristics。
新增节进行填充的数据:
1.复制导出表IMAGE_EXPORT_DIRECTORY到新节中,大小固定40字节
2.复制AddressOfFunctions指向的函数地址表到新节中(注意RVA转FOA),数据大小为4 * NumberOfFunctions字节
3.复制AddressOfNameOrdinals指向的函数序号表到新节中,数据大小为2 * NumberOfNames字节
4.复制AddressOfNames指向的函数名称表到新节中,数据大小为4 * NumberOfNames字节
5.依次复制函数名称表中元素所指向的函数名称字符串到新节中,注意以00结尾。遍历函数名称表,用strlen()函数依次求出每个字符串长度,再求和。每复制一个字符串到新节中,就修改对应的函数名称表中的地址值
注意:函数名称表中存的是RVA,所以把字符串复制到新节后还需要把FOA转成RVA,再存入
如果只移动函数名称表的话,对剩下的数据加密后,当需要根据函数名去调用导出函数时,由于字符串被加密,只能根据函数名称表查到名称字符串所在地址,但是无法匹配字符串。
6.修改导出表中的成员值,指向三个子表在新节中的位置,由于各个子表在导出表的地址数据是RVA,因此需要将FOA转成RVA
7.最后修复导出表数据目录中的VirtualAddress,指向导出表IMAGE_EXPORT_DIRECTORY在新节中的位置,该地址也要FOA转RVA
步骤说明
结合图示来理解移动重定位表过程:
1.新增节,大小能够循环遍历重定位表的每个块,即把所有块的SizeOfBlock求和 + 结束标记8字节最后再对齐,粗略估算0x1000就满足大部分情况了
2.复制重定位表到新节中,大小为所有块的SizeOfBlock求和 + 结束标记8字节
3.修改重定位表数据目录中的VirtualAddress,指向重定位表在新节中的位置即可(FOA转RVA)
4.修复重定位表:根据重定位表中的各个具体项,去修改具体项指向的地方的值。这个值可能是DLL中全局变量的绝对地址,或者是DLL中函数的绝对地址等
一般情况下一个PE文件自身的.exe的ImageBase很少会和其子PE文件ImageBase发生冲突,但是.dll文件容易发生冲突。默认情况下.DLL的ImageBase为0x10000000,所以如果一个程序要用的DLL没有合理的修改分配装载起始地址,就可能出现多个DLL的ImageBase都是同一个地址,造成装载冲突
现在我们自己故意修改一个DLL文件的ImageBase,再根据其重定位表去修正,最后存盘,看此DLL文件是否可以正常使用,即手动模拟操作系统修复重定位表的过程
修正过程:比如DLL的ImageBase原来为0x400000,现在修改为0x500000;接着找到重定位表,遍历每个块中的具体项,根据具体项去找哪里的地址值需要修改重定位;找到后,将原来的地址值 + (0x500000 - 0x400000)即可
1.在DLL新增一个节,并将导出表信息移动到这个新的节中
- #include<stdio.h>
- #include<Windows.h>
-
- //本程序均建立在SecHeader->SizeOfRawData >= SecHeader->Misc.VirtualSize的前提下
- DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
- {
- FILE* pFile = NULL;
- pFile = fopen(lpszFile, "rb");
- DWORD FileSize = 0;
- if (!pFile)
- {
- printf("无法打开EXE文件");
- return 0;
- }
- fseek(pFile, 0, SEEK_END);
- FileSize = ftell(pFile);
- return FileSize;
- }
- char* ReadPEFile(const char* lpszFile)
- {
- FILE* pFile = NULL;
- pFile = fopen(lpszFile, "rb");
- DWORD FileSize = ReadPEFileSize(lpszFile);
- fseek(pFile, 0, SEEK_SET);
- char* pFileBuffer = NULL;
- pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
- if (!pFileBuffer)
- {
- printf("分配空间失败");
- fclose(pFile);
- return 0;
- }
- if (!fread(pFileBuffer, FileSize, 1, pFile))
- {
- printf("读取数据失败!");
- free(pFileBuffer);
- fclose(pFile);
- return 0;
- }
- IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
- IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
- if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
- {
- printf("不是有效MZ标志,结束\n");
- free(pFileBuffer);
- fclose(pFile);
- return 0;
- }
- if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
- {
- printf("不是有效的PE标志,打印结束\n");
- free(pFileBuffer);
- pFileBuffer = NULL;
- return 0;
- }
- fclose(pFile);
- return pFileBuffer;
- }
- DWORD Alige(DWORD mes, DWORD aligement) //计算内存对齐后的数据空间
- {
- return mes <= aligement ? aligement : mes + aligement - mes % aligement;
- }
- char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
- {
- char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
- if (pFileBuffer == NULL)
- {
- printf("缓冲区指针无效\n");
- return FALSE;
- }
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
- PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
- char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
- char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
- if (ImageBuffer == NULL)
- {
- printf("申请虚拟内存失败\n");
- return FALSE;
- }
- memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
- memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
- PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
- PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
- char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
- for (int i = 0; i < pFileHeader->NumberOfSections; i++)
- {
- memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
- iSecHeader++;
- iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
- pSecHeader++;
- pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
- }
- char* ExportTable = ImageBuffer + 0x00013B70;
- return ImageBuffer;
- }
- DWORD RVAtoFOA(char* ImageBuffer, char* FileBuffer, DWORD RVA) //该函数用于将内存偏移地址转换为文件绝对地址
- {
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
- PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
- char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
- PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
- PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
- PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
- PIMAGE_SECTION_HEADER iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 使节表指针指向最后一个节表
- char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
- //以下开始虚拟内存节地址转换硬盘内存节地址
- DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
- DWORD SecMin = (DWORD)iDosHeader + iSecHeader->VirtualAddress; //节起始位置
- DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//最后一个节末尾
- RVA = RVA + (DWORD)iDosHeader;
- while (1)
- {
- if (RVA < (DWORD)iDosHeader)
- {
- printf("输入地址过小,请重新输入\n");
- scanf("%d", &RVA);
- }
- else if (RVA > LastSecPoint)
- {
- printf("输入地址过大,请重新输入\n");
- scanf("%d", &RVA);
- }
- else if (RVA <= SecMin && RVA >= (DWORD)iDosHeader)
- {
- printf("RVA to FOA: %d\n", RVA - (DWORD)iDosHeader);
- return RVA - (DWORD)iDosHeader + (DWORD)pDosHeader;
- }
- else
- {
- printf("输入地址正确,开始转换地址\n");
- DWORD offset1 = RVA - (DWORD)iDosHeader; //获取转换地址在虚拟存中相对于DOS头的偏移量
- DWORD nNumber = 0; //用于获取转换地址所在第几个节
- for (int i = 0; i < iFileHeader->NumberOfSections - 1; i++)
- {
- if (offset1 >= (iSecHeader + i)->VirtualAddress && offset1 <= (iSecHeader + i + 1)->VirtualAddress)
- {
- break;
- }
- else if (offset1 >= iSecHeaderEnd->VirtualAddress && offset1 <= LastSecPoint)
- {
- nNumber = iFileHeader->NumberOfSections - 1;
- break;
- }
- else
- {
- nNumber++;
- }
- }
- pSecHeader += nNumber;
- iSecHeader += nNumber;
- DWORD offset2 = offset1 - iSecHeader->VirtualAddress; //获取转换地址在内存中相对于目标节的偏移量
- DWORD pSecBufferPoint = pSecHeader->PointerToRawData + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
- printf("RVA to FOA: %d\n", pSecBufferPoint + (DWORD)pDosHeader);
- return pSecBufferPoint + (DWORD)pDosHeader;
- }
- }
- }
-
- DWORD FOAtoRVA(char* FileBuffer, char* ImageBuffer, DWORD FOA) //该函数用于将文件绝对地址转换为内存偏移地址
- {
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
- PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
- PIMAGE_SECTION_HEADER pSecHeaderEnd = pSecHeader + pFileHeader->NumberOfSections - 1;// 获取文件最后一个节的节表指针
- char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取文件第一个节的指针
- PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
- PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
- PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
- PIMAGE_SECTION_HEADER iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 获取内存最后一个节的节表指针
- char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
- //以下开始文件绝对内存节地址转换虚拟内存偏移地址
- DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
- DWORD SecpMin = (DWORD)pDosHeader + pSecHeader->VirtualAddress; //节起始位置
- DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//内存最后一个节末尾
- DWORD LastpSecPoint = (DWORD)pDosHeader + pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;//文件最后一个节结尾
- while (1)
- {
- if (FOA < (DWORD)pDosHeader)
- {
- printf("输入地址过小,请重新输入\n");
- scanf("%d", &FOA);
- }
- else if (FOA > LastpSecPoint)
- {
- printf("输入地址过大,请重新输入\n");
- scanf("%d", &FOA);
- }
- else if (FOA <= SecpMin && FOA >= (DWORD)pDosHeader)
- {
- printf("FOA to RVA: %d\n", FOA - (DWORD)pDosHeader);
- return FOA - (DWORD)pDosHeader;
- }
- else
- {
- printf("输入地址正确,开始转换地址\n");
- DWORD offset1 = FOA - (DWORD)pDosHeader; //获取转换地址在硬盘上中相对于DOS头的偏移量
- DWORD nNumber = 0; //用于获取转换地址所在第几个节
- for (int i = 0; i < pFileHeader->NumberOfSections - 1; i++)
- {
- if (offset1 >= (pSecHeader + i)->PointerToRawData && offset1 <= (pSecHeader + i + 1)->PointerToRawData)
- {
- break;
- }
- else if (offset1 >= iSecHeaderEnd->PointerToRawData && offset1 <= LastpSecPoint)
- {
- nNumber = pFileHeader->NumberOfSections - 1;
- break;
- }
- else
- {
- nNumber++;
- }
- }
- iSecHeader += nNumber;
- pSecHeader += nNumber;
- DWORD offset2 = offset1 - pSecHeader->PointerToRawData; //获取转换地址在内存中相对于目标节的偏移量
- DWORD iSecBufferPoint = iSecHeader->VirtualAddress + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
- printf("FOA to RVA: %d\n", iSecBufferPoint);
- return iSecBufferPoint;
- }
- }
- }
-
- BOOL AddSection(const char* newFile, const char* lpszFile)
- {
- char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
- if (pFileBuffer == NULL)
- {
- printf("缓冲区指针无效\n");
- return FALSE;
- }
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
- PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针
- PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
- DWORD memsize = (DWORD)((char*)pDosHeader + pDosHeader->e_lfanew) - (DWORD)((char*)pDosHeader + 64);
- pDosHeader->e_lfanew = 64;
- memset((char*)pDosHeader + 64, 0, memsize);
- memcpy((char*)pDosHeader + 64, (char*)pNTHeader, 4 + 20 + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * 40);
- pNTHeader = (PIMAGE_NT_HEADERS)((char*)pDosHeader + pDosHeader->e_lfanew); //获取上移后NT头指针
- pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取上移后标准PE头指针
- pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取上移后可选PE头指针
- pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取上移后文件节表指针
- PIMAGE_SECTION_HEADER pSecHeaderLast = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + (pFileHeader->NumberOfSections - 1) * 40);//获取新增节上一个节指针
- if (pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - 4 - 20 - pFileHeader->SizeOfOptionalHeader >= 2 * pFileHeader->SizeOfOptionalHeader)
- {
- printf("节表后剩余空间足够添加一个新的节表\n");
- }
- else
- {
- printf("节表后剩余空间不足以添加一个新的节表,结束\n");
- return FALSE;
- }
- PIMAGE_SECTION_HEADER pSecHeaderEnd = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + pFileHeader->NumberOfSections * 40);
- memcpy(pSecHeaderEnd, pSecHeader, 40);
- memset((char*)pSecHeaderEnd + 40, 0, 40); //此时将节表后初始化40个0,表示节表结束
- pFileHeader->NumberOfSections += 1;
- strcpy((char*)pSecHeaderEnd->Name, (char*)".mov");
- pSecHeaderEnd->PointerToRawData = pSecHeaderLast->PointerToRawData + pSecHeaderLast->SizeOfRawData;
- pSecHeaderEnd->Misc.VirtualSize = 0x2000;
- pSecHeaderEnd->SizeOfRawData = Alige(0x1000, pOptionHeader->FileAlignment);
- pSecHeaderEnd->VirtualAddress = pSecHeaderLast->VirtualAddress + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment); //获取上个节对齐以后添加节的地址
- pOptionHeader->SizeOfImage = pOptionHeader->SizeOfImage + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment);
- DWORD NewFileSize = pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;
- char* pNewFileBuffer = (char*)malloc(NewFileSize); //重新申请一块大的内存
- if (pNewFileBuffer == NULL) // 判断内存是否生成成功
- {
- printf("缓冲区指针无效\n");
- return FALSE;
- }
- memset(pNewFileBuffer, 0, NewFileSize);
- memcpy(pNewFileBuffer, pDosHeader, (NewFileSize - pSecHeaderEnd->SizeOfRawData));
- FILE* NewFile = fopen(newFile, "wb");
- DWORD Success = fwrite(pNewFileBuffer, NewFileSize, 1, NewFile);
- if (!Success)
- {
- printf("存盘失败");
- }
- free(pFileBuffer);
- return TRUE;
- }
-
- BOOL MoveExportTable(const char* NewlpszFile) //移动导出表
- {
- char* pFileBuffer = ReadPEFile(NewlpszFile); //获取自定义文件内存缓冲区指针
- if (pFileBuffer == NULL)
- {
- printf("硬盘缓冲区指针无效\n");
- return FALSE;
- }
- char* ImageBuffer = FileBufferToImageBuffer(NewlpszFile);
- if (pFileBuffer == NULL)
- {
- printf("内存缓冲区指针无效\n");
- return FALSE;
- }
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
- PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
- PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
- PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
- PIMAGE_SECTION_HEADER EndpSecHeader = pSecHeader + pFileHeader->NumberOfSections - 1;//指向最后一个节用于获取整个文件大小
- if (pOptionHeader->DataDirectory[0].VirtualAddress == NULL && pOptionHeader->DataDirectory[0].Size == NULL)
- {
- printf("该PE文件没有导出表");
- return FALSE;
- }
- PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(ImageBuffer, pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
- char* FunctionsTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfFunctions);
- char* NamesTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfNames);
- char* NameOrdinalsTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfNameOrdinals);
- PIMAGE_SECTION_HEADER MovSecHeader = pSecHeader + pFileHeader->NumberOfSections - 1; //指向移动导出表的节表
- char* MovSecBuffer = (char*)pDosHeader + MovSecHeader->PointerToRawData;//指向移动导出表的节
- memcpy(MovSecBuffer, (char*)ExportTable, 40); //复制导出表
- pOptionHeader->DataDirectory[0].VirtualAddress = FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)MovSecBuffer); //修复数据目录中导出表的地址
- MovSecBuffer += 40; //指向下一个空白区
- char* NewFunctionsTable = MovSecBuffer;
- memcpy(NewFunctionsTable, FunctionsTable, 4 * ExportTable->NumberOfFunctions);//复制函数地址表
- MovSecBuffer += 4 * ExportTable->NumberOfFunctions;//指向下一个空白区
- char* NewNamesTable = MovSecBuffer;
- memcpy(NewNamesTable, NamesTable, 4 * ExportTable->NumberOfNames);//复制函数名称表
- MovSecBuffer += 4 * ExportTable->NumberOfNames;//指向一个空白区
- char* NewNameOrdinalsTable = MovSecBuffer;
- memcpy(NewNameOrdinalsTable, NameOrdinalsTable, 2 * ExportTable->NumberOfNames);// 复制函数序号表。
- char* NewName = MovSecBuffer + 2 * ExportTable->NumberOfNames;//此处开始添加各函数名称
- ExportTable->AddressOfFunctions = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewFunctionsTable); //修复函数地址表地址
- ExportTable->AddressOfNames = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewNamesTable);//修复函数名称表地址
- ExportTable->AddressOfNameOrdinals = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewNameOrdinalsTable);//修复函数序号表地址
- for (int i = 0; i < ExportTable->NumberOfNames; i++)//该循环进行字符串的复制以及函数名称表的地址转换
- {
- char* NameAddress = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, *(DWORD*)NewNamesTable);
- strcpy(NewName, NameAddress);//复制函数名完毕
- DWORD NewNamesAddress = FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewName);
- *(DWORD*) NewNamesTable = NewNamesAddress;
- NewNamesTable += 4; //指向下一个函数名称表要修改的地址
- NewName += strlen(NewName) + 1; //指向下一个要复制函数名的地址,注意结尾处00要加1
- }
- DWORD NewFileSize = EndpSecHeader->PointerToRawData + EndpSecHeader->SizeOfRawData;
- FILE* NewFile = fopen(NewlpszFile, "wb");
- DWORD Success = fwrite(pFileBuffer, NewFileSize, 1, NewFile);
- if (!Success)
- {
- printf("存盘失败");
- }
- return TRUE;
- }
- int main(int argc, char* argv[])
- {
- const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\appverifUI.dll";
- const char* NewlpszFile = "C:\\Users\\扶摇\\Desktop\\NewappverifUI.dll";
-
- AddSection(NewlpszFile, lpszFile);
- MoveExportTable(NewlpszFile);
- return 0;
- }
-
2.使用工具打开修改后的DLL看能否正常解析.
如图经测试,没有一点毛病
3.在DLL中新增一个节,并将重定位表移动到这个新的节中,
4.修改DLL的ImageBase,根据重定位表修正,然后存盘.看叫号否可以使用.