我在上篇文章舉了一個(gè)簡(jiǎn)單的C++程序非常簡(jiǎn)略的解釋C++代碼和匯編代碼的對(duì)應(yīng)關(guān)系,在后面的文章中我將按照不同的Topic來(lái)仔細(xì)介紹更多" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

C++反匯編揭秘2 – VC編譯器的運(yùn)行時(shí)錯(cuò)誤檢查(R

系統(tǒng) 3131 0
<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog336280.html" frameborder="0" width="336" scrolling="no" height="280"></iframe>

我在上篇文章舉了一個(gè)簡(jiǎn)單的 C++ 程序非常簡(jiǎn)略的解釋 C++ 代碼和匯編代碼的對(duì)應(yīng)關(guān)系,在后面的文章中我將按照不同的 Topic 來(lái)仔細(xì)介紹更多相關(guān)的細(xì)節(jié)。雖然我很想一開(kāi)始的時(shí)候就開(kāi)始直接介紹 C++ 和匯編代碼的對(duì)應(yīng)關(guān)系,不過(guò)由于 VC 編譯器會(huì)在代碼中插入各種檢查, SEH C++ 異常等代碼,因此我覺(jué)得有必要先寫(xiě)一下一些在閱讀 VC 生成的匯編代碼的時(shí)候常見(jiàn)的一些東西,然后再開(kāi)始具體的分析 C++ 代碼的反匯編。這篇文章會(huì)首先涉及到運(yùn)行時(shí)檢查( Runtime Checking

Runtime Checking

運(yùn)行時(shí)檢查是 VC 編譯器提供了運(yùn)行時(shí)刻的對(duì)程序正確性 / 安全性的一種動(dòng)態(tài)檢查,可以在項(xiàng)目的 C++ 選項(xiàng)中打開(kāi) Small Type Check Basic Runtime Checks 來(lái)啟用 Runtime Check

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="Picture_x0020_4" style="VISIBILITY: visible; WIDTH: 453pt; HEIGHT: 28.5pt; mso-wrap-style: square" type="#_x0000_t75" o:spid="_x0000_i1027"><imagedata o:title="" src="file:///D:%5Ctmp%5Cmsohtmlclip1%5C01%5Cclip_image001.png"></imagedata></shape>

同時(shí),也可以使用 /RTC 開(kāi)關(guān)來(lái)打開(kāi)檢查, /RTC 后面跟 c, u, s 代表啟用不同類(lèi)型的檢查。 Smaller Type Check 對(duì)應(yīng) /RTCc, Basic Runtime Checks 對(duì)應(yīng) /RTCs /RTCu

/RTCc 開(kāi)關(guān)

RTCc 開(kāi)關(guān)可以用來(lái)檢查在進(jìn)行類(lèi)型轉(zhuǎn)換的保證沒(méi)有不希望的截?cái)啵? Truncation )發(fā)生。以下面的代碼為例:

char ch = 0;

short s = 0x101;

ch = s;

當(dāng) VC 執(zhí)行到 ch = s 的時(shí)候會(huì)報(bào)告如下錯(cuò)誤:

C++反匯編揭秘2 – VC編譯器的運(yùn)行時(shí)錯(cuò)誤檢查(RTC)

<shape id="Picture_x0020_1" style="VISIBILITY: visible; WIDTH: 327pt; HEIGHT: 144.75pt; mso-wrap-style: square" type="#_x0000_t75" o:spid="_x0000_i1026"><imagedata o:title="" src="file:///D:%5Ctmp%5Cmsohtmlclip1%5C01%5Cclip_image003.png"></imagedata></shape>

原因是 0x101 已經(jīng)超過(guò)了 char 的表示范圍。

之前會(huì)導(dǎo)致錯(cuò)誤地的代碼對(duì)應(yīng)的匯編代碼如下所示:

; 42 : char ch = 0;

mov BYTE PTR _ch$[ebp], 0

; 43 : short s = 0x101;

mov WORD PTR _s$[ebp], 257 ; 00000101H

; 44 : ch = s;

mov cx, WORD PTR _s$[ebp]

call @_RTC_Check_2_to_1@4

mov BYTE PTR _ch$[ebp], al

可以看到,賦值的時(shí)候, VC 編譯器先將 s 的值放到 cx 寄存器中,然后調(diào)用 _RTC_Check_2_to_1@4 函數(shù)來(lái)檢查是否有數(shù)據(jù)截?cái)嗟膯?wèn)題,結(jié)果放在 al 中,最后將 al 放到 ch 之中。 _RTC_Check_2_to_1@4 顧名思義是檢查 2 個(gè) byte 的數(shù)據(jù)被轉(zhuǎn)換成 1 個(gè) byte 的數(shù)據(jù)( short 2 個(gè) byte char 是一個(gè) byte ),代碼如下:

_RTC_Check_2_to_1:

00411900 push ebp

00411901 mov ebp,esp

00411903 push ebx

00411904 mov ebx,ecx

00411906 mov eax,ebx

00411908 and eax,0FF00h

0041190D je _RTC_Check_2_to_1+24h (411924h)

0041190F cmp eax,0FF00h

00411914 je _RTC_Check_2_to_1+24h (411924h)

00411916 mov eax,dword ptr [ebp+4]

00411919 push 1

0041191B push eax

0041191C call _RTC_Failure (411195h)

00411921 add esp,8

00411924 mov al,bl

00411926 pop ebx

00411927 pop ebp

00411928 ret

1. 00411904~00411906 ecx 保存著 s 的值,然后又被轉(zhuǎn)移到 eax 中。

2. 00411908~0041190D :檢查 eax 0xff00 相與,并檢查是否結(jié)果為 0 ,如果結(jié)果為 0 ,說(shuō)明這個(gè) short 值是 0 或者 的正數(shù),沒(méi)有超過(guò)范圍,直接跳轉(zhuǎn)到 00411924 獲得結(jié)果并返回

3. 0041190F~00411914 :檢查 eax 是否等于 0xff00 ,如果相等,說(shuō)明這個(gè) short 值是負(fù)數(shù),并且 >=-128 ,在 char 的表示范圍之內(nèi),可以接受,跳轉(zhuǎn)到 00411924

4. 如果上面檢查都沒(méi)有通過(guò),說(shuō)明這個(gè)值已經(jīng)超過(guò)了范圍,調(diào)用 _RTC_Failure 函數(shù)報(bào)錯(cuò)

要解決這個(gè)問(wèn)題,很簡(jiǎn)單,把代碼改為下面這樣就可以了:

char ch = 0;

short s = 0x101;

ch = s & 0xff;

/RTCu 開(kāi)關(guān)

這個(gè)開(kāi)關(guān)的作用是打開(kāi)對(duì)未初始化變量的檢查,比靜態(tài)的警告要有用一些。考慮下面的代碼:

int a;

char ch;

scanf("%c", &ch);

if( ch = 'y' ) a = 10;

printf("%d", a);

編譯器無(wú)從通過(guò) Flow Analysis 知道 a printf 之前是否被正確初始化,因?yàn)? a = 10 這個(gè)分支是由外部條件決定的,所以只有動(dòng)態(tài)的監(jiān)測(cè)方法才可以知道到底程序有沒(méi)有 Bug (當(dāng)然從這里我們可以很明顯的看出這個(gè)程序必然是有 Bug 的)。顯然把變量的值和一個(gè)具體值來(lái)比較是無(wú)法知道變量是否被初始化的,所以編譯器需要通過(guò)一個(gè)額外的 BYTE 來(lái)跟蹤此變量是否被初始化:

函數(shù)的開(kāi)始代碼如下:

push ebp

mov ebp, esp

sub esp, 228 ; 000000e4H

push ebx

push esi

push edi

lea edi, DWORD PTR [ebp-228]

mov ecx, 57 ; 00000039H

mov eax, -858993460 ; ccccccccH

rep stosd

mov BYTE PTR $T5147[ebp], 0

最后一句很關(guān)鍵,把 $T5147 變量的值設(shè)置為 0 ,表示并沒(méi)有初始化 a 這個(gè)變量。

當(dāng) ch = ‘y’ 的時(shí)候,編譯器除了執(zhí)行 a=10 之外還會(huì)將 $T5147 設(shè)置為 1

mov BYTE PTR $T5147[ebp], 1

mov DWORD PTR _a$[ebp], 10 ; 0000000aH

之后,在 printf 之前,編譯器會(huì)檢查 $T5147 這個(gè)變量的值,如果為 0 ,說(shuō)明沒(méi)有初始化,執(zhí)行 __RTC_UninitUse 報(bào)告錯(cuò)誤,否則跳轉(zhuǎn)到相應(yīng)代碼執(zhí)行 printf 語(yǔ)句:

cmp BYTE PTR $T5147[ebp], 0

jne SHORT $LN4@wmain

push OFFSET $LN5@wmain

call __RTC_UninitUse

add esp, 4

$LN4@wmain:

mov esi, esp

mov eax, DWORD PTR _a$[ebp]

push eax

push OFFSET ??_C@_02DPKJAMEF@?$CFd?$AA@

call DWORD PTR __imp__printf

add esp, 8

cmp esi, esp

call __RTC_CheckEsp

/RTCs 開(kāi)關(guān)

這個(gè)開(kāi)關(guān)是用來(lái)檢查和 Stack 相關(guān)的問(wèn)題:

1. Debug 模式下把 Stack 上的變量初始化為 0xcc ,檢查未初始化的問(wèn)題

2. 檢查數(shù)組變量的 Overrun

3. 檢查 ESP 是否被毀壞

Debug 模式下初始化變量為 0xcc

假設(shè)我們有下面的代碼:

void func()

{

int a;

int b;

int c;

}

對(duì)應(yīng)的匯編代碼如下:

?func@@YAXXZ PROC ; func, COMDAT

; 38 : {

push ebp

mov ebp, esp

sub esp, 228 ; 000000e4H

push ebx

push esi

push edi

lea edi, DWORD PTR [ebp-228]

mov ecx, 57 ; 00000039H

mov eax, -858993460 ; ccccccccH

rep stosd

; 39 : int a;

; 40 : int b;

; 41 : int c;

; 42 :

; 43 : }

pop edi

pop esi

pop ebx

mov esp, ebp

pop ebp

ret 0

?func@@YAXXZ ENDP

1. sub esp, 228 s 編譯器為 棧分配了 228 個(gè) byte

2. 接著 3 個(gè) push 指令保存寄存器

3. Lea edi, DWORD PTR [ebp-228] 一直到 repstosd 指令是初始化從 ebp-228 開(kāi)始寫(xiě) 57 個(gè) 0xcccccccc ,也就是 57*4=228 個(gè) 0xcc ,正好填滿(mǎn)之前 sub esp, 228 所分配的空間。這段代碼會(huì)把所有的變量初始化為 0xcc

選擇 0xcc 是有一定理由的 :

1. 0xcc 不同于一般的初始化值,人們一般傾向于把變量初始化為 0, 1, -1 等比較簡(jiǎn)單的值,而 0xcc 一般情況下足夠大,而且是負(fù)數(shù),容易引起注意,而且一般變量的值很有可能不允許是 0xcc ,比較容易造成錯(cuò)誤

2. 0xcc = int 3 ,如果作為代碼執(zhí)行,則會(huì)引發(fā)斷點(diǎn)異常,比較容易引起注意

檢查數(shù)組變量的 Overrun

假設(shè)我們有下面的代碼:

void func

{

char buf[104];

scanf("%s", buf);

return 0;

}

scanf 調(diào)用之后,會(huì)執(zhí)行下面的代碼:

mov ecx, ebp

push eax

lea edx, DWORD PTR $LN5@wmain

call @_RTC_CheckStackVars@8

這段代碼會(huì)調(diào)用 _RTC_CheckStackVars@8 函數(shù)會(huì)在數(shù)組的開(kāi)始和結(jié)束的地方檢查 0xcccccccc 有否被破壞,如果是,則報(bào)告錯(cuò)誤。 _RTC_CheckStackVars 由于代碼過(guò)長(zhǎng)這里就不給出了,這個(gè)函數(shù)主要是利用編譯器保存的數(shù)組位置和長(zhǎng)度信息,檢查數(shù)組的開(kāi)頭和結(jié)尾:

$LN5@func:

DD 1

DD $LN4@func

$LN4@func:

DD -112 ; ffffff90H

DD 104 ; 00000068H

DD $LN3@func

$LN3@func:

DB 98 ; 00000062H

DB 117 ; 00000075H

DB 102 ; 00000066H

DB 0

$LN5@func 紀(jì)錄了數(shù)組的個(gè)數(shù),而 $LN4@func 保存了數(shù)組的偏移量 ebp - 112 和數(shù)組的長(zhǎng)度 104 ,而 $LN3@func 則保存了變量的名稱(chēng)( 0x62, 0x75, 0x66, 0 = “buf” )。

檢查 ESP

ESP 的錯(cuò)誤很有可能是由調(diào)用協(xié)定的 mistach 造成,或者 Stack 本身沒(méi)有平衡。編譯器會(huì)在調(diào)用其他函數(shù)和在函數(shù) Prolog Epilog (開(kāi)始和結(jié)束代碼)的時(shí)候插入對(duì) ESP 的檢查:

1. 在調(diào)用其他外部函數(shù)的時(shí)候:

假設(shè)我們有下面的代碼:

printf( "%d", 1 );

對(duì)應(yīng)的匯編代碼如下:

mov esi, esp

push 1

push OFFSET ??_C@_02DPKJAMEF@?$CFd?$AA@

call DWORD PTR __imp__printf

add esp, 8

cmp esi, esp

call __RTC_CheckEsp

可以看到檢查的代碼非常簡(jiǎn)單直接,把 ESP 保存在 ESI 之中,當(dāng)調(diào)用 printf ,平衡堆棧之后,檢查 esp esi 的是否一致,然后調(diào)用 __RTC_CheckESP __RTC_CheckESP 代碼也很簡(jiǎn)單:

_RTC_CheckEsp:

00412730 jne esperror (412733h)

00412732 ret

esperror:

……

00412744 call _RTC_Failure (411195h)

……

00412754 ret

如果不一致,跳轉(zhuǎn)到 esperror 標(biāo)號(hào)報(bào)告錯(cuò)誤。

2. 函數(shù)返回的時(shí)候:

以下面的代碼為例:

void func()

{

__asm

{

push eax

}

}

Func 函數(shù)故意 push eax 來(lái)破壞堆棧的平衡性,對(duì)應(yīng)的匯編代碼如下:

?func@@YAXXZ PROC ; func, COMDAT

; 38 : {

push ebp

mov ebp, esp

sub esp, 192 ; 000000c0H

push ebx

push esi

push edi

lea edi, DWORD PTR [ebp-192]

mov ecx, 48 ; 00000030H

mov eax, -858993460 ; ccccccccH

rep stosd

; 39 : __asm

; 40 : {

; 41 : push eax

push eax

; 42 : }

; 43 : }

pop edi

pop esi

pop ebx

add esp, 192 ; 000000c0H

cmp ebp, esp

call __RTC_CheckEsp

mov esp, ebp

pop ebp

ret 0

?func@@YAXXZ ENDP

在函數(shù)的初始化代碼中, func 會(huì)將 ebp 保存在 Stack 中,并且把當(dāng)前 esp 保存在 ebp 中。

?func@@YAXXZ PROC ; func, COMDAT

push ebp

mov ebp, esp

關(guān)鍵的檢查代碼在后面,當(dāng) func 函數(shù)恢復(fù)了堆棧之后,堆棧會(huì)恢復(fù)到之前剛保存 esp ebp 的那個(gè)狀態(tài),這個(gè)時(shí)候 ebp 必然等于 esp ,否則出錯(cuò)

分享到:
評(píng)論
happmaoo
  • 瀏覽: 1292026 次
  • 性別: Icon_minigender_1
  • 來(lái)自: 杭州
存檔分類(lèi)
最新評(píng)論

C++反匯編揭秘2 – VC編譯器的運(yùn)行時(shí)錯(cuò)誤檢查(RTC)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日韩欧国产精品一区综合无码 | 亚洲成a人v大片在线观看 | 亚洲精品久久久久中文字小说 | 97碰公开在线观看免费视频 | jiucao在线观看精品 | 国产成+人+亚洲+欧美+日韩 | 欧美毛片性视频区 | 欧美乱妇高清视频免欢看关 | 久草美女视频 | 九九精品免费观看在线 | 亚洲天堂一区在线 | 久久久www成人免费精品 | 99久久99热久久精品免 | 高清在线不卡 | 久久99精品久久久久久臀蜜桃 | 爱神马午夜 | 91在线高清 | 国产精品亚洲第一区二区三区 | 夜夜爽www | 免费一级a毛片夜夜看 | 久久国产影视免费精品 | 久久中文字幕亚洲精品最新 | 久久精品看片 | 欧美性猛交xxxx免费看久久久 | 伊人第一页 | 久久国产精品99精品国产 | 天天操天天透 | 免费特黄一级欧美大片在线看 | 成人a网站| 欧美日韩国产成人综合在线影院 | 一级毛片高清免费播放 | 亚洲欧美一区二区三区四区 | 狠狠色噜噜狠狠狠97影音先锋 | 97免费视频观看 | 黄页免费观看1 | 奇米影视四色中文字幕 | 好吊妞欧美视频免费 | 中文字幕一区二区在线视频 | 88国产精品欧美一区二区三区 | 欧美成人aaaaa免费视频 | 一级毛片 在线播放 |