今天比較搞笑,在看《Inside vc.net》的com的時(shí)候,講到了c++中的vftable,這個(gè)虛擬表還真不是很清楚,所以就在網(wǎng)上查了一些關(guān)于vftable的資料,地址為:
http://blog.csdn.net/gzjh1/archive/2006/05/26/756024.aspx,轉(zhuǎn)貼一下:)
在面向?qū)ο蟮腃++語(yǔ)言中,虛函數(shù)(virtual?function)是一個(gè)非常重要的概念。因?yàn)樗浞煮w現(xiàn)了面向?qū)ο笏枷胫械睦^承和多態(tài)性這兩大特性,在C++語(yǔ)言里應(yīng)用極廣。比如在微軟的MFC類庫(kù)中,你會(huì)發(fā)現(xiàn)很多函數(shù)都有virtual關(guān)鍵字,也就是說(shuō),它們都是虛函數(shù)。難怪有人甚至稱虛函數(shù)是C++語(yǔ)言的精髓。?
????????那么,什么是虛函數(shù)呢,我們先來(lái)看看微軟的解釋:
????????虛函數(shù)是指一個(gè)類中你希望重載的成員函數(shù),當(dāng)你用一個(gè)基類指針或引用指向一個(gè)繼承類對(duì)象的時(shí)候,你調(diào)用一個(gè)虛函數(shù),實(shí)際調(diào)用的是繼承類的版本。
???????????????????????????????????????????????????????????????——摘自MSDN
????????這個(gè)定義說(shuō)得不是很明白。MSDN中還給出了一個(gè)例子,但是它的例子也并不能很好的說(shuō)明問(wèn)題。我們自己編寫(xiě)這樣一個(gè)例子:
#include?"stdio.h"
#include?"conio.h"
?
class?Parent


{
public:
?char?data[20];
?void?Function1();
?virtual?void?Function2();??//?這里聲明Function2是虛函數(shù)
}parent;
void?Parent::Function1()


{
?printf("This?is?parent,function1\n");
}
void?Parent::Function2()


{
?printf("This?is?parent,function2\n");
}
?
class?Child:public?Parent


{
?void?Function1();
?void?Function2();
}?child;
void?Child::Function1()


{
?printf("This?is?child,function1\n");
}
void?Child::Function2()


{
?printf("This?is?child,function2\n");
}
?
int?main(int?argc,?char*?argv[])


{
?Parent?*p;?//?定義一個(gè)基類指針
?if(_getch()=='c')?????//?如果輸入一個(gè)小寫(xiě)字母c
???????p=&child;?????????//?指向繼承類對(duì)象
?else
???????p=&parent;??????//?否則指向基類對(duì)象
p->Function1();???//?這里在編譯時(shí)會(huì)直接給出Parent::Function1()的
???????????????????????????????????入口地址。
?p->Function2();????//?注意這里,執(zhí)行的是哪一個(gè)Function2?
?return?0;
}
????????用任意版本的Visual?C++或Borland?C++編譯并運(yùn)行,輸入一個(gè)小寫(xiě)字母c,得到下面的結(jié)果:
This?is?parent,function1
This?is?child,function2
??????為什么會(huì)有第一行的結(jié)果呢?因?yàn)槲覀兪怯靡粋€(gè)Parent類的指針調(diào)用函數(shù)Fuction1(),雖然實(shí)際上這個(gè)指針指向的是Child類的對(duì)象,但編譯器無(wú)法知道這一事實(shí)(直到運(yùn)行的時(shí)候,程序才可以根據(jù)用戶的輸入判斷出指針指向的對(duì)象),它只能按照調(diào)用Parent類的函數(shù)來(lái)理解并編譯,所以我們看到了第一行的結(jié)果。
????????那么第二行的結(jié)果又是怎么回事呢?我們注意到,F(xiàn)unction2()函數(shù)在基類中被virtual關(guān)鍵字修飾,也就是說(shuō),它是一個(gè)虛函數(shù)。虛函數(shù)最關(guān)鍵的特點(diǎn)是“動(dòng)態(tài)聯(lián)編”,它可以在運(yùn)行時(shí)判斷指針指向的對(duì)象,并自動(dòng)調(diào)用相應(yīng)的函數(shù)。如果我們?cè)谶\(yùn)行上面的程序時(shí)任意輸入一個(gè)非c的字符,結(jié)果如下:
This?is?parent,function1
This?is?parent,function2
????????請(qǐng)注意看第二行,它的結(jié)果出現(xiàn)了變化。程序中僅僅調(diào)用了一個(gè)Function2()函數(shù),卻可以根據(jù)用戶的輸入自動(dòng)決定到底調(diào)用基類中的Function2還是繼承類中的Function2,這就是虛函數(shù)的作用。我們知道,在MFC中,很多類都是需要你繼承的,它們的成員函數(shù)很多都要重載,比如編寫(xiě)MFC應(yīng)用程序最常用的CView::OnDraw(CDC*)函數(shù),就必須重載使用。把它定義為虛函數(shù)(實(shí)際上,在MFC中OnDraw不僅是虛函數(shù),還是純虛函數(shù)),可以保證時(shí)刻調(diào)用的是用戶自己編寫(xiě)的OnDraw。虛函數(shù)的重要用途在這里可見(jiàn)一斑。
??????在了解虛函數(shù)的基礎(chǔ)之上,我們考慮這樣的問(wèn)題:一個(gè)基類指針必須知道它所指向的對(duì)象是基類還是繼承類的示例,才能在調(diào)用虛函數(shù)時(shí)“自動(dòng)”決定應(yīng)該調(diào)用哪個(gè)版本,它是如何知道的?有些講C++的書(shū)上提到,這種“動(dòng)態(tài)聯(lián)編”的機(jī)制是通過(guò)一個(gè)“vtable”實(shí)現(xiàn)的,vtable是什么?微軟在關(guān)于COM的文檔里這樣描述:
????????vtable是指一張函數(shù)指針表,如同C++中類的實(shí)現(xiàn)一樣,vtable中的指針指向一個(gè)對(duì)象支持的接口成員函數(shù)。????????
???????????????????????????????????????????????????????????????——摘自MSDN
????????很遺憾,微軟這次還是沒(méi)有把問(wèn)題說(shuō)清楚,當(dāng)然,上面的文檔本來(lái)就是關(guān)于COM的,與我們關(guān)心的問(wèn)題不同。
????????那么vtable是什么?我們先來(lái)看看下面的實(shí)驗(yàn):
????????在前面的示例程序中加一句printf(“%d”,sizeof(Child));運(yùn)行,然后去掉Function2()前的virtual關(guān)鍵字,再運(yùn)行,得到這樣的結(jié)果:當(dāng)Function2定義成虛函數(shù)的時(shí)候,結(jié)果是24,否則結(jié)果是20。也就是說(shuō),如果Function2不是虛函數(shù),一個(gè)Child類的示例所占空間的大小僅僅是它的成員變量data數(shù)組的大小,如果Function2是虛函數(shù),結(jié)果多了4個(gè)字節(jié)。我們使用的是32位的Visual?C++?6.0,4個(gè)字節(jié)恰好是一個(gè)指針,或者是一個(gè)整數(shù)所占的空間。
????????那么這多出來(lái)的四個(gè)字節(jié)究竟起到了什么作用?
???????用Visual?C++打開(kāi)前面的示例程序,在main函數(shù)中p->Function1();?一句前面按F9設(shè)斷點(diǎn),按F5開(kāi)始調(diào)試,輸入一個(gè)小寫(xiě)c,程序停到了我們?cè)O(shè)的斷點(diǎn)上。找到Debug工具條,按Disassembly按鈕,如圖所示:
(圖呢?CSDN上的這個(gè)應(yīng)該是作者的原創(chuàng)了吧?可是圖竟然不見(jiàn)了>_<!!)
算了,不管圖了,反正也不是很重要,go?on
????????我們看到了反匯編后的代碼。由上圖可見(jiàn),對(duì)Function1和Function2的調(diào)用反匯編后生成的代碼截然不同。Function1不是虛函數(shù),因此對(duì)它的調(diào)用僅僅被編譯成為一條call指令,轉(zhuǎn)向Parent::Function1子程序;而Function2是虛函數(shù),它的代碼要復(fù)雜一些,我們來(lái)仔細(xì)分析:
45:???????p->Function2();????
004012CA???mov??????eax,dword?ptr?[ebp-4]
//?eax就是我們的p指針
004012CD???mov??????edx,dword?ptr?[eax]
//?edx取child對(duì)象頭部四個(gè)字節(jié)
004012CF???mov???????esi,esp
004012D1???mov???????ecx,dword?ptr?[ebp-4]
//?可能要檢查棧,不管它
004012D4???call????????dword?ptr?[edx]
//?注意這里,調(diào)用了child對(duì)象頭部的一個(gè)函數(shù)指針
004012D6???cmp???????esi,esp
004012D8???call?????????__chkesp?(004013b0)
這里最關(guān)鍵的一句是call?dword?ptr[edx],edx是child對(duì)象頭部,前面我們分析過(guò)了,child對(duì)象共有24字節(jié),其中成員變量占用20字節(jié),還有4個(gè)字節(jié)作用未知。現(xiàn)在從這段匯編代碼上看,那4個(gè)字節(jié)很可能就是child對(duì)象開(kāi)頭的這個(gè)函數(shù)指針,因?yàn)榫幾g器并不知道我們的成員變量data是做什么用的,更不可能把data的任何一部分當(dāng)成一個(gè)函數(shù)指針來(lái)處理。
那么這個(gè)函數(shù)指針會(huì)跳轉(zhuǎn)到那里去呢?我們按F10單步運(yùn)行到這個(gè)call指令,然后按F11跟進(jìn)去:
00401032???jmp?????????Parent::Function2?(0040bfe0)
00401037???jmp?????????Parent::Parent?(004010d0)
????→?0040103C???jmp?????????Child::Function2?(00401250)
00401041???jmp?????????Child::Child?(004011c0)
光標(biāo)停在了第三行,40103C的地方,執(zhí)行這里的jmp指令后,又跳轉(zhuǎn)到Child::Function2的位置,從而得到我們上面所看到的結(jié)果。
這并不是最終的結(jié)論,我們看看40103C周圍的幾行代碼,連續(xù)幾行全都是jmp指令,這是什么程序結(jié)構(gòu)?有匯編語(yǔ)言編程經(jīng)驗(yàn)的朋友可能會(huì)想起來(lái)了,這是一張入口表,分別存放著到幾個(gè)重要函數(shù)的跳轉(zhuǎn)指令!我們?cè)倩厝タ纯次④泴?duì)于vtable的描述:vtable是指一張函數(shù)指針表,(如同C++中類的實(shí)現(xiàn)一樣,)vtable中的指針指向(一個(gè)對(duì)象支持的接口)成員函數(shù)。打括號(hào)的字不要看,這句話的主干就是:vtable是一張函數(shù)指針表,指向成員函數(shù)。種種事實(shí)證明,上面的四行代碼就是我們要找的這個(gè)vtable!
現(xiàn)在我們應(yīng)該對(duì)虛函數(shù)的原理有一個(gè)認(rèn)識(shí)了。每個(gè)虛函數(shù)都在vtable中占了一個(gè)表項(xiàng),保存著一條跳轉(zhuǎn)到它的入口地址的指令(實(shí)際上就是保存了它的入口地址)。當(dāng)一個(gè)包含虛函數(shù)的對(duì)象(注意,不是對(duì)象的指針)被創(chuàng)建的時(shí)候,它在頭部附加一個(gè)指針,指向vtable中相應(yīng)位置。調(diào)用虛函數(shù)的時(shí)候,不管你是用什么指針調(diào)用的,它先根據(jù)vtable找到入口地址再執(zhí)行,從而實(shí)現(xiàn)了“動(dòng)態(tài)聯(lián)編”。而不像普通函數(shù)那樣簡(jiǎn)單地跳轉(zhuǎn)到一個(gè)固定地址。
以上結(jié)論僅僅是針對(duì)Visual?C++?6.0編譯器而言的,對(duì)于其他編譯器,具體實(shí)現(xiàn)并不完全相同,但都大同小異。著名的“綠色兵團(tuán)”雜志上撰文介紹,Linux平臺(tái)上的GNU?C++編譯器就把指向vtable的指針?lè)旁趯?duì)象尾部而不是頭部,而且vtable中僅僅存放虛函數(shù)的入口地址,而不是跳轉(zhuǎn)到虛函數(shù)的指令。具體的一些細(xì)節(jié),篇幅所限,我們這里不再討論,希望有興趣的朋友能繼續(xù)研究。
PS:綜上所述,只有對(duì)指針和引用,虛函數(shù)的魔力才有作用!這篇文章又講到了匯編的知識(shí),以前看過(guò)一些匯編的資料,但是一直沒(méi)有弄明白,主要還是因?yàn)闆](méi)有自己去寫(xiě)代碼,把上面的代碼在vc.net2005中運(yùn)行了一下,打開(kāi)Disassemble、Register窗口,里面的東西還真是稀里糊涂的。狂人日記中講的Assemble還不錯(cuò),
http://krrj.blogbus.com/s42550/(主要是講注冊(cè)機(jī)的內(nèi)容,截取其中Assemble的部分)轉(zhuǎn)貼如下,也可以看看
http://www.xker.com/Html/bcyy/hbyy/20051118784.htm的《
簡(jiǎn)明x86匯編語(yǔ)言教程》
稍微有點(diǎn)兒計(jì)算機(jī)知識(shí)的朋友一定知道,計(jì)算機(jī)是只識(shí)別0和1的,最初那會(huì)兒,要寫(xiě)程序,就要用0和1來(lái)寫(xiě),呵呵,Cool吧!所以曾經(jīng)有過(guò)的對(duì)程序員的崇拜,可能就源自那個(gè)時(shí)候吧??后來(lái),人們發(fā)現(xiàn)用0和1來(lái)寫(xiě)程序,太不爽了,不但寫(xiě)起來(lái)不上手,而且回過(guò)頭來(lái)看的話,應(yīng)該很難再看明白了,總之出于這些原因,就有了匯編語(yǔ)言。?


匯編語(yǔ)言用一些助記符來(lái)代替0和1的多種組合,也就是各個(gè)指令,這樣的話,從一定程度上來(lái)說(shuō),方便了許多(一頭老牛:方便太多了)(一只菜鳥(niǎo):一點(diǎn)兒也不方便,完全看不懂)。但是,匯編也同樣不方便,同樣寫(xiě)起來(lái)不爽,而且后期維護(hù)同樣不方便,再加上人們慢慢地需要寫(xiě)一些更大的程序,在這樣的情況下,高級(jí)語(yǔ)言就被人發(fā)明了出來(lái),就是我們今天用的Basic、pascal、C、C++等等等等,這些語(yǔ)言的出現(xiàn),一下了使程序的開(kāi)發(fā)難度大大減低了(一頭老牛:減低太多了,我膝蓋就能寫(xiě)程序了)(一只菜鳥(niǎo):還不是一樣難),以前用匯編要很長(zhǎng)時(shí)間才能開(kāi)發(fā)出來(lái)的程序,現(xiàn)在只需要很短的時(shí)間且很輕松的就可以搞定了,特別是最近幾年,可視化編程的大肆普及,使程序員的神秘感一下子摔了下來(lái),Coder這樣的詞現(xiàn)在都滿天飛了。最慘的就是匯編,一夜之間變成了低級(jí)語(yǔ)言、下流的語(yǔ)言、吃完大蒜不刷牙的民工、開(kāi)車加完油不給錢的地痞、在公共汽車上吐口水的冰島人等等等等??


(匯編:?jiǎn)鑶鑶琛也换盍耍?


但是匯編還是有它先天的優(yōu)勢(shì)的,因?yàn)槠渑cCPU內(nèi)部的指令一一對(duì)應(yīng),所以在一些特殊的場(chǎng)合,必須由匯編來(lái)實(shí)現(xiàn),比如訪問(wèn)硬件的端口、寫(xiě)病毒….?


而且生成的可執(zhí)行文件效率巨高,且生成的可執(zhí)行文件賊小,寫(xiě)小程序是很爽的,呵呵,而且用匯編寫(xiě)注冊(cè)機(jī),是件很輕松的事,你不用再為怎樣還原為你所熟悉的語(yǔ)言而為難。說(shuō)了這么多,還是切入主題吧(昏倒觀眾若干):?


既然計(jì)算機(jī)只識(shí)別0和1,那么,所有存儲(chǔ)在計(jì)算機(jī)上的文件,也都是以二進(jìn)制的形式存放的,當(dāng)然也包括可執(zhí)行文件了。?


所以,你只要找一個(gè)十六進(jìn)制編輯器比如Ultra?Edit什么的,就可直接打開(kāi)并查看可執(zhí)行文件了,呵呵,如果你能看懂的話??你會(huì)發(fā)現(xiàn),此時(shí)看到的,全是些十六進(jìn)制數(shù)值(每4位二進(jìn)制數(shù)可轉(zhuǎn)換為一位十六進(jìn)制數(shù)),這就是可執(zhí)行文件的具體內(nèi)容,當(dāng)然,其中就包括可執(zhí)行文件的代碼了。(一頭老牛:好親切啊)(一只菜鳥(niǎo):笨牛,你給我閉嘴,我眼都花了)。?


呵呵,此時(shí),你是不是覺(jué)得看這些東西,有些那個(gè)??


這些東西看起來(lái)就像有字天書(shū),沒(méi)人能*這玩意兒來(lái)進(jìn)行分析,于是乎。就有了相應(yīng)的軟件,可以將這些十六進(jìn)制數(shù)值轉(zhuǎn)換為相應(yīng)的匯編代碼,這樣的話,我們就可以對(duì)別人的軟件進(jìn)行分析了。這就是所謂的逆向分析了。?


呵呵,聰明的你現(xiàn)在一定在想,如果找到軟件計(jì)算注冊(cè)碼的部分,并對(duì)其進(jìn)行分析,弄懂它的計(jì)算方法,那么你不就不用通過(guò)¥的方式來(lái)進(jìn)行軟件注冊(cè)了嗎?當(dāng)然,你也可以將此計(jì)算過(guò)程還原為任意一個(gè)你所熟悉的編程語(yǔ)言,那么,編譯后的這個(gè)程序,就叫做注冊(cè)機(jī),它的功能就是計(jì)算某一特定軟件的注冊(cè)碼。(呵呵,是不是經(jīng)常在軟件中看到此類說(shuō)明?"禁止制作和提供該軟件的注冊(cè)機(jī)及破解程序;禁止對(duì)本軟件進(jìn)行反向工程,如反匯編、反編譯等")?


作者這樣做,心情我們是可以理解的,畢竟人家花了那么多心思在自己的軟件上,所以,我不希望你僅僅是因?yàn)榻徊黄鹱?cè)費(fèi)的原因來(lái)學(xué)習(xí)破解。?


總的說(shuō)來(lái),上邊兒的介紹有點(diǎn)兒太理想化了,上面提到的分析方法,就是所謂的靜態(tài)分析,此類分析常用的工具有W32DASM、IDA和HIEW等。靜態(tài)分析,顧名思義,就是只通過(guò)查看軟件的反匯編代碼來(lái)對(duì)軟件進(jìn)行分析。一般如果只是想暴破軟件,只進(jìn)行靜態(tài)分析就夠了。但要想真正的弄清注冊(cè)算法,一般還是要進(jìn)行動(dòng)態(tài)分析的,即能過(guò)調(diào)試器來(lái)一邊執(zhí)行程序一邊進(jìn)行分析。具體內(nèi)容,我會(huì)在《破解原理》和《調(diào)試器入門》中詳細(xì)說(shuō)明,呵呵,畢竟現(xiàn)在都以經(jīng)有點(diǎn)兒跑題了。?


我廢話說(shuō)了這么多,其實(shí)就是想告訴你匯編的重要性,我不要求你精通,但最少你也得能看懂吧,要不,還談什么分析?雖然有哥們兒一點(diǎn)兒匯編都不懂就上路了,甚至還破掉了幾個(gè)軟件,但是,這樣是不是慘了點(diǎn)兒?難不成你想暴破軟件暴破一輩子??


其實(shí)你完全不用懼怕匯編的,看上去怪嚇人的,其實(shí)跟你平時(shí)背那些控件的屬性方法差不多,MFC那么多你都搞的定,匯編命令才有多少?而且,匯編不光只是在Crack軟件時(shí)有用,在好多地方也都有用,且用處巨大,所以我覺(jué)得,把匯編拿下,是件義不容辭的事:?


你只要相信它并不難就好了。?


(以下為第二次修改時(shí)加入)?


先給你講一下CPU的組成吧:?


CPU的任務(wù)就是執(zhí)行存放在存儲(chǔ)器里的指令序列。為此,除要完成算術(shù)邏輯操作外,還需要擔(dān)負(fù)CPU和存儲(chǔ)器以及I/O之間的數(shù)據(jù)傳送任務(wù)。早期的CPU芯片只包括運(yùn)算器和控制器兩大部分。到了近幾年,為了使存儲(chǔ)器速度能更好地與運(yùn)算器的速度相匹配,又在芯片中引入了高速緩沖存儲(chǔ)器(知道為什么P4比P4賽揚(yáng)貴那么多嗎?)。(當(dāng)!一個(gè)硬物飛了過(guò)來(lái),話外音:你講這些做什么,我們又不要設(shè)計(jì)CPU)?


你急什么嘛,由于匯編比較“低級(jí)”??;;所以它是直接操作硬件的,你以為這是用VB呢,想什么時(shí)候用變量隨手就可以拿來(lái)用,你不掌握好CPU內(nèi)部的一些工作分配情況,到時(shí)怎么來(lái)看匯編代碼啊。(當(dāng)!又一聲,重要還不快點(diǎn)兒說(shuō))?


除了高速緩沖存儲(chǔ)器之外的組成,大體上可以分為3個(gè)部分:?


1.算術(shù)邏輯部件ALU(arithmetic?logic?unit)用來(lái)進(jìn)行算術(shù)和邏輯運(yùn)算。這部分與我們的關(guān)系不太大,我們沒(méi)必要管它。?


2.控制邏輯。同樣與我們的關(guān)系不大。?


3.這個(gè)才是最最重要的。工作寄存器,它在計(jì)算機(jī)中起著重要的作用,每一個(gè)寄存器相當(dāng)于運(yùn)算器中的一個(gè)存儲(chǔ)單元,但它的存取速度卻賊快賊快,比存儲(chǔ)器要快很多了。它用來(lái)存放計(jì)算過(guò)程中所需要的或所得到的各種信息,包括操作數(shù)地址、操作數(shù)及運(yùn)算的中間結(jié)果等。下面我們專門的介紹這些寄存器。?


在介紹之前,有必要說(shuō)點(diǎn)兒基礎(chǔ)性的知識(shí)。知道什么是32位吧,就是說(shuō)寄存器是32位的,暈~~等于沒(méi)說(shuō)。在CPU中,一個(gè)二進(jìn)制位被看作是一位,八位就是一個(gè)字節(jié),在內(nèi)存中,就是以字節(jié)為單位來(lái)在存儲(chǔ)信息的,每一個(gè)字節(jié)單元給以一唯一的存儲(chǔ)器地址,稱為物理地址,到時(shí)候訪問(wèn)相應(yīng)的內(nèi)存,就是通過(guò)這個(gè)地址。八個(gè)二進(jìn)制位都能表達(dá)些什么呢?可以表達(dá)所有的ASCII碼,也就是說(shuō)一個(gè)內(nèi)存單元可以存儲(chǔ)一個(gè)英文字符或數(shù)字什么的,而中文要用Unicode碼來(lái)表示,也就是說(shuō)兩個(gè)內(nèi)存單元,才能裝一個(gè)漢字。十六位就是兩個(gè)字節(jié)這不難理解吧,當(dāng)然啦,那有了十六位,就肯定有三十二位六十四位什么的,三十二位叫做雙字,六十四位就叫做四字。今天我們所使的CPU,相信全是32位的了,除非你用的是286或更早的話。自然而然,CPU中的寄存器,也就是32位的了,也就是說(shuō)一個(gè)寄存器,可以裝下32個(gè)0或1(這其中不包括段寄存器)。?


大體上來(lái)說(shuō),你需要掌握的寄存器,有十六個(gè),我一個(gè)一個(gè)給介紹給你:?


首先,介紹小翠兒(當(dāng)!,我自己打我自己一下得了,最近看周星馳看多了),重說(shuō),首先,介紹通用寄存器。?


一共八個(gè),分別是EAX、EBX、ECX、EDX、ESP、EBP、EDI、ESI。?


其中,EAX—EDX這四個(gè)寄存器又可稱為數(shù)據(jù)寄存器,你除了直接訪問(wèn)外,還可分別對(duì)其高十六位和低十六位(還計(jì)的我說(shuō)它們是32位的嗎?)進(jìn)行訪問(wèn)。它們的低十六位就是把它們前邊兒的E去掉,即EAX的低十六位就是AX。而且它們的低十六位又可以分別進(jìn)行八位訪問(wèn),也就是說(shuō),AX還可以再進(jìn)行分解,即AX還可分為AH(高八位)AL(低八位)。其它三個(gè)寄存器請(qǐng)自行推斷。這樣的話,你就可以應(yīng)付各種情況,如果你想操作的是一個(gè)八位數(shù)據(jù),那么可以用?MOV?AL?(八位數(shù)據(jù))或MOV?AH?(八位數(shù)據(jù)),如果你要操作的是一個(gè)十六位數(shù)據(jù),可以用MOV?AX?(十六位數(shù)據(jù))三十二位的話,就用MOV?EAX?(三十二位數(shù)據(jù))也許我這樣說(shuō),你還是會(huì)不明白,沒(méi)關(guān)系,慢慢來(lái),我給你大概畫(huà)張圖吧,雖然不怎么漂亮:?


───────────────────────?


│????????????????????│??????????│??????????│?


│????????????????????│??????????│??????????│?


│?????高十六位??????EAX????AH??AX????AL??????│?


│????????????????????│??????????│??????????│?


│????????????????????│??????????│??????????│?


───────────────────────?


(我倒啊
這個(gè)圖為啥老是不能正常顯示?我都重畫(huà)三遍了)??


明白了嗎?不明白沒(méi)有關(guān)系,你就按你自己的理解能力,能理解多少,就理解多少。?


這四個(gè)寄存器,主要就是用來(lái)暫時(shí)存放計(jì)算過(guò)程中所用的操作數(shù)、結(jié)果或其它信息。?


而ESP、EBP、EDI、ESI這四個(gè)呢,就只能用字來(lái)訪問(wèn),它們的主要用途就是在存儲(chǔ)器尋址時(shí),提供偏移地址。因此,它們可以稱為指針或變址寄存器。話說(shuō)回來(lái),從386以后,所有的寄存器都可以用來(lái)存儲(chǔ)內(nèi)存地址。(這里給你講一個(gè)小知識(shí),你在破解的時(shí)候是不是看到過(guò)[EBX]這樣的形式呢?這就是說(shuō)此時(shí)EBX中裝的是一個(gè)內(nèi)存地址,而真正要訪問(wèn)的,就是那那個(gè)內(nèi)存單元中所存儲(chǔ)的值)。?


在這幾個(gè)寄存器中,ESP稱為堆棧指針寄存。堆棧是一個(gè)很重要的概念,它是以“后進(jìn)先出”方式工作的一個(gè)存儲(chǔ)區(qū),它必須存在于堆棧段中,因而其段地址存放于SS寄存器中。它只有一個(gè)出入口,所以只有一個(gè)堆棧指針寄存器。ESP的內(nèi)容在任何時(shí)候都指向當(dāng)前的棧頂。我這樣說(shuō)你可能會(huì)覺(jué)的還是不明白,那我舉個(gè)例子吧,知道民工蓋房吧,假設(shè)有兩個(gè)民工,一個(gè)民工(以下簡(jiǎn)稱民工A)要向地上鋪磚,另一個(gè)民工(以下簡(jiǎn)稱民工B)給民工A遞磚,民工A趴在地上,手邊是民工B從遠(yuǎn)處搬來(lái)的板磚,他拿起來(lái)就用,民工B從遠(yuǎn)處搬來(lái)后,就還放在那一堆磚上,這樣,民工A拿著用后,民工B隨既就又補(bǔ)了上去,這就是后進(jìn)先出。你在腦子里想象一下這個(gè)這程。有沒(méi)有想明白,民工A永遠(yuǎn)是從最上邊開(kāi)始拿磚。堆棧就是這樣,它的基址開(kāi)始于一個(gè)高地址,然后每當(dāng)有數(shù)據(jù)入棧,它就向低地址的方向進(jìn)行存儲(chǔ)。相應(yīng)的入棧指令是PUSH。每當(dāng)有數(shù)據(jù)入棧,ESP就跟著改變,總之,它永遠(yuǎn)指向最后一個(gè)壓入棧的數(shù)據(jù)。之后,如果要用壓入堆棧的數(shù)據(jù),就用出棧指令將其取出。相應(yīng)的指令是POP,POP指令執(zhí)行后,ESP會(huì)加上相應(yīng)的數(shù)據(jù)位數(shù)。?


特別是現(xiàn)在到了Win32系統(tǒng)下面,堆棧的作用更是不可忽視,API所用的數(shù)據(jù),均是*堆棧來(lái)傳送的,即先將要傳送的數(shù)據(jù)壓入堆棧,然后CALL至API函數(shù),API函數(shù)會(huì)在函數(shù)體內(nèi)用出棧指令將相應(yīng)的數(shù)據(jù)出棧。然后進(jìn)行操作。以后你就會(huì)知道這點(diǎn)的重要性了。許多明碼比較的軟件,一般都是在關(guān)鍵CALL前,將真假兩個(gè)注冊(cè)碼壓入棧。然后在CALL內(nèi)出棧后進(jìn)行比較。所以,只要找到個(gè)關(guān)鍵CALL,就能在壓棧指令處,下d命令來(lái)查看真正的注冊(cè)碼。具體內(nèi)容會(huì)在后面詳細(xì)介紹,本章暫不予討論。?


另外還有EBP,它稱為基址指針寄存器,它們都可以與堆棧段寄存器SS聯(lián)用來(lái)確定堆棧中的某一存儲(chǔ)單元的地址,ESP用來(lái)指示段頂?shù)钠频刂罚鳨BP可作為堆棧區(qū)中的一個(gè)基地址以便訪問(wèn)堆棧中的信息。ESI(源變址寄存器)和EDI(目的變址寄存器)一般與數(shù)據(jù)段寄存器DS聯(lián)用,用來(lái)確定數(shù)據(jù)段中某一存儲(chǔ)單元的地址。這兩個(gè)變址寄存器有自動(dòng)增量和自動(dòng)減量的功能,可以很方便地用于變址。在串處理指令中,ESI和EDI作為隱含的源變址和目的變址寄存器時(shí),ESI和DS聯(lián)用,EDI和附加段ES聯(lián)用,分別達(dá)到在數(shù)據(jù)段和附加段中尋址的目的。目前暫時(shí)不明白不要緊。?


接下來(lái),再介紹如花(當(dāng)當(dāng)當(dāng),我再打自己三下算了)接下來(lái),介紹一下專用寄存器,呵呵,有沒(méi)有被這個(gè)名字嚇倒?看起來(lái)怪專業(yè)的。?


所謂的專用寄存器,有兩個(gè),一個(gè)是EIP,一個(gè)是FLAGS。?


我們先來(lái)說(shuō)這個(gè)EIP,可以說(shuō),EIP算是所有寄存器中最重要的一個(gè)了。它的意思就是指令指針寄存器,它用來(lái)存放代碼段中的偏移地址。在程序運(yùn)行的過(guò)程中,它始終指向下一條指令的首地址。它與段寄存器CS聯(lián)用確定下一條指令的物理地址。當(dāng)這一地址送到存儲(chǔ)器后,控制器可以取得下一條要執(zhí)行的指令,而控制器一旦取得這條指令就馬上修改EIP的內(nèi)容,使它始終指向下一條指令的首地址。可見(jiàn),計(jì)算機(jī)就是用EIP寄存器來(lái)控制指令序列的執(zhí)行流程的。?


那些跳轉(zhuǎn)指令,就是通過(guò)修改EIP的值來(lái)達(dá)到相應(yīng)的目的的。?


再接著我們說(shuō)一下這個(gè)FLAGS,標(biāo)志寄存器,又稱PSW(program?status?word),即程序狀態(tài)寄存器。這一個(gè)是存放條件標(biāo)志碼、控制標(biāo)志和系統(tǒng)標(biāo)志的寄存器。?


其實(shí)我們根本不需要太多的去了解它,你目前只需知道它的工作原理就成了,我舉個(gè)例子吧:?


Cmp?EAX,EBX??;用EAX與EBX相減?


JNZ?00470395???;不相等的話,就跳到這里;?


這兩條指令很簡(jiǎn)單,就是用EAX寄存器裝的數(shù)減去EBX寄存器中裝的數(shù)。來(lái)比較這兩個(gè)數(shù)是不是相等,當(dāng)Cmp指令執(zhí)行過(guò)后,就會(huì)在FLAGS的ZF(zero?flag)零標(biāo)志位上置相應(yīng)值,如果結(jié)果為0,也就是他們兩個(gè)相等的話,ZF置1,否則置0。其它還有OF(溢出標(biāo)志)SF(符號(hào)標(biāo)志)CF(進(jìn)位標(biāo)志)AF(輔助進(jìn)位標(biāo)志)PF(奇偶標(biāo)志)等。?


這些你目前沒(méi)必要了解那么清楚,會(huì)用相應(yīng)的轉(zhuǎn)移指令就行了。?


最后要介紹的就是段寄存器了(剛才是誰(shuí)說(shuō)的櫻紅?反正不是我)?


這部分寄存器一共六個(gè),分別是CS代碼段,DS數(shù)據(jù)段,ES附加段,SS堆棧段,F(xiàn)S以及GS這兩個(gè)還是附加段。?


其實(shí)現(xiàn)在到了Win32環(huán)境下,段寄存器以經(jīng)不如DOS時(shí)代那樣重要了。?


所以,我們知道就行了。?


啰嗦了這么多,相信你對(duì)CPU以經(jīng)有了個(gè)大概的了解了吧。什么?還是什么也不明白?呵呵,那也不要灰心,請(qǐng)相信這是我的錯(cuò),是我沒(méi)有講清楚而已,你可以去參考一些書(shū)籍。我始終覺(jué)的,你案頭有一本講匯編的書(shū)是非常非常有必要的,我這邊兒是清華版的《80x86匯編語(yǔ)言程序設(shè)計(jì)》沈美明主編,46元。?


我們接下來(lái)就再講一講一些常用的匯編指令吧。(由于考慮到目前以經(jīng)有了相應(yīng)的帖子,所以,我只是從匯編指令中,挑出一些最常用,需要掌握的,更多內(nèi)容,還請(qǐng)參見(jiàn)書(shū)本。)?


CMP?A,B?比較A與B其中A與B可以是寄存器或內(nèi)存地址,也可同時(shí)是兩個(gè)寄存器,但不能同都是內(nèi)存地址。這個(gè)指令太長(zhǎng)見(jiàn)了,許多明碼比較的軟件,就用這個(gè)指令。?


MOV?A,B?把B的值送給A其中,A與B可是寄存器或內(nèi)存地址,也可同時(shí)是兩個(gè)寄存器,但不能同都是內(nèi)存地址。?


Xor?a,a異或操作,主要是用來(lái)將a清空?


LEA裝入地址,例如LEA?DX,string?將字符的地址裝入DX寄存器?


PUSH?壓棧?


POP?出棧?


ADD?加法指令?格式:ADD?DST,SRC?執(zhí)行的操作:(DST)<-(SRC)+(DST)?


SUB?減法指令?格式:SUB?DST,SRC?執(zhí)行的操作:(DST)<-(DST)-(SRC)?


MUL?無(wú)符號(hào)乘法指令?格式:?MUL?SRC??執(zhí)行的操作:字節(jié)操作(AX)<-(AL)*(SRC);字操作(DX,AX)<-(AX)*(SRC);雙字操作:(EDX,EAX)<-(EAX)*(SRC)?


DIV?無(wú)符號(hào)除法指令?格式:DIV?SRC??執(zhí)行的操作:字節(jié)操作:16們被除數(shù)在AX中,8位除數(shù)為源操作數(shù),結(jié)果的8位商在AL中,8位余數(shù)在AH中。表示為:?


(AL)<-(AX)/(SRC)的商,(AH)<-(AX)/(SRC)的余數(shù)。字操作:32位被除數(shù)在DX,AX中。其中DX為高位字,16位除數(shù)為源操作數(shù),結(jié)果的16位商在AX中,16位余數(shù)在DX中。表示為:(AX)<-(DX,AX)/(SRC)的商,(DX)<-(DX,AX)/(SRC)的余數(shù)。?


雙字操作:64位的被除數(shù)在EDX,EAX中。其中EDX為高位雙字;32位除數(shù)為源操作數(shù),結(jié)果的32位商在EAX中,32位余數(shù)在EDX中。表示為:?


(EAX)<-(EDX,EAX)/(SRC)的商,(EDX)<-(EDX,EAX)/(SRC)的余數(shù)。?


NOP?無(wú)作用,可以用來(lái)抹去相應(yīng)的語(yǔ)句,這樣的話,嘿嘿嘿…?


CALL調(diào)用子程序,你可以把它當(dāng)作高級(jí)語(yǔ)言中的過(guò)程來(lái)理解。?


控制轉(zhuǎn)移指令:?


JE?或JZ?若相等則跳?


JNE或JNZ?若不相等則跳?


JMP?無(wú)條件跳?


JB?若小于則跳?


JA?若大于則跳?


JG?若大于則跳?


JGE?若大于等于則跳?


JL?若小于則跳?


JLE?若小于等于則跳?


總的來(lái)說(shuō),以上幾個(gè),都是比較常見(jiàn)的,需要掌握,但需要掌握的絕不止這幾個(gè),其它的指令希望你能在私下里再了解一下,可以找相應(yīng)的教程來(lái)看。?


剛才忘了,現(xiàn)在再把數(shù)制轉(zhuǎn)換也給貼上:?


首先說(shuō)二進(jìn)制轉(zhuǎn)換為十進(jìn)制的問(wèn)題:?


各位二進(jìn)制數(shù)碼乘以與其對(duì)應(yīng)的權(quán)之和即為該二進(jìn)制相對(duì)應(yīng)的十進(jìn)制數(shù)。例如:?


10100=2的4次方+2的2次方,也就是十進(jìn)制數(shù)20。?


11000=2的4次方+2的3次方,也就是十進(jìn)制數(shù)24。?


接著說(shuō)一下十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)的方法:?


這樣的方法到底有多少,我也不清楚,我只講最簡(jiǎn)單的一個(gè)-除法:?


把要轉(zhuǎn)換的十進(jìn)制數(shù)的整數(shù)部分不斷除以2,并記下余數(shù),直到商為0為止。?


例:N=34D(說(shuō)明一下,你可能在某些數(shù)字的后邊看到過(guò)加有一個(gè)字母,這個(gè)字母便是用來(lái)表示數(shù)制的,十進(jìn)制數(shù)用D,二進(jìn)制數(shù)用B,八進(jìn)制數(shù)用O,十六進(jìn)制數(shù)用H)?


??34/2=17?????(a0=0)?


??17/2=8??????(a1=1)?


??8/2=4???????(a2=0)?


??4/2=2???????(a3=0)?


??2/2=1???????(a4=0)?


??1/2=0???????(a5=1)?


所以N=34D=100010B。?


對(duì)于被轉(zhuǎn)換的十進(jìn)制數(shù)的小數(shù)部分則應(yīng)不斷乘以2,并記下其整數(shù)部分,直到結(jié)果的小數(shù)部分為0為止。?


十六進(jìn)制數(shù)與二進(jìn)制數(shù)、十進(jìn)制數(shù)之間的轉(zhuǎn)換:?


總的來(lái)說(shuō),十六進(jìn)制數(shù)與二進(jìn)數(shù)之間的轉(zhuǎn)換,應(yīng)該算是很簡(jiǎn)單的了,你只需把與之相對(duì)應(yīng)的數(shù)值進(jìn)行轉(zhuǎn)換就成了。?


十六進(jìn)制數(shù)的基數(shù)是16,共有16個(gè)數(shù)碼,它們是0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。其中A表示十進(jìn)制中的10,其余類推。它們與二進(jìn)制和十進(jìn)制數(shù)的關(guān)系如下:?


0H=0D=0000B,1H=1D=0001B,2H=2D=0010B,3H=3D=0011B,4H=4D=0100B,5H=5D=0101B,6H=6D=0110B,7H=7D=0111B,8H=8D=1000B,9H=9D=1001B,AH=10D=1010B,BH=11D=1011B,CH=12D=1100B,DH=13D=1101B,EH=14D=1110B,FH=15D=1111B?


所以,二進(jìn)制與十六進(jìn)制之間要進(jìn)行轉(zhuǎn)換的話,只要把它們由低到高每四位組成一級(jí),直接用十六進(jìn)制來(lái)表示就可以了:?


例:???1000??????1010??????0011???????0101?


???????8?????????A?????????3??????????5?


十六進(jìn)制轉(zhuǎn)二進(jìn)制則用只需將每一位用四位二進(jìn)制數(shù)來(lái)表示就成了:?


例:?????A?????????B?????????1??????????0?


?????1010??????1011???????0001???????0000?


最后是十六進(jìn)制數(shù)與十進(jìn)制數(shù)之間的互相轉(zhuǎn)換?


十六進(jìn)制數(shù)轉(zhuǎn)十進(jìn)制數(shù)?


各位十六進(jìn)制數(shù)與其對(duì)應(yīng)權(quán)值的乘積之和即為與此十六進(jìn)制數(shù)相對(duì)應(yīng)的十進(jìn)制數(shù)。?


例:N=BF3CH?


???=11*16的3次方+15*16的2次方+3*16的1次方+12*16的0次方?


???=11*4096+15*256+3*16+12*1?


???=48956D?


十進(jìn)制轉(zhuǎn)十六進(jìn)制?


我還是只講最簡(jiǎn)單的除法:?


把要轉(zhuǎn)換的十進(jìn)制數(shù)的整數(shù)值部分不斷除以16,并記下余數(shù),直到商為0為止。?


例N=48956D?


???48956/16=3059???????(a0=12)?


???3059/16=191?????????(a1=3)?


???191/16=11???????????(a2=15)?


???11/16=0?????????????(a3=11)?


所以N=48956D=BF3CH。?


通過(guò)以上的介紹,我不知道你到底看懂沒(méi)有,如果有的話,請(qǐng)你去看一下書(shū)本,把我沒(méi)講到的地方和講過(guò)了的地方都仔細(xì)地看幾遍。如果你根本就沒(méi)有看懂,那么你就更需要去看書(shū)了,不要因?yàn)檫@就喪失掉學(xué)習(xí)的信心。你認(rèn)真地把前邊兒的CPU介紹看完,弄清楚寄存器的概念,再把后邊匯編指令拿下,就可以上路了。你認(rèn)真學(xué),認(rèn)真背的話,會(huì)發(fā)現(xiàn)其實(shí)并沒(méi)你想像中的那么難。一星期的時(shí)間,就可大概掌握了,但只是掌握而已,最起碼可以看懂匯編代碼了。要真想學(xué)好的話,就連后邊兒的也一同看了吧,再寫(xiě)一些小程序來(lái)練練手。當(dāng)然想精通匯編,那可不是一天兩天一月兩月的事,但你只要有恒心,有什么搞不定的?CPU也是人做的,指令只是其中的一部分而已,人家能做出CPU,你還怕連使用都學(xué)不會(huì)??



課后FAQ?


Q:我以前學(xué)過(guò)8086/8088,并且也在DOS下寫(xiě)過(guò)程序,能行嗎??


A:絕對(duì)能行,相對(duì)8086/8088,現(xiàn)在的CPU在基本指令方面,也沒(méi)有添加多少新的指令。你只需了解一下各寄存器的變化以及補(bǔ)充一下Windows程序的知識(shí)就成了。而且,既然你用匯編在DOS下寫(xiě)過(guò)程序,那么對(duì)Debug等調(diào)試器,肯定已經(jīng)很上手了,所以你有先天的優(yōu)勢(shì)。?


Q:匯編對(duì)我來(lái)說(shuō)不成問(wèn)題,可我為什么總是不上手呢??


A:呵呵,這樣的老鳥(niǎo)倒還有不少,他們把匯編用的相當(dāng)熟練,但是,只是因?yàn)榻?jīng)驗(yàn)的原因,所以才覺(jué)的不上手的,許多人當(dāng)初不也都這樣嗎?最起碼我就是,見(jiàn)了CALL就跟進(jìn),呵呵,倒跟了不少API,所以對(duì)于這部分高手,你只需多練練手以及掌握一些分析的技巧就成了。?


Q:我沒(méi)學(xué)過(guò)編程,能學(xué)匯編嗎??


A:總的來(lái)說(shuō),也行。不過(guò)希望匯編的學(xué)習(xí),不會(huì)使你丟掉學(xué)習(xí)其它高級(jí)語(yǔ)言的信心。:)?



答網(wǎng)友問(wèn)?


Q:寄存器可以隨便用么,有沒(méi)有什么限制?寫(xiě)個(gè)程序的時(shí)候那些變量什么的可以放在任意的寄存器么??


A:呵呵,我現(xiàn)在就來(lái)回答樓上朋友的問(wèn)題。?


寄存器有它的使用機(jī)制,及各個(gè)寄存器都有著明確的分工。?


如小翠兒??如數(shù)據(jù)寄存器(EAX-EDX),它們都是通用寄存器,及在軟件中,任何數(shù)據(jù)都可存放于此。但是除此之外,它們又可以都可以用于各自的專用目的。?


例如:?


EAX可以作為累加器來(lái)使用,所以它是算術(shù)運(yùn)算的主要寄存器。在乘除法等指令中指定用來(lái)存放操作數(shù)。比如在乘法中,你可以用AL或AX或EAX來(lái)裝被乘數(shù),而AX或DX:AX或EAX或EDX:EAX則用來(lái)裝最后的積。?


EBX一般在計(jì)算存儲(chǔ)器地址時(shí),它經(jīng)常用作基址寄存器。?


ECX則常用來(lái)保存計(jì)數(shù)值,如在移位指令它用來(lái)裝位移量、循環(huán)和串處理指令中作隱含的計(jì)數(shù)器。?


最后就剩下四大天王中的黎明了,近一段時(shí)間來(lái),他總是比較低調(diào)
(你別打我了,我去撞墻好了)最后就剩下EDX了,一般在作雙字長(zhǎng)運(yùn)算時(shí)把DX和AX組在一起存放一個(gè)雙字長(zhǎng)數(shù)(你還記的什么是雙字長(zhǎng)吧,舉個(gè)例子,比如說(shuō)有一個(gè)數(shù)二進(jìn)制數(shù)據(jù)01101000110101000100100111010001,你要把它寄存起來(lái),就可以把0110100011010100(即高十六位)放在DX中,把0100100111010001(即低十六位)放在AX中,這個(gè)數(shù)表示為DX:AX)當(dāng)然完全可以用一個(gè)EDX就把這個(gè)數(shù)給裝下。所以,還可以用EDX:EAX來(lái)裝一個(gè)64位數(shù)據(jù),這個(gè)你會(huì)推斷出來(lái)吧。?


而ESP、EBP、EDI、ESI,我上邊兒以經(jīng)大概介紹的差不多了,所以這里不說(shuō)它們了。?


當(dāng)然還有其它的一些限制,因?yàn)槲覀冎皇且闯绦虻膮R編代碼(人家寫(xiě)好了的,肯定不會(huì)犯錯(cuò)誤吧),而不是要去寫(xiě),所以可以不必掌握。有性趣的話,去看相關(guān)書(shū)籍。?


另外再說(shuō)一下你的最后一個(gè)問(wèn)題“寫(xiě)個(gè)程序的時(shí)候那些變量什么的可以放在任意的寄存器么??”這句話我不明白你要問(wèn)的是什么。我想你可能是把一些關(guān)點(diǎn)給搞錯(cuò)了,變量這詞通常都是出現(xiàn)在高級(jí)語(yǔ)言中的,而你用高級(jí)語(yǔ)言寫(xiě)程序的話,完全不用理解那些寄存器什么的,這些都跟高級(jí)語(yǔ)言沒(méi)什么關(guān)系。但是最終,高級(jí)語(yǔ)言也還是把你寫(xiě)的程序轉(zhuǎn)換為對(duì)寄存器、內(nèi)部存儲(chǔ)器的操作。?


<本章完>


?1
int?fun()
?2

{?
?3
????int?a=0;?
?4
????register?int?i;?
?5
????for?(i=0;?i<1000;?i++)
?6
????
{
?7
????????a+=i;?
?8
????}
?9
????return?a;?
10
}
11
12
int?main(int?argc,?char*?argv[])
13

{
14
????int?a;
15
????a?=?fun();
16
????printf("%d\n",a);
17
18
????return?0;
19
} 上面這段程序的Assembly為:
//ESP為堆棧指針,每push一個(gè)register,ESP減4始終指向棧頂
//每一個(gè)語(yǔ)句的前部分那些數(shù)字表示EIP

004125F0??push????????ebp????????//ebp?0012FFC0
004125F1??mov?????????ebp,esp????//esp?0012FF70->EBP?=?0012FF70
004125F3??sub?????????esp,44h????//ESP?=?0012FF2C
004125F6??push????????ebx????????//ESP?=?0012FF28
004125F7??push????????esi????????//ESP?=?0012FF24
004125F8??push????????edi????????//ESP?=?0012FF20
????int?a;
????a?=?fun();
004125F9??call????????fun?(4116D6h)

/**//*
004116D6??jmp?????????fun?(4125A0h)????//ESP?=?0012FF1C

int?fun()
{?????????????????????//ESP?=?0012FF1C
004125A0??push????????ebp??????????//EBP?=?0012FF70????ESP?=?0012FF18
004125A1??mov?????????ebp,esp?????????//EBP?=?0012FF18在fun函數(shù)中未變
004125A3??sub?????????esp,48h?????????//ESP?=?0012FED0
004125A6??push????????ebx??????????//ESP?=?0012FECC
004125A7??push????????esi??????????//ESP?=?0012FEC8
004125A8??push????????edi??????????//ESP?=?0012FEC4一直未變直到pop棧
????int?a=0;?
//a的值:0x6bc9b010(隨即數(shù))???????????//0012FF14?=?6BC9B010(Register窗口中)?//表示0012FF14處的值為6BC9B010
//dword表示雙字節(jié),ptr表示a為內(nèi)存中的變量?[a]表示a的值
004125A9??mov?????????dword?ptr?[a],0??????//0012FF14(a的地址?)之后連續(xù)四個(gè)字節(jié)的值為0
//i的值:0x0041b4e6????????????//0012FF10?=?0041B4E6
????register?int?i;?
????for?(i=0;?i<1000;?i++)
004125B0??mov?????????dword?ptr?[i],0?????//0012FF10之后連續(xù)四個(gè)字節(jié)的值為0
//fun函數(shù)的起始地址為0x004125a0
004125B7??jmp?????????fun+22h?(4125C2h)?//0x004125a0+22h==0x4125C2h

004125B9??mov?????????eax,dword?ptr?[i]?
004125BC??add?????????eax,1?
//0012FF10?=?00000001?
//第一次運(yùn)行到這里可以在Memory窗口中看到0x0012FF10處的值為01?00?00?00要反過(guò)來(lái)看值
004125BF??mov?????????dword?ptr?[i],eax?
//0012FF10?=?00000000
004125C2??cmp?????????dword?ptr?[i],3E8h?
//如果i地址中的之[i]與3E8h(1000)相等,跳到EIP=0x4125D6處即跳出循環(huán)
004125C9??jge?????????fun+36h?(4125D6h)?
????{
????????a+=i;?
//將a的值放到寄存器Eax中
004125CB??mov?????????eax,dword?ptr?[a]????//EAX?=?00000000?a、i都為0
004125CE??add?????????eax,dword?ptr?[i]?//EAX?=?00000000
004125D1??mov?????????dword?ptr?[a],eax?
????}
//無(wú)條件跳到4125B9h處即i++
004125D4??jmp?????????fun+19h?(4125B9h)?
????return?a;?
004125D6??mov?????????eax,dword?ptr?[a]?
}
//之前ESP?=?0012FEC4?EBP?=?0012FF18
004125D9??pop?????????edi??//ESP?=?0012FEC8
004125DA??pop?????????esi??//ESP?=?0012FECC
004125DB??pop?????????ebx??//ESP?=?0012FED0
004125DC??mov?????????esp,ebp?//ESP?=?0012FF18
004125DE??pop?????????ebp??//ESP?=?0012FF1C?EBP?=?0012FF70與main函數(shù)調(diào)用fun時(shí)的值相等
004125DF??ret????
*/
//ESP?=?0012FF20?EBP?=?0012FF70
004125FE??mov?????????dword?ptr?[a],eax?
????printf("%d\n",a);
00412601??mov?????????eax,dword?ptr?[a]?
00412604??push????????eax??
00412605??push????????offset?string?"%d\n"?(42DB5Ch)?
0041260A??call????????@ILT+2495(_printf)?(4119C4h)?
0041260F??add?????????esp,8?

????return?0;
//將Eax中的值置為0
00412612??xor?????????eax,eax?
}其中那些進(jìn)棧出棧的作用還不明白