静态代码分析之函数的调用序列
静态代码分析之函数的调用序列
这个是我调试栈回溯欺骗得到的一小部分,一直在一个地方卡住,后来才发现是原码、补码问题,郁闷死..
这部分主要是search call,就是静态检测一个函数中调用了哪几个函数,由于是静态检测,遇到递归类函数应该会出现问题,还没测试..
以下代码中的Trace()函数可以检测到main函数里调用了哪几个函数,调用顺序是什么。检测深度为一层,类似调试里的
逐过程 调试,修改下代码应该也可以完成逐语句的功能,即遇到call跟进,而不是步过.
代码如下:
2 #include "stdarg.h"
3 using namespace std;
4
5 void a(){}
6
7 void b(){}
8
9 void c(){}
10
11 void d(){}
12
13 void e(){}
14
15 void Trace(int count,
![](https://www.cnblogs.com/Images/dot.gif)
16 {
17 cout<<"Main()";
18 if (count<=0)
19 return;
20 int* ary = new int[count];
21 va_list arg_ptr;
22 va_start(arg_ptr,count);
23 int m = va_arg(arg_ptr,int);
24 for (int i=1;i<count;i++)
25 {
26 ary[i] = va_arg(arg_ptr,int);
27 }
28 ary[0] = (int)Trace;
29 va_end(arg_ptr);
30
31 m = *(int*)(m+1) + m + 4;
32 while(true)
33 {
34 if (*(unsigned char*)(m++) == 0xE8)// search the call
35 {
36 int temp = *(int*)m + m + 4;
37 if (temp == ary[0])
38 {
39 cout<<"->Trace()"<<endl;
40 break;
41 }
42 for (int i=1;i<count;i++)
43 {
44 if (temp == ary[i])
45 {
46 cout<<"->";
47 printf("0x%08x",ary[i]);
48 break;
49 }
50 }
51 }
52 }
53
54 }
55
56 int main()
57 {
58 a();
59 b();
60 c();
61 d();
62
63 printf("a:\t0x%08x\n",(int)a);
64 printf("b:\t0x%08x\n",(int)b);
65 printf("c:\t0x%08x\n",(int)c);
66 printf("d:\t0x%08x\n",(int)d);
67 printf("e:\t0x%08x\n",(int)e);
68
69 Trace(6,int(main),(int)c,(int)d,(int)b,(int)a,(int)e);
70
71 return 0;
72 }
先解释Trace()函数:
原型:void Trace(int count,...);
使用了不定参数列表技术,第一个参数代表调用时给了多少个参数(不包括第一个参数)。
其它参数均为各个函数的函数名,其实函数名就是一个指针,对应的是函数的首地址,这个首地址还有点特殊,调用时其实是对应一个IDT表的索引,假设函数a,其首地址为a,反汇编下观察a的位置:
00411145 E9 C6 0E 00 00 jmp std::basic_ostream<char,std::char_traits<char>
![](https://www.cnblogs.com/Images/dot.gif)
0041114A E9 41 04 00 00 jmp d (411590h)
0041114F E9 0C 04 00 00 jmp c (411560h)
00411154 E9 A7 03 00 00 jmp a (411500h) //就是这里
如图,a应该为0x00411500,但是call
a;这句话是如何执行的?
其实就是先索引到0x00411154,然后执行jmp 0x00411500
注意机器码:
E9:JMP的机器码
A7 03 00 00如何解释?
经过我连续两天晚上无聊的调试,我终于明白了…
(0x00411154 + 0x01) + 0x000003A7 + 0x04 = ?
就等于0x00411500 ,就是a的代码段(code
segment)的首地址!
我来解释下为何如此:
首先现在代码走到了0x00411154,越过一个字节的jmp指令即(0x00411154 + 0x01)
A7 03 00 00,这个由Intel大顶机模式可知,在内存中为逆序,实际为0x000003A7,这个代表什么,其实代表的是偏移量(下一条语句的偏移量,所以要加0x05)..我一直把这句话当call来看待,就一直没弄懂,在这卡了两天郁闷死..
还有,我之所以卡着的情况比较复杂,这里是0x0003A7,一眼就明白是偏移量,我调试的时候是0Xfffff748,原因是它是向上偏移的,结果就是个负值,然后负数的反码表示我又没想起,多个原因加起来,我就死这了..
Trace的原理很简单,就是先摸到Main函数的code segment,然后搜索0xE8,也就是call的机器码位置,再判断是哪个函数。
根据上面即可判断:
调用顺序是:
Main()->a()->b()->c()->d()->Trace();
现修改Trace的调用参数:
Trace(5,int(main),(int)c,(int)d,(int)a,(int)e);
截图如下:
正确的发现了所有的调用顺序..
这个玩意是我弄栈回溯的一丁点东西,主要是浪费时间太多了,拿来说下,比较无聊的东西,而且很鸡肋,必须加上回溯功能才有可能被利用,例如溢出的检测等..
-----by NewSketcher