這是從別人博客上摘的一段C嵌匯編碼
( http://www.shnenglu.com/kevinlynx/archive/2011/01/02/137886.html
)

__declspec(naked)

void caller(void* pfn,

)



{

__asm


{

pop eax;

add eax, 3;

xchg dword ptr[esp], eax;

push eax;

ret;

}

}
下面是調用方法
void print_str( const char *s )

{
printf( "%s\n", s );
}{ ...
caller( print_str, "a string" ); __asm add esp, 4
...}
原作者講了一些基礎,這里就不提了
看了一遍,發現 "ADD EAX, 3" 的用法有點奇怪(我相信搞破解的人一定比較熟悉,但正常的程序不會這么寫。)
初看 EAX 是地址,+3是很危險的,但仔細一看,發現代碼是為了從最外層主調函數一路穿越"caller" 直達 print_str,這里牽涉到一個重要問題,就是在CALL指令時,會有將“CALL指令下一條地址壓棧”的操作,那么代碼思路很明了了,就是為了要造出 調用print_str時,ESP(+0) 指向 caller(..)調用的下一個地址。
第一關已經順利搞定,但又碰到個問題,由于 print_str 的入參是可變的,所以必須用 cdecl調用,那RET之后 如何平棧呢? 如果直接跳到 caller下一條地址,就喪失了平棧的機會,最終會在某個主調函數上被微軟的 stack cookie捕獲拋個SEH。
這里就用到文章開頭提到的 ADD EAX, 3。
必須要造一個環境,讓 caller 調用完成后,給個機會清理現場。于是乎,caller之后就有了 ADD ESP, 4。其實這里的4是與print_str的入參數目相關的,每個參數要多加 4字節,如此一來,整個代碼就理順了。
那為什么 是 ADD EAX, 3呢? 應該是預估出一條ADD指令占用多少長度,和具體的環境有關。因為沒看INTEL手冊,這里只能認為ADD 寄存器+WORD的長度是3個字節。我用VC試驗了一下,的確是如此,我也嘗試了ADD 寄存器+DWORD,長度變為了5個字節。