研究了一段时间反汇编,突然发现多年以前羡慕的printf样式的函数我也会写了
如下面的这个函数(测试环境VS2008)
DWORD Fun(int n,...) //n为参数书目,最多处理100个参数,并且每个参数要是一个4字节的变量,如DWORD/int/long/HANDLE
{
DWORD para[100]; //最多处理100个参数,如果用动态内存方式可以处理参数数量不收限制
for(int i=0;i<n;i++)
{
__asm //从栈中的参数列表把参数复制到para数组里
{
lea ebx,[ebp+12] //[ebp+12]为第1个参数(把n算做第0个参数)
mov eax,i
mov edx,[ebx+eax*4]
lea eax,[para+eax*4]
mov [eax],edx
}
}
for(int i=0;i<n;i++)
printf("%u/n",para[i]); //把这些参数依次打印到屏幕
return 7;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD d=Fun(10,65,3,7,88,23,11,8,9,7734,3784); //调用这个可变参数的函数
return 0;
}
堆栈平衡由编译器在编译的时候控制。由于第0个参数只是简单的说明一下传递过来的参数数目没有对参数类型有任何说明,所以只能正确处理单个参数占4个字节的参数。
printf函数使用了一些其它机制来获取参数列表,单原理应该不会变:
int __cdecl printf (const char *format,...)
{
va_list arglist;
int buffing;
int retval;
_VALIDATE_RETURN( (format != NULL), EINVAL, -1);
va_start(arglist, format); //能够使arglist指向盏中的参数列表
_lock_str2(1, stdout);
__try {
buffing = _stbuf(stdout);
retval = _output_l(stdout,format,NULL,arglist);
_ftbuf(buffing, stdout);
}
__finally {
_unlock_str2(1, stdout);
}
return(retval);
}
这些va_start,va_arg,va_end之类的东西暂时还不太会用。
char* arglist;
va_start(arglist, format); //这个宏能直接得到参数列表首地址,比用一段内联汇编方便多了
于是前面的那个函数就可以修改成如下样子:
DWORD Fun3(int n,...)
{
char* arglist;
va_start(arglist,n);
for(int i=0;i<n;i++)
{
printf("%X/n",((DWORD*)arglist)[i]);
}
return 7788;
}