2、條件轉(zhuǎn)移指令
指令的匯編格式及功能根據(jù)條件碼的值轉(zhuǎn)移:
ja 大于時跳轉(zhuǎn)
jae 大于等于
jb 小于
jbe 小于等于
je 相等
jna 不大于
jnae 不大于或者等于
jnb 不小于
jnbe 不小于或等于
jne 不等于
jg 大于(有符號)
jge 大于等于(有符號)
jl 小于(有符號)
jle 小于等于(有符號)
jng 不大于(有符號)
jnge 不大于等于(有符號)
jnl 不小于
jnle 不小于等于
jns 無符號
jnz 非零
js 如果帶符號
jz 如果為零
例子:
mov eax, 1
cmp eax, 100
jle xiao_deng_yu_100
sub eax, 20
xiao_deng_yu_100:
add eax, 1
ret
這個例子中就是讓eax中存儲的值和100比較,如果小于等于則跳轉(zhuǎn)到xiao_deng_yu_100處。 從這個例子也可以看出:
-
- 條件轉(zhuǎn)移一般會和cmp比較指令配合使用,因?yàn)楸容^指令會改變狀態(tài)寄存器中的標(biāo)志位,而jle等跳轉(zhuǎn)指令回去狀態(tài)寄存器中讀取這些標(biāo)志位 。
- 2.跳轉(zhuǎn)指令不會返回到原來的指令地址處,后面講解函數(shù)跳轉(zhuǎn)的時候可以看到會返回到原來的指令地址處,根據(jù)返回地址。
條件跳轉(zhuǎn)指令這么多,怎么記住呢?這里面是有套路的: 首先,跳轉(zhuǎn)指令的前面都是字母j,關(guān)鍵是j后面的的字母,比如j后面是ne,對應(yīng)的是jne跳轉(zhuǎn)指令,n和e分別對應(yīng)not和equal,也就是“不相等”,也就是說在比較指令的結(jié)果為“不想等”的時候,就會跳轉(zhuǎn)。
a: above
e: equal
b: below
n: not
g: greater
l: lower
s: signed
z: zero
好了,這里列出來了j后面的字母所對應(yīng)的含義。根據(jù)這些字母的組合,和上述大概的規(guī)則,你就能清楚怎么寫出這些跳轉(zhuǎn)指令了。當(dāng)然,這里有“有符號”和“無符號”之分,后面有機(jī)會再扯,讀者也可以自行了解。
5.堆棧操作指令
我們知道,在一個函數(shù)作用域中會使用到一些寄存器,如果在這個函數(shù)中又調(diào)用了另外一個函數(shù),那這些寄存器信息可能會被覆蓋掉,怎么辦呢? 首先CPU會在內(nèi)存中開辟一塊空間,叫棧空間,CPU將這些寄存器壓入到棧中,叫入棧,待另外一個函數(shù)返回時,再將當(dāng)前棧中的信息恢復(fù)回來,叫出棧。
1.入棧指令push
格式:push src
功能: 把數(shù)據(jù)src壓入到棧中
注意:src可以是寄存器或者內(nèi)存中的數(shù)據(jù)。
2.出棧指令pop
格式:pop dst
功能: 把數(shù)據(jù)彈出到到棧中
注意:dst可以是通用寄存器和段寄存器,但不能是CS,可以是字存儲單元。
6.函數(shù)調(diào)用跳轉(zhuǎn)指令
格式:call 標(biāo)號
功能:跳轉(zhuǎn)到另外一個函數(shù)去執(zhí)行。
舉例:
eax_plus_1s:
add eax, 1
ret
main:
mov eax, 0
call eax_plus_1s
ret
這里的eax_plus_1s就是一個函數(shù),使用call跳轉(zhuǎn)
call方法執(zhí)行之前CPU還會做一個動作,就是將當(dāng)前eip保存起來,然后再去跳轉(zhuǎn),這是為了函數(shù)執(zhí)行完成后可以找到回來執(zhí)行的地址。
好了。有了以上基礎(chǔ)后,我們再來看C/C++中的hack環(huán)節(jié)。
hack
那我們就拿前面的舉得例子來講解下hack過程
1.前增++i和后增i++
我們打開vs中輸入下面一段代碼:
int main()
{
int a = 0;
int b = ++a;
return 0;
}
如何反匯編呢, 在main的兩個括號{}處打兩個斷點(diǎn),然后執(zhí)行程序,右擊選擇反匯編 。 得到的反匯編代碼如下:
int main()
//段1:初始化
{
001947C0 push ebp
001947C1 mov ebp,esp
001947C3 sub esp,0D8h
001947C9 push ebx
001947CA push esi
001947CB push edi
001947CC lea edi,[ebp-18h]
001947CF mov ecx,6
001947D4 mov eax,0CCCCCCCCh
001947D9 rep stos dword ptr es:[edi]
001947DB mov ecx,offset _BCEF6C65_20221229-demo-cAndCpp@cpp (01A8052h)
001947E0 call @__CheckForDebuggerJustMyCode@4 (0173F1Eh)
//段2:
int a = 0;
001947E5 mov dword ptr [a],0
int b = ++a;
001947EC mov eax,dword ptr [a]
001947EF add eax,1
001947F2 mov dword ptr [a],eax
001947F5 mov ecx,dword ptr [a]
001947F8 mov dword ptr [b],ecx
return 0;
001947FB xor eax,eax
//段3:反初始化
}
001947FD pop edi
001947FE pop esi
001947FF pop ebx
00194800 add esp,0D8h
00194806 cmp ebp,esp
00194808 call __RTC_CheckEsp (0173AC8h)
0019480D mov esp,ebp
0019480F pop ebp
00194810 ret
這段匯編代碼怎么讀呢?由于main方法中沒有其他函數(shù)調(diào)用,所以CPU會按順序執(zhí)行這段代碼。 我們將代碼分為3段: 段1:首先來看前面這段代碼:
001947C0 push ebp
001947C1 mov ebp,esp
001947C3 sub esp,0D8h
001947C9 push ebx
001947CA push esi
001947CB push edi
001947CC lea edi,[ebp-18h]
001947CF mov ecx,6
001947D4 mov eax,0CCCCCCCCh
001947D9 rep stos dword ptr es:[edi]
001947DB mov ecx,offset _BCEF6C65_20221229-demo-cAndCpp@cpp (01A8052h)
001947E0 call @__CheckForDebuggerJustMyCode@4 (0173F1Eh)
這段代碼不用管太多,只是對函數(shù)棧的一個初始化的操作,ebp指向棧底,esp指向棧底,還有一些就是當(dāng)前棧的基地址,返回地址等等信息。因?yàn)楹瘮?shù)中可能還會調(diào)用函數(shù),所以每個函數(shù)調(diào)用都會有自己的棧,也叫函數(shù)的棧幀,函數(shù)的深度就代表?xiàng)I?,但是CPU中的寄存器又是共享的,函數(shù)a在使用這些寄存器的時候,函數(shù)b又要使用了,那怎么辦?方法就是 將寄存器中的數(shù)值壓入棧幀中保存起來,等函數(shù)b結(jié)束后,再從棧幀中恢復(fù)起來就好了 。
下面看重點(diǎn)代碼:
段2:
int a = 0;
001947E5 mov dword ptr [a],0 //1
int b = ++a;
001947EC mov eax,dword ptr [a] //2
001947EF add eax,1 //3
001947F2 mov dword ptr [a],eax //4
001947F5 mov ecx,dword ptr [a] //5
001947F8 mov dword ptr [b],ecx //6
return 0;
001947FB xor eax,eax //7
分析:
- 1.mov指令將0賦值到內(nèi)存中的一個a變量地址中。
- 2.mov指令將變量a地址中的值賦值給eax,此時eax變?yōu)?.
- 3.add指令給eax+1,此時eax變?yōu)榱?.
- 4.mov指令將eax的值傳遞到變量a中,此時a變?yōu)榱?.
- 5.mov指令將a中的值傳遞給寄存器ecx,此時ecx值變?yōu)?.
- 6.mov指令將ecx的值傳遞給變量b地址,這樣b就變?yōu)榱?。
以上過程需要注意點(diǎn):
a的值變化是在b的值變化之前,所以++a是先a+1,然后再將a+1賦值給b。
下面我們再來看下這段代碼:
int main()
{
int a = 0;
int b = a++;
return 0;
}
反匯編后:同樣找到關(guān)鍵代碼:
int a = 0;
00AB47E5 mov dword ptr [a],0 //1
int b = a++;
00AB47EC mov eax,dword ptr [a] //2
00AB47EF mov dword ptr [b],eax //3
00AB47F2 mov ecx,dword ptr [a] //4
00AB47F5 add ecx,1 //5
00AB47F8 mov dword ptr [a],ecx //6
return 0;
00AB47FB xor eax,eax
}
分析:
- 1.給a賦值為0
- 2.將a傳遞給eax,此時eax變?yōu)榱?
- 3.將eax傳遞給b,此時b變?yōu)榱?
- 4.將a值傳遞給ecx,此時ecx變?yōu)榱?
- 5.給ecx+1,此時ecx變?yōu)榱?
- 6.將ecx值傳遞給a,此時a變?yōu)榱?
這個過程可以看到CPU是先賦值給b,然后再讓a進(jìn)行+1的操作。
通過對a++和++a的分析,也可以看到hack可以讓我們了解底層是如何執(zhí)行我們寫的代碼的。
下面我們再來看一個函數(shù)調(diào)用過程:
2.函數(shù)體hack過程
int maxab(int a,int b) {
return a > b ? a : b;
}
int main()
{
maxab(3, 4);
return 0;
}
反匯編后:
maxab(3, 4);
00A347E5 push 4 //1
00A347E7 push 3 //2
00A347E9 call maxab (0A13794h) //3
00A347EE add esp,8 //4
int maxab(int a,int b) {
return a > b ? a : b;
00A32685 mov eax,dword ptr [a] //5
00A32688 cmp eax,dword ptr [b] //6
00A3268B jle __$EncStackInitStart+2Ch (0A32698h) //7
00A3268D mov ecx,dword ptr [a] //8
00A32690 mov dword ptr [ebp-0C4h],ecx //9
00A32696 jmp __$EncStackInitStart+35h (0A326A1h) //10
00A32698 mov edx,dword ptr [b] //11
00A3269B mov dword ptr [ebp-0C4h],edx //12
00A326A1 mov eax,dword ptr [ebp-0C4h] //13
}
分析:
- 1和2處將立即數(shù)4和3壓入到棧內(nèi)。注意他是先push的右邊參數(shù)4,再push的左邊參數(shù)3。
- 3處調(diào)用call跳轉(zhuǎn)到maxab函數(shù)處,注意5處以后這個時候是處于另外一個函數(shù)棧內(nèi)了。
- 5處將變量a的值3傳遞給eax,a的值是怎么得到的呢?看前面壓棧操作是先壓入右邊再壓入左邊,根據(jù)FILO規(guī)則,此時先找到的參數(shù)就是先出左邊3,再出右邊4.
- 6處讓eax也就是前面的a的值和b的值進(jìn)行比較,比較結(jié)果會寫入到狀態(tài)寄存器中。
- 7處的jle是小余等于就跳轉(zhuǎn),這個比較結(jié)果是在狀態(tài)寄存器中獲取到的,a是3,b是4,所以結(jié)果就是小余咯。條件成功跳轉(zhuǎn)到指定的0A32698h位置。0A32698h指向11處:00A32698 mov edx,dword ptr [b] //11
- 11處使用mov指令將b中的值傳遞給edx。
- 然后在12處將edx存入到ebp-0C4h位置,ebp是棧底,根據(jù)棧的特點(diǎn),ebp-C4H其實(shí)是一個往棧頂走的一個操作,如果你觀察仔細(xì),可以看到其實(shí)就是棧頂。
- 最終在13處將ebp-0C4h處的值傳遞給eax,最終返回eax。 前面也說過所有函數(shù)都使用eax進(jìn)行返回。
上面講解了一個max函數(shù)的hack過程,如果你對hack過程還比較模糊,還可以使用vs提供的 內(nèi)存監(jiān)視器查看具體內(nèi)存變化 :
好了,關(guān)于函數(shù)的hack分析過程就這里了,大部分操作其實(shí)還是要理解匯編一些基本指令以及CPU和內(nèi)存,棧的模型等。
總結(jié)
本篇文章主要講解了hack的定義以及hack過程中需要了解的幾個基本知識: 如 寄存器,內(nèi)存,CPU以及一些基本匯編指令 。并使用兩個例子來講解如何在C/C++中進(jìn)行hack的過程。
-
C++
+關(guān)注
關(guān)注
22文章
2114瀏覽量
73883 -
匯編代碼
+關(guān)注
關(guān)注
0文章
24瀏覽量
7567 -
hacker
+關(guān)注
關(guān)注
0文章
4瀏覽量
1373
發(fā)布評論請先 登錄
相關(guān)推薦
C++筆記004:C++類通俗點(diǎn)說—— C結(jié)構(gòu)體復(fù)習(xí)
C++課程資料詳細(xì)資料合集包括了:面向?qū)ο蟪绦蛟O(shè)計(jì)與C++,算法,函數(shù)等
![<b class='flag-5'>C++</b>課程資料詳細(xì)資料合集包括了:面向?qū)ο蟪绦蛟O(shè)計(jì)與<b class='flag-5'>C++</b>,算法,<b class='flag-5'>函數(shù)</b>等](https://file.elecfans.com/web1/M00/56/EE/pIYBAFtDKEWAY420AAB8HnKodUs131.png)
如何在中斷C函數(shù)中調(diào)用C++
![如何在中斷<b class='flag-5'>C</b><b class='flag-5'>函數(shù)</b>中調(diào)用<b class='flag-5'>C++</b>](https://file.elecfans.com/web1/M00/91/BA/pIYBAFzTzJuAT0pBAAEyN2vFGuQ683.png)
C++之重載函數(shù)學(xué)習(xí)總結(jié)
EE-128:C++中的DSP:從C++調(diào)用匯編類成員函數(shù)
![EE-128:<b class='flag-5'>C++</b>中的DSP:從<b class='flag-5'>C++</b>調(diào)用匯編類成員<b class='flag-5'>函數(shù)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
C++基礎(chǔ)語法之inline 內(nèi)聯(lián)函數(shù)
深度解析C++中的虛函數(shù)
![深度解析<b class='flag-5'>C++</b>中的虛<b class='flag-5'>函數(shù)</b>](https://file.elecfans.com/web2/M00/91/66/pYYBAGPsTWqAcRlFAAEnF_VliNI953.jpg)
評論