前言
逆向是一種新型的思維模式也是軟件開(kāi)發(fā)領(lǐng)域中極為重要的技術(shù),涵蓋各種維度去深挖軟件架構(gòu)的本質(zhì)和操作系統(tǒng)原理,學(xué)習(xí)逆向后可以在各領(lǐng)域中發(fā)揮至關(guān)重要的作用,其中包括黑灰色,安全開(kāi)發(fā),客戶(hù)端安全,物聯(lián)網(wǎng),車(chē)聯(lián)網(wǎng),游戲安全,紅隊(duì)免殺等行業(yè)中繪制出更高的閃光點(diǎn)。
C與匯編的關(guān)系
基本語(yǔ)法的學(xué)習(xí):
各種進(jìn)制的轉(zhuǎn)換和原理
- 十進(jìn)制的定義:由十個(gè)符號(hào)組成,分別是0 1 2 3 4 5 6 7 8 9 逢十進(jìn)一
- 九進(jìn)制的定義:由九個(gè)符號(hào)組成,分別是0 1 2 3 4 5 6 7 8 逢九進(jìn)一
- 十六進(jìn)制的定義:由十六個(gè)符號(hào)組成,分別是0 1 2 3 4 5 6 7 8 9 A B C D E F
- N進(jìn)制的定義:由N個(gè)符號(hào)組成 逢N進(jìn)一
數(shù)據(jù)類(lèi)型與邏輯運(yùn)算
在計(jì)算機(jī)中,由于硬件的制約,數(shù)據(jù)是有長(zhǎng)度限制的,超過(guò)數(shù)據(jù)寬度的數(shù)據(jù)會(huì)被丟棄
同一個(gè)數(shù)據(jù),表示無(wú)符號(hào)數(shù)和有符號(hào)數(shù)則其含義不同
- 無(wú)符號(hào)數(shù):正數(shù)
- 有符號(hào)數(shù):正數(shù)、負(fù)數(shù)
例:
- 當(dāng)數(shù)據(jù)寬度為4時(shí),即數(shù)據(jù)只能存儲(chǔ)4位2進(jìn)制位0000~1111
無(wú)符號(hào)數(shù):
- 數(shù)據(jù):0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- 十六進(jìn)制:0 1 2 3 4 5 6 7 8 9 A B C D E F
- 二進(jìn)制:0000000100100011010001010110011110001001101010111100110111101111
有符號(hào)數(shù):
正數(shù):
- 數(shù)據(jù):0 1 2 3 4 5 6 7
- 十六進(jìn)制:0 1 2 3 4 5 6 7
- 二進(jìn)制:00001001000110100010101100111
負(fù)數(shù):
- 數(shù)據(jù):-1 -2 -3 -4 -5 -6 -7 -8
十六進(jìn)制:F E D C B A 9 8
二進(jìn)制: 11111110110111001011101010011000 - 可以發(fā)現(xiàn)當(dāng)數(shù)據(jù)為1011,把數(shù)據(jù)看作無(wú)符號(hào)數(shù)時(shí),數(shù)據(jù)表示為B
- 把數(shù)據(jù)看作有符號(hào)數(shù)時(shí),數(shù)據(jù)表示為-5
- 無(wú)符號(hào)數(shù)的表示范圍為0~2^4-1即0~15\\ 有符號(hào)數(shù)的表示范圍為-23~23-1即-8~7
常見(jiàn)的數(shù)據(jù)類(lèi)型(重要):
- BYTE 字節(jié) 8BIT 1字節(jié)
- WORD 字 16BIT 2字節(jié)
- DWORD 雙字 32BIT 4字節(jié)
常見(jiàn)的運(yùn)算符類(lèi)型(重要):
或運(yùn)算(or |):
兩個(gè)數(shù)只要有一個(gè)為1則結(jié)果為1
與運(yùn)算(and &):
兩個(gè)數(shù)都是1結(jié)果才為1
異或運(yùn)算(xor ^):
兩個(gè)數(shù)相同為0, 不同為1
非運(yùn)算(not !):
兩個(gè)數(shù)取反 1是0, 0是1
CPU如何計(jì)算2+3?
X:0010
Y:0011
先異或
R:0001
異或完以后要判斷是否運(yùn)算結(jié)束
將兩個(gè)數(shù)進(jìn)行與運(yùn)算 然后左移一位
0010<<1 ==0100
如果結(jié)果全為0,結(jié)果則為我們所要的結(jié)果
否則,把上面異或得到的值賦值到X
把左移后的結(jié)果賦值到Y(jié)
X:0001
Y:0100
重復(fù)操作
先異或
R:0101
再將兩個(gè)數(shù)進(jìn)行與運(yùn)算 然后左移一位
左移完結(jié)果全是0,結(jié)果則為我們所要的
最終結(jié)果為0101=5
CPU如何計(jì)算2-3?
X:0010
Y:1101
先異或
R:1111
將兩個(gè)數(shù)進(jìn)行與運(yùn)算 然后左移一位
0000<<1=0000
如果結(jié)果全為0,結(jié)果則為我們所要的結(jié)果
最終結(jié)果為1111 = -1
如何取某個(gè)值的第N位的數(shù)值:
與操作
如我們想要查看23h這個(gè)十六進(jìn)制數(shù)的第3位則可以進(jìn)行如下運(yùn)算:
先將23h轉(zhuǎn)化為二進(jìn)制:0010 0011
最簡(jiǎn)單的加密算法:
通過(guò)異或加密數(shù)據(jù) 再次異或后則解密數(shù)據(jù)
要加密的數(shù)據(jù):2021:0010 0000 0010 0001
密鑰:54:0101 0100
高位:0111 0100 = 74
低位:0111 0101 = 75
原本的2021加密成了7475
然后再次進(jìn)行異或操作進(jìn)行解密:
高位:0010 0000 = 20
低位:0010 0100 = 21
解密回了原來(lái)的數(shù)值2021
通用寄存器和內(nèi)存讀寫(xiě)
32位通用寄存器的指定用途如下:
堆棧相關(guān)匯編指令:
MOV指令
MOV 的語(yǔ)法:
MOV r/m8,r8
MOV r/m16,r16
MOV r/m32,r32
MOV r8,r/m8
MOV r16,r/m16
MOV r32,r/m32
MOV r8, imm8
MOV r16, imm16
MOV r32, imm32
MOV 目標(biāo)操作數(shù),源操作數(shù)
作用:拷貝源操作數(shù)到目標(biāo)操作數(shù)
源操作數(shù)可以是立即數(shù)、通用寄存器、段寄存器、或者內(nèi)存單元
目標(biāo)操作數(shù)可以是通用寄存器、段寄存器或者內(nèi)存單元
操作數(shù)的寬度必須一樣
源操作數(shù)和目標(biāo)操作數(shù)不能同時(shí)為內(nèi)存單元
ADD指令
ADD 的語(yǔ)法:
ADD r/m8, imm8
ADD r/m16,imm16
ADD r/m32,imm32
ADD r/m16, imm8
ADD r/m32, imm8
ADD r/m8, r8
ADD r/m16, r16
ADD r/m32, r32
ADD r8, r/m8
ADD r16, r/m16
ADD r32, r/m32
ADD 目標(biāo)操作數(shù),源操作數(shù)
作用:將源操作數(shù)加到目標(biāo)操作數(shù)上
SUB指令
SUB 的語(yǔ)法:
SUB r/m8, imm8
SUB r/m16,imm16
SUB r/m32,imm32
SUB r/m16, imm8
SUB r/m32, imm8
SUB r/m8, r8
SUB r/m16, r16
SUB r/m32, r32
SUB r8, r/m8
SUB r16, r/m16
SUB r32, r/m32
SUB 目標(biāo)操作數(shù),源操作數(shù)
作用:將源操作數(shù)減到目標(biāo)操作數(shù)上
AND指令
AND 的語(yǔ)法:
AND r/m8, imm8
AND r/m16,imm16
AND r/m32,imm32
AND r/m16, imm8
AND r/m32, imm8
AND r/m8, r8
AND r/m16, r16
AND r/m32, r32
AND r8, r/m8
AND r16, r/m16
AND r32, r/m32
AND 目標(biāo)操作數(shù),源操作數(shù)
作用:將源操作數(shù)與目標(biāo)操作數(shù)與運(yùn)算后將結(jié)果保存到目標(biāo)操作數(shù)中
OR指令
OR 的語(yǔ)法:
OR r/m8, imm8
OR r/m16,imm16
OR r/m32,imm32
OR r/m16, imm8
OR r/m8, r8
OR r/m16, r16
OR r/m32, r32
OR r8, r/m8
OR r16, r/m16
OR r32, r/m32
OR 目標(biāo)操作數(shù),源操作數(shù)
作用:將源操作數(shù)與目標(biāo)操作數(shù)或運(yùn)算后將結(jié)果保存到目標(biāo)操作數(shù)中
XOR指令
XOR 的語(yǔ)法:
XOR r/m8, imm8
XOR r/m16,imm16
XOR r/m32,imm32
XOR r/m16, imm8
XOR r/m8, r8
XOR r/m32, r32
XOR r8, r/m8
XOR r16, r/m16
XOR r32, r/m32
XOR 目標(biāo)操作數(shù),源操作數(shù)
作用:將源操作數(shù)與目標(biāo)操作數(shù)異或運(yùn)算后將結(jié)果保存到目標(biāo)操作數(shù)中
NOT指令
NOT 的語(yǔ)法:
NOT r/m8
NOT r/m16
NOT r/m32
NOT 操作數(shù)
作用:取反
LEA指令
lea:Load Effective Address,即裝入有效地址的意思,它的操作數(shù)就是地址
lea r32,dword ptr ds:[內(nèi)存編號(hào)(地址)]
將內(nèi)存地址賦值給32位通用寄存器
lea是傳址,mov是傳值,注意區(qū)別
堆棧結(jié)構(gòu)
Windows分配棧時(shí) 是從高地址往低地址分配:
- MOV EBX,0x13FFDC BASE
- MOV EDX,0x13FFDC TOP
棧底和棧頂可以是兩個(gè)任意的寄存器(Windows采用的是EBP和ESP)
剛開(kāi)始堆棧為空,棧頂和棧底相同
先將數(shù)據(jù)壓入后再修改棧頂
數(shù)據(jù)壓入
MOV DWORD PTR DS:[EDX-4],0xAAAAAAAA
修改棧頂
SUB EDX,4
先修改棧頂后再將數(shù)據(jù)壓入
修改棧頂
LEA EDX,DWORD PTR DS:[EDX-4] (和上面的SUB一樣)
數(shù)據(jù)壓入
MOV DOWRD PTR DS:[EDX],0xAAAAAAAA
棧頂加偏移讀取
MOV ESI,DWORD PTR DS:[EBX-8]
棧底加偏移讀取
MOV EDI,DWORD PTR DS:[EDX+4]
先取出數(shù)據(jù)再修改棧頂
取出數(shù)據(jù)
MOV EAX,DOWRD PTR DS:[EDX]
修改棧頂
ADD EDX,4
先修改棧頂再取出數(shù)據(jù)
修改棧頂
LEA EDX,DWORD PTR DS:[EDX+4]
取出數(shù)據(jù)
MOV EAX,DOWRD PTR DS:[EDX-4]
入棧和出棧操作也有對(duì)應(yīng)的指令:
上面我們自己模擬的兩個(gè)用作棧頂和棧底的寄存器在WINDOWS中分別對(duì)應(yīng)ESP和EBP
并且前面我們自己模擬的入棧和出棧操作也有對(duì)應(yīng)的指令:PUSH 和 POP
就是封裝了壓入數(shù)據(jù)和修改棧頂?shù)牟僮?/p>
- PUSH 和 POP
- push xxx將 xxx的數(shù)據(jù)壓入堆棧
- pop xxx將棧頂?shù)臄?shù)據(jù)存儲(chǔ)到xxx中
PUSH指令:
PUSH r32
PUSH r16
PUSH m16
PUSH m32
PUSH imm8/imm16/imm32
所有的push都是將esp-4?
壓入的數(shù)據(jù)的數(shù)據(jù)寬度:
當(dāng)push的是立即數(shù)將esp-4
當(dāng)push r32如push eax時(shí)將esp-4
當(dāng)push dword ptr ds:[12FFDA]即壓入雙字內(nèi)存地址中的數(shù)據(jù)時(shí)將esp-4
當(dāng)push word ptr ds:[12FFDA]即壓入字內(nèi)存地址中的數(shù)據(jù)時(shí)將esp-2
當(dāng)push ax,即r16 ,16位通用寄存器時(shí),esp-2
push 不允許壓入數(shù)據(jù)寬度為8的數(shù)據(jù) 如ah al 和byte ptr ds:[內(nèi)存編號(hào)]
POP指令
POP r32
POP r16
POP m16
POP m32
PUSHAD和POPAD指令
將所有的32位通用寄存器壓入堆棧,方便后面隨意使用寄存器,用于保護(hù)現(xiàn)場(chǎng)
與POPAD對(duì)應(yīng)
PUSHFD和POPFD指令
然后將32位標(biāo)志寄存器EFLAGS壓入堆棧
與POPAD對(duì)應(yīng)
其它相關(guān)指令
pusha:將所有的16位通用寄存器壓入堆棧
popa:將所有的16位通用寄存器取出堆棧
pushf::將的16位標(biāo)志寄存器EFLAGS壓入堆棧
popf:將16位標(biāo)志寄存器EFLAGS取出堆棧
棧底和棧頂原理:
- 控制棧頂和棧底分別為兩個(gè)固定的寄存器(EBP 基址指針寄存器 和 ESP 堆棧指針寄存器)
- 剛開(kāi)始堆棧為空,棧頂和棧底相同
標(biāo)志寄存器
EFLAGS寄存器
進(jìn)位標(biāo)志CF(Carry Flag)
如果運(yùn)算結(jié)果的最高位產(chǎn)生了一個(gè)進(jìn)位或借位,那么,其值為1,否則其值為0
例子:
MOV AL,0xFF
ADD AL,1
- 0x80+0x40
加黑的為最高位
0x80:0 1000 0000
0x40:0 0100 0000
結(jié)果為1100 0000 最高位并沒(méi)有發(fā)生變化,于是CF位為0
- 0x80-0x40
注意這里借位的位是1000 0000中的加黑部分
而非0 1000 0000這里的最高位
結(jié)果為0100 0000 最高位并沒(méi)有發(fā)生變化,于是CF位為0
- 0x80-0x81
0x80:1000 0000
0x81:1000 0001
結(jié)果為1111 1111= -1,最高位被借位,于是CF位為1
奇偶標(biāo)志PF(Parity Flag)
奇偶標(biāo)志PF用于反映運(yùn)算結(jié)果中最低有效字節(jié)中“1”的個(gè)數(shù)的奇偶性
如果“1”的個(gè)數(shù)為偶數(shù),則PF的值為1,否則其值為0。
指令指令執(zhí)行后AL的結(jié)果PFMOV AL,300111ADD AL,301101ADD AL,210000
例:
MOV AX,803
ADD AX,1
0x803: 0000 1000 0000 0011
執(zhí)行結(jié)果
0x804: 0000 1000 0000 0100 總共2個(gè)1 ,PF應(yīng)為1,但實(shí)際運(yùn)行結(jié)果PF為0
因?yàn)镻F是根據(jù)最低有效字節(jié)來(lái)看,即804后面04的這部分
04: 0000 0100 總共1個(gè)1,所以PF為0
輔助進(jìn)位標(biāo)志AF(Auxiliary Carry Flag)
在發(fā)生下列情況時(shí),輔助進(jìn)位標(biāo)志AF的值被置為1,否則其值為0:
- 在字操作時(shí),發(fā)生低字節(jié)向高字節(jié)進(jìn)位或借位時(shí)
- 在字節(jié)操作時(shí),發(fā)生低4位向高4位進(jìn)位或借位時(shí)
AF與數(shù)據(jù)寬度相關(guān)
32位時(shí) FFFF F FFF
16位時(shí) FF F F
8位時(shí) F F
加黑的字體為AF標(biāo)志位判斷的位置,如果該位置要向前進(jìn)位則AF為1,否則為0,和CF相似,不過(guò)判斷的位置不同
32位例:
MOV EAX,55EEFFFF
ADD EAX,2
16位例:
MOV AX,5EFE
ADD AX,2
8位例:
MOV AL,4E
ADD AL,2
零標(biāo)志ZF(Zero Flag)
零標(biāo)志ZF用來(lái)反映運(yùn)算結(jié)果是否為0
如果運(yùn)算結(jié)果為0,則其值為1,否則其值為0
作用:在判斷運(yùn)算結(jié)果是否為0時(shí),可使用此標(biāo)志位
例子:
XOR EAX,EAX
通過(guò)xor將eax清零,會(huì)改變zf標(biāo)志位為1
MOV EAX,0
通過(guò)MOV將EAX賦值為0,非運(yùn)算,不改變zf標(biāo)志位
符號(hào)標(biāo)志SF(Sign Flag)
符號(hào)標(biāo)志SF用來(lái)反映運(yùn)算結(jié)果的符號(hào)位,它與運(yùn)算結(jié)果的最高位相同
例子:
MOV AL,7F
ADD AL,2
溢出標(biāo)志OF(Overflow Flag)
溢出標(biāo)志OF用于反映有符號(hào)數(shù)加減運(yùn)算所得結(jié)果是否溢出
注意與CF區(qū)分?。?!
最高位進(jìn)位與溢出的區(qū)別:
進(jìn)位標(biāo)志表示無(wú)符號(hào)數(shù)運(yùn)算結(jié)果是否超出范圍.
溢出標(biāo)志表示有符號(hào)數(shù)運(yùn)算結(jié)果是否超出范圍.
溢出主要是給有符號(hào)運(yùn)算使用的,在有符號(hào)的運(yùn)算中,有如下的規(guī)律:
- 正 + 正 = 正 如果結(jié)果是負(fù)數(shù),則說(shuō)明有溢出
- 負(fù) + 負(fù) = 負(fù) 如果結(jié)果是正數(shù),則說(shuō)明有溢出
- 正 + 負(fù) 永遠(yuǎn)都不會(huì)有溢出
無(wú)符號(hào)、有符號(hào)都不溢出例
MOV AL,8
ADD AL,8
AL的數(shù)據(jù)寬度為8,即
無(wú)符號(hào)數(shù)范圍為0~FF即0~255
8+8=16在0~255內(nèi) 不溢出
有符號(hào)數(shù)的范圍為
正數(shù):0~7F 即0~127
負(fù)數(shù):80~FF 即 -128~0
8+8=16 在0~127內(nèi) 兩正數(shù)相加結(jié)果仍為正數(shù),不溢出
無(wú)符號(hào)溢出、有符號(hào)不溢出例
MOV AL,0FF
ADD AL,2
無(wú)符號(hào)數(shù)時(shí)
FF+2=255+2=257 在0~255外,溢出
有符號(hào)數(shù)時(shí)
FF+2=-1+2=1
正 + 負(fù) 永遠(yuǎn)都不會(huì)有溢出
無(wú)符號(hào)不溢出、有符號(hào)溢出例
MOV AL,7F
ADD AL,2
無(wú)符號(hào)數(shù)時(shí)
7F+2=127+2=129 在0~255內(nèi) 不溢出
有符號(hào)數(shù)時(shí)
7F+2=0x81在80~FF (負(fù)數(shù)范圍)內(nèi),兩正數(shù)相加結(jié)果為負(fù)數(shù),溢出
無(wú)符號(hào)、有符號(hào)都溢出
MOV AL,0FE
ADD AL,80
無(wú)符號(hào)數(shù)時(shí)
FE+2=254+2=256=0x100 在0~255外 溢出
有符號(hào)數(shù)時(shí)
FE+2=0x100在0~FF外,溢出
CPU如何計(jì)算OF位
首先引入兩個(gè)概念:
- 符號(hào)位有進(jìn)位
- 最高有效數(shù)值位向符號(hào)位產(chǎn)生的進(jìn)位
對(duì)于一個(gè)有符號(hào)數(shù):如0x80和0xC0
符號(hào)位有進(jìn)位
0x80:1 000 0000
0xC0:1 100 0000
最高有效數(shù)值位向符號(hào)位產(chǎn)生的進(jìn)位
0x80:1 0 00 0000
0xC0:1 1 00 0000
接下來(lái)看一組匯編指令
MOV AL,80
ADD AL,0C0
就是運(yùn)算0x80+0xc0
0x80:1 0 00 0000
0xC0:1 1 00 0000
符號(hào)位1+1有產(chǎn)生進(jìn)位,于是符號(hào)位有進(jìn)位為1
最高有效數(shù)值位向符號(hào)位產(chǎn)生的進(jìn)位0+1沒(méi)有產(chǎn)生進(jìn)位,于是最高有效數(shù)值位向符號(hào)位產(chǎn)生的進(jìn)位為0
OF = 符號(hào)位有進(jìn)位 xor 最高有效數(shù)值位向符號(hào)位產(chǎn)生的進(jìn)位
OF = 1 xor 0 = 1 所以此時(shí)OF=1
方向標(biāo)志DF(Direction Flag)
DF:方向標(biāo)志位
DF=1時(shí)串操作為減地址方式 DF=0為增地址方式
下面的MOVS指令有說(shuō)明DF的具體應(yīng)用
ADC指令:帶進(jìn)位加法
格式:ADC R/M,R/M/IMM 兩邊不能同時(shí)為內(nèi)存 數(shù)據(jù)寬度要一樣
例:
mov ax,2
mov bx,1
手動(dòng)修改CF為1
adc ax,1
執(zhí)行后,(ax)=4.adc執(zhí)行時(shí),相當(dāng)于計(jì)算:(ax)+1+CF=2+1+1=4
計(jì)算結(jié)果為4,原本1+2=3,但是現(xiàn)在變成了4,注意與ADD的區(qū)別就在于進(jìn)位
SBB指令:帶借位減法
格式:SBB R/M,R/M/IMM 兩邊不能同時(shí)為內(nèi)存 數(shù)據(jù)寬度要一樣
MOV AL,4
MOV CL,2
手動(dòng)修改CF為1
SBB AL,CL
執(zhí)行后,(al)=1,sbb執(zhí)行時(shí),相當(dāng)于計(jì)算:(al)-2-CF=4-2-1=1
計(jì)算結(jié)果為1,原本4-2=2,但是現(xiàn)在變成了1,注意與SUB的區(qū)別就在于進(jìn)位
XCHG指令:交換數(shù)據(jù)
格式:XCHG R/M,R/M 兩邊不能同時(shí)為內(nèi)存 數(shù)據(jù)寬度要一樣
XCHG AL,CL
XCHG DWORD PTR DS:[12FFC4],EAX
XCHG BYTE PTR DS:[12FFC4],AL
例:
MOV AL,1
MOV CL,2
XCHG AL,CL
執(zhí)行前:AL=1 CL=2
執(zhí)行后:AL=2 CL=1
MOVS指令:移動(dòng)數(shù)據(jù) 內(nèi)存-內(nèi)存
BYTE/WORD/DWORD
MOVS指令常用于復(fù)制字符串
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 簡(jiǎn)寫(xiě)為:MOVSB
MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI] 簡(jiǎn)寫(xiě)為:MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 簡(jiǎn)寫(xiě)為:MOVSD
例:
MOV EDI,12FFD8
MOV ESI,12FFD0
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
執(zhí)行后,EDI內(nèi)存里的值被修改為ESI內(nèi)存里的值,且EDI和ESI各加4
為什么各加4?
和DOWRD數(shù)據(jù)寬度相關(guān),如果為WORD 則各加2
為什么執(zhí)行完是加而不是減?
由DF(Direction Flag)方向標(biāo)志位決定,當(dāng)DF位為1時(shí)為減,當(dāng)DF位為0時(shí),則為加
STOS指令
將Al/AX/EAX的值存儲(chǔ)到[EDI]指定的內(nèi)存單元,和數(shù)據(jù)寬度相關(guān)
STOS BYTE PTR ES:[EDI] 將AL存儲(chǔ)到[EDI]
STOS WORD PTR ES:[EDI] 將AX存儲(chǔ)到[EDI]
STOS DWORD PTR ES:[EDI] 將EAX存儲(chǔ)到[EDI]
注意這里使用的是ES: 之前寫(xiě)的都是DS:
當(dāng)后面為[EDI]時(shí)要使用ES: 這和后面要學(xué)的段寄存器有關(guān),先記住
存儲(chǔ)完數(shù)據(jù)后EDI地址的變化方向也受DF標(biāo)志控制,1減0增
REP指令
按計(jì)數(shù)寄存器 (ECX) 中指定的次數(shù)重復(fù)執(zhí)行指令
MOV ECX,10
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 也可以寫(xiě)成REP MOVSD
這里的10為十六進(jìn)制,也就是0x10=16
代碼將會(huì)重復(fù)執(zhí)行16次,會(huì)不會(huì)往同一個(gè)地方覆蓋?
不會(huì),因?yàn)槊繄?zhí)行一次EDI和ESI都會(huì)變化4,變化方向由DF決定
跳轉(zhuǎn)指令
JCC指令
cc 代表 condition code(狀態(tài)碼)
Jcc不是單個(gè)指令,它只是描述了跳轉(zhuǎn)之前檢查條件代碼的跳轉(zhuǎn)助記符
例如JNE,在跳轉(zhuǎn)之前檢查條件代碼
典型的情況是進(jìn)行比較(設(shè)置CC),然后使用跳轉(zhuǎn)助記符之一
CMP EAX,0
JNE XXXXX
條件代碼也可以用AND、OR、XOR、加法、減法(當(dāng)然也可以是CMP)等指令來(lái)設(shè)置
JCC指令用于改變EIP(CPU要讀取的指令地址)
JMP指令
JMP指令:修改EIP的值
JMP指令只影響了EIP,不影響堆棧和其它通用寄存器
JMP 寄存器/立即數(shù) 相當(dāng)于 MOV EIP,寄存器/立即數(shù)
CALL指令
CALL指令和JMP指令都會(huì)修改EIP的值
但CALL指令會(huì)將返回地址(CALL指令的下一條指令地址)壓入堆棧
因此也會(huì)引起esp的變化
RET指令
call調(diào)用跳轉(zhuǎn)后執(zhí)行完相關(guān)代碼完要返回到call的下一條指令時(shí)使用ret指令
ret指令相當(dāng)于pop eip
比較指令
CMP指令
指令格式:CMP R/M,R/M/IMM
CMP指令只改變標(biāo)志寄存器的值
該指令是比較兩個(gè)操作數(shù),實(shí)際上,它相當(dāng)于SUB指令,但是相減的結(jié)果并不保存到第一個(gè)操作數(shù)中
只是根據(jù)相減的結(jié)果來(lái)改變ZF零標(biāo)志位的,當(dāng)兩個(gè)操作數(shù)相等的時(shí)候,零標(biāo)志位置1
例:
MOV EAX,100
MOV EBX,200
CMP EAX,EBX
CMP AX,WORD PTR DS:[405000]
CMP AL,BYTE PTR DS:[405000]
CMP EAX,DWORD PTR DS:[405000]
TEST指令
指令格式:TEST R/M,R/M/IMM
該指令在一定程度上和CMP指令時(shí)類(lèi)似的,兩個(gè)數(shù)值進(jìn)行與操作,結(jié)果不保存,但是會(huì)改變相應(yīng)標(biāo)志位
與的操作表項(xiàng)如下:
可以看到只要有任一操作數(shù)為0時(shí),結(jié)果就為0
常見(jiàn)用法:用這個(gè)指令,可以確定某寄存器是否等于0
只有當(dāng)eax=0時(shí) eax and eax才會(huì)是0
所以
TEST EAX,EAX
觀察ZF(零標(biāo)志位)就可以判斷EAX是否為0
JCC指令表
首先要明確一點(diǎn),所有的判斷跳轉(zhuǎn)指令都是根據(jù)標(biāo)志位來(lái)進(jìn)行判斷的
JCC指令也只影響EIP
堆棧圖
首先給定一段反匯編代碼,分析該段代碼的堆棧的變化情況,并繪制出堆棧圖
函數(shù)調(diào)用
00401168 |. 6A 02 push 0x2
0040116A |. 6A 01 push 0x1
0040116C |. E8 99FEFFFF call HelloWor.0040100A
00401171 |. 83C4 08 add esp,0x8
CALL內(nèi)部
00401040 /> \55 push ebp
00401041 |. 8BEC mov ebp,esp
00401043 |. 83EC 40 sub esp,0x40
00401046 |. 53 push ebx
00401047 |. 56 push esi
00401048 |. 57 push edi
00401049 |. 8D7D C0 lea edi,dword ptr ss:[ebp-0x40]
0040104C |. B9 10000000 mov ecx,0x10
00401051 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401056 |. F3:AB rep stos dword ptr es:[edi]
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
0040105E |. 5F pop edi ; HelloWor.00401171
0040105F |. 5E pop esi ; HelloWor.00401171
00401060 |. 5B pop ebx ; HelloWor.00401171
00401061 |. 8BE5 mov esp,ebp
00401063 |. 5D pop ebp ; HelloWor.00401171
00401064 \. C3 retn
開(kāi)始分析
分析流程較為冗長(zhǎng),可能會(huì)有些乏味,可以先看最后的流程總結(jié),再來(lái)看分析的細(xì)節(jié)
我們現(xiàn)在開(kāi)始逐語(yǔ)句分析堆棧的變化情況:
初始堆棧圖
我們觀察堆棧的情況:
此時(shí)ESP:0012FF34 EBP:0012FF80
結(jié)合寄存器和堆棧內(nèi)容繪出簡(jiǎn)易堆棧圖
壓入?yún)?shù)
00401168 |. 6A 02 push 0x2
可以看到執(zhí)行后ESP減少了4=0012FF30 并且0012FF30里的內(nèi)容為2,這就是所謂的入棧操作
0040116A |. 6A 01 push 0x1
可以看到執(zhí)行后ESP又減少了4=0012FF2C ,并且0012FF2C里的內(nèi)容為1
上面的兩條push語(yǔ)句是將兩個(gè)立即數(shù) 2和1壓入到堆棧中,我們可以畫(huà)出對(duì)應(yīng)的堆棧圖:
CALL指令
0040116C |. E8 99FEFFFF call HelloWor.0040100A
F7單步步入
可以看到CALL之后跳轉(zhuǎn)到了0040100A,并且esp又減少了4=0012FF28
而且我們可以注意到此時(shí)堆棧中0012FF28存放的內(nèi)容是:00401171正好是我們call指令的下一行指令的地址
0040116C |. E8 99FEFFFF call HelloWor.0040100A
00401171 |. 83C4 08 add esp,0x8
所以應(yīng)證了前面所學(xué)的call指令會(huì)將要返回的地址壓入棧中來(lái)保存現(xiàn)場(chǎng)
此時(shí)的堆棧圖為
接著我們就跳轉(zhuǎn)到了call的內(nèi)部
CALL內(nèi)部指令
00401040 /> \55 push ebp
EBP被壓入到堆棧中,此時(shí)堆棧圖為
接著執(zhí)行
00401041 |. 8BEC mov ebp,esp
ebp賦值為esp,此時(shí)堆棧圖為
接著執(zhí)行
00401043 |. 83EC 40 sub esp,0x40
將esp的值減去0x40=64,我們這里的相差的數(shù)據(jù)寬度為4即16,64/4=16,因此堆棧圖里多了16格(藍(lán)色部分),這種操作常被叫做提升堆棧,此時(shí)堆棧圖為:
我們可以發(fā)現(xiàn)提升完堆棧以后,堆棧的數(shù)據(jù)有些★意義不明★,這是因?yàn)槎褩V写娣诺氖桥R時(shí)的數(shù)據(jù),可能是之前使用時(shí)沒(méi)有清理的垃圾數(shù)據(jù)
接著執(zhí)行
00401046 |. 53 push ebx
00401047 |. 56 push esi
00401048 |. 57 push edi
將三個(gè)通用寄存器壓入堆棧,用于保護(hù)現(xiàn)場(chǎng),注意CALL之前和CALL之后,其前后環(huán)境要一致,這就是所謂的堆棧平衡
根據(jù)此時(shí)的堆棧內(nèi)容繪制堆棧圖
接著執(zhí)行
00401049 |. 8D7D C0 lea edi,dword ptr ss:[ebp-0x40]
將ebp-40所指向的內(nèi)存地址賦給edi
前面我們執(zhí)行了sub esp,0x40 所以這里其實(shí)就是將那時(shí)esp的地址傳給了edi(就是push ebx esi edi)之前的的esp
此時(shí)堆棧圖并發(fā)生沒(méi)有變化
接著看下一行
0040104C |. B9 10000000 mov ecx,0x10
00401051 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
分別給ecx和eax賦值,堆棧圖依舊沒(méi)有發(fā)生變化
接著看下一行
00401056 |. F3:AB rep stos dword ptr es:[edi]
這條語(yǔ)句用到了我們前面所學(xué)的逆向基礎(chǔ)筆記五 標(biāo)志寄存器中的內(nèi)容(如有疑惑可前往查看)
rep的作用是,重復(fù)執(zhí)行 stos dword ptr es:[edi],每次執(zhí)行都會(huì)使ecx-1,直到ecx為0再執(zhí)行下一條語(yǔ)句
前面賦值ecx為0x10=16,正好對(duì)應(yīng)我們堆棧圖中藍(lán)色的格子數(shù),所以將會(huì)執(zhí)行16次
stos dword ptr es:[edi]則是將eax的值賦值給edi所指向的內(nèi)存地址里的值,并且每執(zhí)行一次edi都會(huì)增加4(D標(biāo)志位為0所以是增加)
結(jié)合前面edi==esp,這里其實(shí)是將我們提升堆棧的那部分內(nèi)存區(qū)域初始化
此時(shí)的堆棧內(nèi)容為
很明顯地看到原本的垃圾數(shù)據(jù)被我們初始化為了CCCCCCCC
堆棧圖也變成了
實(shí)際執(zhí)行內(nèi)容
接著看下面的代碼
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
根據(jù)堆棧圖我們可以很清晰地看出
[ebp+0x8]正是我們call外部push的參數(shù):1
[ebp+0xc]正是我們call外部push的參數(shù):2
這里是將eax賦值為1,然后再給eax+2,最終結(jié)果eax=3
還原現(xiàn)場(chǎng)并返回
此時(shí)堆棧圖依舊沒(méi)有發(fā)生變化,接著看下面的語(yǔ)句
0040105E |. 5F pop edi ; HelloWor.00401171
0040105F |. 5E pop esi ; HelloWor.00401171
00401060 |. 5B pop ebx ; HelloWor.00401171
出棧,還原現(xiàn)場(chǎng),堆棧圖
下一條
還原esp,前面mov ebp,esp對(duì)應(yīng)也要還原
此時(shí)堆棧圖為:
繼續(xù)看下一條指令
00401063 |. 5D pop ebp ; HelloWor.00401171
將ebp出棧,恢復(fù)現(xiàn)場(chǎng),此時(shí)的堆棧圖為
最后一句
00401064 \. C3 retn
此時(shí)棧頂為
返回,相當(dāng)于于pop eip
執(zhí)行后
執(zhí)行后的堆棧圖為
執(zhí)行返回后
此時(shí)返回到了
也就是之前call的下一句指
00401171 |. 83C4 08 add esp,0x8
此時(shí)的堆棧圖
我們可以發(fā)現(xiàn)此時(shí)的ESP和EBP又變回到了原本執(zhí)行前的狀態(tài),(寄存器也一樣),這就是所謂的堆棧平衡
總結(jié)
通過(guò)上面的分析,我們可以得出這段代碼所處理的大致流程
可分為三個(gè)部分:壓入?yún)?shù)、調(diào)用CALL、CALL返回后
壓入?yún)?shù)
壓入?yún)?shù)部分十分簡(jiǎn)單,就是將調(diào)用CALL所需的參數(shù)壓入堆棧,方便CALL內(nèi)部執(zhí)行時(shí)調(diào)用
這里對(duì)應(yīng)的語(yǔ)句為
00401168 |. 6A 02 push 0x2
0040116A |. 6A 01 push 0x1
即這個(gè)CALL得到的參數(shù)為2和1
調(diào)用CALL
調(diào)用CALL又可以分為六個(gè)部分:
提升堆棧
保護(hù)現(xiàn)場(chǎng)
初始化提升的堆棧
執(zhí)行實(shí)際內(nèi)容
恢復(fù)現(xiàn)場(chǎng)
返回
提升堆棧
對(duì)應(yīng)語(yǔ)句為
00401040 /> \55 push ebp
00401041 |. 8BEC mov ebp,esp
00401043 |. 83EC 40 sub esp,0x40
將堆棧提升了0x40
保護(hù)現(xiàn)場(chǎng)
對(duì)應(yīng)語(yǔ)句為
00401046 |. 53 push ebx
00401047 |. 56 push esi
00401048 |. 57 push edi
將ebx、esi、edi三個(gè)通用寄存器保存到堆棧中,前面的push ebp其實(shí)也屬于保護(hù)現(xiàn)場(chǎng)
初始化提升的堆棧
00401049 |. 8D7D C0 lea edi,dword ptr ss:[ebp-0x40]
0040104C |. B9 10000000 mov ecx,0x10
00401051 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401056 |. F3:AB rep stos dword ptr es:[edi]
這里將我們提升的堆棧中的內(nèi)容全部初始化為CCCCCCCC
為什么是初始化為CC?防止緩沖溢出
CC的硬編碼對(duì)應(yīng)的指令為int 3,即斷點(diǎn)
這么做有什么好處呢?當(dāng)程序執(zhí)行超過(guò)緩沖區(qū)時(shí),遇到int 3就會(huì)自動(dòng)停下來(lái)
執(zhí)行實(shí)際的內(nèi)容
對(duì)應(yīng)語(yǔ)句為
00401058 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
就是將前面壓入的參數(shù)2和1進(jìn)行相加得到3
恢復(fù)現(xiàn)場(chǎng)
對(duì)應(yīng)語(yǔ)句為
0040105E |. 5F pop edi ; HelloWor.00401171
0040105F |. 5E pop esi ; HelloWor.00401171
00401060 |. 5B pop ebx ; HelloWor.00401171
00401061 |. 8BE5 mov esp,ebp
00401063 |. 5D pop ebp ; HelloWor.00401171
與前面保護(hù)現(xiàn)場(chǎng)相對(duì)應(yīng)
返回
對(duì)應(yīng)語(yǔ)句為
00401064 \. C3 retn
CALL返回后
對(duì)應(yīng)語(yǔ)句為文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-731929.html
00401171 |. 83C4 08 add esp,0x8
作用為平衡堆棧
逆推C語(yǔ)言代碼
根據(jù)我們前面的分析,我們不難發(fā)現(xiàn)這其實(shí)就是個(gè)簡(jiǎn)單的加法函數(shù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-731929.html
int add(int x,int y){
x=x+y; //這里的x和y分別對(duì)應(yīng)壓入的參數(shù)
return x; //對(duì)應(yīng)RETN 默認(rèn)采用eax作為返回值的傳遞載體
}
到了這里,關(guān)于Windows逆向安全(一)之基礎(chǔ)知識(shí)(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!