简单的栈回溯Trackback文件简析.docx
《简单的栈回溯Trackback文件简析.docx》由会员分享,可在线阅读,更多相关《简单的栈回溯Trackback文件简析.docx(15页珍藏版)》请在冰豆网上搜索。
![简单的栈回溯Trackback文件简析.docx](https://file1.bdocx.com/fileroot1/2022-10/9/5d6d9e13-63bd-416d-8268-1b7ccc0a3afe/5d6d9e13-63bd-416d-8268-1b7ccc0a3afe1.gif)
简单的栈回溯Trackback文件简析
简单的栈回溯&简单的栈回溯欺骗--简单分析
1.#include
2.#include
3.#include
4.
5.#pragma warning(disable:
4311 4312 4313)
6.
7.int fake_ebp_1, fake_ebp_2;
8.
9.void __stdcall _StackTrace(int StackBase, int ebp, int esp)
10.{
11. int limit = 30, retaddr, calladdr;
12. printf("ebp ret call\n");
13. while ((ebp > esp) && (ebp < StackBase) && (limit--))
14. {
15. retaddr = *(int *)(ebp + 4);
16. calladdr = 0;
17. __try
18. {
19. if (*(unsigned char *)(retaddr - 5) == 0xe8)
20. {
21. calladdr = *(int *)(retaddr - 4) + retaddr;
22. }
23. } __except (EXCEPTION_EXECUTE_HANDLER) {}
24. printf("%08x %08x %08x\n", ebp, retaddr, calladdr);
25. ebp = *(int *)ebp;
26. }
27. printf("trace completed.\n");
28.}
29.
30.__declspec(naked) void __stdcall StackTrace()
31.{
32. // iceboy's stack trace
33. __asm {
34. push esp
35. push ebp
36. push fs:
[0x4] //; StackBase
37. call _StackTrace
38. retn
39. }
40.}
41.
42.void b(int, int)
43.{
44. StackTrace();
45.}
46.
47.void a(int, int, int)
48.{
49. b(0, 0);
50.}
51.
52.int search_call(int fn1, int fn2)
53.{
54. while (true)
55. {
56. if (*(unsigned char *)(fn1++) == 0xe8)
57. {
58. if ((*(int *)fn1 + fn1 + 4) == fn2)
59. {
60. return fn1 + 4;
61. }
62. }
63. }
64.}
65.
66.// fake call
67.__declspec(naked) void __stdcall d(int, int)
68.{
69. __asm {
70. push fake_ebp_1
71. push ebp
72. mov ebp, esp
73. push fake_ebp_2
74. push ebp
75. mov ebp, esp
76. call StackTrace
77. pop esp
78. pop ebp
79. pop eax
80. retn 8
81. }
82.}
83.
84.// fake call & hide self
85.__declspec(naked) void __stdcall e(int, int)
86.{
87. __asm {
88. push ebp
89. mov ebp, [ebp]
90. push 0
91. push 0
92. call d
93. pop ebp
94. retn 8
95. }
96.}
97.
98.void c(int, int, bool hideself)
99.{
100. if (!
hideself)
101. {
102. d(0, 0);
103. } else
104. {
105. e(0, 0);
106. }
107.}
108.
109.int main()
110.{
111. fake_ebp_1 = search_call((int)main, (int)a);
112. fake_ebp_2 = search_call((int)a, (int)b);
113.
114.
115. printf("address of function a:
0x%08x\n", a);
116. printf("address of function b:
0x%08x\n", b);
117. printf("address of function c:
0x%08x\n", c);
118. printf("address of function main:
0x%08x\n", main);
119.
120. printf("\ntest 1:
standard call\n");
121. a(0, 0, 0);
122.
123. printf("\ntest 2:
fake call\n");
124. c(0, 0, false);
125.
126. printf("\ntest 3:
fake call & hide self\n");
127. c(0, 0, true);
128.
129. printf("\npress any key to continue...");
130. _getch();
131. printf("\n");
132. return 0;
133.}
在我的机器上输出如下:
我们就顺着程序来一步一 步分析,需要什么知识点时就说明什么知识点。
首先
1.fake_ebp_1 = search_call((int)main, (int)a);
2.fake_ebp_2 = search_call((int)a, (int)b);
search_call的代码如下:
1.int search_call(int fn1, int fn2)
2.{
3. while (true)
4. {
5. if (*(unsigned char *)(fn1++) == 0xe8)
6. {
7. if ((*(int *)fn1 + fn1 + 4) == fn2)
8. {
9. return fn1 + 4;
10. }
11. }
12. }
13.}
14.首先要知道的是x86平台call指令有很多种,有e8call,ff15call,还有regcall.对于e8call,我们可以计算出call的地址,也就是被调函数首地址,其它的call就困难了许多.那么e8call具体是如何计算机被调函数的首地址的呢?
15.e8是call的指令机器码,这点大家都很清楚。
e8后面的四字节机器码是此时指令指针(EIP)的值与目的地址(被调函数首地址)的差值,也即两地址间的相对偏移。
指令指针在这里可以叫做返回地址,意思就是从被调函数返回后应执行的第一条指令的地址。
16.明白了这些,就很容易计算出目的地址(被调函数首地址):
目的地址= 返回地址+ 相对偏移 举个例子吧:
17.0045F761 > \8B8D D8000000 MOV ECX,DWORD PTR SS:
[EBP+D8]
18.0045F767 . 53 PUSH EBX
19.0045F768 . 03CA ADD ECX,EDX
20.0045F76A . 51 P