(继上)这种间接调用的方式要比前面看到的直接调用方式麻烦很多。来看一个复杂一点的例子:
#include <stdio.h> void function0() { return; } void function1() { return; } int main() { int fn[2]; fn[0] = (int)function0; fn[1] = (int)function1; void (*f)(); for (int i = 0;i < 2;i++) { f = (void (*)())fn[i]; f(); } return 0; }
这段函数定义了两个函数function1()和function2(),两个函数都是空的,只有一个返回语句。在主函数定义了一个int型的数组,大小为2,并分别给他们赋于函function1()function2()地址,再定义一个函数指针f,使用一个for循环,分别获取储存在fn数组里的函数function1()和function2()地址并调用。编译后使用IDA分析:
.text:00401010 push ebp .text:00401011 mov ebp, esp .text:00401013 sub esp, 10h
这里给整型数组fn、函数指针f、循环变量i开辟储存空间。
.text:00401016 mov [ebp+fn], offset ?function1@@YAXXZ ; function1(void) .text:0040101D mov [ebp+fn+4], offset ?function1@@YAXXZ ; function1(void)
获取两个函数的地址放到数组fn中。
.text:00401024 mov [ebp+i], 0 .text:0040102B jmp short loc_401036 .text:0040102D mov eax, [ebp+i] .text:00401030 add eax, 1 .text:00401033 mov [ebp+i], eax .text:00401036 cmp [ebp+i], 2 .text:0040103A jge short loc_40104B .text:0040103C mov ecx, [ebp+i] .text:0040103F mov edx, [ebp+ecx*4+fn] .text:00401043 mov [ebp+f], edx .text:00401046 call [ebp+f] .text:00401049 jmp short loc_40102D
这里是循环体的代码,首先给循环变量赋初值0,然后跳转到0x00401036把循环变量和2进行比较,如果大于等于2则跳出循环体;如果小于2则不执行跳转,取变量fn[i]中的数据,赋值给函数指针f,通过函数指针f调用函数,最后跳回循环头部
.text:0040104B xor eax, eax .text:0040104D mov esp, ebp .text:0040104F pop ebp .text:00401050 retn