前言
首先來(lái)說(shuō)下 什么是hack ? hack字面意思“ 非法入侵 ”,那么在C/C++中其實(shí)就是 使用反匯編查看C/C++代碼對(duì)應(yīng)的匯編代碼 。
那可能有人要問(wèn)了,C/C++不是高級(jí)語(yǔ)言么,為什么還要看匯編代碼?理由嘛見(jiàn)仁見(jiàn)智,
個(gè)人理解有下面幾種:
- 1.C/C/C++應(yīng)用不像java,python那樣,報(bào)錯(cuò)信息可以在日志中一目了然,C/C++應(yīng)用的報(bào)錯(cuò)可以讓你查的懷疑人生,為什么呢?因?yàn)?報(bào)錯(cuò)日志提供的信息可能會(huì)誤導(dǎo)你 ,比如真實(shí)錯(cuò)誤是在100行,但是報(bào)錯(cuò)信息會(huì)在第一行或者全局都有報(bào)錯(cuò),就很讓人上頭。。有了這個(gè)hack過(guò)程,就可以定位到可能報(bào)錯(cuò)的地方,使用hack反編譯得到匯編代碼,匯編代碼可以讓你看清你寫的C/C/C++代碼對(duì)于CPU來(lái)說(shuō)實(shí)際執(zhí)行的行為。
- 2.當(dāng)我們?cè)谡{(diào)試C/C++應(yīng)用的時(shí)候,經(jīng)常會(huì) 有些疑惑 ,比如i++和++i為什么結(jié)果不同,又比如inline內(nèi)聯(lián)函數(shù)有什么用處?原理是什么?等等,這些也可以使用hack來(lái)解釋,那有人要說(shuō)了,這些我都知道啥意思啊,i++和++i前自增和后自增么,內(nèi)聯(lián)函數(shù)原理就是編譯期將函數(shù)代碼拷貝到調(diào)用處嘛。可是這些都是別人告訴你的,那如果一個(gè)代碼段別人沒(méi)告訴你呢?百度是不是什么都可以找到的,特別是對(duì)于C/C++開(kāi)發(fā)來(lái)說(shuō),信息更是少的可憐,此時(shí)你就會(huì)知道hack是多重要了。
- 3.如果你的C/C++應(yīng)用需要實(shí)現(xiàn)一些性 能上優(yōu)化 ,也可以使用hack過(guò)程查看CPU和內(nèi)存的行為,從而對(duì)應(yīng)用性能做一些提升,是不是很cool。。
既然有這么多用處,那么下面小余就來(lái)講解下如何在我們的應(yīng)用中hack。
不過(guò)在hack前我們需要了解一些匯編語(yǔ)言基礎(chǔ)。
匯編語(yǔ)言基礎(chǔ)
首先我們得了解匯編語(yǔ)言中的幾個(gè)模塊:1.寄存器 2.內(nèi)存 3.CPU
1.寄存器
寄存器是我們計(jì)算機(jī)中的最小存儲(chǔ)單位,在計(jì)算機(jī)金字塔的頂端, 屬于CPU的內(nèi)部存儲(chǔ) ,主要用來(lái)存儲(chǔ)CPU需要使用到的臨時(shí)數(shù)據(jù)。
CPU訪問(wèn)寄存器的速度比訪問(wèn)內(nèi)存的速度快了100倍左右。
CPU中寄存器種類包括:通用寄存器,指令地址寄存器,狀態(tài)寄存器等等等等。。 這里只介紹幾個(gè)比較關(guān)鍵的寄存器:
1.通用寄存器
寄存器 | 原文 | 解釋 | 說(shuō)明 |
---|---|---|---|
AX | accumulator | 累加寄存器 | 通常用來(lái)執(zhí)行加法,函數(shù)調(diào)用的返回值一般也放在這里面 |
CX | counter | 計(jì)數(shù)寄存器 | 通常用來(lái)作為計(jì)數(shù)器,比如for循環(huán) |
DX | data | 數(shù)據(jù)寄存器 | 數(shù)據(jù)存取 |
BX | base | 基址寄存器 | 讀寫I/O端口時(shí),edx用來(lái)存放端口號(hào) |
SP | stack pointer | 棧指針寄存器 | 棧頂指針,指向棧的頂部 |
BP | base pointer | 基址指針寄存器 | 棧底指針,指向棧的底部,通常用ebp+偏移量的形式來(lái)定位函數(shù)存放在棧中的局部變量 |
SI | source index | 源變址寄存器 | 字符串操作時(shí),用于存放數(shù)據(jù)源的地址 |
DI | destination index | 目標(biāo)變址寄存器 | 字符串操作時(shí),用于存放目的地址的,和esi兩個(gè)經(jīng)常搭配一起使用,執(zhí)行字符串的復(fù)制等操作 |
這里說(shuō)明幾點(diǎn):
- 1.在具體hack的過(guò)程中你可能看到的是eax,ecx,edx等而不是AX,CX,DX,他們區(qū)別: 帶e開(kāi)頭的說(shuō)明是32位寄存器,也就是4個(gè)字節(jié)存儲(chǔ)的寄存器,而AX是eax的低16位的“子寄存器”, 這是早期一些寄存器是16位的,而為了兼容早期這些寄存器就使用“子寄存器”來(lái)標(biāo)識(shí)。
- 2.匯編語(yǔ)言中,在函數(shù)體返回時(shí),統(tǒng)一使用eax為返回值,這個(gè)為什么這樣?我也不清楚,可能是早期大家都這么用就約定下來(lái)了吧。
- 3.當(dāng)我們?cè)谑褂煤瘮?shù)的時(shí)候,會(huì)將ebp的內(nèi)容壓入棧底,作為棧底指針,指向棧的底部,局部變量使用ebp+偏移地址來(lái)定位。對(duì)于棧頂則使用esp寄存器來(lái)定位,關(guān)于函數(shù)棧會(huì)在后面講解。
2.eip寄存器
eip寄存器可以說(shuō)是CPU中最關(guān)鍵的一個(gè)寄存器了,它指向了下一條要執(zhí)行的指令所存放的地址, CPU工作其實(shí)就是取出eip中的地址中的指令,然后去執(zhí)行這條指令,并將下一條指令的地址賦值給eip ,這樣CPU就可以依次執(zhí)行完成所有的匯編代碼.
如果你要問(wèn)我它是怎么找到下一條指令的地址的,首先正常執(zhí)行指令是+1位下一條指令地址,如果碰到一些比如if語(yǔ)句,函數(shù)調(diào)用等情況,就會(huì)在上一條指令中得到這個(gè)地址,并賦值給eip,這個(gè)賦值動(dòng)作是CPU自動(dòng)完成的,開(kāi)發(fā)是不能隨便更改的,這也變相的防止你搞出bug不是?
3.狀態(tài)寄存器
狀態(tài)寄存器中記錄了CPU執(zhí)行過(guò)程中的一系列狀態(tài),對(duì)于32位就有32個(gè)標(biāo)志位,這些標(biāo)志大部分都是由CPU自行設(shè)置:
寄存器標(biāo)志位 | 解釋 |
---|---|
CF | 進(jìn)位標(biāo)志 |
PF | 奇偶標(biāo)志 |
ZF | 零標(biāo)志 |
SF | 符號(hào)標(biāo)志 |
OF | 補(bǔ)碼溢出標(biāo)志 |
TF | 跟蹤標(biāo)志 |
IF | 中斷標(biāo)志 |
…… |
4.指令寄存器:
根據(jù)指令在存貯器中的地址(由指令地址計(jì)數(shù)器給出),把指令從存貯器中取出來(lái)之后,需要有一個(gè)專門用于存放指令的地方,以便對(duì)指令進(jìn)行分析和執(zhí)行。 這個(gè)專門存放現(xiàn)行指令的部件就叫做指令寄存器 。
后面會(huì)具體匯編代碼會(huì)涉及到。
關(guān)于寄存器就講這么幾個(gè)吧,hack過(guò)程夠用了。
2.內(nèi)存
這里講解的內(nèi)存一般是指高速緩存(Cache)或者主存。
計(jì)算機(jī)在運(yùn)行程序時(shí),首先將程序從磁盤讀取到主存,然后CPU按規(guī)則從主存中取出指令,數(shù)據(jù)并執(zhí)行指令,但是直接從主存(一般是DRAM)中讀寫是很慢的,所以引入了高速緩存(Cache)(使用的是SRAM)。
在程序運(yùn)行前首先會(huì)試圖將指令,數(shù)據(jù)從主存中讀取到Cache中,然后在程序執(zhí)行時(shí)直接訪問(wèn)Cache, 如果指令和數(shù)據(jù)可以從Cache中讀取到,那么就說(shuō)是“命中(hit)”,反之就是“不命中(miss)”, miss情況下需要從主存中讀取指令或者數(shù)據(jù),這樣會(huì)直接影響CPU的性能,所以命中率對(duì)CPU來(lái)說(shuō)至關(guān)重要。
內(nèi)存在計(jì)算中又會(huì)分為:堆區(qū),棧區(qū),全局(靜態(tài))區(qū),常量區(qū),代碼區(qū)等等。
這里講解下hack中比較關(guān)鍵的棧區(qū)。
棧區(qū)
- 棧中存放了局部變量,參數(shù),函數(shù)的返回地址等
- 棧區(qū)由編譯器自動(dòng)分配釋放,由操作系統(tǒng)自動(dòng)管理,無(wú)須手動(dòng)管理
- 棧區(qū)中的局部變量只在該函數(shù)作用域中有效,函數(shù)退出后就會(huì)被銷毀、
棧區(qū)就像一個(gè)桶一樣,桶底就是棧底為高地址區(qū),桶的頂部為低地址, 桶中放東西當(dāng)然是先放桶底然后依次疊加咯,也就是平時(shí)所說(shuō)的FILO先進(jìn)后出的模式。
3.CPU
1.CPU的功能
指令控制(程序的順序控制)
操作控制(一條指令由若干操作信號(hào)實(shí)現(xiàn))
時(shí)間控制(指令各個(gè)操作實(shí)施時(shí)間的定時(shí))
數(shù)據(jù)加工(算術(shù)運(yùn)算和邏輯運(yùn)算)
2.CPU的基本組成
中央處理器 CPU= 運(yùn)算器 + 控制器
運(yùn)算器:
ALU
累加器
暫存器
控制器
程序計(jì)數(shù)器 (PC) 、指令寄存器 (IR) 、數(shù)據(jù)緩沖器 (DR) 、地址寄存器 (AR) 、通用寄存器、狀態(tài)寄存器 (PSW) 、時(shí)序發(fā)生器、指令譯碼器 (ID) 、總線
CPU的工作分為以下 5 個(gè)階段:取指令階段、指令譯碼階段、執(zhí)行指令階段、訪存取數(shù)和結(jié)果寫回。
- 取指令 (IF,instruction fetch),即將一條指令從主存儲(chǔ)器中取到指令寄存器的過(guò)程。程序計(jì)數(shù)器中的數(shù)值,用來(lái)指示當(dāng)前指令在主存中的位置。當(dāng) 一條指令被取出后,程序計(jì)數(shù)器(PC)中的數(shù)值將根據(jù)指令字長(zhǎng)度自動(dòng)遞增。
- 指令譯碼階段 (ID,instruction decode),取出指令后,指令譯碼器按照預(yù)定的指令格式,對(duì)取回的指令進(jìn)行拆分和解釋,識(shí)別區(qū)分出不同的指令類 別以及各種獲取操作數(shù)的方法。現(xiàn)代CISC處理器會(huì)將拆分已提高并行率和效率
- 執(zhí)行指令階段 (EX,execute),具體實(shí)現(xiàn)指令的功能。CPU的不同部分被連接起來(lái),以執(zhí)行所需的操作。
- 訪存取數(shù)階段 (MEM,memory),根據(jù)指令需要訪問(wèn)主存、讀取操作數(shù),CPU得到操作數(shù)在主存中的地址,并從主存中讀取該操作數(shù)用于運(yùn)算。部分指令不需要訪問(wèn)主存,則可以跳過(guò)該階段。
- 結(jié)果寫回階段 (WB,write back),作為最后一個(gè)階段,結(jié)果寫回階段把執(zhí)行指令階段的運(yùn)行結(jié)果數(shù)據(jù)“寫回”到某種存儲(chǔ)形式。結(jié)果數(shù)據(jù)一般會(huì)被寫到CPU的內(nèi)部寄存器中,以便被后續(xù)的指令快速地存取;許多指令還會(huì)改變程序狀態(tài)字寄存器中標(biāo)志位的狀態(tài),這些標(biāo)志位標(biāo)識(shí)著不同的操作結(jié)果,可被用來(lái)影響程序的動(dòng)作。 在指令執(zhí)行完畢、結(jié)果數(shù)據(jù)寫回之后,若無(wú)意外事件(如結(jié)果溢出等)發(fā)生,計(jì)算機(jī)就從程序計(jì)數(shù)器中取得下一條指令地址,開(kāi)始新一輪的循環(huán),下一個(gè)指令周期將順序取出下一條指令。 [1] 許多復(fù)雜的CPU可以一次提取多個(gè)指令、解碼,并且同時(shí)執(zhí)行。
一些基礎(chǔ)的匯編命令:
1.數(shù)據(jù)傳送指令
-
1.mov :傳送指令,也可以單純理解為賦值語(yǔ)句,這個(gè)語(yǔ)句在匯編中是最常用的。 格式:mov det,src
首先mov語(yǔ)句分以下幾種情況:
- 1.CPU內(nèi)部寄存器之間數(shù)據(jù)傳送:mov ebp,esp ,將esp寄存器中的數(shù)據(jù)傳遞給ebp寄存器。
- 2.立即數(shù)傳遞到通用寄存器:mov ebp 3 ,將立即數(shù)3傳遞到ebp寄存器中。
- 3.立即數(shù)傳遞到內(nèi)存存儲(chǔ)單元:mov [bx] 123h,將立即數(shù)傳遞到bx指向的內(nèi)存地址中。
- 4.內(nèi)存和CPU之間數(shù)據(jù)傳送:內(nèi)存?zhèn)鬟f到CPU寄存器中:mov eax,dword ptr [a] ,CPU寄存器傳遞到內(nèi)存中:mov dword ptr [a],eax。
2.加減運(yùn)算指令
首先要了解,加減法運(yùn)算是會(huì)改變狀態(tài)寄存器中的值的,如一個(gè)有符號(hào)數(shù)的運(yùn)算高位溢出標(biāo)志(OF),無(wú)符號(hào)的運(yùn)算進(jìn)位標(biāo)志(CF)等。
加法指令:add、adc、inc
- 1.add
格式:add OPRD1,OPRD2 功能:OPRD1 = OPRD1 + OPRD2 舉例: add eax ecx;//得到的結(jié)果為eax = eax+ecx;
- 2.adc
格式:add OPRD1,OPRD2 功能:OPRD1 = OPRD1 + OPRD2 舉例: adc eax ecx;得到的結(jié)果為eax = eax+ecx+狀態(tài)寄存器中給的進(jìn)位標(biāo)志CF;
- 3.inc
格式:add OPRD 功能:OPRD = OPRD + 1 舉例: inc eax;//得到的結(jié)果為eax = eax+1;
減法指令:sub,dec,cmp,當(dāng)然還有和adc對(duì)應(yīng)的sbb 這里講解下sub和dec
- 1.sub
格式:sub OPRD1,OPRD2 功能:OPRD1 = OPRD1 - OPRD2 舉例: sub eax ecx;//得到的結(jié)果為eax = eax - ecx;
- 2.dec
格式:dec OPRD 功能:OPRD = OPRD - 1 舉例: dec eax;//得到的結(jié)果為eax = eax - 1;
- 3.cmp
格式:cmp OPRD1,OPRD2 功能:執(zhí)行OPRD1 - OPRD2,但運(yùn)算結(jié)果不運(yùn)送到OPRD1 注:該指令通過(guò)OPRD - OPRD2影響標(biāo)志位CF、ZF、SF、OF、AF、PF來(lái)判斷OPRD1和OPRD2的大小關(guān)系。 通過(guò)ZF判斷是否相等;如果是無(wú)符號(hào)數(shù),通過(guò)CF可判斷大??;如果是有符號(hào)數(shù),通過(guò)SF和OF判斷大小
3.邏輯運(yùn)算和移位指令
1、邏輯運(yùn)算指令:not、and、or、xor、test
- (1) NOT:非運(yùn)算
格式:NOT OPRD 功能:把操作數(shù)OPRD取反,然后送回OPRD。 注: OPRD可以是通用寄存器,也可以是存儲(chǔ)器操作數(shù),此指令對(duì)標(biāo)志沒(méi)有影響
- (2) AND:與運(yùn)算
格式:AND OPRD1,OPRD2 功能:對(duì)兩個(gè)操作數(shù)進(jìn)行按位邏輯“與”運(yùn)算,結(jié)果送到OPRD1中 注: 該指令執(zhí)行后,CF=0,OF=0,標(biāo)志PF、ZF、SF反映運(yùn)算結(jié)果,AF未定義。 某個(gè)操作數(shù)與自身相與,值不變,但可以使CF置0。
- (3) OR:或運(yùn)算
格式:OR OPRD1,OPRD2 功能:對(duì)兩個(gè)操作數(shù)進(jìn)行按位邏輯“或”運(yùn)算,結(jié)果送到OPRD1中 注: 該指令執(zhí)行后,CF=0,OF=0,標(biāo)志PF、ZF、SF反映運(yùn)算結(jié)果,AF未定義。 某個(gè)操作數(shù)與自身相或,值不變,但可以使CF置0。
- (4) XOR:異或運(yùn)算
格式:XOR OPRD1,OPRD2 功能:對(duì)兩個(gè)操作數(shù)進(jìn)行按位邏輯“異或”運(yùn)算,結(jié)果送到OPRD1中
2、一般移位指令:SAL/SHL,SAR/SHR
- (1) SAL/SHL(Shift Arithmetic Left / Shift Logic Left);算術(shù)左移/邏輯左移
格式:SAL OPRD,m SHL OPRD,m 功能:把操作數(shù)OPRD左移m位,每移動(dòng)一位,右邊用0補(bǔ)足1位,移出的最高位進(jìn)入標(biāo)志位CF 注: 算術(shù)左移和邏輯左移進(jìn)行相同的動(dòng)作,為了方便提供了兩個(gè)助記符。
- (2) SAR(Shift Arithmetic Right) ;算數(shù)右移指令
格式:SAR OPRD,m 功能:操作數(shù)右移m位,同時(shí)每移1位,左邊的符號(hào)位保持不變,移出的最低位進(jìn)入標(biāo)志位CF 注: 對(duì)有符號(hào)數(shù)和無(wú)符號(hào)數(shù),算數(shù)右移1位相當(dāng)于除以2 如:SAR BH,1 ;(BH)= 80H,指令執(zhí)行后(BH)= C0H
- (3) SHR(Shift Logic Right) ;邏輯右移指令
格式:SHR OPRD,m 功能:操作數(shù)右移m位,同時(shí)每移1位,左邊用0補(bǔ)足,移出的最低位進(jìn)入標(biāo)志位CF 注: 對(duì)無(wú)符號(hào)數(shù),邏輯右移1位相當(dāng)于除以2 如:SHR AH,1 ;(AH)= 80H,指令執(zhí)行后(AH)= 40H
切記:算術(shù)移位和邏輯移位的區(qū)別:
對(duì)于左移不管是算術(shù)移位和邏輯移位,低位都補(bǔ)0; 對(duì)于右移的算術(shù)移位:對(duì)于有符號(hào)數(shù),符號(hào)位不變,然后整體右移。
4.轉(zhuǎn)移指令
1、無(wú)條件轉(zhuǎn)移指令:JMP(Jump)
直接跳轉(zhuǎn)到Jump指定的地址, 跳轉(zhuǎn)指令也是改變當(dāng)前eip的值來(lái)決定的 ,這樣下次取指令會(huì)去新的eip地址下取。
格式:JMP OPRD
功能:使控制指令無(wú)條件轉(zhuǎn)移到OPRD的內(nèi)容給定的目標(biāo)地址處。操作數(shù)OPRD可以是通用寄存器,也可以是字存儲(chǔ)單元
例如:
jmp cx ;CX寄存器的內(nèi)容送IP
jmp word ptr [1234h] ;字存儲(chǔ)單元[1234h]的內(nèi)容送IP
-
C++
+關(guān)注
關(guān)注
21文章
2090瀏覽量
73406 -
匯編代碼
+關(guān)注
關(guān)注
0文章
23瀏覽量
7524 -
hacker
+關(guān)注
關(guān)注
0文章
4瀏覽量
1355
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論