Posted on 2012-03-18 17:51
魔のkyo 阅读(561)
评论(0) 编辑 收藏 引用
首先需要读者了解x86平台上的函数调用及返回机制。
通过以上我们了解了函数的参数、函数中的局部变量、函数的返回地址都在堆栈中,那么通过操作堆栈就可以控制函数的返回点,而且我们可以精心构造堆栈来传递函数参数。
来看这样一个例子
1: #include <stdio.h>
2:
3: void func2()
4: {
5: puts("func2");
6: }
7:
8: void func1()
9: {
10: puts("func1");
11: }
12:
13: int main()
14: {
15: __asm
16: {
17: push lable1;
18: push func2;
19: jmp func1;
20: }
21: lable1:
22: puts("after lable1");
23:
24: scanf("%*s");
25: }
输出的结果是
func1
func2
after lable1
我们在jmp到func1之前构造了堆栈,让func1返回时取出的返回地址是func2的首地址,因此在执行完func1之后自动跳转到另func2执行,而执行完func2之后又取出了我们压入的lable1,所以返回了回来。
再来看一个稍微复杂点的例子,还是通过操作堆栈。我们在两个函数直接来回跳转。
Func1使用的是操作系统分配的堆栈,而Func2使用的是利用数组char stack2[STACK_SIZE]模拟的堆栈。
1: #include <stdio.h>
2:
3: void* esp1;
4: void Func1();
5:
6: #define STACK_SIZE 0x80000
7: char stack2[STACK_SIZE];
8: void* esp2 = stack2+STACK_SIZE;
9: void Func2();
10:
11: __declspec( naked )
12: void SetStack2()
13: {
14: __asm
15: {
16: push ebp
17: mov ebp,esp
18: sub esp, __LOCAL_SIZE
19:
20: mov esp, esp2
21: push Func2
22: pushad
23:
24: mov esp2, esp
25: mov esp,ebp
26: pop ebp
27: ret
28: }
29: }
30:
31: __declspec ( naked )
32: void SwitchToFunc1()
33: {
34: __asm
35: {
36: pushad
37: mov esp2, esp
38: mov esp, esp1
39: popad
40: ret
41: }
42: }
43:
44: __declspec ( naked )
45: void SwitchToFunc2()
46: {
47: __asm
48: {
49: pushad
50: mov esp1, esp
51: mov esp, esp2
52: popad
53: ret
54: }
55: }
56:
57: void Func1()
58: {
59: while(1)
60: {
61: puts("func1");
62: SwitchToFunc2();
63: }
64: }
65:
66: void Func2()
67: {
68: while(1)
69: {
70: puts("func2");
71: SwitchToFunc1();
72: }
73: }
74:
75: int main()
76: {
77: SetStack2();
78: Func1();
79:
80: scanf("%*s");
81: }
如果我们把SetStack2写得更通用一些,比如将函数指针通过参数传递进去,在把eps1,eps2做成一个环状链表,这样SwitchToFunc1/SwitchToFunc2也可以合并成在环状链表上切换。
这样就只要SetStack在加入到环状链表中就好像创建了一个线程一样,通过Switch函数切换线程的控制权。具体的代码就不贴出来了,大家可以