DLL引入表重定位 .reloc

本文探讨了DLL文件在不同基址加载时,原生代码与补丁代码中字符串引用的表现差异,深入分析了PE文件的重定位机制及其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   前几天日,在做彩虹显IP补丁程序的时候,需要将原格式化字符串从"%d.%d.*.*"修改成"%d.%d.%d.%d"以使其能显示完整的IP,在补丁代码中需要引用该字符串,于是直接从原代码中copy了一段引用该字符串的代码。头几天还能正常工作,不知什么原因程序突然无法运行了,打开调试器跟踪才发现问题就出在对字符串的引用上,模块载入后的基址与前几天相比发生了改变,补丁中对字符串的引用指向了一个无效空间,但是原代码中对字符串的引用却能修正到正确地址空间上。于是,我看到了“奇怪”的现象:相同的二进制代码却能导致不同运行结果。

    虽然绕道解决了该字符串调用的问题,但是没弄懂其中原因始终觉得不爽,于是决定写点代码实验一下。写一个空的DLL,在DllMain()中添加一条OutputDebugString("BB7887"),然后写一个可执行程序用LoadLibrary()载入该DLL文件,为了使“同一个”DLL能加载到多个地址空间,最简单的方法就是将该DLL文件复制为多个DLL文件然后在可执行程序中多次加载,呵呵,偷个懒!用OllyDbg跟踪,得到的结论是,无论该DLL被加载到任何基址上,原代码中对字符串的引用能成功,但相同的以补丁方式修改的代码,只有在DLL加载到默认的0x10000000基址上能正确引用字符串外,其余的都指向了无效空间。……Google之后才认识到,PE文件在被载入时可以根据基地址对代码中的地址进修修正,也就是“重定位”。所以虽然同样是push xxxx却能有不同的结果,原代码中的push xxxx可以被修正为push bbbb,其中bbbb = xxxx + aaaa,aaaa为DLL模块的基址,而补丁中的push xxxx就没这么幸运了,没人来管这个后娘养的孩子,xxxx被直接入栈。

    重定位的基本原理:重定位以来与PE头文件的.reloc段,该段以索引的方式指出代码段中所有需要修正的数的地址,该地址以RVA相对虚地址的方式来表示,即该数值表示到基地址的偏移量。.reloc段由一系列子项组成,每项的数据结构如下:
DWORD VirtualAddress ;    起始 RVA 值,每一个偏移位置必须加上此值才能够构成一个真正的 RVA
DWORD SizeOfBlock ;       该项的总长
WORD offset[n] ;            偏移地址,只用到该字的低12bits,高4bits为标志为,一般为3
如:
Offset        0  1  2   3   4   5   6  7  8   9   A  B   C  D   E  F   Ascii
00000000  00 10 00 00 60 00 00 00 35 30 43 30 5F 30 80 30  ...`...50C0_0€0
00000010  8C 30 92 30 9C 30 AE 30 B8 30 BE 30 D2 30 D8 30  ????????
00000020  F0 30 F5 30 FC 30 03 31 0A 31 17 31 32 31 38 31  ???1.112181
00000030  3E 31 44 31 4A 31 50 31 5E 31 66 31 6C 31 77 31  >1D1J1P1^1f1l1w1
00000040  84 31 8C 31 9A 31 9F 31 A4 31 A9 31 B4 31 C1 31  ????????
00000050  CB 31 E0 31 EC 31 F2 31 14 32 26 32 82 32 9E 32  ????2&2??
对应于:
->Relocation Directory
   1. Relocation Block:
    VirtualAddress:  0x00001000  (".text")
    SizeOfBlock:     0x00000060  (0x002C block entries)
    RVA        Type
    ---------- -----------------
    0x00001035 HIGHLOW
    0x00001043 HIGHLOW
    0x0000105F HIGHLOW
    0x00001080 HIGHLOW
    0x0000108C HIGHLOW
    0x00001092 HIGHLOW
    0x0000109C HIGHLOW
    0x000010AE HIGHLOW
    0x000010B8 HIGHLOW
    0x000010BE HIGHLOW

    在上面的例子中,所能表示的最大偏移仅仅为0x1ffff,远远是不够用的,所以.reloc段通常是由多个偏移索引的子项组成的,每个子项只能负责0x1000范围内的偏移量,每个子项以0x1000来分段对齐,比如,要指出RVA为0x43569865处的数据需重定位,那么VirtualAddress = 0x43569000,其中某索引的值offset = 0x865 or 0x3000 = 0x3865 。

    用LordPE查看一下,定有更深刻的认识。另外:被重定位的数据在OllyDbg中都以下划线的方式加以表示。

<think>好的,我现在需要帮助用户了解如何从PE可执行文件中移除.reloc节区。根据用户提供的引用内容,我需要整理出一个清晰的步骤指南。首先,我应该确认用户可能的需求:他们可能是在优化可执行文件的大小,或者出于其他目的需要删除.reloc节区。需要注意的是,删除.reloc可能会影响程序的兼容性,尤其是在地址空间随机化(ASLR)的情况下,但根据引用[2]和[3],对于EXE文件来说,删除后通常仍能正常运行,而DLL或系统文件可能不行。 接下来,我需要从提供的引用中提取关键步骤。引用[2]提到使用PEView和Win Hex Editor手动删除,并指出.reloc通常位于最后。另外,引用[1]展示了删除后文件大小减少的例子。我需要将这些信息整合成一个步骤流程,确保用户能按部就班操作。 步骤可能包括:使用工具定位.reloc节区、调整节区头、修改文件大小、更新PE头中的相关字。需要注意,手动编辑二进制文件存在风险,可能需要备份。同时,要提醒用户删除.reloc可能影响ASLR,尽管EXE文件可能不受影响,但需根据具体情况评估。 还需要考虑用户可能遇到的问题,例如如何找到DataDirectory[5]的地址,或者如何确保没有其他依赖.reloc的情况。此外,是否需要使用自动化工具还是手动操作,引用中提到的工具是否仍然适用,可能需要进一步说明。 最后,生成相关问题,确保覆盖用户可能的后续疑问,比如其他优化方法、ASLR的影响,或者自动化工具推荐。</think>### 如何从PE可执行文件中删除.reloc节区? #### 操作步骤 1. **备份原始文件** 在修改前务必备份可执行文件,避免操作失误导致文件损坏。 2. **定位.reloc节区** - 使用工具(如PEView或CFF Explorer)查看PE文件结构。 - 在`IMAGE_OPTIONAL_HEADER`的`DataDirectory[5]`中获取基址重定位的RVA和大小[^2][^3]。 - 确认.reloc节区位置(通常位于所有节区末尾)。 3. **删除.reloc节区** - **修改节区头**:将.reloc节区对应的`IMAGE_SECTION_HEADER`内容清零,或删除其条目。 - **调整文件大小**:用十六进制编辑器(如Win Hex)直接删除.reloc节区的二进制数据,并裁剪文件末尾对应的字节数[^1][^2]。 - **更新PE头**: - 将`DataDirectory[5]`的`VirtualAddress`和`Size`设为0。 - 修改`NumberOfSections`字,减少节区总数。 4. **验证文件功能** 运行修改后的文件,测试其是否正常执行(注意:DLL/SYS文件删除.reloc可能导致崩溃,EXE文件一般不受影响)[^3]。 #### 注意事项 - **兼容性影响**:删除.reloc后,程序将无法支持ASLR(地址空间布局随机化),可能降低安全性[^2]。 - **工具推荐**:可使用自动化工具(如`LordPE`或`PE Tools`)简化操作,减少手动编辑错误风险。 - **文件对齐**:删除后需检查节区对齐是否符合PE格式要求(如文件对齐值一般为0x200)。 ```plaintext 示例修改前后对比(引用[1]): 原始文件大小:7168字节 删除.reloc后:减少400H(1024字节)→ 最终大小6144字节 ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值