• PE文件(十一)移动导出表和重定位表


    移动表的原因

    一个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新增一个节,并将导出表信息移动到这个新的节中

    1. #include<stdio.h>
    2. #include<Windows.h>
    3. //本程序均建立在SecHeader->SizeOfRawData >= SecHeader->Misc.VirtualSize的前提下
    4. DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
    5. {
    6. FILE* pFile = NULL;
    7. pFile = fopen(lpszFile, "rb");
    8. DWORD FileSize = 0;
    9. if (!pFile)
    10. {
    11. printf("无法打开EXE文件");
    12. return 0;
    13. }
    14. fseek(pFile, 0, SEEK_END);
    15. FileSize = ftell(pFile);
    16. return FileSize;
    17. }
    18. char* ReadPEFile(const char* lpszFile)
    19. {
    20. FILE* pFile = NULL;
    21. pFile = fopen(lpszFile, "rb");
    22. DWORD FileSize = ReadPEFileSize(lpszFile);
    23. fseek(pFile, 0, SEEK_SET);
    24. char* pFileBuffer = NULL;
    25. pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
    26. if (!pFileBuffer)
    27. {
    28. printf("分配空间失败");
    29. fclose(pFile);
    30. return 0;
    31. }
    32. if (!fread(pFileBuffer, FileSize, 1, pFile))
    33. {
    34. printf("读取数据失败!");
    35. free(pFileBuffer);
    36. fclose(pFile);
    37. return 0;
    38. }
    39. IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
    40. IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
    41. if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    42. {
    43. printf("不是有效MZ标志,结束\n");
    44. free(pFileBuffer);
    45. fclose(pFile);
    46. return 0;
    47. }
    48. if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
    49. {
    50. printf("不是有效的PE标志,打印结束\n");
    51. free(pFileBuffer);
    52. pFileBuffer = NULL;
    53. return 0;
    54. }
    55. fclose(pFile);
    56. return pFileBuffer;
    57. }
    58. DWORD Alige(DWORD mes, DWORD aligement) //计算内存对齐后的数据空间
    59. {
    60. return mes <= aligement ? aligement : mes + aligement - mes % aligement;
    61. }
    62. char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
    63. {
    64. char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
    65. if (pFileBuffer == NULL)
    66. {
    67. printf("缓冲区指针无效\n");
    68. return FALSE;
    69. }
    70. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
    71. PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
    72. PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
    73. PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
    74. char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
    75. char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
    76. if (ImageBuffer == NULL)
    77. {
    78. printf("申请虚拟内存失败\n");
    79. return FALSE;
    80. }
    81. memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
    82. memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
    83. PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
    84. PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
    85. PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
    86. PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
    87. char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
    88. for (int i = 0; i < pFileHeader->NumberOfSections; i++)
    89. {
    90. memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
    91. iSecHeader++;
    92. iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
    93. pSecHeader++;
    94. pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
    95. }
    96. char* ExportTable = ImageBuffer + 0x00013B70;
    97. return ImageBuffer;
    98. }
    99. DWORD RVAtoFOA(char* ImageBuffer, char* FileBuffer, DWORD RVA) //该函数用于将内存偏移地址转换为文件绝对地址
    100. {
    101. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
    102. PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
    103. PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
    104. PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
    105. char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
    106. PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
    107. PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
    108. PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
    109. PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
    110. PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
    111. PIMAGE_SECTION_HEADER iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 使节表指针指向最后一个节表
    112. char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
    113. //以下开始虚拟内存节地址转换硬盘内存节地址
    114. DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
    115. DWORD SecMin = (DWORD)iDosHeader + iSecHeader->VirtualAddress; //节起始位置
    116. DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//最后一个节末尾
    117. RVA = RVA + (DWORD)iDosHeader;
    118. while (1)
    119. {
    120. if (RVA < (DWORD)iDosHeader)
    121. {
    122. printf("输入地址过小,请重新输入\n");
    123. scanf("%d", &RVA);
    124. }
    125. else if (RVA > LastSecPoint)
    126. {
    127. printf("输入地址过大,请重新输入\n");
    128. scanf("%d", &RVA);
    129. }
    130. else if (RVA <= SecMin && RVA >= (DWORD)iDosHeader)
    131. {
    132. printf("RVA to FOA: %d\n", RVA - (DWORD)iDosHeader);
    133. return RVA - (DWORD)iDosHeader + (DWORD)pDosHeader;
    134. }
    135. else
    136. {
    137. printf("输入地址正确,开始转换地址\n");
    138. DWORD offset1 = RVA - (DWORD)iDosHeader; //获取转换地址在虚拟存中相对于DOS头的偏移量
    139. DWORD nNumber = 0; //用于获取转换地址所在第几个节
    140. for (int i = 0; i < iFileHeader->NumberOfSections - 1; i++)
    141. {
    142. if (offset1 >= (iSecHeader + i)->VirtualAddress && offset1 <= (iSecHeader + i + 1)->VirtualAddress)
    143. {
    144. break;
    145. }
    146. else if (offset1 >= iSecHeaderEnd->VirtualAddress && offset1 <= LastSecPoint)
    147. {
    148. nNumber = iFileHeader->NumberOfSections - 1;
    149. break;
    150. }
    151. else
    152. {
    153. nNumber++;
    154. }
    155. }
    156. pSecHeader += nNumber;
    157. iSecHeader += nNumber;
    158. DWORD offset2 = offset1 - iSecHeader->VirtualAddress; //获取转换地址在内存中相对于目标节的偏移量
    159. DWORD pSecBufferPoint = pSecHeader->PointerToRawData + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
    160. printf("RVA to FOA: %d\n", pSecBufferPoint + (DWORD)pDosHeader);
    161. return pSecBufferPoint + (DWORD)pDosHeader;
    162. }
    163. }
    164. }
    165. DWORD FOAtoRVA(char* FileBuffer, char* ImageBuffer, DWORD FOA) //该函数用于将文件绝对地址转换为内存偏移地址
    166. {
    167. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
    168. PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
    169. PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
    170. PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
    171. PIMAGE_SECTION_HEADER pSecHeaderEnd = pSecHeader + pFileHeader->NumberOfSections - 1;// 获取文件最后一个节的节表指针
    172. char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取文件第一个节的指针
    173. PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
    174. PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
    175. PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
    176. PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
    177. PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
    178. PIMAGE_SECTION_HEADER iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 获取内存最后一个节的节表指针
    179. char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
    180. //以下开始文件绝对内存节地址转换虚拟内存偏移地址
    181. DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
    182. DWORD SecpMin = (DWORD)pDosHeader + pSecHeader->VirtualAddress; //节起始位置
    183. DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//内存最后一个节末尾
    184. DWORD LastpSecPoint = (DWORD)pDosHeader + pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;//文件最后一个节结尾
    185. while (1)
    186. {
    187. if (FOA < (DWORD)pDosHeader)
    188. {
    189. printf("输入地址过小,请重新输入\n");
    190. scanf("%d", &FOA);
    191. }
    192. else if (FOA > LastpSecPoint)
    193. {
    194. printf("输入地址过大,请重新输入\n");
    195. scanf("%d", &FOA);
    196. }
    197. else if (FOA <= SecpMin && FOA >= (DWORD)pDosHeader)
    198. {
    199. printf("FOA to RVA: %d\n", FOA - (DWORD)pDosHeader);
    200. return FOA - (DWORD)pDosHeader;
    201. }
    202. else
    203. {
    204. printf("输入地址正确,开始转换地址\n");
    205. DWORD offset1 = FOA - (DWORD)pDosHeader; //获取转换地址在硬盘上中相对于DOS头的偏移量
    206. DWORD nNumber = 0; //用于获取转换地址所在第几个节
    207. for (int i = 0; i < pFileHeader->NumberOfSections - 1; i++)
    208. {
    209. if (offset1 >= (pSecHeader + i)->PointerToRawData && offset1 <= (pSecHeader + i + 1)->PointerToRawData)
    210. {
    211. break;
    212. }
    213. else if (offset1 >= iSecHeaderEnd->PointerToRawData && offset1 <= LastpSecPoint)
    214. {
    215. nNumber = pFileHeader->NumberOfSections - 1;
    216. break;
    217. }
    218. else
    219. {
    220. nNumber++;
    221. }
    222. }
    223. iSecHeader += nNumber;
    224. pSecHeader += nNumber;
    225. DWORD offset2 = offset1 - pSecHeader->PointerToRawData; //获取转换地址在内存中相对于目标节的偏移量
    226. DWORD iSecBufferPoint = iSecHeader->VirtualAddress + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
    227. printf("FOA to RVA: %d\n", iSecBufferPoint);
    228. return iSecBufferPoint;
    229. }
    230. }
    231. }
    232. BOOL AddSection(const char* newFile, const char* lpszFile)
    233. {
    234. char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
    235. if (pFileBuffer == NULL)
    236. {
    237. printf("缓冲区指针无效\n");
    238. return FALSE;
    239. }
    240. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
    241. PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针
    242. PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取标准PE头指针
    243. PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
    244. PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
    245. DWORD memsize = (DWORD)((char*)pDosHeader + pDosHeader->e_lfanew) - (DWORD)((char*)pDosHeader + 64);
    246. pDosHeader->e_lfanew = 64;
    247. memset((char*)pDosHeader + 64, 0, memsize);
    248. memcpy((char*)pDosHeader + 64, (char*)pNTHeader, 4 + 20 + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * 40);
    249. pNTHeader = (PIMAGE_NT_HEADERS)((char*)pDosHeader + pDosHeader->e_lfanew); //获取上移后NT头指针
    250. pFileHeader = (PIMAGE_FILE_HEADER)((char*)pNTHeader + 4);//获取上移后标准PE头指针
    251. pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取上移后可选PE头指针
    252. pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取上移后文件节表指针
    253. PIMAGE_SECTION_HEADER pSecHeaderLast = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + (pFileHeader->NumberOfSections - 1) * 40);//获取新增节上一个节指针
    254. if (pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - 4 - 20 - pFileHeader->SizeOfOptionalHeader >= 2 * pFileHeader->SizeOfOptionalHeader)
    255. {
    256. printf("节表后剩余空间足够添加一个新的节表\n");
    257. }
    258. else
    259. {
    260. printf("节表后剩余空间不足以添加一个新的节表,结束\n");
    261. return FALSE;
    262. }
    263. PIMAGE_SECTION_HEADER pSecHeaderEnd = (PIMAGE_SECTION_HEADER)((char*)pSecHeader + pFileHeader->NumberOfSections * 40);
    264. memcpy(pSecHeaderEnd, pSecHeader, 40);
    265. memset((char*)pSecHeaderEnd + 40, 0, 40); //此时将节表后初始化400,表示节表结束
    266. pFileHeader->NumberOfSections += 1;
    267. strcpy((char*)pSecHeaderEnd->Name, (char*)".mov");
    268. pSecHeaderEnd->PointerToRawData = pSecHeaderLast->PointerToRawData + pSecHeaderLast->SizeOfRawData;
    269. pSecHeaderEnd->Misc.VirtualSize = 0x2000;
    270. pSecHeaderEnd->SizeOfRawData = Alige(0x1000, pOptionHeader->FileAlignment);
    271. pSecHeaderEnd->VirtualAddress = pSecHeaderLast->VirtualAddress + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment); //获取上个节对齐以后添加节的地址
    272. pOptionHeader->SizeOfImage = pOptionHeader->SizeOfImage + Alige(pSecHeaderLast->Misc.VirtualSize, pOptionHeader->SectionAlignment);
    273. DWORD NewFileSize = pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;
    274. char* pNewFileBuffer = (char*)malloc(NewFileSize); //重新申请一块大的内存
    275. if (pNewFileBuffer == NULL) // 判断内存是否生成成功
    276. {
    277. printf("缓冲区指针无效\n");
    278. return FALSE;
    279. }
    280. memset(pNewFileBuffer, 0, NewFileSize);
    281. memcpy(pNewFileBuffer, pDosHeader, (NewFileSize - pSecHeaderEnd->SizeOfRawData));
    282. FILE* NewFile = fopen(newFile, "wb");
    283. DWORD Success = fwrite(pNewFileBuffer, NewFileSize, 1, NewFile);
    284. if (!Success)
    285. {
    286. printf("存盘失败");
    287. }
    288. free(pFileBuffer);
    289. return TRUE;
    290. }
    291. BOOL MoveExportTable(const char* NewlpszFile) //移动导出表
    292. {
    293. char* pFileBuffer = ReadPEFile(NewlpszFile); //获取自定义文件内存缓冲区指针
    294. if (pFileBuffer == NULL)
    295. {
    296. printf("硬盘缓冲区指针无效\n");
    297. return FALSE;
    298. }
    299. char* ImageBuffer = FileBufferToImageBuffer(NewlpszFile);
    300. if (pFileBuffer == NULL)
    301. {
    302. printf("内存缓冲区指针无效\n");
    303. return FALSE;
    304. }
    305. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
    306. PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
    307. PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
    308. PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
    309. PIMAGE_SECTION_HEADER EndpSecHeader = pSecHeader + pFileHeader->NumberOfSections - 1;//指向最后一个节用于获取整个文件大小
    310. if (pOptionHeader->DataDirectory[0].VirtualAddress == NULL && pOptionHeader->DataDirectory[0].Size == NULL)
    311. {
    312. printf("该PE文件没有导出表");
    313. return FALSE;
    314. }
    315. PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(ImageBuffer, pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
    316. char* FunctionsTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfFunctions);
    317. char* NamesTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfNames);
    318. char* NameOrdinalsTable = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, ExportTable->AddressOfNameOrdinals);
    319. PIMAGE_SECTION_HEADER MovSecHeader = pSecHeader + pFileHeader->NumberOfSections - 1; //指向移动导出表的节表
    320. char* MovSecBuffer = (char*)pDosHeader + MovSecHeader->PointerToRawData;//指向移动导出表的节
    321. memcpy(MovSecBuffer, (char*)ExportTable, 40); //复制导出表
    322. pOptionHeader->DataDirectory[0].VirtualAddress = FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)MovSecBuffer); //修复数据目录中导出表的地址
    323. MovSecBuffer += 40; //指向下一个空白区
    324. char* NewFunctionsTable = MovSecBuffer;
    325. memcpy(NewFunctionsTable, FunctionsTable, 4 * ExportTable->NumberOfFunctions);//复制函数地址表
    326. MovSecBuffer += 4 * ExportTable->NumberOfFunctions;//指向下一个空白区
    327. char* NewNamesTable = MovSecBuffer;
    328. memcpy(NewNamesTable, NamesTable, 4 * ExportTable->NumberOfNames);//复制函数名称表
    329. MovSecBuffer += 4 * ExportTable->NumberOfNames;//指向一个空白区
    330. char* NewNameOrdinalsTable = MovSecBuffer;
    331. memcpy(NewNameOrdinalsTable, NameOrdinalsTable, 2 * ExportTable->NumberOfNames);// 复制函数序号表。
    332. char* NewName = MovSecBuffer + 2 * ExportTable->NumberOfNames;//此处开始添加各函数名称
    333. ExportTable->AddressOfFunctions = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewFunctionsTable); //修复函数地址表地址
    334. ExportTable->AddressOfNames = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewNamesTable);//修复函数名称表地址
    335. ExportTable->AddressOfNameOrdinals = (DWORD)FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewNameOrdinalsTable);//修复函数序号表地址
    336. for (int i = 0; i < ExportTable->NumberOfNames; i++)//该循环进行字符串的复制以及函数名称表的地址转换
    337. {
    338. char* NameAddress = (char*)RVAtoFOA(ImageBuffer, pFileBuffer, *(DWORD*)NewNamesTable);
    339. strcpy(NewName, NameAddress);//复制函数名完毕
    340. DWORD NewNamesAddress = FOAtoRVA(pFileBuffer, ImageBuffer, (DWORD)NewName);
    341. *(DWORD*) NewNamesTable = NewNamesAddress;
    342. NewNamesTable += 4; //指向下一个函数名称表要修改的地址
    343. NewName += strlen(NewName) + 1; //指向下一个要复制函数名的地址,注意结尾处00要加1
    344. }
    345. DWORD NewFileSize = EndpSecHeader->PointerToRawData + EndpSecHeader->SizeOfRawData;
    346. FILE* NewFile = fopen(NewlpszFile, "wb");
    347. DWORD Success = fwrite(pFileBuffer, NewFileSize, 1, NewFile);
    348. if (!Success)
    349. {
    350. printf("存盘失败");
    351. }
    352. return TRUE;
    353. }
    354. int main(int argc, char* argv[])
    355. {
    356. const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\appverifUI.dll";
    357. const char* NewlpszFile = "C:\\Users\\扶摇\\Desktop\\NewappverifUI.dll";
    358. AddSection(NewlpszFile, lpszFile);
    359. MoveExportTable(NewlpszFile);
    360. return 0;
    361. }

    2.使用工具打开修改后的DLL看能否正常解析.

    如图经测试,没有一点毛病
    3.在DLL中新增一个节,并将重定位表移动到这个新的节中,
    4.修改DLL的ImageBase,根据重定位表修正,然后存盘.看叫号否可以使用.

  • 相关阅读:
    [架构之路-58]:目标系统 - 平台软件 - 中间件软件(嵌入式)与中间件平台(中台)以及中间件的发展阶段与提供服务的方式
    一文读懂在Postgresql数据库中进行窗口函数的操作
    Docker 之 高级篇
    线上答题活动小程序结合线下大屏复盘总结
    基于JAVA人事管理系统测试视频计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    Linux安装Ansible管理工具
    报错 documentation/kbuild: is a directory. stop(Windows 内置Linux子系统WSL编译Linux内核)
    Python Math模块
    nuxt使用echarts
    六.方法与接口
  • 原文地址:https://blog.csdn.net/2301_78838647/article/details/140448903