逆向入门(5)汇编篇-函数相关学习与JCC指令
0x01 函數
匯編眼中的函數,函數就是一系列指令的集合,為了完成某個會重復使用的特定功能。
可以使用JMP指令或者CALL指令來進行調用函數,先看JMP指令。
JMP指令調用函數
假設定義一個函數功能為將eax,ecx的值賦值為0,假設使用JMP來進行調用
此時就會出現一個問題,當通過JMP調用了指令后,無法再次回到使用JMP指令的地方,解決的話可以在函數中再次使用JMP指令跳轉回來。
但是這樣做同樣也會出現問題,回想函數的定義,重復使用的特定功能,那么下次再進行函數時,仍然會回到首次定義的JMP地方,無法回到下次使用函數的地方,所以使用JMP指令來調用函數就不太方便。
CALL指令調用函數
這里再使用CALL指令來調用函數,由于CALL指令會將當前指令的下一行存儲在堆棧中,所以直接在函數的最下面進行ret就可以回到之前執行函數的地方了。
運行后觀察結果
函數的參數和返回值
以寫一個加法的函數為例子
add eax,ecx ret這里的參數指的是就是eax和ecx,返回值就是eax,如下
運行結果后,eax應該為7,同時指針回到0040ef44,運行后觀察結果。
0x02 堆棧傳參
如果在參數很多的情況下,計數器可能不夠用情況,此時就可以用堆棧進行傳遞參數。
這里以計算5個參數值為例,先將值壓入棧中
push 1 push 2 push 3 push 4 push 5定義函數,此時應該要將最上層的棧的值給到eax中,然后連續讓eax加上下面的幾層棧存儲的值
mov eax,dword ptr ds:[esp+4] add eax,dword ptr ds:[esp+8] add eax,dword ptr ds:[esp+C] add eax,dword ptr ds:[esp+10] add eax,dword ptr ds:[esp+14] ret運行測試
效果正常實現
堆棧平衡
雖然上述實驗成功實現效果,但是存在一個小問題,最后堆棧并沒有還原,也就是所謂的沒有堆棧平衡。
上述程序在運行前,棧的最上面是12ffc4,但是函數運行結束后,則變成了12ffb0
針對上面的問題,第一個解決方案就是采用外平棧,在call指令后使用add esp,8就可以恢復棧的原有值了。
當然還可以直接將ret改為ret 8(等同于ret后再add esp,8),實現函數內的棧平衡,稱為內平棧。
esp尋址
從上面的例子可以看到最終拿出之前壓入棧中的值時,是以esp為基址進行查找的,這種行為稱為esp尋址。
mov eax,dowrd ptr ss:[esp+8] add eax,dowrd ptr ss:[esp+4] ret這種尋址方式有非常明顯的好處,因為esp尋找起來非常簡單和直白。同樣的,也是有存在缺點的。
假設某函數在使用時需要用寄存器,但是又無法將寄存器的值進行直接清空,需要保留,所以在執行函數前需要先保留寄存器中的值
push eax push ecx mov eax,dowrd ptr ss:[esp+8] add eax,dowrd ptr ss:[esp+4] ret但是此時就會存在一個問題,由于push指令改變了棧,所以此時esp的值不能再直接去加了,而是要根據使用的指令情況來增加,這里由于使用了兩個push,所以整體函數變成了
push ecx push edx mov eax,dowrd ptr ss:[esp+C] add eax,dowrd ptr ss:[esp+10] ret同時在使用完ecx和edx后也需要還原,所以還得繼續使用pop做堆棧平衡。
push ecx push edx mov eax,dowrd ptr ss:[esp+C] add eax,dowrd ptr ss:[esp+10] pop edx pop ecx ret從這個例子中也能看到缺點,如果之前push的指令比較多,影響了堆棧,那么在使用esp尋址時就需要手動計算esp的變更后的值,相對麻煩一些。
EBP尋址
從剛剛的情況中找到了不足,這里可以使用ebp來進行尋址,ebp是棧底指針。可以看下面的例子
push ebp mov ebp,esp sub esp,10先將ebp的值存儲棧中以便后續還原,將著將ebp設置到原有的esp的位置,接著減少esp的值,這樣就可以重新擴展出一塊堆棧了,使用時不會影響原有的棧。此時以ebp來尋址的話,就不會再重新計算參數的位置了,因為在使用堆棧的時候ebp的值是不會改變的。所以此時可以直接取值
mov eax,dword ptr ss:[ebp+4] add eax,dowrd ptr ss:[bgp+8]同時在完成函數后,還需要做平棧,還原ebp和esp。
mov esp,ebp pop ebp ret雖然感覺多花了一些步驟,但是實際上如果函數步驟復雜,使用的堆棧較多的情況下,使用ebp尋址還是很有優勢的。
0x03 JCC指令
有條件修改eip寄存器的指令,比如JMP和CALL都是無條件修改。
JCC指令是通過查看標記寄存器來進行判斷的
- CF,carry flag主要用來判斷無符號數計算以后是否溢出,如果發生進位或者借位則將其置1,反之清零。
- PF,Parity flag,如果結果的最低有效字節包含偶數個1位則置為1,否則清0,一般用于傳遞數值后的校驗完整性
- AF,auxilary Carry flag,如果算術操作在結果的第3位發生進行或者進位,則為1,一般用于BCD運算。
- ZF,zero flag,如果運算結果為0,則置為1
使用cmp或者test指令都會使用到此指令
cmp可以判斷兩數是否相等(相當于sub,但是不把值進行存儲)
test可以判斷否數是否為0(相當于and,也不存儲數值)
- SF,Sigh flag,有符號整數的最高有效位,0代表為正,1代表為負
- OF,Overflow flag,有符號數加減運算所得結果是否溢出,溢出為1,反之為0
有符號數看of,無符號數看cf - DF,direction flag,方向位,控制棧的傳遞方向,比如movs,stos等指令,STD和CLD指令分別 用于設置以及清除DF標志。
常見指令如下
JE,JZ,是結果為0則跳轉,ZF=1 JNE,JNZ,是結果不為0則跳轉,ZF=0 JS,結果為負則跳轉,SF=1 JNS,結果為非負則跳轉,SF=0 JP,JPE,結果中1的個數要是偶數則跳轉,PF=1 JNP,JPO,結果中1的個數要是奇數則跳轉,PF=0 JO:結果溢出則跳轉,OF=1 JNO,結果未溢出則跳轉,OF=0 JB,JNAE,是無符號數小于則跳轉,CF=1 JNB,JAE,是無符號數大于等于則跳轉,CF=0 JBE,JNA,是無符號數小于等于則跳轉,CF=1 or ZF=1 JNBE,JA,是無符號數大于則跳轉,CF=0 and ZF=0 JL,JNGE,是有符號數小于則跳轉,SF!=OF JNL,JGE,是有符號數大于等于則跳轉SF=OF JLE,JNG,是有符號數小于等于則跳轉,ZF=1 or SF!=OF JNLE,JG ,是有符號數大于則跳轉 ZF=0 and SF=OF總結
以上是生活随笔為你收集整理的逆向入门(5)汇编篇-函数相关学习与JCC指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度解析FPS游戏外挂形成原因与“破局”
- 下一篇: PHP八字强弱计算,八字强弱计算法