~~
首先,這個(gè)不是特性,也不是什么BUG,也不是什么,純屬娛樂! 對(duì)了,請(qǐng)大家不要用Release模式等會(huì)進(jìn)行優(yōu)化的編譯方案。很明顯,上面的a和p都是臨時(shí)變量,并且沒有使用。。優(yōu)化就啥都沒了。 我用的是VS 2005 其它環(huán)境。執(zhí)行時(shí),輸出 Success!提示: 輸出時(shí)會(huì)DOWN掉,不過(guò)不影響結(jié)果的查看!不知道各位有何高見!我心中也有一個(gè)答案。但先不說(shuō),大家一起來(lái)討論。。。 共同完成這篇貼子。 隨后,大家的高端回復(fù)會(huì)以如下方式出現(xiàn)如果不希望最后出現(xiàn)在這里,請(qǐng)大家注明。 默認(rèn)情況下,表示同意!ID:XXXX解釋:*****************=======================ID:XXXXX解釋:********************
posted on 2010-05-06 12:41 麒麟子 閱讀(2129) 評(píng)論(38) 編輯 收藏 引用 所屬分類: Programming
什么也沒有輸出吧1 那估計(jì)輸出了也是隨即的! 回復(fù) 更多評(píng)論
不會(huì)有輸出。也不會(huì)出現(xiàn)CORE DUMP 回復(fù) 更多評(píng)論
。。。這不就是指針段內(nèi)偏移指向函數(shù)了么而且是和編譯器有關(guān)的,不同的編譯器生成的結(jié)果不一定相同。這是bug,不是特性。 回復(fù) 更多評(píng)論
其實(shí)原理很簡(jiǎn)單,系統(tǒng)調(diào)用 main 函數(shù)的時(shí)候先壓入了 返回地址, 現(xiàn)在 p 恰好位于棧中返回地址處,然后你修改成了test函數(shù),main函數(shù)退出后發(fā)現(xiàn)將返回地址是test函數(shù),于是跳過(guò)去執(zhí)行啦。 程序崩潰時(shí)必然的,你沒有ExitProcess. 回復(fù) 更多評(píng)論
@skykrnl 嗯,過(guò)程是這樣的。。 但我想看看大家目前到底有多無(wú)聊。能把這個(gè)問題提升到一個(gè)什么樣的層次! 一起來(lái)吧,哈哈 回復(fù) 更多評(píng)論
這個(gè)問題以前試驗(yàn)過(guò)了,但是gcc沒有生成對(duì)main的函數(shù)調(diào)用,所以這個(gè)效果沒有出來(lái)。改一下就可以了:#include <iostream>using namespace std;void test( void ){ cout << "Success!" << endl;}void test2(void){ int a[ 1 ]; int* p = (int*)&a[0]+2; *p = ( int )test;}int main( ){ test2(); return 0;} 回復(fù) 更多評(píng)論
@打醬油的 嗯,自己構(gòu)造,只要地址偏移正確就OK啦! 回復(fù) 更多評(píng)論
在VS中,我們還可以這樣 #include <iostream> using namespace std; void test( void ) { cout << "Success!" << endl; } int main( void ) { _asm { mov dword ptr[ebp+4],offset test;把test的地址給返回地址 } return 0; } 回復(fù) 更多評(píng)論
這個(gè)可以從call和ret指令所做的事情來(lái)看,更涉及到函數(shù)調(diào)用在編譯器以及目標(biāo)機(jī)器指令問題。不過(guò)因?yàn)檫@里不存在虛擬機(jī)問題,都是x86,也就只針對(duì)call和ret而言: 不難想象在main之前的地方有如下代碼: ; 壓參數(shù) push xxx push xxx push xxx call main ;main xxx xxx ret 首先call的動(dòng)作主要包括:先壓入返回地址到堆棧上(ebp指向),而c函數(shù)中,函數(shù)負(fù)責(zé)堆棧平衡,那么main中清除局部變量,改變ebp后,可以肯定ebp指向的當(dāng)前堆棧中的值就是返回地址。ret指令則是從棧頂取出該地址并執(zhí)行PC寄存器的跳轉(zhuǎn)。 另一方面,函數(shù)調(diào)用時(shí)的運(yùn)行時(shí)堆棧問題:首先棧是向下增長(zhǎng)的,函數(shù)A調(diào)用函數(shù)B,那么首先壓入?yún)?shù)到棧中,在函數(shù)B中因?yàn)榫植孔兞康脑鲩L(zhǎng)棧繼續(xù)向下增長(zhǎng),也就是說(shuō),最終可以通過(guò)ebp的偏移取得函數(shù)A中局部變量的信息。他們貢獻(xiàn)同一個(gè)棧: --stack-- A:local_var1 A:local_var2 A:ret_addr B:arg_var1 B:arg_var2 B:local_var1 .... 基于以上兩個(gè)條件,指針a[0]+3,則向高地址偏移了12字節(jié)的地址(3*sizeof(int)),看下main函數(shù)的參數(shù),實(shí)際上是3個(gè):argc, argv, env。這樣偏移后,恰好就是調(diào)用main那個(gè)函數(shù)在使用call時(shí),壓入的返回地址。 因此,在main返回時(shí),ret彈出的地址已經(jīng)被改變。 ps: 在錯(cuò)誤地跳轉(zhuǎn)到test后,test執(zhí)行完去ret時(shí),堆棧上提供的返回地址是不定的,崩潰也很正常了。 回復(fù) 更多評(píng)論
@Kevin Lynx 嗯,分析得很好哦。。但是,我覺得這和main的參數(shù)沒關(guān)系。。偏移到ret_addr就已經(jīng)停下了。還沒經(jīng)過(guò)B:arg_var1 B:arg_var2 B:local_var1 回復(fù) 更多評(píng)論
*p = ( int )test;這句沒看明白。 回復(fù) 更多評(píng)論
1- CALL會(huì)把下一個(gè)指令的地址放進(jìn)堆棧。 2- RET就讓這個(gè)地址出棧,并跳轉(zhuǎn)至這個(gè)地址。 3- 局部變量也是在棧上的。 代碼中,你用局部變量的地址定位到棧內(nèi)的ret返回地址,然后將其修改為TEST的函數(shù)地址。RET后,就跳轉(zhuǎn)到TEST函數(shù)了。因?yàn)闆]有CALL,所以棧內(nèi)不會(huì)壓入返回地址,然后棧就亂掉了,后面依賴棧的指令,就可能會(huì)導(dǎo)致出錯(cuò)。 在一些軟件保護(hù)里面,經(jīng)常會(huì)用到這種手段,PUSH FUNCPTR, RET。這樣可以用CALL來(lái)調(diào)用函數(shù)。從而迷惑分析者。通過(guò)ESP寄存器直接操作,更讓分析者頭大。再用一些無(wú)效指令插在其中,做成花指令,就更高端了。特別是花連跳,分析者就很難一眼分辨出走向了。 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 我說(shuō)的是有點(diǎn)問題。跟參數(shù)沒關(guān)系。參數(shù)先于返回地址壓棧。- - 昏頭了。 話說(shuō)回來(lái),仔細(xì)分析的話,我突然發(fā)覺: int* p = (int*)&a[0]+3; 這里為什么會(huì)是3呢?跟了下匯編,發(fā)覺直接被翻譯為ebp+4了: push ebp mov ebp, esp ... mov eax, [ebp+4] 不是很明白這個(gè)地方。 飯老大說(shuō)得和我一樣。 回復(fù) 更多評(píng)論
典型的 "撞大運(yùn)編程" 回復(fù) 更多評(píng)論
@Kevin Lynx int a; int *p 這兩個(gè)是臨時(shí)變量 a 的地址是 ebp -8 p 的地址是 ebp-4 所以,&a[0]+3其實(shí)就是 ebp-8+ 3*4 = ebp+4 回復(fù) 更多評(píng)論
@壞 撞大運(yùn)和這可不一樣。 這是明明知道的后果! 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 a 的地址是 ebp -8 p 的地址是 ebp-4 這兩個(gè)結(jié)論從何而來(lái)?何況,為什么要+3? ps,話說(shuō)這樣N多回復(fù)會(huì)給你BLOG增加不少積分啊 :D 回復(fù) 更多評(píng)論
@Kevin Lynx 都說(shuō)了,這個(gè)程序是故意寫出來(lái)的。不是撞大運(yùn)撞的。 +3是為了正好得到我們的偏移地址! 上面那個(gè)結(jié)論,就是臨時(shí)變量在棧上分配內(nèi)存分成那樣的撒。至于BLOG的分,我不知道這回事! 回復(fù) 更多評(píng)論
@Kevin Lynx p的地址大于a。 所以a在棧頂,p在a下面。還有個(gè)push ebp,接下來(lái)就是返回地址。 正好三個(gè)。 回復(fù) 更多評(píng)論
@飯中淹 嗯,就是這樣的。 不過(guò)如果你在DEBUG下看的話,會(huì)發(fā)現(xiàn)a不在棧頂。 DEBUG會(huì)多分一些 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 { int a; int b; int c; } 按我的理解,c應(yīng)該在棧頂,而不是a。但是實(shí)際上跟蹤你的代碼來(lái)看,a確實(shí)在棧頂,在p后添加變量int i ,又有意外: a : 0x0012ff74 &p:0x0012ff78 &i:0x0012ff70 回復(fù) 更多評(píng)論
@小時(shí)候可靚了在我這里gcc和BCB都是無(wú)任何輸出的。隨便在前面加個(gè)局部變量VC也同樣無(wú)輸出。這不就是撞大運(yùn)么.... 回復(fù) 更多評(píng)論
@壞 需要適當(dāng)調(diào)整 3 這個(gè)偏移量 ps,當(dāng)然也取決于編譯器生成的指令。鑒于目前我也不明白為什么偏移是3,看起來(lái)LZ也無(wú)法給出詳細(xì)的說(shuō)明,這個(gè)3很有可能只是個(gè)巧合。 1、除了push ebp外可能還有壓入其他寄存器的值(保存寄存器信息) 2、a理論上應(yīng)該不在棧頂,就像我例子中的i,而p應(yīng)該在棧頂 回復(fù) 更多評(píng)論
@Kevin Lynx 棧頂是低地址~~ 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 這個(gè)不需要你重申,我更不希望我來(lái)重申我的問題: 解釋下這個(gè): int a[1]; int *p; int i; a : 0x0012ff74 &p:0x0012ff78 &i:0x0012ff70 注意p在中間 。 回復(fù) 更多評(píng)論
@Kevin Lynx 這個(gè)巧合是建立在剛剛那個(gè)代碼上的。。這個(gè)+3,是人為構(gòu)造的。 不管+幾,我們就是想把他指向main的返回地址。 不過(guò)呢,在編譯器環(huán)境條件眾多的情況下, 如 @壞 所說(shuō),真是撞大運(yùn)。 至于合理的解釋,上面有兄弟給了答案了。。。 回復(fù) 更多評(píng)論
@Kevin Lynx 其實(shí)我貼出來(lái),同樣是想看到關(guān)于這個(gè)臨時(shí)空間的解釋。。你知道嗎?在我的機(jī)器上是這樣的。 0x0012ff60 0x0012ff54 0x0012ff48 如果我能一一解釋清楚,我就不讓大家討論了。。。 嘿嘿 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 飯給的解釋是我在群里跟他談過(guò)的。 這個(gè)解釋是我在看匯編的時(shí)候看到的: 00401750 push ebp 00401751 mov ebp,esp 00401753 sub esp,0Ch 00401756 lea eax,[ebp+4] 00401759 mov dword ptr [p],eax 恰好a莫名其妙地出現(xiàn)在棧頂,而不是p,(而在我舉的包含i的例子中,作為出現(xiàn)在最后定義的i卻莫名其妙地出現(xiàn)在棧頂),加上這個(gè)push ebp,就出現(xiàn)了3。 誰(shuí)能給個(gè)解釋:為什么a、p、i三者的相對(duì)地址和其定義順序存在差別?(難道編譯器對(duì)數(shù)組的處理要特殊點(diǎn)?) 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 0x0012ff60 a 0x0012ff54 p 0x0012ff48 i ??? 如果是這樣的話,那這個(gè)才是正確的排列地址啊。詭異的是我那個(gè)情況。 回復(fù) 更多評(píng)論
@Kevin Lynx 可是,他們的差值很鬼異! 回復(fù) 更多評(píng)論
@小時(shí)候可靚了 剛想補(bǔ)上這句,差距都是12.。。- - 我這邊差距都是4,正常差距,但是順序詭異。。。 回復(fù) 更多評(píng)論
唉,淡定吧。這種情況讓我頭疼。 回復(fù) 更多評(píng)論
汗。。- - 。。偶然間看到LZ 博客HGE一欄居然轉(zhuǎn)載有我N久以前亂寫的東西,真巧啊。。 回復(fù) 更多評(píng)論
@Kevin Lynx 是啊,以前在學(xué)校的時(shí)候,搗鼓了兩天HGE。 回復(fù) 更多評(píng)論
又是offset.....看了王爽匯編之后,你一定會(huì)發(fā)現(xiàn)這復(fù)雜的世界又是多么的和諧 回復(fù) 更多評(píng)論
@zuhd 王爽的我沒看過(guò)耶?。。?汗?。?! 回復(fù) 更多評(píng)論
高手好多哦。你們想得太復(fù)雜了。 我認(rèn)為這一句 *p = ( int )test; 調(diào)用了test函數(shù),只不過(guò)test函數(shù)沒有返回值,且p指向非法內(nèi)存,所以給*P賦值就會(huì)犯錯(cuò),于是程序就會(huì)掛掉。 樓主說(shuō)的特性是不是test和test()是一樣的啊? 回復(fù) 更多評(píng)論
我發(fā)現(xiàn)我上面的結(jié)論錯(cuò)了。剛才把代碼輸入到VS2008,調(diào)試了一下。發(fā)現(xiàn): *p=(int)test;確實(shí)只是把test的地址放到了*p里面,而不是調(diào)用test(); 而在return的時(shí)候調(diào)用了test,然后出錯(cuò)。 說(shuō)明*p這個(gè)非法內(nèi)存訪問是真的改變了main函數(shù)返回后執(zhí)行的下一條指令的地址。 回復(fù) 更多評(píng)論
Powered by: C++博客 Copyright © 麒麟子