32位彙編第一講x86和8086的區別,以及OllyDbg偵錯程式的使用

site008發表於2017-09-13

 

 

             32位彙編第一講x86和8086的區別,以及OllyDbg偵錯程式的使用

一丶32位(x86也稱為80386)與8086(16位)彙編的區別

1.暫存器的改變

  AX 變為 EAX  可以這樣想,16位通用暫存器前邊都加個E開頭

例如:

  

EAX  EBX ECX EDX ESI EDI ESP EDP    ;八位暫存器
EIP EFLAGES                         ;特殊暫存器
CS ES SS DS GS FS            ;其中GS FS是新增加的暫存器,這些段暫存器,並不是4個位元組(32位的)還是以前16位的

注意在32位下沒有分段的概念的,因為定址能力是 0- FFFFFFFF  ,在當時的inter認為當初的4G已經很厲害了,那是後最好的記憶體才1G,放到現在看

我們感覺4G不夠用了,但也是近幾年才開始用的8G

有分割槽的概念,比如我們16位彙編中,給程式碼分段的時候,順便分了一下區,分割槽是為了更好的管理程式碼的編寫

2.地址有20根匯流排變為32根匯流排(也就是4G)

3.暫存器的數量沒有做改變

2.32位暫存器和16位暫存器的相容

EAX 的低16位變為AX了,所以相容的16位,其餘的暫存器同理

32位中的段暫存器不是我們能操作的了,給作業系統使用,所以有了許可權一說

在16位中,我們可以直接操作段暫存器分段,或者定址,而這樣很不安全,萬一你分段的時候,正好在作業系統的程式碼區,那麼你可以修改程式碼,那麼作業系統就崩潰了

所以為了系統的穩定,作業系統不讓使用段暫存器了,而這些段暫存器作業系統都記錄了一些表的資訊

二丶編寫32位中的彙編程式碼

1.介紹

  在編寫32位彙編的時候,介紹一下編譯器和聯結器,以前我們使用的彙編編譯器是可以編譯32位彙編的,但是聯結器是不能連線32位彙編程式

所以link聯結器需要改為32位的,如果有安裝過vc++6.0 那麼是可以找到它的聯結器的,我們使用它的聯結器即可.

2.分割槽概念

  上面說了,作業系統不讓我們使用段暫存器,那麼我們可以去分割槽,分為 常量區  全域性資料區  程式碼區 (沒有棧區,棧區由編譯器維護,編譯器分配)

首先介紹一下偽指令的用法(偽指令在16位彙編最後一講都講了,那麼這節課就要呼叫偽指令去編寫彙編程式碼了,還會增加偽指令去講解)

1.偽指令

      ①.model偽指令的使用

memorymodel: 表示你要設定的記憶體模式 這裡我們設定平坦模式(表示記憶體是連續的,因為不能分段了)平坦模式 FLAT

[,langtype]呼叫約定: 如果這裡寫了呼叫約定,那麼以後我們使用 函式的偽指令(PROC)的時候,就不用指明呼叫約定

了而且win32可以呼叫作業系統API,而呼叫API的時候,這些API的呼叫約定,也是你這裡給指定的

用法例子:

  

.386   ;這裡表示我們要寫386的程式(也就是32位)彙編程式,指明一下,這個不是偽指令
.model FLAT,stdcall ;記憶體設定為平坦模式,預設呼叫約定stdcall

  ②偽指令PROTO(函式宣告)

函式宣告的偽指令,這個主要是針對我們自己寫的函式,如果呼叫的時候,函式正好在下面(他會從上面找,找不到報錯)所以宣告一下告訴它存在即可

例子:

  

;使用偽指令宣告
My_ADD PROTO n1:dword,n2:dword

;呼叫
invoke My_Add ,1,2  ;函式實現在下面,如果不寫宣告告訴存在,就會呼叫出錯
My_ADD PROC n1:dword,n2:dword ;定義了一個函式,引數是n1,n2,指明的大小是DWORD(4個位元組的),這裡沒有寫呼叫約定,上面寫了預設的呼叫約定了

My_ADD endp  ;函式定義的結束標誌

  ③偽指令 option(選項的偽指令)

這個偽指令主要是增加額外選項,比如上面我們呼叫函式,彙編不區分大小寫,你這樣寫是可以呼叫的,但是為了

不必要的麻煩,我們加上一個選項,也就是大小寫敏感,也就是區分大小寫,這樣我們呼叫系統API的時候就不用怕出錯了

使用例子:

  

option casemap:none  ;使用大小寫敏感的選項

   ④定義常量去的偽指令(.const)

上面說了,記憶體有了保護模式,分為了 可讀可寫可執行,如果是常量去,那麼只能讀,不能寫,不能執行

語法:

  

這個比較簡單了

使用例子:

  

.const ;定義常量區
    g_szTitle db "Hello"  ;在常量區中定義常量字串

   ⑤資料區的定義(.data)

資料區,專門定義資料使用的,是可讀可寫的

語法:

它分為兩種,一種是初始化的資料區,一種是未初始化的資料區

初始化資料區的寫法:

  

.data ;定義資料區
      ....;你自己的資料 

未初始化的資料區寫法

.data ?;加?號表示未初始化
    g_szData dw ? ;資料的申請必須是? 也就是未初始化的

兩者的區別

  初始化的資料,不過你定義資料的時候,是否給? 都會寫的EXE(PE檔案中)

  未初始化的資料, 定義資料的時候只能給? 不在PE檔案中儲存

  ⑥程式碼區的偽指令(.code)

定義執行的程式碼區

語法:

例子:

  

.code
START:     ;程式碼開始執行的標號

end START; end表示檔案結束START表示要從START開始執行程式碼

 

  ⑦多檔案編譯ASM(#include 字尾名.inc)

我們有時候會想,程式碼不可能一個檔案寫完,比如多個檔案聯合編譯,所以就有了.inc檔案

一般我們定義資料區,或者定義的巨集都放在.inc的檔案中

然後ASM檔案使用include xxxx.inc 包含你自己的.inc檔案即可

3.一段完整的win32彙編程式碼框架

上面的偽指令已經講完了,這裡寫一段完整的彙編程式碼

.386                                ;定義為386的彙編程式
.model FLAT,stdcall                ;記憶體為平坦模式,預設呼叫約定stdcall
option casemap:none             ;增加選項,區分大小寫

.const                              ;定義常量區(這些應該放到.inc檔案中這裡不妨了,放的話就是拷貝過去,然後這個檔案引用即可)

    g_szTitle db "Title",0        ;win32字串結尾都是0結尾了
    g_szMsg  db "Hello 51asm.com",0

.data                               ;定義資料區
 
.code                               ;定義程式碼區
START:
    .....                ;你的核心程式碼
end START

三丶編譯連線Win32彙編程式

在32位中,編譯彙編程式和連線彙編程式就有點不同了

1.編譯:

在CMD中輸入

ml /c  /coff 檔名.asm

 上面說過,我們在32位下,有了PE檔案格式(exe檔案),而PE檔案格式是 COFF格式,也稱作為PE

編譯幫助:

表示我們要編譯為一個PE的obj格式

編譯我們的程式碼

然後出現這個,表示編譯成功,看下obj檔案

如果我們不加,就會編譯成了16位的了,而連線的時候就會找16位的聯結器,就會出錯,顯示找不到入口點的

錯誤

2.連線

連線的時候,不能在使用16位的聯結器了,這裡可以使用VC自帶的link,沒有沒有關係,我會在每天的資料中上傳所用的工具

連線選項(對我們有用的)

這個對我們有用,因為在32系統下,有了視窗的概念的,表示你要連線成什麼程式,控制檯的還是視窗的

假設我們要連線為一個控制檯的程式

link /subsystem:console 檔名.obj  ;連線成一個控制檯的程式

程式碼沒有出錯,則正常顯示

 四丶寫一個視窗版本探彈訊息的程式,並用OllyDbg去分析

1.編寫視窗程式

我們基於上面的32位程式的框架,寫一個簡單版本的資訊框,彈出一個訊息,把我們常量區的資料彈出來

並用OlleyDbg去分析

首先查一下MessageBox的用法

我們知道了,第一個引數是視窗控制程式碼,沒有我們可以給NULL 而NULL 在彙編中沒有,我們就用巨集定義 (EQU)

第二個引數是一個0結尾字串的首地址,那麼在彙編中可以通過 offset偽指令,把常量區的地址給它

第三個引數一樣

第四個引數是顯示彈框的按鈕風格,我們一般使用MB_OK,而MB_OK 是0,彙編中也沒有,所以我們定義一下

彙編程式碼例子:

.386
.model FLAT,stdcall  ;設定記憶體為平坦模式,預設呼叫約定STDCALL
option casemap:none     ;區分大小寫

NULL EQU 0      ;定義NULL
MB_OK EQU 0     ;定義為0
MessageBoxA PROTO hWnd:DWORD, :DWORD,:DWORD,:DWORD    ;函式宣告,宣告為有4個引數,預設呼叫約定是Stdcall
.const
    g_szTitle db "Title",0
    g_szMsg   db  "Hello www.w1x8.com ",0

.data

.code
START:
    
    invoke MessageBoxA,NULL, offset g_szMsg,offset g_szTitle,MB_OK;呼叫API
end START

這裡使用的是MessageBoxA,因為作業系統分為寬位元組和Ascii碼版本

這裡編譯的時候命令還是上面的那個命令,(ml /c /coff 檔名.obj)

連線的時候不一樣了

連線的時候我們需要連結為Windows視窗程式,而且最重要的一點就是MessageBoxA的實現程式碼在User32.lib中,所以也要一併的加入進來

link /subsystem:windows 檔名.obj  user32.lib ;注意,user32.lib放到彙編程式所在的目錄下

看下編譯出來的程式

2.使用OllyDbg分析

把我們的exe放到OllyDbg中分析

這裡先說下常用的快捷鍵

F2      : 設定/取消斷點
F3      : 載入檔案
F4      : 執行到選中的位置
F5      : 縮小/還原視窗
F7      : 單步步入
F8      : 單步步過
F9      : 執行程式
F10     : 彈出右鍵選單
F12     : 使正在執行的程式暫停
CTRL+F2 : 重新載入檔案
CTRL+F8 : 以指令為單位逐條自動執行
CTRL+F9 : 執行到當前函式的return處
CTRL+F11: 跟蹤進入
CTRL+F12: 跟蹤跳過
CTRL+G  : 跳到某地址處
CTRL+B  : 查詢二進位制字串
CTRL+N  : 檢視當前模組中的名稱
CTRL+K  : 檢視呼叫樹
CTRL+E  : 編輯選中行的二進位制資料
CTRL+減 : 轉到上一個函式過程處
CTRL+加 : 轉到下一個函式過程處
ALT+F2  : 關閉已載入的檔案
ALT+F9  : 執行到到使用者空間程式碼
ALT+B   : 檢視斷點
ALT+E   : 檢視已載入的可執行模組
ALT+Q   : 關閉並退出OD偵錯程式
分號    : 加註釋
冒號    : 加標籤
空格    : 啟用/禁用斷點;彙編選中行
-/+     : 回/前看執行過的指令
快捷鍵引用自: http://bdxnote.blog.163.com/blog/static/84442352015112333530610/
今天我們用到的是 F7  F8 Ctrl + F2的指令
載入我們的EXE檔案

可以看到我們的彙編程式碼都在這裡,我們F8單步執行,找到第一個Call,也就是MessageBoxA,F8走到Call的地方

F7進入

然後看下,他也是一樣壓棧ebp,出棧ebp,然後看下棧區,

看下EBP的位置(這裡的EBP是執指向棧頂的,因為我們 mov ebp,esp了)

然後棧的格式和我們前邊講的是一樣的

棧的當前結構:

 

儲存棧底的值(ebp)

返回地址  

引數一

引數二

引數三

引數四

3.使用OD把我們的標題修改了成輸出的訊息,把以前的標題,修改為輸出的訊息(有點繞,就是兩個互換輸出)

思路:

  我們把壓棧的順序修改一下

雙擊,把當前的壓棧的順序修改一下

push 1.00402008  修改為: push 1.0040200E
push 1.0040200E  修改為: push 1.00402008

 

然後選擇這兩行,右鍵 -> 複製到可執行檔案 然後選擇 選擇所有複製(相當於修改後的EXE)

最後彈出了個新的,我們點選儲存檔案即可

修改後的EXE

五丶關於PE檔案的那點事

 

 上面說了我們的資訊會儲存在exe檔案中(也就是PE)我們用WinHex(16進位制編輯器)檢視一下

1.32位執行的開始

我們的EXE在這裡上面的位置,都是為了相容16位的,而真正的32位程式是從PE這裡開始執行的,

上面的某些欄位儲存了PE所在的偏移,比如PE所在的位置是C8,那麼上面的欄位就會有C8儲存,因為軟體已啟動

會根據這個偏移尋找PE檔案的位置,這裡C8位置在3C的位置

那麼就代表我們不改變這個值,其餘的隨便修改,那麼不影響32位程式的使用,我們修改一下

修改後還是能執行的

 

這個彙編程式會崩潰,原因是我們沒有寫退出,比如16位彙編中的退出是 

mov ah,4c00h
int 21h

這裡就不寫了

2.32彙編中簡單的Dll劫持和API HOOK(思想)

注入方法很多,這裡有個簡單的,比如我們上面呼叫了一個MessageBoxA

他是在Lib中尋找dll的路徑,以及MessageBoxA在那個Dll中

我們使用的這個Dll是動態的Dll,裡面記錄了Dll所在的路徑,以及匯出函式

而我們彙編中剛在這樣用則是把user32.lib中當前呼叫的MessageBoxA所在的Dll路徑,以及Dll匯出函式的資訊

連線到EXE檔案中

所以說EXE檔案中也會儲存Dll的資訊

我們使用WinHex查詢一下EXE中是否有MessageBoxA

CTRL + F 查詢,輸入字串

找到了所在的位置,我們把USER32.DLL改下名

 

 

 

可以看到,他找不到AAER32.dll,如果厲害的自己可以寫一個AAER32.DLL,(當然細節很多,這裡只是簡單的思想)

我們就可以把DLL劫持了

比如我們把前邊的函式名字修改了,那麼如果你厲害,可以寫個相同函式,就形成了APIHOOK

 

課堂資料下載地址:

 當前第一課課程資料: 連結:http://pan.baidu.com/s/1geLWBzP 密碼:2ko2

當前32位彙編所有課程資料:  連結:http://pan.baidu.com/s/1geC3iNL 密碼:0hpc

相關文章