這是從別人博客上摘的一段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;

}

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

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