32位彙編第一講x86和8086的區別,以及OllyDbg偵錯程式的使用
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中分析
這裡先說下常用的快捷鍵
![](https://i.iter01.com/images/96990029ad005175e25f0c145b2610287b386bd33f8b9416d289bccbe94de33b.png)
可以看到我們的彙編程式碼都在這裡,我們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
相關文章
- MacOs 下除錯 8086 彙編Mac除錯
- ==和is的區別 以及編碼和解碼
- 常用的x86彙編指令
- 8086彙編指令快速參考
- 8086 彙編學習 Part 9
- 8086 彙編學習 Part 7
- 8086 彙編學習 Part 6
- 8086 彙編學習 Part 2
- 8086 彙編學習 Part 3
- apply 、call 以及 bind 的使用和區別APP
- 幽默:編寫Python程式碼你們使用什麼偵錯程式?Python
- x86彙編之棧與子程式呼叫
- 2.IDEA,Maven,偵錯程式的基本使用IdeaMaven
- 使用匯編和反彙編引擎寫一個x86任意地址hookHook
- Flutter StatefulWidget和StatelessWidget的區別和使用以及更深入的思考Flutter
- 關於Ajax和websocket的區別以及使用場景!Web
- 三,TreeMap和HashMap,TreeSet和HashMap的區別以及方法使用上的不同HashMap
- 2020-12-6(從反彙編理解指標和引用的區別)指標
- Rails開發中使用byebug偵錯程式AI
- mybatis collection解析以及和association的區別MyBatis
- js中==和===的區別以及總結JS
- C語言程式碼區錯誤以及編譯過程C語言編譯
- Xcode偵錯程式LLDBXCodeLLDB
- 對程式、執行緒和協程的理解以及它們的區別執行緒
- 異常-throw的概述以及和throws的區別
- go的 & 和 * 的區別,以及應用場景Go
- Window X86和X64的區別 Window x64和Window x86是什麼意思
- Css預編語言以及區別CSS
- DeFi和CeFi的區別詳細講解
- 如何在Docker內部使用gdb偵錯程式Docker
- savedInstanceState和 fragment.setRetainInstance以及 viewmodel的區別FragmentAIView
- ajax和fetch、axios的區別以及axios原理iOS
- 靜態方法和例項方法的區別以及如何恰當使用
- 在Docker內部使用gdb偵錯程式報錯-Operation not permittedDockerMIT
- 使用jquery和使用框架的區別jQuery框架
- i7-8086K深度評測:i7-8086K和8700K區別大嗎?
- 【思考】URI和URL的區別?以及URL的結構
- PHP 中`Closure`和`Callable`的區別以及在 Redis 訂閱方法中的使用PHPRedis
- Python 偵錯程式入門Python