返回地址(call压进来的)
1
2使⽤汇编来传递不定参数
前⾔
有时候我会想能不能 : 有个统⼀的⼊⼝函数func(id, …), 只要输⼊id和不定参数args, 例如输⼊id_X,args_X,  就能调⽤到id_X对应的func_X,⽽且传⼊args_X给funcX
即: func(id_x, args_x) ==> func_x(args)
为什么我有这个想法呢?
例如: 我们的类⼯⼚创建某些product的时候, 因为每⼀个类的构造函数不⼀样,因⽽很难统⼀⼀个CreateProduct函数
C++有不定参数 … ,但是输⼊之后就要通过va_list来传递. 这样传递给构造函数是⾮常不优雅的~
解决原理
下⾯是我的解决⽅法:
PS:
(1)函数都是 __cdecl, 不同的的调⽤⽅法会导致编译出来的汇编不⼀样. 另外,选 __cdecl 的另外⼀个原因是因为它有不定参数
(2)我的机⼦是win32, ⽤的是vc
先来清楚⼀些概念
栈底为⾼地址, 等于寄存器 EBP的值
栈顶为低地址, 等于寄存器 ESP的值
函数中的参数是压⼈栈来传递的, 返回值存在EAX⾥⾯
如:
(1) 调⽤ func(1, 2), 对应的汇编:
push 2
push 1
call func
这时候栈为
(2) 在函数⾥⾯ func⾥⾯,开始做的汇编处理:
push ebp
mov ebp, esp
sub esp, 40+xx //抬⾼堆栈, xx为局域变量的⼤⼩
push ebx
push esi
push edi
这时候栈为
edi
esi
ebx
⼤⼩为40+xx
ebp
返回地址
1
2
(3)函数处理完成之后:
先将结果保存到EAX,  然后将栈弹出到如(1)的状态,  最后调⽤ret指令, 弹出返回地址, 跳到原来的函数⾥⾯去
(4)回到原来的函数, 把最后压进去的参数弹出, 然后就读EAX
汇编代码:
add esp, 8 // 8为两个int参数的⼤⼩
解决思路
⼊⼝函数 bool func(int id, ...)
⽬标函数 bool func_xx(int* ret, int x, int y, int k)
(1)先获取到⽬标函数的地址,保存到eax  (后⾯修改栈后, 那些局域变量都没⽤了)
(2)然后把函数的状态回退到下⾯的状态
怎么用printf输出bool函数值栈:
返回地址
id
不定参数
(3)弹出返回地址, 并且⽤全部变量保存
栈:
id
不定参数
(4)弹出id, 并⽤全局变量保存这个时候的esp
栈:
不定参数
(5)压⼈⼀个label RETHANDLE
栈:
label RETHANDLE
不定参数
这个状态func_xx就能处理传进来的不定参数了
⽽且完成之后调回我们的 RETHANDLE
(6)jmp eax(跳到⽬标函数⾥⾯,上⾯保存了)
(7)这⾥就是RETHANDLE 了…. 等函数返回,
函数返回后, 栈什么东西都没了~~~~
(8)抬⾼栈到保存的esp的⾼度, 为的就是返回⽽已~
栈:
乱码.. 但是和(3)的⾼度⼀样
(9)压⼈函数应该返回地址
栈:
函数返回地址
乱码
(10)终于返回了 :-)
⽤掉了那个返回地址, 调⽤代码也帮我们清理了乱码~~更神奇的是结果保存到EAX了....
代码
如果我说得不明⽩, 看代码吧~
#include <stdio.h>
#define id_xx 1
bool func_xx(int* ret, int x, int y, int k)
{
*ret = x + y + k;
return true;
}
#define id_yy 2
bool func_yy(int* ret, int x, int y)
{
*ret = x * y;
return true;
}
int retAddrVal;
int espVal;
bool func(int id, ...)
{
int funcTarget;
if(id == id_xx)
funcTarget = (int)func_xx;
else
if(id == id_yy)
funcTarget = (int)func_yy;
else
return false;
__asm
{
mov eax, funcTarget
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
pop retAddrVal
mov espVal, esp
add esp, 4 // sizeof(id)
push offset RETHANDLE
jmp eax
RETHANDLE:
mov esp, espVal
push retAddrVal
retn
}
return false;
}
int main()
{
int k = 0;
if(func(id_xx, &k, 100, 2, 1000))
printf("%d\n", k);
if(func(id_yy, &k, 100, 2))
printf("%d\n", k);
return 0;
}
后记
上⾯的代码可以顺利编译过, 经过实验, 能通过ID指定的⽬标函数, 然后把不定参数传到传到⽬标函数.
当然, 这个代码会受到编译器和x86, x64机器的影响, 但是思路还是⼀样的...
另外也没做线程保护.
Just for fun, by YJL…..