函数指针
1.函数指针类型变量是如何定义的?
函数指针变量为变量的一种,其定义的格式遵循一般变量定义的格式,变量名为指针类型,那么就可以得出以下格式:
返回类型 (*变量名)(参数列表);
但是在函数指针变量定义时,需要指定调用约定,调用约定常年的有三种:__cdecl,__stdcal,__fastcall。三种模式均遵守从右向左入栈,其中__cdecl处理堆栈平衡的手段为外平栈,__stdcall和__fastcall处理堆栈平衡的手段为内平栈。而__stdcall与__fastcall的不同点在于,__fastcall在处理传参时,前俩个参数用寄存器ecx和edx来处理,处理效率更高。而stdcall统一用堆栈处理,不占用寄存器,效率略低。但是,__fastcall只有前俩个参数是用寄存器的,若传参数量超过俩个,也是要用堆栈的,超过俩个参数的采用fastcall意义不大。
综上所述,函数指针变量的定义规范应该是:返回类型(调用约定 *变量名)(参数列表);
例如:int (__cdecl *pFun)(int,int);
这里要明确一个点,函数指针的变量,实质上是一个变量,其类型是一个函数的指针。
2.为什么要使用函数指针变量?
我们定义一个变量,当然是要在代码中去完成代入,继而实现我们想要的需求,那么将一个变量设置为函数指针类型,是为了完成什么样的逻辑呢?
我们首先来尝试给函数指针变量进行赋值:
pFun = (__cdecl *pFun)(int,int) 12345;
由上述代码,我们将函数指针类型的12345赋值给pFun,其在内存中的表现就是在内存地址为12345的内存空间中,存放着pFun函数。

根据C代码查看其反汇编代码:

当给pFun赋值的时候,编译器指令mov dword ptr [ebp-4],3039h,将pFun的值设为3039,这也验证了pFun其本质是一个指针类型。
往下走,可以看到编译器将pFun的值放到了ebp-4中,这里是在给局部变量赋值,所赋的值本该为一个有效的内存地址,但显然12345并非有效地址,故而使用pFun时,即调用该地址代码,但是3039指向的地址为未申请空间,报错
由上可见,我们在掌握了函数指针的时候,即可通过调用函数指针指向的地址来调用地址内存储的函数句柄,从而达到了绕过前置逻辑的效果。但是,调用函数时,必定要符合调用规范,尤其是函数的返回类型,参数数量等。
这里其实有个关键点,内存地址的确定,函数指针的值对调用过程中影响甚大,故而如何准确的获取到内存地址编号也是个很重要的工作。

浙公网安备 33010602011771号