使用汇编来传递不定参数

前言

有时候我会想能不能 : 有个统一的入口函数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

这时候栈为

  返回地址(call压进来的)
  1
  2

 

 

 

 

(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)然后把函数的状态回退到下面的状态

栈:

   返回地址
    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…..

posted @ 2011-11-29 23:20  yanjielong  阅读(1180)  评论(0)    收藏  举报