linux 下实现在程序运行时的函数替换(热补丁)
时间 来源
声明:以下的代码成果,是参考了网上的 技术,在本文的最后会给出地址,同时非常感谢 技
术原作者的分享。
但是 文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的。也正因为
这些错误,加深了我的学习深度。
最近因为在学习一些调试的技术,但是很少有提到如何在函数运行时实现函数替换的。
为什么会想到这一点?因为在学习调试时,难免会看到一些内核方面的调试技术,内核中的调试有一
个 ,很强大,可以实现运行时的函数替换。其原理就是 ,钩子,但是学习了这个
之后会发现, 内部有检测所要钩的函数是不是属于内核空间,必须是内核函数才能实现替换。而
实际上,我的工作大部分还是在应用层的,所以想要实现应用程序的热补丁技术。
一些基础的知识这边的就不展开了,需要的基础有, ! 文件格式,","#,应用程序间
通信时的信号,汇编。
、 ! 文件加载过程
! 简单地说是由以下四部分组成的, ! 文件头,# 和 "#,内容。其
中 # 是运行时使用的,而 "# 并不会被加载进程序运行空间,但他们可以
在编译时被指定该段的加载地址等信息,当然一般这个链接脚本 # 是由 默认的。
第一步,加载 ! 文件头,检验文件类型版本等,重要的是找到 # 的地址和 #
的个数,如果连接器脚本是默认的,那么 ! 文件头会被加载在 $%$ 地址处。
第二步,加载 #,接着扫描 #,找到一个类型为 &'()*'+,& 的
#,这个 # 里面放着的是有关解释器的地址,这时候将解释器程序的 ! 文件头加
载进来。一般是这样:
)*'+,&%$%$%$%$%,
-,."""/ / # 0
第三步,扫描 #,如果类型为 &'(1234,则将该段加载进来。
第四步,判断是否需要解释器程序,如果需要,把解释器程序加载进来,并把程序入口设置为解释器
程序的地址。否则是应用程序本身的入口。反汇编为("" 标号。
第五步,设置命令行传入的参数等应用程序需要的信息。
第六步,解释器程序开始运行,加载程序需要的库,填写重定向符号表中的地址信息。
! 文件动态链接过程
上一步,解释器程序根据 # 已经将应用程序的段都加载进内存了,接下来再扫描
#,找到类型为 &'(45*36)7,这里面包含了很多由 "# 描述的内容,包
括重定向表,符号表,字符串表等等。解释器需要这个段描述的一些信息。
4'(*++4+4 描述了所需要的动态库名称,4'(,+1 描述了重定位表地址,4'(86&,+1 描述了重定
位表地址9这个表是懒惰链接使用的:,4'(&1';2' 全局偏移表地址。
此时解释器程序就可以根据所需要的动态库,将其加载进内存。每一个被加载进来的库的相关信息会
被记录在 ( 结构中,这个结构是一个链表,保存了所有的动态信息。
其中,全局偏移表 ","-0保存了 &'(45*36)7 的起始地址,"-0保存 ( 的地址,
而 ( 中就可以找到 &'(45*36)7 的起始地址,和下一个或者上一个共享文件或者可执行文件的
( 地址。
4'(,+1 这个重定向表中的符号必须在此时就被解析完成。
而 4'(86&,+1 这个重定向表中的符号可以在运行时再解析。
评论3
最新资源