算法分析SUCTF-2016全國(guó)賽的一道逆向題
1.下載文件先進(jìn)行查殼

2.vc編譯無(wú)殼,先跑一邊程序,弄清楚程序運(yùn)行邏輯

依照這界面來(lái)看,vc寫(xiě)的話99%就是利用mfc開(kāi)發(fā)的,兩個(gè)輸入框分別提示輸入郵箱地址和一段連續(xù)的數(shù)字,輸入完后按下ok按鈕得到一個(gè)它程序的判斷提示

3.初步了解后我們拖入od中進(jìn)行簡(jiǎn)單分析其關(guān)鍵邏輯部分(也就是這個(gè)按鈕事件)
因?yàn)槲也聹y(cè)程序99%是利用mfc寫(xiě)的,這個(gè)提示信息框時(shí)來(lái)在按鈕被按下發(fā)生的,所以肯定在這個(gè)按鈕按下后程序?qū)ξ覀冚斎氲膬?nèi)容進(jìn)行了一系列邏輯運(yùn)算讓后根據(jù)邏輯運(yùn)算的結(jié)果產(chǎn)生不同的提示信息,因此我們可以通過(guò)GetDlgItemTextA這個(gè)api來(lái)作為關(guān)鍵斷點(diǎn)地方。

通過(guò)搜索確實(shí)存在這個(gè)api,也成功斷下了,那我們先把程序跑起來(lái)斷點(diǎn)在這個(gè)函數(shù)上,發(fā)現(xiàn)按鈕按下后斷下來(lái)了,接著跑完GetDlgItemTextA函數(shù)(因?yàn)橥ǔ_@個(gè)哈函數(shù)執(zhí)行完我們的輸入的數(shù)據(jù)就被程序拿到了,那也就就該執(zhí)行數(shù)據(jù)的操作了)
跑出來(lái)發(fā)現(xiàn)兩個(gè)GetDlgItemTextA,因?yàn)槲覀冇袃蓚€(gè)輸入框組件,所以時(shí)兩個(gè)GetDlgItemTextA(這句話好像屁話)

當(dāng)我們沒(méi)有只是跑函數(shù)沒(méi)有分析函數(shù)內(nèi)部的時(shí)候要著重看函數(shù)跑完寄存器的值

查看寄存器發(fā)現(xiàn)就eax的值比較特殊,8,仔細(xì)想想才發(fā)現(xiàn)我們輸入的有一組數(shù)據(jù)就是八個(gè)

4.F8單步走

?
走完34133a這個(gè)函數(shù)以后發(fā)現(xiàn)eax變?yōu)榱薂qq.com所在的地址了,ecx變?yōu)榱?,繼續(xù)往下走便看到了一個(gè)跳轉(zhuǎn),如果沒(méi)跳轉(zhuǎn)則會(huì)出現(xiàn)一個(gè)與Your E-mail address in not valid.字符有關(guān)的函數(shù),中文也就是提示用戶郵箱地址不存在,這里涉及了一個(gè)開(kāi)發(fā)中常見(jiàn)的判斷用戶輸入郵箱地址是否存在的知識(shí),就是用正則表達(dá)式判斷用戶輸入的字符串中是否存在@xxx.xxx的字符,如果存在那么就是存在郵箱地址,為了驗(yàn)證我們的猜想我們?cè)?41344這個(gè)jnz處下斷點(diǎn),來(lái)試一下這個(gè)jnz是否是用來(lái)判斷郵箱存在與否的

果然,當(dāng)我們沒(méi)有按照郵箱格式輸入的時(shí)候就跳轉(zhuǎn)不實(shí)現(xiàn)了,也就是觸發(fā)了一個(gè)與Your E-mail address in not valid.字符有關(guān)的函數(shù),這是我們往上看34133a這個(gè)call的有一個(gè)參數(shù)是@便更可確認(rèn)
? 00341334????????????????????????? |.? 68 380A3500????????? push 9d2361b3.00350A38??? ???????????????;? @
? 00341339????????????????????????? |.? 50?????????????????? push eax
? 0034133A????????????????????????? |.? E8 E10D0000????????? call 9d2361b3.00342120
? 0034133F????????????????????????? |.? 83C4 08????????????? add esp,0x8
? 00341342????????????????????????? |.? 85C0???????????????? test eax,eax
? 00341344????????????????????????? |.? 75 1E??????????????? jnz short 9d2361b3.00341364
? 00341346????????????????????????? |>? 68 3C0A3500????????? push 9d2361b3.00350A3C?????????????????? ;? Your E-mail address in not valid.
? 0034134B????????????????????????? |>? 8D85 C0FEFFFF??????? lea eax,[local.80]?????????????????????? ; |
? 00341351????????????????????????? |.? 68 00010000????????? push 0x100?????????????????????????????? ; |Arg2 = 00000100
? 00341356????????????????????????? |.? 50?????????????????? push eax???????????????????????????????? ; |Arg1 = 00000000
? 00341357????????????????????????? |.? E8 6B1D0000????????? call 9d2361b3.003430C7?????????????????? ;
?
以上代碼是用來(lái)判斷郵箱是否存在的了,但是由于34133a這個(gè)函數(shù)字符參數(shù)只用一個(gè)@,難道這個(gè)作者判斷得更簡(jiǎn)單?只判斷是否有@字符而且@后是否有字符,那我就試了一下,的確在341344這個(gè)判斷點(diǎn)跳轉(zhuǎn)了,但是繼續(xù)往下的時(shí)候遇到一個(gè)跳轉(zhuǎn)又跳回了提示郵箱不存在的函數(shù)那里了

?
5.那我們繼續(xù)按照正確的格式繼續(xù)調(diào)試
[b]003413F0? |> /8A01????????? /mov al,byte ptr ds:[ecx]
[b]003413F2? |. |41??????????? |inc ecx
[b]003413F3? |. |84C0???? ?????|test al,al
[b]003413F5? |.^\75 F9???????? \jnz short 9d2361b3.003413F0
[b]003413F7? |.? 2BCA????????? sub ecx,edx
[b]003413F9? |.? 83F9 10?????? cmp ecx,0x10

這段代碼的作用便是循環(huán)把用戶輸入的Serial Number一個(gè)一個(gè)的傳入al中(嚴(yán)謹(jǐn)點(diǎn)說(shuō)應(yīng)該是每次將al中的值更新為ecx指向的字符串),通過(guò)判斷al是否為空,如果為部位空ecx+1(其實(shí)就是指向Serial Number的指針以此向后偏移),如果是空的話那就結(jié)束循環(huán),其實(shí)這里的ecx和edx都是指向Serial Number的指針,同時(shí)再利用這個(gè)位置來(lái)計(jì)數(shù),計(jì)數(shù)什么呢?當(dāng)然是Serial Number的個(gè)數(shù),怎么實(shí)現(xiàn)的呢?sub ecx,edx,這里就是利用最后ecx指向Serial Number字符串末尾,edx指向Serial Number字符串首,然后ecx-edx便得到了Serial Number的個(gè)數(shù),最后cmp ecx,0x10,將得到的Serial Number字符數(shù)與0x10(16)比較,通常在設(shè)計(jì)中這種比較就是比如校驗(yàn)用戶輸入的密碼是否小于6個(gè),如果小于6個(gè)則提示用戶繼續(xù)輸入,那我猜測(cè)這里作者也是為了同種要求,此時(shí)我們輸入的只有8個(gè),那么我們就繼續(xù)按照8個(gè)執(zhí)行看它會(huì)發(fā)生什么

當(dāng)用戶輸入的Serial Number不等于16時(shí)提示錯(cuò)誤,那說(shuō)明“對(duì)于Serial Number的第一個(gè)要求便是等于16“
6.那我們?cè)赟erial Number輸入框內(nèi)輸入16個(gè)整數(shù),滿足Serial Number個(gè)數(shù)等于16跳轉(zhuǎn)以后
003413FC? |. /74 09????? ???je short 9d2361b3.00341407
003413FE? |> |8D45 E4?????? lea eax,[local.7]
00341401? |. |50??????????? push eax
00341402? |.^|E9 44FFFFFF?? jmp 9d2361b3.0034134B
00341407? |> \8B8D C0FDFFFF mov ecx,[local.144]
0034140D? |.? 80F9 43?????? cmp cl,0x43
00341410? |.^ 75 EC???????? jnz short 9d2361b3.003413FE
00341412? |.? 0FBE85 CFFDFF>movsx eax,byte ptr ss:[ebp-0x231]
00341419? |.? 83C0 43?????? add eax,0x43
0034141C? |.? 3D 9B000000?? cmp eax,0x9B
00341421? |.^ 75 DB???????? jnz short 9d2361b3.003413FE
00341423? |.? 0FBECD??????? movsx ecx,ch
00341426? |.? 8D41 FD?????? lea eax,dword ptr ds:[ecx-0x3]
00341429? |.? 83F8 57?????? cmp eax,0x57
0034142C? |.^ 75 D0???????? jnz short 9d2361b3.003413FE
0034142E? |.? 0FBE85 CEFDFF>movsx eax,byte ptr ss:[ebp-0x232]
00341435? |.? 03C1????????? add eax,ecx
00341437? |.? 3D 9B000000?? cmp eax,0x9B
0034143C? |.^ 75 C0???????? jnz short 9d2361b3.003413FE
0034143E? |.? 0FBE8D C2FDFF>movsx ecx,byte ptr ss:[ebp-0x23E]
00341445? |.? 8D41 01?????? lea eax,dword ptr ds:[ecx+0x1]
00341448? |.? 83F8 3A?????? cmp eax,0x3A
0034144B? |.^ 75 B1???????? jnz short 9d2361b3.003413FE
0034144D? |.? 0FBE85 CDFDFF>movsx eax,byte ptr ss:[ebp-0x233]
00341454? |.? 03C1????????? add eax,ecx
00341456? |.? 3D 9B000000?? cmp eax,0x9B
0034145B? |.^ 75 A1???????? jnz short 9d2361b3.003413FE
0034145D? |.? 80BD C3FDFFFF>cmp byte ptr ss:[ebp-0x23D],0x64
00341464? |.^ 75 98???????? jnz short 9d2361b3.003413FE
00341466? |.? 0FBE85 CCFDFF>movsx eax,byte ptr ss:[ebp-0x234]
0034146D? |.? 83C0 64?????? add eax,0x64
00341470? |.? 3D 9B000000?? cmp eax,0x9B
00341475? |.^ 75 87???????? jnz short 9d2361b3.003413FE
00341477? |.? 80BD C4FDFFFF>cmp byte ptr ss:[ebp-0x23C],0x6D
0034147E? |.^ 0F85 7AFFFFFF jnz 9d2361b3.003413FE
00341484? |.? 0FBE85 CBFDFF>movsx eax,byte ptr ss:[ebp-0x235]
0034148B? |.? 05 81000000?? add eax,0x81
00341490? |.? 3D C8000000?? cmp eax,0xC8
00341495? |.^ 0F85 63FFFFFF jnz 9d2361b3.003413FE
0034149B? |.? 0FBE8D C5FDFF>movsx ecx,byte ptr ss:[ebp-0x23B]
003414A2? |.? 8D41 D3?????? lea eax,dword ptr ds:[ecx-0x2D]
003414A5? |.? 83F8 44?????? cmp eax,0x44
003414A8? |.^ 0F85 50FFFFFF jnz 9d2361b3.003413FE
003414AE? |.? 0FBE85 CAFDFF>movsx eax,byte ptr ss:[ebp-0x236]
003414B5? |.? 03C1????????? add eax,ecx
003414B7? |.? 3D AA000000?? cmp eax,0xAA
003414BC? |.^ 0F85 3CFFFFFF jnz 9d2361b3.003413FE
003414C2? |.? 80BD C6FDFFFF>cmp byte ptr ss:[ebp-0x23A],0x34
003414C9? |.^ 0F85 2FFFFFFF jnz 9d2361b3.003413FE
003414CF? |.? 0FBE85 C9FDFF>movsx eax,byte ptr ss:[ebp-0x237]
003414D6? |.? 83C0 34?????? add eax,0x34
003414D9? |.? 3D 9B000000?? cmp eax,0x9B
003414DE? |.^ 0F85 1AFFFFFF jnz 9d2361b3.003413FE
003414E4? |.? 80BD C7FDFFFF>cmp byte ptr ss:[ebp-0x239],0x63
003414EB? |.^ 0F85 0DFFFFFF jnz 9d2361b3.003413FE
003414F1? |.? 0FBE85 C8FDFF>movsx eax,byte ptr ss:[ebp-0x238]
003414F8? |.? 83C0 63?????? add eax,0x63
003414FB? |.? 3D 9B000000?? cmp eax,0x9B
00341500? |.^ 0F85 F8FEFFFF jnz 9d2361b3.003413FExxxxxxxxxx 003413FC

34333231,仔細(xì)一看就是我們輸入的1234的倒敘4321的十六進(jìn)制,之后又將cl與0x43作比較,如果cl與0x43不相等則跳轉(zhuǎn),為了提高貼子的質(zhì)量,這里我測(cè)試過(guò)的就不截圖了,經(jīng)測(cè)試,這個(gè)jnz跳轉(zhuǎn)是提示注冊(cè)失敗,003413FC??到 00341500的jnz都是跳轉(zhuǎn)到注冊(cè)失敗,那此時(shí)我們就知道了Serial Number的第二個(gè)條件,“所有的jnz條件不成立”,那我們先通過(guò)爆破試試滿足所有的第二個(gè)條件后程序的執(zhí)行又是什么。

?
這里我們通過(guò)改z標(biāo)志位實(shí)現(xiàn)jnz不跳轉(zhuǎn)進(jìn)行爆破,經(jīng)過(guò)最后一步爆破后如圖:


原來(lái)只要我們輸入的數(shù)不滿足jnz跳轉(zhuǎn)那就是正確的flag,那么我們就去探尋我們輸入的Serial Number還需要滿足什么?
7.探尋最后的Serial Number
令我們輸入的數(shù)字為一個(gè)整數(shù)數(shù)組 input[16]
00341419? |.? 83C0 43?????? add eax,0x43
0034141C? |.? 3D 9B000000?? cmp eax,0x9B

這里要注意一個(gè)細(xì)節(jié),eax除了第一次裝入的是我們輸入的Serial Number中的一個(gè)原數(shù)外,其他的都加上了一個(gè)其他數(shù)再對(duì)比,如上圖,所以我們真正滿足要求的Serial Number數(shù)是需要減去代碼中加上的那個(gè)數(shù),得到如下結(jié)果
今我們輸入的整數(shù)為一個(gè)整數(shù)數(shù)組input[16],以上出現(xiàn)的16個(gè)jnz的判斷便是有以下16個(gè)
input[0] ?= 67
input[15] ?= 88
input[1] ?= 90
input[14] ?= 65
input[2] ?= 57
input[13] ?= 98
input[3] ?= 100
input[12] ?= 55
input[4] ?= 109
input[11] ?= 71
input[5] ?= 113
input[10] ?= 57
input[6] ?= 52
input[9] ?= 103
input[7] ?= 99
input[8] ?= 56
因?yàn)槲覀冎巴ㄟ^(guò)爆破得知我們輸入的 Serial Number就是正確的flag,所以我們要把這寫(xiě)不滿足jnz的條件按照input[0]-input[15]排序出來(lái)就是正確的flag,而且flag都是字符
8.解密代碼:
#include <stdio.h>
int main()
{
?????? int num[16] = { 67, 57, 100, 109, 88, 52, 99, 56, 103, 57, 71, 55, 98, 65, 88 };
????? for (int i = 0; i < 16; i++)
??????? {
?????????????? printf("%c",num[i]);
?????? }
}
