Allen Zhao的学习笔记

非淡泊无以明志,非宁静无以致远。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

(注:本文属于《浅谈C语言下如何批量设置函数指针数组》一文的续写)

 

        前几天由于太懒不想手动将动态链接库里共计上百个子函数的指针逐个赋入函数指针数组,所以想了个通过以第一个装入的函数的地址为起点,扫描函数头机器码并比对来自动设置函数指针数组的方法,成功达到理想效果(具体参见前一篇文章)并顺便在校内、Q空间和博客园发了出来供学习。虽然后来有某专家级程序猿童鞋指出这种方法其实很常用..但考虑到在解决实验问题的当初在网上查找相关资料时并没有找到类似的线索,就还是决定把方案完善后再补一贴希望能给其它初学者一定启示和帮助吧。

 

        关于子函数“函数头”的问题(严格来讲并不能说是函数头,至少和源码中的函数头概念不一样,毕竟源码编译装入后再通过函数指针读出的数据只是函数位于首部的机器码),以编译器为区别分为两部分简述吧。

 

一、gcc

        子函数经gcc编译后再做逆向,可以看到前两条统一是一条压栈指令和一条传送指令:“push %ebp(机器码是0x55)和mov %esp,%ebp(机器码是0x89e5)”,具体意义不再赘述了,有兴趣的自己翻查资料。之后的指令就会因具体实现功能不同而有所区别,比如会因为形参类型不同会导转移指令源操作数的偏移量有所差异。所以如果遇到我之前遇到的那个问题,需要将大量函数装入函数指针数组,屏蔽函数头4字节的高(低)1字节后通过搜索0x5589e5来依次入位应该是比较靠谱的,具体是高还是低,具体是0x5589e5还是0x00e58955可能还跟具体环境的大(小)端法表示有关,总之用这种方法时还得做几个小实验确定具体环境参数。

 

二、cl

        今晚我回windows下用cl编译器重复之前实验时遇到了点意外情况:同样是定义多个子函数,但不管子函数内塞了多少代码,编译运行后去读该函数的头4字节,结果永远是0x0000b1e9,再输出各函数的指针,发现相邻函数的距离恒等于5字节(见下图)。

 

        翻查Intel8086汇编/机器码对照表后得知E9(1110 1001)是一条段内直接转移指令。

 

        也就是说,通过cl输出的可执行程序,在对子程序入口的处理上采取了类似于单片机中处理中断时常用的中断向量表+转移指令的机制,调用子函数时,函数指针所指的区域只有一条转移指令,转移指令所指的地址才是具体的功能实现部分,且各函数指针所指区域以5字节对齐(包含main函数)。

 

        额才疏学浅暂时还说不清cl做这种设定的重要意义有多少,但这么一来批量设置函数指针数组时确实比在linux下用gcc时省力多了。另外,额哪里写的有问题或干脆写错了随时欢迎高手批评指正啊,共同学习……

 

补:之所以是5字节对齐的原因是:cpu在取指令后要译码,针对32位机,译码实际上主要译的是第一个字节,而后根据第一个字节的指令所对应的操作数个数及类型再决定取操作数的长度,0xe9这条转移指令的操作数是4字节(对应32位地址空间),所以一条完整的段内转移就是1+4=5字节了。(转移地址不足4字节的地方用0补齐)

 

另外:由于编译器产生的基于32位机的机器码和基于64位机的机器码差异比较大(比如在gcc下加上-m64后输出的汇编码就不太好摸规律了),而且我还没有在64位下做实验,所以这种方法在64位平台下的修正问题暂时存疑。

posted on 2012-04-29 13:48  Allen Zhao  阅读(609)  评论(0编辑  收藏  举报