如何通过.plt的sh_addr计算其内存虚拟地址?PLT/GOT篡改检测方法
问题解答:从磁盘ELF的PLT/GOT地址计算内存地址,及PLT/GOT篡改检测
核心问题:从磁盘ELF的sh_addr计算内存中PLT/GOT地址
可以计算,但需要结合共享库的加载基地址,具体逻辑如下:
- 磁盘上共享库的
.plt/.got段的sh_addr是链接时的虚拟偏移量(因为共享库是PIC/位置无关代码,链接器默认将其基地址设为0)。 - 内存中该段的实际虚拟地址 = 共享库的加载基地址 + 磁盘ELF中对应段的
sh_addr。
如何获取加载基地址?
加载基地址是操作系统动态加载器为共享库分配的内存起始地址,可通过以下方式获取:
- 读取目标进程的
/proc/[pid]/maps文件:找到对应共享库的映射条目,行首的起始地址即为加载基地址。例如:
这里的7f1234000000-7f123421e000 r-xp 00000000 08:01 131073 /lib/x86_64-linux-gnu/libc-2.31.so7f1234000000就是libc.so.6的加载基地址。 - 附加到进程(如用
ptrace)后,读取内存中共享库的ELF头部,解析程序头(PT_LOAD段),第一个可执行PT_LOAD段的实际加载地址即为基地址。
实际需求:检测进程内存中共享库的PLT/GOT篡改
结合上述地址计算方法,可按以下步骤实现检测:
步骤1:定位内存中的PLT/GOT
- 从磁盘上的共享库文件,用
readelf或自定义解析工具获取.plt/.got段的sh_addr(虚拟偏移)、sh_offset(文件偏移)和sh_size(段长度)。 - 从目标进程的
/proc/[pid]/maps获取该共享库的加载基地址。 - 计算内存中PLT/GOT的起始地址:
内存地址 = 加载基地址 + sh_addr。
步骤2:检测PLT篡改
- 读取磁盘上共享库
.plt段的原始内容(从文件偏移sh_offset开始,读取sh_size字节)。 - 读取目标进程内存中对应PLT地址的内容(可用
process_vm_readv或ptrace读取)。 - 逐字节对比两者内容:若存在不一致,说明PLT操作码被篡改。
步骤3:检测GOT篡改
- 读取内存中GOT段的所有条目(每个条目是一个指针,长度对应架构,如x86-64为8字节)。
- 从
/proc/[pid]/maps获取目标进程所有合法共享库的内存地址范围(执行段r-xp、数据段r--p等)。 - 检查每个GOT条目指向的地址:
- 若地址不在任何合法共享库的地址范围内,属于异常。
- 若地址指向非执行区域但被用作函数跳转目标,也属于异常。
- 注意区分正常的延迟绑定:GOT条目在函数首次调用前会指向PLT的跳转代码,调用后会更新为实际函数地址,这些都属于合法情况。
注意事项
- ASLR影响:共享库的加载基地址每次启动可能不同,必须实时从目标进程获取,不能使用固定值。
- 权限要求:读取
/proc文件和进程内存需要对应权限(如root或同用户权限)。 - 反调试对抗:部分恶意进程会使用反调试技术阻止
ptrace或内存读取,需针对性处理。
内容的提问来源于stack exchange,提问作者localacct
相关产品推荐
相关产品推荐

