0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C/C++之函數(shù)體hack(下)

jf_78858299 ? 來源:小余的自習(xí)室 ? 作者:小余的自習(xí)室 ? 2023-03-30 16:53 ? 次閱讀

2、條件轉(zhuǎn)移指令

指令的匯編格式及功能根據(jù)條件碼的值轉(zhuǎn)移:

ja 大于時(shí)跳轉(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

這個(gè)例子中就是讓eax中存儲(chǔ)的值和100比較,如果小于等于則跳轉(zhuǎn)到xiao_deng_yu_100處。 從這個(gè)例子也可以看出:

    1. 條件轉(zhuǎn)移一般會(huì)和cmp比較指令配合使用,因?yàn)楸容^指令會(huì)改變狀態(tài)寄存器中的標(biāo)志位,而jle等跳轉(zhuǎn)指令回去狀態(tài)寄存器中讀取這些標(biāo)志位
  • 2.跳轉(zhuǎn)指令不會(huì)返回到原來的指令地址處,后面講解函數(shù)跳轉(zhuǎn)的時(shí)候可以看到會(huì)返回到原來的指令地址處,根據(jù)返回地址。

條件跳轉(zhuǎn)指令這么多,怎么記住呢?這里面是有套路的: 首先,跳轉(zhuǎn)指令的前面都是字母j,關(guān)鍵是j后面的的字母,比如j后面是ne,對應(yīng)的是jne跳轉(zhuǎn)指令,n和e分別對應(yīng)not和equal,也就是“不相等”,也就是說在比較指令的結(jié)果為“不想等”的時(shí)候,就會(huì)跳轉(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ī)會(huì)再扯,讀者也可以自行了解。

5.堆棧操作指令

我們知道,在一個(gè)函數(shù)作用域中會(huì)使用到一些寄存器,如果在這個(gè)函數(shù)中又調(diào)用了另外一個(gè)函數(shù),那這些寄存器信息可能會(huì)被覆蓋掉,怎么辦呢? 首先CPU會(huì)在內(nèi)存中開辟一塊空間,叫棧空間,CPU將這些寄存器壓入到棧中,叫入棧,待另外一個(gè)函數(shù)返回時(shí),再將當(dāng)前棧中的信息恢復(fù)回來,叫出棧。

1.入棧指令push

格式:push src
功能: 把數(shù)據(jù)src壓入到棧中
注意:src可以是寄存器或者內(nèi)存中的數(shù)據(jù)。

2.出棧指令pop

格式:pop dst
功能: 把數(shù)據(jù)彈出到到棧中
注意:dst可以是通用寄存器和段寄存器,但不能是CS,可以是字存儲(chǔ)單元。

6.函數(shù)調(diào)用跳轉(zhuǎn)指令

格式:call   標(biāo)號
功能:跳轉(zhuǎn)到另外一個(gè)函數(shù)去執(zhí)行。

舉例:

eax_plus_1s:
    add eax, 1
    ret
main:
    mov eax, 0
    call eax_plus_1s
    ret

這里的eax_plus_1s就是一個(gè)函數(shù),使用call跳轉(zhuǎn)

call方法執(zhí)行之前CPU還會(huì)做一個(gè)動(dòng)作,就是將當(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的兩個(gè)括號{}處打兩個(gè)斷點(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會(huì)按順序執(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ù)棧的一個(gè)初始化的操作,ebp指向棧底,esp指向棧底,還有一些就是當(dāng)前棧的基地址,返回地址等等信息。因?yàn)楹瘮?shù)中可能還會(huì)調(diào)用函數(shù),所以每個(gè)函數(shù)調(diào)用都會(huì)有自己的棧,也叫函數(shù)的棧幀,函數(shù)的深度就代表?xiàng)I?,但是CPU中的寄存器又是共享的,函數(shù)a在使用這些寄存器的時(shí)候,函數(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)存中的一個(gè)a變量地址中。
  • 2.mov指令將變量a地址中的值賦值給eax,此時(shí)eax變?yōu)?.
  • 3.add指令給eax+1,此時(shí)eax變?yōu)榱?.
  • 4.mov指令將eax的值傳遞到變量a中,此時(shí)a變?yōu)榱?.
  • 5.mov指令將a中的值傳遞給寄存器ecx,此時(shí)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,此時(shí)eax變?yōu)榱?
  • 3.將eax傳遞給b,此時(shí)b變?yōu)榱?
  • 4.將a值傳遞給ecx,此時(shí)ecx變?yōu)榱?
  • 5.給ecx+1,此時(shí)ecx變?yōu)榱?
  • 6.將ecx值傳遞給a,此時(shí)a變?yōu)榱?

這個(gè)過程可以看到CPU是先賦值給b,然后再讓a進(jìn)行+1的操作。

通過對a++和++a的分析,也可以看到hack可以讓我們了解底層是如何執(zhí)行我們寫的代碼的。

下面我們再來看一個(gè)函數(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處以后這個(gè)時(shí)候是處于另外一個(gè)函數(shù)棧內(nèi)了。
  • 5處將變量a的值3傳遞給eax,a的值是怎么得到的呢?看前面壓棧操作是先壓入右邊再壓入左邊,根據(jù)FILO規(guī)則,此時(shí)先找到的參數(shù)就是先出左邊3,再出右邊4.
  • 6處讓eax也就是前面的a的值和b的值進(jìn)行比較,比較結(jié)果會(huì)寫入到狀態(tài)寄存器中。
  • 7處的jle是小余等于就跳轉(zhuǎn),這個(gè)比較結(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í)是一個(gè)往棧頂走的一個(gè)操作,如果你觀察仔細(xì),可以看到其實(shí)就是棧頂。
  • 最終在13處將ebp-0C4h處的值傳遞給eax,最終返回eax。 前面也說過所有函數(shù)都使用eax進(jìn)行返回。

上面講解了一個(gè)max函數(shù)的hack過程,如果你對hack過程還比較模糊,還可以使用vs提供的 內(nèi)存監(jiān)視器查看具體內(nèi)存變化

圖片

好了,關(guān)于函數(shù)的hack分析過程就這里了,大部分操作其實(shí)還是要理解匯編一些基本指令以及CPU和內(nèi)存,棧的模型等。

總結(jié)

本篇文章主要講解了hack的定義以及hack過程中需要了解的幾個(gè)基本知識: 如 寄存器,內(nèi)存,CPU以及一些基本匯編指令 。并使用兩個(gè)例子來講解如何在C/C++中進(jìn)行hack的過程。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2085

    瀏覽量

    73301
  • 匯編代碼
    +關(guān)注

    關(guān)注

    0

    文章

    23

    瀏覽量

    7520
  • hacker
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    1349
收藏 人收藏

    評論

    相關(guān)推薦

    C++STL算法(二)

    C++STL算法(二)
    的頭像 發(fā)表于 07-18 14:49 ?852次閱讀
    <b class='flag-5'>C++</b><b class='flag-5'>之</b>STL算法(二)

    c++STL算法(三)

    c++STL算法(三)
    的頭像 發(fā)表于 07-18 15:00 ?1070次閱讀
    <b class='flag-5'>c++</b><b class='flag-5'>之</b>STL算法(三)

    C++文件操作

    C++文件操作
    的頭像 發(fā)表于 07-21 10:52 ?1019次閱讀
    <b class='flag-5'>C++</b><b class='flag-5'>之</b>文件操作

    C++筆記004:C++類通俗點(diǎn)說—— C結(jié)構(gòu)復(fù)習(xí)

    ` 本帖最后由 tyyhmtyyhm 于 2018-5-12 09:37 編輯 C++類通俗點(diǎn)說—— C結(jié)構(gòu)復(fù)習(xí)核心:C++的類就是對C
    發(fā)表于 03-05 12:53

    C++課程資料詳細(xì)資料合集包括了:面向?qū)ο蟪绦蛟O(shè)計(jì)與C++,算法,函數(shù)

    本文檔的主要內(nèi)容詳細(xì)介紹的是C++課程資料資料合集包括了:面向?qū)ο蟪绦蛟O(shè)計(jì)與C++,算法,函數(shù),概述, C++語言基礎(chǔ),構(gòu)造數(shù)據(jù)類型,數(shù)據(jù)類型,C+
    發(fā)表于 07-09 08:00 ?18次下載
    <b class='flag-5'>C++</b>課程資料詳細(xì)資料合集包括了:面向?qū)ο蟪绦蛟O(shè)計(jì)與<b class='flag-5'>C++</b>,算法,<b class='flag-5'>函數(shù)</b>等

    如何在中斷C函數(shù)中調(diào)用C++

    之前,我們在單片機(jī)程序開發(fā)時(shí)都會(huì)面對中斷函數(shù)。眾所周知的,這個(gè)中斷函數(shù)肯定是要用C函數(shù)來定義的。我在用C++進(jìn)行程序開發(fā)的時(shí)候就發(fā)現(xiàn)了一個(gè)需
    發(fā)表于 05-09 18:17 ?0次下載
    如何在中斷<b class='flag-5'>C</b><b class='flag-5'>函數(shù)</b>中調(diào)用<b class='flag-5'>C++</b>

    C++重載函數(shù)學(xué)習(xí)總結(jié)

    函數(shù)重載是c++c的一個(gè)重要升級;函數(shù)重載通過參數(shù)列表區(qū)分不同的同名函數(shù);extern關(guān)鍵字能夠?qū)崿F(xiàn)c
    的頭像 發(fā)表于 12-24 17:10 ?728次閱讀

    EE-128:C++中的DSP:從C++調(diào)用匯編類成員函數(shù)

    EE-128:C++中的DSP:從C++調(diào)用匯編類成員函數(shù)
    發(fā)表于 04-16 17:04 ?2次下載
    EE-128:<b class='flag-5'>C++</b>中的DSP:從<b class='flag-5'>C++</b>調(diào)用匯編類成員<b class='flag-5'>函數(shù)</b>

    C++基礎(chǔ)語法inline 內(nèi)聯(lián)函數(shù)

    上節(jié)我們分析了C++基礎(chǔ)語法的const,static以及 this 指針,那么這節(jié)內(nèi)容我們來看一 inline 內(nèi)聯(lián)函數(shù)吧! inline 內(nèi)聯(lián)函數(shù) 特征 相當(dāng)于把內(nèi)聯(lián)
    的頭像 發(fā)表于 09-09 09:38 ?2049次閱讀

    C++ C語言函數(shù)查詢電子版下載

    C++ C語言函數(shù)查詢電子版下載
    發(fā)表于 01-18 10:15 ?0次下載

    深度解析C++中的虛函數(shù)

    函數(shù)作為C++的重要特性,讓人又愛又怕,愛它功能強(qiáng)大,但又怕駕馭不好,讓它反咬一口,今天我們用CPU的角度,撕掉語法的偽裝,重新認(rèn)識一函數(shù)。 虛
    的頭像 發(fā)表于 02-15 11:14 ?738次閱讀
    深度解析<b class='flag-5'>C++</b>中的虛<b class='flag-5'>函數(shù)</b>

    C++學(xué)習(xí)筆記c++的基本認(rèn)識

    自這篇文章我們即將開始C++的奇幻之旅,其內(nèi)容主要是讀C++ Primer的總結(jié)和筆記,有興趣可以找原版書看看,對于學(xué)習(xí)C++還是有很大幫助的。這篇文章將從一個(gè)經(jīng)典的程序開始介紹C++
    的頭像 發(fā)表于 03-17 13:57 ?624次閱讀

    C/C++函數(shù)hack(上)

    首先來說下 什么是hackhack字面意思“ 非法入侵 ”,那么在C/C++中其實(shí)就是 使用反匯編查看C/
    的頭像 發(fā)表于 03-30 16:54 ?784次閱讀
    <b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>之</b><b class='flag-5'>函數(shù)</b><b class='flag-5'>體</b><b class='flag-5'>hack</b>(上)

    C++基礎(chǔ)知識函數(shù)1

    函數(shù)C++ 中的一個(gè)重要概念,它可以讓我們將一段代碼封裝起來,然后在需要的時(shí)候調(diào)用它。C++ 中的函數(shù)有以下幾個(gè)特點(diǎn): * 函數(shù)
    的頭像 發(fā)表于 04-03 10:34 ?459次閱讀

    C++生成Dll與回調(diào)函數(shù)測試

    描述了VS環(huán)境,通過C++生成dll的方法,測試回調(diào)函數(shù)
    的頭像 發(fā)表于 08-29 16:05 ?1283次閱讀
    <b class='flag-5'>C++</b>生成Dll與回調(diào)<b class='flag-5'>函數(shù)</b>測試