研究了一段時間反彙編,突然發現多年以前羨慕的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;
}