逆向入門——匯編基礎


0x00 IA32處理器體系結構

微機的基本結構

指令執行周期

當指令使用了內存操作數時還需要兩個額外的步驟:取操作數和存儲輸出操作數。
機器指令的執行;

  1. 取指令
  2. 解碼
  3. 執行

操作模式

保護模式:處理器的基本模式。
虛擬8086模式:多任務環境中執行是地址模式的軟件。
實地址模式:用於運行那些需要直接訪問系統內存和硬件設備的MS-DOS程序。
系統管理模式:實現電源管理和系統安全等功能的機制。

基本執行環境

基本寄存器

寄存器中數據在內存中存放數據遵循高高低低的原則

8個通用寄存器

EAX EBX ECX EDX
EBP ESP ESI EDI

6個段寄存器

一個處理器狀態標志寄存器(EFLAGS)和一個指令指針(EIP)寄存器。

ESP:棧地址寄存器 任意時刻指向棧頂元素
EBP:擴展幀指針寄存器 指向堆棧上的函數參數和局部變量
EIP:指令指針寄存器
EIP寄存器不能作為MOV指令的⽬標操作數

EFLAGS:由控制CPU的操作或反映CPU某些運算的結果的獨立二進制位構成

狀態標志

Name
CF 進位標志 進位或借位時CF=1
AC 輔助進位標志 低4位進位或借位時A=1
PF 奇偶標志 偶數P=1
ZF 零標志 結果為0則Z=1
SF 符號標志 S = 符號位值(補碼時0=正,1=負)
OF 溢出標志 運算結果超界時O=1
DF Direction Flag
IF Intertupt Flag
TF Trace Flag

內存管理

實地址模式

可以尋址1MB內存 0~FFFFF

20位線性地址

linear address or abssolute address is 20 bits,range from 0 to FFFFF
用段-偏移地址表示

  • CS:16位代碼段
  • DS:16位數據段
  • SS:16位堆棧段
  • ES,FS,GS可指向其他數據段

保護模式

可以尋址4GB內存 0~FFFFFFFF
段寄存器指向段描述符表,操作系統使用段描述符表定位程序使用的段的位置。

0x01 匯編語言基礎

補碼的表示法

  • 正數的補碼:與源碼相同
  • 負數的補碼:反碼加1

尋址方式


基本元素

16進制數第一個是字母時要在前面加0

指令

一條匯編指令包括4個部分:

  • 標號(可選)
  • 助記符
  • 操作數
  • 注釋(可選)

INVOKE

相當於call,調用函數或過程

偽指令

偽指令課用於定義變量、宏以及過程,可用於執行命名段以及執行其他與匯編器相關任務。
.data? :指明未初始化的數據段

NOP指令

占用一個字節的存儲,什么也不做。

程序模板

TITLE Program Template
; 程序描述:
; 作者:
; 創建日期:
; 修改:
; 日期:  修改者:
INCLUDE Irvine32.inc
.data
	;(在此插入變量)
.code
main PROC
	;(在此插入可執行代碼)
	exit
main ENDP
	;(在此插入其他子程序)
END main

匯編-鏈接-執行


定義數據

字符常量/字符串常量

  • 以單引號或雙引號括起來的單個/一串字符
  • 存儲為對應字符的ASCII碼
后綴 含義
d 十進制
b 二進制
q 八進制
h 十六進制

數據定義語句

初始值可以用?表示不確定,可以是表達式。
可以指定多個初始值,用逗號隔開,變量名代表第一個初始值的偏移。

DUP可以為多個數據項分配存儲空間。
V1 BYTE 10 dup (0)V1占用10個字節空間,初值均為0

符號常量

等號偽指令:名字=表達式

計算數組和字符串大小:
list BYTE 10, 20, 30
ListSize = ($ - list)

list word 10,20,30,40
ListSize = ($-list)/2

myString_len = ($ - myString)
EQU和TEXTEQU偽指令:

將符號名和整數表達式,文本聯系起來。

name EQU expression
name EQU symbol
name EQU <text>

rowSize = 5
count TEXTEQU %(rowSize * 5)
move TEXTEQU <mov>
setupAL TEXTEQU <move al, count> 

setupAL將被匯編成mov al, 10

0x02 數據傳送,尋址,算術運算

小尾(小端)順序

intel處理器使用小端順序存儲,最低字節存儲在最低地址單元
Val DWORD 12345678h

數據傳送指令

操作數類型

  • 立即操作數(immediate)
  • 寄存器操作數(register)
  • 內存操作數(memory)

MOV指令
MOV destination, source

  • 兩個操作數尺寸必須一致
  • 不能同時為內存操作數
  • 目的操作數不能是CS, EIP,IP
  • 立即數不能直接送至段寄存器

MOVZX
復制較小值至較大值中。
低八位原樣復制,高八位補0擴展,僅適用於無符號整數。
MOVSX
低八位原樣復制,高八位補F擴展,僅適用於有符號整數。
LAHF/SAHF
LAHF將標志局存起EFLAGS的低8位復制到AH寄存器,SAHF是將AH復制到EFLAGS
XCHG指令
交換兩個操作數的內容。
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg

算數指令

名稱 作用 影響標志位
INC 加1 AF OF PF SF ZF 不影響CF
DEC 減1 AF OF PF SF ZF 不影響CF
ADD 相加 CF ZF SF OF AF PF
SUB 相減 CF ZF SF OF AF PF
NEG 取相反數 CF ZF SF OF AF PF

加減法影響標志位

INC和DEC不會影響CF標志位

NEG影響的標志位和ADD SUB一樣

名稱 作用
CF進位位 無符號數是無溢出
OF溢出位 有符號數有無溢出
ZF零標位 判斷結果是否為0
SF符號位 結果正負
PF奇偶標志 最低有效字節內1的個數是否為偶數
AC輔助進位標志 最低有效字節的第三位向高位進位

加減法算術運算指令的操作數自身不區分有無符號數,程序通過判斷不同的標志位來實現對有符號數和無符號數的處理。

和數據相關的操作符和偽指令

名稱 作用
OFFSET 取偏移地址
ALIGN 設置對齊值
PTR 重載默認尺寸
TYPE 返回單個元素大小
LENGTHOF 計算數組中元素的數目
SIZEOF 返回LENGTHOF*TYPE
LABEL 插入一個標號並賦予尺寸

加逗號可以多行定義

LABEL不會分配存儲空間

JMP和LOOP

JMP

無條件轉移與條件轉移

JMP 目的地址
功能:接着從目的地址開始執行指令

  • 目的地址一般為標號
  • 通常在當前過程內跳轉

LOOP

LOOP 目的地址
功能:將ecx的值減1,接着與0比較,如果不等於0,就執行目的地址開始的指令,如果等於0 ,則不跳轉,接着執行緊跟在LOOP指令后的指令

  • 通常,ecx里的值就是循環次數。但如果初值為0,因是先減1再判斷是否等於0,所以,實際實際循環次數就是 1 00 00 00 00 H
  • LOOPD也使用ecx控制循環,LOOPW使用cx控制循環。
  • 實模式下,使用的是cx作為控制循環的寄存器

實例

數組求和:

INCLUDE irvine32.inc
.data 
  vb1 byte  1 , 2 , 3 
.code
  main proc
      mov esi  , offset vb1
      mov ecx , lengthof vb1
      mov al    , 0 
   L1:
      add  al   , [ esi ]
      add  esi , type vb1
      loop L1
      exit
  main endp
end main 

復制字符串:

INCLUDE irvine32.inc
.data 
  s1  byte  "source string",0
  s2  byte sizeof s1 dup(0) 
.code
  main proc
      mov esi , 0
      mov ecx , sizeof s1
   L1:
      mov al , s1[ esi ]
      mov s2[esi] , al
      inc esi  
      loop L1
      exit
   main endp
End main 

尋址方式總結

操作數尋址方式

數據尋址的基本方式:

  1. 立即尋址
  2. 寄存器尋址
  3. 存儲器尋址

存儲器尋址有六種類型

用寄存器作為指針並操縱寄存器的值。操作數使用間接尋址則叫間接操作數。

0x03 過程

堆棧操作

運行時棧

運行時棧是CPU直接管理的內存數組,使用到兩個寄存器:SS和ESP

  • 保護模式下,SS是段選擇子,應用程序不應該修改它
  • ESP是指向棧的特定位置的一個32位偏移值
  • 一般不會直接修改ESP,而是通過使用CALL,RET,PUSH,POP等指令,由這些指令間接操作ESP。
  • ESP指向最后壓入到棧的數據
  • 實模式下,使用的SS和SP

PUSH
PUSH r/m16
PUSH r/m32
PUSH imm32

壓棧,將操作數放入堆棧中:

  1. 將ESP減4
  2. 將要壓入的32位值拷貝到ESP指向的內存。

對於32位操作數,ESP減4,存到棧中的內容為雙字;對於16位操作數,ESP減2,存到棧中的內容為字
POP
POP r/m16
POP r/m32

出棧,從堆棧中取出操作數放到指令中的操作數中

  1. 將ESP所指向內存中的內容取出放到操作數中
  2. 將ESP加4

對於32位操作數,是先從棧中拷貝雙字到操作數中,然后ESP加4;對於16位操作數,是先從棧中拷貝字到操作數中,然后ESP加2。

PUSHFD 把32位標志寄存器壓入堆棧
POPFD 從堆棧中彈出32位值到標志寄存器中
兩指令無操作數
實模式下標志寄存器是16位的,入棧出棧指令分別是PUSHF,POPF。
PUSHAD 把八個32位通用寄存器按序全部壓入堆棧
POPAD是以上序反序從堆棧中依次彈出值到八個32位通用寄存器中

過程定義

PROC

main proc
...
main endp

一般過程需要返回指令ret,起始過程需要調ExitProcess

CALL和RET

call 過程名
將EIP壓棧(即當前CALL指令的下一條指令的地址),然后將過程名所在地址賦給EIP(相當於跳轉到過程名所在的代碼處)
RET
RET指令是從棧中取出32位地址,賦給EIP。

使用寄存器傳遞過程參數

.data
dArray DD  1, 2 , 3
dSum  DD ?
.code
Main proc
       mov ebx , offset dArray
       mov ecx , lengthof dArray
       call SumOf
       mov dSum, eax
       exit
Main endp
SumOf proc
    push ebx
    push ecx
    mov eax , 0 
L2: add eax , [ebx]
    add ebx , 4
    loop L2
    pop ecx
    pop ebx     
    ret
SumOf endp
End main

0x04 條件處理

布爾和比較指令

名稱 作用
AND
OR
XOR 異或
NOT
TEST 與,不改變目的操作數只改變標志位
BT,BTC,BTR,BTS 求補/清零/置位

尺寸相同
AND, OR,XOR總是清除溢出標志和進位標志(CF和OF)
NOT不影響任何標志位

實例

小寫轉大寫:

同一字母的大寫字母和小寫字母的ASCII碼的區別只在第5位不同,其他各位相同,小寫字母第5位為1,大寫字母第5位為0
如要把小寫轉大寫,則可將小寫的ASCII碼與11011111B相與

.data
aName byte “Abraham” , 0
nameSize=($-aName)-1
.code
Main proc
     mov ecx , nameSize
     mov esi , 0
L1:AND  aName[esi] , 11011111B
     inc esi
     loop L1
Main endp
End Main
將0-9之間的整數轉換為對應數字符號的ASCII碼
.data
aNum byte 1,3,2,0
numSize=($-aNum)-1
.code
Main proc
     mov ecx , numSize
     mov esi , 0
L1:OR  aNum[esi] , 110000B
     inc esi
     loop L1
     exit
Main endp
End Main

CMP
功能:對兩個操作數作相減運算,不修改操作數,但會影響標志位。會修改OF、SF、ZF、CF、AF、PF。

設置和清除單個CPU狀態標志

條件跳轉

基於特定標志位

為真時跳轉 為假時跳轉 相關標志位
JZ JNZ ZF
JC JNC CF
JO JNO OF
JS JNS SF
JP JNP PF

基於相等比較

助記符 描述
JE 相等跳轉 同JZ
JNE 不相等跳轉 同JNZ
JCXZ CX=0跳轉
JECXZ ECX=0跳轉

基於無符號數比較

助記符 描述
JA 大於跳轉
JB 小於跳轉
JAE 大於等於
JBE 小於等於
JNA 不大於
JNB 不小於
JNBE 同JA
JNAE 同JB

基於有符號數比較

助記符 描述
JG 大於跳轉
JL 小於跳轉
JGE 大於等於
JLE 小於等於
JNG 不大於
JNL 不小於
JNLE 同JG
JNGE 同JL

實例

將最小有符號數存到AX:

     Mov ax,v1
     Cmp ax,v2
     JL   L1
     mov ax,v2
L1:cmp ax,v3
     JL    L2
     mov ax, v3
L2:

數組的順序查找
查找第一個非0值

INCLUDE Irvine32.inc

.data
intArray SWORD 0,0,0,0,5,20,35,-12,66,4,0
noneMsg BYTE "A non-zero value was not found", 0
.code
main PROC
	mov		ebx, OFFSET intArray
	mov		ecx, LENGTHOF intArray
L1: cmp		WORD PTR [ebx], 0
	jne		found
	add		ebx, 2
	loop	L1
	jmp		notFound
found:
	movsx	eax, WORD PTR[ebx]
	call	WriteInt
	jmp		quit
notFound:
	mov		edx, OFFSET noneMsg
	call	WriteString
quit:
	call	Crlf
	exit
main ENDP
END main

條件循環指令

指令 循環條件
LOOPZ ECX>0 && ZF=1
LOOPE ECX>0 && ZF=1
LOOPNZ ECX>0 && ZF=0
LOOPNE ECX>0 && ZF=0

LOOPE和LOOPZ不影響任何狀態標志

.data 
Array        SWORD    -3,-6,-1,-10,10,30,40,5
Sentinel   SWORD    0
.code
; …
    mov esi , offset array
    mov ecx , lengthof array
L1:test word ptr [esi],8000h
     pushfd            ; pushfd不修改標志位
     add esi , type array
     popfd
     loopnz   L1       ; 注意:loopnz不修改標志位
     jnz quit
     sub  esi , type array
Quit:

0x05 整數算術指令

移位和循環移位

指令 含義
SHL 邏輯左移
SHR 邏輯右移
SAL 算術左移
SAR 算術右移
ROL 循環左移
ROR 循環右移
RCL 帶進位的循環左移
RCR 帶進位的循環右移
SHLD 雙精度左移
SHRD 雙精度右移

邏輯移位和算術移位

SHL/SAL
SHL 目的操作數, 移位位數
功能:對目的操作數執行左移操作,最低位補0,移出的最高位送入進位標志CF,原來的進位位將丟失。SHL和SAL功能完全一樣。

左移的SHL和SAL是等價的。算術移位不改變符號位,邏輯移位可能改變符號位
SHR
SHR 目的操作數, 移位位數
功能:將目的操作數邏輯右移,左邊空出的位添0,右邊最低位被移出,復制到CF位中
SHR可以實現無符號數的快速除法

SAR
有符號數的快速除法,右移過程中最高位保持不變

ROL/ROR/RCL/RCR
移出的位又送回另一端

SHLD/SHRD

應用

BinToAsc PROC  uses eax ebx ecx  esi
 ;將EAX中的數轉換成二進制ASCII碼存到ESI指向的數組中
  Add esi , 31
  Mov ecx ,32
Nxt:
    Mov bl,  al
    And bl , 1
    Add bl , 30H
    Mov [esi],bl
    Shr  eax,1
    Dec esi
  Loop nxt
  Ret
BinToAsc ENDP

乘法和除法指令

助記符 描述
MUL 無符號乘法
IMUL 有符號乘法
DIV 無符號除法
IDIV 有符號除法
應用
Mov al, 30h
Mov bl, 4h
Mul  bl   ;AX =0C0H,CF=0


Mov ax , 2000h
Mov bx ,100h
Mul  bx   ;DX:AX=0020 0000h,CF=1

Mov al, -4
Mov bl, 4
IMUL  bl             ;AX=0FFF0H,CF=0

Mov ax, 30h
Mov bx, 4h
IMul bx            ;DX:AX =0C0H,CF=0

Mov al, 48
Mov bl, 4
IMUL  bl             ;AX=00C0H(即十進制的192),CF=1
任意進制的碼制轉換
.data 
ASCIICHAR BYTE  '0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX'
.code
ToASC PROC  uses eax ebx  ecx  esi  
;將EAX中的數按BL中指定的進制數,轉換成ASCII字符串放到ESI指向的數組中
   mov ecx , 0   ;
   mov cl , bl      ; movzx  ecx, bl
   add esi , 31
nxt_ta:
   mov edx , 0 
   div   ecx
   mov bl,ASCIICHAR[edx]
   mov [esi],bl
   dec esi
   cmp eax , 0
   jnz nxt_ta	; jne
   ret 	
ToASC ENDP

0x06 高級過程

stack frame

給子過程傳遞參數的兩種基本方式

  1. 通過寄存器傳遞
  • 執行效率高
  • 代碼可能顯得混亂
  • 寄存器數量有限
mov esi , offset array
mov ecx,lengthof array
mov ebx , type array
call DumpMem
  1. 通過堆棧傳遞
  • 方式靈活通用
  • 效率偏低
push offset array
push lengthof array
push type array
call DumpMem2

使用堆棧傳遞參數時壓入了兩類參數:

  • 值參數(變量或常量的值)
  • 引用/指針參數(變量的地址)
實例

傳遞值

.data 
val1 dword 5
val2 dword 6
.code
push val2
push val1
call AddTwo

AddTwo(val1,val2)

傳遞引用

.data 
val1 dword 5
val2 dword 6
.code
push offset val2
push offset val1
call AddTwo

AddTwo(&val1,&val2)

重點:參數訪問
.data 
Val1 dword 5
Val2 dword 6
.code
Push val2
Push val1
Call AddTwo
…
AddTwo proc
  push ebp
  Mov  ebp , esp
  mov  eax , [ebp + 12]     ;取得val2
  add  eax ,  [ebp + 8]       ;加上val1
  pop   ebp
  ret
AddTwo endp

堆棧清理

因為在調用子過程前,給堆棧壓入了一些內容,在子過程返回時,必須調整堆棧指針。

  • 在調用完子過程后通過加法指令改變ESP值
  • 通過 RET imm 指令的形式
    add方法:
.data 
Val1 dword 5
Val2 dword 6
.code
Push  val2
Push  val1
Call    AddTwo
Add    esp , 8

ret方法,在子過程中調用:

.data 
Val1 dword 5
Val2 dword 6
.code
Push val2
Push val1
Call AddTwo
AddTwo proc
  push ebp
  mov ebp,esp
  mov eax,[ebp+12]
  add eax,[ebp+8]
  pop ebp
  ret  8
AddTwo endp

采用uses操作符保存寄存器,則要注意uses指令是將寄存器的壓棧指令放在子過程的開始,即在堆棧幀里push ebp語句之前,這時,參數偏移地址計算將會受到影響

0x07 字符串和數組

CLD 清除方向標志
STD設置方向標志

MOVSB,MOVSW,MOVSD

指令 功能 ESI和EDI修改量
MOVSB 復制字節 1
MOVSW 復制字 2
MOVSD 復制雙字 4

復制雙字數組

.data 
  source dword 20 dup(0ffh)
  target  dword 20 dup(?)
.code
  ; … 
  cld
  mov ecx , lengthof source
  mov esi , offset source
  mov edi , offset target
  rep movsd    ;將source開始的20個雙字復制到target中
  ; …

CMPSB,CMPSW,CMPSD

指令 操作
CMPSB 比較字節
CMPSW 比較字
CMPSD 比較雙字

單個比較

.data 
  source dword 1234h
  target  dword  5678h
.code
  ; …
  mov esi , offset source
  mov edi , offset target
  cmpsd   ;比較雙字
  ja L1     ;如果source>targe跳轉至L1 
  jmp L2  ;如果source<=target跳轉至L2,本例即是
  ; ….

字符串比較

.data
CmpsTestSource byte "ABCDE"
CmpsTestTarget  byte "AB   "
.code
CMPSTEST proc
  cld
  mov  esi , offset CmpsTestSource
  mov  edi , offset CmpsTestTarget
  mov  ecx, lengthof CmpsTestSource  ;最多比較次數,此例為5
  repe  cmpsb ; 比較到第三個字母時,因兩者不等,重復不再繼續,但當前串
                         ; 操作執行完,esi和edi還會增加。所以,最后,esi和edi會指向
                         ; 第四個字母的位置。
  ret
CMPSTEST endp

SCASB,SCASW,SCASD

將AL的值與EDI指向的內存內容相比較(相當於cmp AL , [edi]),即相當於是做查找操作,通常會跟重復前綴

  • 如果使用repe前綴,則將查找到EDI開始的內存中第一個不等於AL時中止重復;
  • 如果使用repne前綴,則將查找到EDI開始的內存中第一個等於AL時中止重復;
  • 當然,如果ecx減到0,也會結束查找
    SCASW是用AX作字查找,SCASD是用EAX作雙字查找

掃描一個匹配字符

.data 
  alpha byte “ABCDEFGH”,0
.code
  mov edi , offset alpha
  mov al , ‘F’
  mov ecx , lengthof alpha
  cld
  repne scasb   ;不相等則重復,即找到第一個相等的
  jnz quit    ; 如果這個條件滿足,表示是找完整個ecx長度,也沒有找到
  dec edi   ;回減一,讓edi指向找到第一個相等的位置
  …
Quit:

STOSB,STOSW,STOSD

把AL/AX/EAX的內容存儲在EDI指向的內存單元中,同時EDI的值根據方向標志的值增加和減少。
Stosb是存儲AL,stosw存儲AX,stosd存儲EAX 使用rep前綴可以對一段內存進行填充

LODSB,LODSW,LODSD

將從esi指向的內存內容取出存到累加器中,同時,修改esi的值。
lodsb是取出一個字節存到AL中,lodsw是取出一個字存到AX中,lodsd是取出一個雙字存到EAX中。
該指令一般不會跟重復前綴

串操作指令對標志位的影響

cmpsscas指令會對標志位有影響,影響效果如同CMP指令。
movs,lods,stos不會影響標志位。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM