16匯編第十講完結Call變為函數以及指令的最後講解
16匯編完結Call變為函數以及指令的最後講解
學了10天的16位匯編,這一講就結束了,這裏總結一下昨天的LOOP指令的缺陷,因為lOOP指令的缺陷,所以我們都改為下面的匯編代碼使用了,自己去寫,其中條件是你自己寫的
請看匯編代碼:
do while 的匯編代碼 WHILE: mov ax,ax cmp ax, 10 jl WHILE
while 的匯編代碼 WHILE: cmp ax, 10 jge WHILE_END mov ax,ax jmp WHILE WHILE_END:
一丶Call指令(子程序)變為函數調用(重要,這個以後逆向會天天看到所以一定掌握)
首先我們明白一點,昨天我們寫的只是一個單獨的子程序(什麽保存棧地址,開辟局部變量空間,....都沒有寫,只是單獨的平棧),今天我們就寫一個Call變為函數調用的例子,一步一步的看為什麽這麽做,對以後的逆向很有幫助
1.首先我們明白Call調用的幾種方式
段內: 一個段中去調用,或者跳轉
段間: 代碼不在同一個段中,從一個段跳躍到另一個段
Call指令分為4中類型 (類似於JMP)
Call label(標號) ; 段內調用丶直接尋址
Call r16/m16 (16位寄存器,或者2字節內存) 段內調用丶間接選址
Call far ptr label; 段間調用丶直接尋址
Call far ptr mem; 段間調用丶間接尋址
昨天我們寫的簡單的子程序例子:
mov ax,1 push ax mov bx,2 push bx Call 標號 add sp,4
.... 標號: mov bp,sp
mov ax,[bp +2]
mov bx,[bp +4]
add ax,bx ret
我們壓入了兩個參數,一個Ax,一個Bx,在Call的時候,會把下一行的地址壓入到棧中,也就是 add sp,4的所在的地址
我們畫一下棧
可以看出,這個就是當前的棧結構了, 我們執行代碼的時候,首先 bp 和 sp是平等的了
現在 bp+2 能尋得bx, bp +4 能尋得 ax
然後ret 註意,ret只是把sp +2了,也就是彈棧彈出了返回地址,並且給IP了,現在IP就會跳轉到下一條指令執行的位置
也就是(add sp,4) 現在要註意了,bx 和 ax 都還在棧中,我們沒辦法讓棧恢復所以在外面用 add sp,4 讓sp的位置(現在的位置+2了,已經在bx的位置了)變為棧底了
所以這個就是C語言的 C調用約定
如果我們想StdCall (std 調用約定,標準的調用約定) 就要用retn 這個指令了,不能是ret了,
寫成 retn 4 代表先把返回地址返回出去,然後再讓sp +4 個字節,相當於在函數內部就平棧了,這樣外部就不用寫
add sp,4了,不用自己平棧了
2.由Call 變為函數一步一步來
現在基於上面的原理我們知道如何平衡一個棧了,但是你有沒有發現,為什麽我們一開始要把sp 給Bp
也就是讓 BP和SP一樣的位置
這個是有原因的
首先C語言調用函數的時候會進行幾步操作
1.保存棧底
2.申請局部變量空間
3.保存環境
4.恢復環境
5.釋放局部變量空間
6.恢復棧底
7.彈棧返回
好看上面的我們可能有點不明白,下面我把整體的棧圖放出來
整體的棧是這樣的,這裏為什麽要一開始把bp和sp相等,是有原因的,我們不妨這樣想,如果我們申請局部變量空間的
時候,是不是參數的偏移也要改動,這樣每次都要自己計算偏移,相當麻煩,所以只能這樣,
我們以後找參數就 bp + xxx (因為bp一開始就在棧底的值這一欄) 這樣就能尋找到參數,你開辟多大的局部變量看空間都和我沒關系,所以以後我只需要 bp-xxx 就是找到的局部變量
現在看下匯編代碼的模版吧
MY_ADD: ;stdcall push bp mov bp, sp ;1. 保存棧底 sub sp, 10 ;2. 申請局部變量空間這裏隨你便,申請的時候先擡棧,讓sp高一點,但是不會影響bp push bx ;3.保存環境 保存環境的意思就是外面的寄存器的值要保存一下,這樣恢復寄存器的值 mov word ptr [bp-4], 1 ;每次我只需要 bp -xxx 就知道尋找局部變量 mov word ptr [bp-2], 2 mov ax, [bp+6] ;參數1 ;每次我bp+ xxx 就知道我是找參數,所以不會沖突,打死參數的地址不會變 add ax, [bp+8] ;參數2 ;xxxxxxx pop bx ;4. 恢復環境 彈棧的時候寄存器信息先回復 mov sp, bp ;5. 釋放局部變量空間 而且恢復變量控件的時候也很容易,直接把bp當前的位置給sp即可,釋放空間了 pop bp ;恢復以前的棧底的值 retf ;6. 返回 ,retf下面詳細講
在這裏主要是掌握bp所在的位置即可,就能明白為什麽這樣寫了,不信的話自己寫個程序,看下反匯編,大體的就是這個套路,這裏講解的是為什麽這樣做,不是和市面的匯編視頻一樣,你看到 bp -xxx 就知道他在訪問局部變量就行
其實這個是錯誤的,我們要知其然,並知其所以然
看下棧圖,掌握bp所在的位置
只要掌握bp所在的位置即可,上面的代碼即可明白
3.Call指令的retf段間轉移
這個我們首先要明白,在Call的時候會把Call下邊一條指令的地址保存到棧中,出棧的時候要給IP,讓其更改跳轉,
跳轉到Call下一條指令執行的位置的地方
但是現在我們是段間Call,也就是不在一個段中,這個時候棧不光會保存返回地址,還會保存當前CS段寄存器的地址
這樣返回的時候 CS:IP返回,但是現在有一個問題,就是我們自己根本就平不了棧,我們把IP拿出來了,給IP,CS段寄存器根本沒辦法改,這樣我們必須同時修改CS:IP的值才能回到以前的地方,但是現在沒辦法了,因為你改IP回跳,改CS會跳,必須同時改,弄不了,所以弄一個retf的指令去幫我們去做
註意retf 你也需要平棧,比如我們壓入了兩個參數,就要 retf 4, retf會默認把棧頂4個字節的數據取出來分別給 ip和CS段寄存器,但是剩下4個字節都是我們的參數,比如自己去釋放,讓SP的加4到棧底才可以
二丶中斷指令
1.什麽是中斷指令
中斷,是有一種改變程序執行順序的方法
中斷具有很多的中斷類型
中斷的指令有3條
1.INT i8(i8代表一個八位的立即數)
2.IRET IRET 和Call差不對,Call的ret返回的時候會把棧頂的元素彈出兩個字節,這兩個字節是返回地址,所以可以回到正確的地方執行指令,但是IRET明顯比ret保存的東西多,其中ret我們可以手工的pop和jmp去執行,IRET也可以自己去做,但是你要完整的模擬才可以,一般還是調用IRET即可
3.INTO
2.中斷指令的自我理解
其實中斷指令就是調用硬件提供的API(也稱為系統調用)我們前邊用過很多次了
比如顯示一個字符串
mov ah,09h int 21h
其中參數是09,int 21h代表執行,還有很多
介紹下指令
INT I8: 中斷的調用指令: 產生I8號中斷,就是調用int代表我要調用了,其中指令是什麽使我們給的,是一個八位立即數比如 09
IRET: 中斷的返回指令,理解為返回,可以進行下一條指令的執行
INTO: 不常用,不講解.
3.21h中斷,到底是個啥玩意
我們每次都調用21h什麽的,但是不知道他是個啥玩意
他是DOS提供給用戶的,用於調用系統功能的中斷(簡單理解就是DOS提供的API,讓用戶調用),他有近百個功能讓公戶選擇使用.包括設備管理,目錄管理,和文件管理
ROM-BIOS(主板的BIOS)也是這種形式,這就是為什麽程序一開機顯示器就會顯示字符,別忘了這個時候系統還沒有啟動,還沒有操作系統一說
看下圖:
現在操作系統也還是這樣調用,操作系統的API很多,底層就依賴於這256個中斷,只不過操作系統可能處理的方式更多,比如根據AH的值,調用一個函數指針,也就是一個函數的地址,這個函數地址裏面又有很多封裝,慢慢的操作系統API就越來越多.這些都是我們不關心的
具體的中斷,可以看下第一課所有的課堂資料中的指令字典,尋找一下中斷號自己去調用
比如判斷按鍵的匯編例子
getkey: mov ah,01h ;功能號:ah←01h int 21h ;功能調用 cmp al,’Y’ ;處理出口參數al je yeskey ;是“Y” cmp al,’N’ je nokey ;是“N” jne getkey ... yeskey: ... nokey: ...
三丶最後的指令詳解
LOCK指令 封鎖總線,不讓總線接受指令
這個常用與多線程的時候操作,多線程的操作中的同步對象的 自加鎖就是這樣實現的
比如:
lock add word ptr[bp],3
當我們吧3給內存的時候,其余的程序也可能再往這裏寫數據,所以同步一下,這樣就完成這一條指令,才可以進行下一條指令的操作
自減鎖就是 把add 變為sub,交換鎖就是 把指令變為 xchg lock只能同時處理一條指令,這是為防止我們把系統總線都鎖死,這樣操作系統就會崩潰,信息到不了,不過這個不是我們關心的
解鎖是CPU自己解鎖的,沒有解鎖指令
HLT 暫停指令
這個就比較簡單的,我們電腦都有休眠功能,就是用的這個指令,讓CPU功耗降低,不執行,以前是無限循環NOP指令
但是NOP也是一個指令也會有功耗,所以現在改為HLT指令了,執行了這個指令HLT不進行任何操作,當我們發送了一條指令過去之後,就會脫離暫停狀態
就好比電腦掛機了,屏幕黑了,就是進入HLT了,我們點擊鼠標或者鍵盤,發送了一條指令,接著就喚醒了
交權指令
ESC 6位立即數,reg/mem
我們都知道,以前算浮點數的時候都是CPU一個去做的,現在有了浮點處理器,也就是協處理器,專門算浮點的一個CPU
我們的CPU計算浮點數的時候,要把權力交給浮點處理器,這時候就稱為交權,在這個時候CPU要等待浮點處理器返回的結果,期間一直等待
FADD FDIV 是浮點計算指令,就是我們計算指令前面加F就會計算浮點數了
浮點數有7個寄存器
ST -> st7 按照標號來的
浮點處理器的st不能和通用寄存器一樣去使用,它是吧ST寄存器壓入棧中,讓前兩個棧中的數據相加返回的
關於浮點處理,後面再說,這個不是16為匯編中使用的
WAIT 等待指令
這個就簡單了,CPU交權後,就使用這個指令去等待浮點處理器返回結果
總結:
16位匯編在我的博客上都精簡了,但是你想搞明白就要多花點時間,細細品味匯編的內容,掌握匯編
過幾天開始32位匯編的講解,如果覺得好,請評論一下,關註一下,加個粉絲.支持一下,謝謝
對於Call變為函數的哪裏,一定要掌握,不懂的可以去看下C語言的棧內存結構,或者看下它的匯編代碼,一定搞明白
這個以後逆向的時候天天看.
學習資料: 鏈接:http://pan.baidu.com/s/1nuPNUFf 密碼:x44c
16匯編第十講完結Call變為函數以及指令的最後講解