1. 程式人生 > >【wp】2021MAR-DASCTF_逆向

【wp】2021MAR-DASCTF_逆向

昨天打完的MAR DASCTF,來複個盤~ 不過就re做了3/4然後有事提前開溜了hhh,拿了drinkSomeTea和replace的三血心滿意足(蜜汁三血執念。 感覺這回的出題人好喜歡TEA啊(正好最近在整理加解密演算法),就是TEA和XTEA都出了卻不帶XXTEA玩有點可惜/doge。 掃雷也覆盤完了!好耶! # Reverse ## drinkSomeTea 是一個邏輯超級明顯但是超——坑的題。 鑑於這是覆盤,那就直擊要害吧,懶得把當時兜兜轉轉的心路歷程複述一遍了>^<。 ![image-20210328160554625](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161522973-1783909829.png) ![image-20210328160608278](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161523280-761016109.png) 邏輯很簡單,就是將`./tea.png`(FileName=“./tea.png”)的內容以二進位制形式讀入到`unk_409988`中,然後以8位元組為單位(`v7+=8`)對其進行處理(`loc_4010A0`這裡),再寫入`./tea.png.out`中。而題目附件給了這個最後輸出的`./tea.png.out`,需要我們還原`./tea.png`。 `loc_4010A0`這裡沒有轉函式的原因是出現了花指令干擾靜態分析,nop掉(74 03 75 01 E8改成90 90 90 90 90)再重新轉程式碼轉函式即可。 ![image-20210328161029250](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161523547-1026662608.png) 然後我們就得到了`sub_4010A0()`。 ![image-20210328161202935](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161523789-372199005.png) 很明顯地可以看到是TEA加密,就是說`tea.png`經過TEA加密以後得到了`tea.png.out`。 a2是加密時的key,退回到上一層可以發現是`dword_407030`: ![image-20210328161829584](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161524026-832128378.png) ![image-20210328161837406](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161524260-2146306157.png) 但是如果拿平時的TEA解密指令碼跑根本就行不通,當時多用了兩三倍的時間去調(patch掉exit,走一遍加密流程進行對比),最後才發現是int的問題,一般TEA加解密都是uint_32的(。就是這個細節浪費了超多時間TvT 最後上解密指令碼,基本上把unsigned int改成int就好(改自[TEA、XTEA、XXTEA加密解密演算法_gsls200808的專欄-CSDN部落格](https://blog.csdn.net/gsls200808/article/details/48243019)): ```c #include #include //加密函式 void encrypt (int* v, int* k) { int v0=v[0], v1=v[1], sum=0, i; /* set up */ int delta=0x9e3779b9; /* a key schedule constant */ int k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ for (i=0; i < 32; i++) { /* basic cycle start */ sum += delta; v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1); v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); } /* end cycle */ v[0]=v0; v[1]=v1; } //解密函式 void decrypt (int* v, int* k) { int v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */ int delta=0x9e3779b9; /* a key schedule constant */ int k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ for (i=0; i<32; i++) { /* basic cycle start */ v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1); sum -= delta; } /* end cycle */ v[0]=v0; v[1]=v1; } int main() { int v[14656]={0},k[4]={0x67616C66, 0x6B61667B, 0x6C665F65, 0x7D216761}; FILE *p1 = fopen("./tea.png.out", "rb"); fread(&v, 4, 14656, p1); fclose(p1); for(int i=0;i<14656;i+=2){ decrypt(&v[i], k); } FILE *p2 = fopen("./tea.png", "wb"); fwrite(&v, 4, 14656, p2); fclose(p2); return 0; } ``` 得到`./tea.png`: ![tea](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161524499-213207632.png) 得到flag:**DASCTF{09066cbb91df55502e6fdc83bf84cf45}** ## Enjoyit-1 又一道TEA。 附件用ExEinfoPE可以看到 ![image-20210328162359343](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161524876-361927088.png) 說明是.NET逆向,於是用ILSpy開啟。 ![image-20210328162618673](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161525190-1160059740.png) 看到不尋常字串`DotfuscatorAttribute`,用搜索引擎一查可以發現是使用Dotfuscator加密混淆程式的產物([使用Dotfuscator加密混淆程式以及如何脫殼反編譯_qwsf01115的專欄-CSDN部落格](https://blog.csdn.net/qwsf01115/article/details/71425296))。 所以根據文章指引用de4dot(可用release:[Release de4dot mod · CodingGuru1989/de4dot](https://github.com/CodingGuru1989/de4dot/releases/tag/snapshot))進行反混淆,得到`Enjoyit-1-cleaned.exe`,再用ILSpy開啟,就可以看到混淆前在Class0裡的main函式。 ![image-20210328163103034](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161525459-270753665.png) 這個邏輯也很簡單,~~無非就是輸入text正確以後執行100000秒就會輸出flag。~~ (當然肯定不可能這麼走啊,100000s=1666.67min=27.78h,必然是等不起的。 ![image-20210328163129604](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161525720-777667521.png) flag產生的邏輯是先用`uint_`和`text`進行`method_3()`的處理,然後再與array3按位元組異或。 而text相當於是已知的,關鍵在 `method_1()`這裡。 ![image-20210328165701712](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161526030-468506997.png) 可以看出是一個base64換表,Table在string_0這裡: ![image-20210328165737537](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161526385-1825033377.png) 於是可以先寫指令碼得到text: ```python import base64 from binascii import * src='yQXHyBvN3g/81gv51QXG1QTBxRr/yvXK1hC=' table='abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ' b64table='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' text=base64.b64decode(src.translate(str.maketrans(table,b64table)).encode()) print(text) # text=b'combustible_oolong_tea_plz' ``` 然後在主函式往下看,來到了第二個關鍵函式`method_3()`: ![image-20210328170318295](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161526599-999493480.png) 顯而易見是個改了delta的XTEA加密,傳進來的uint_0是主函式的uint_,byte_0是text的前四位元組。 依舊是用上面部落格的XTEA指令碼改了一下,得到XTEA加密後的uint_: ```c #include #include /* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */ void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0=v[0], v1=v[1], sum=0, delta=2654435464; for (i=0; i < num_rounds; i++) { v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); sum += delta; v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); } v[0]=v0; v[1]=v1; } void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0=v[0], v1=v[1], delta=2654435464, sum=delta*num_rounds; for (i=0; i < num_rounds; i++) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); sum -= delta; v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); } v[0]=v0; v[1]=v1; } int main() { uint32_t v[2]={288,369}; uint32_t const k[4]={0x63,0x6f,0x6d,0x62}; unsigned int r=32; encipher(r, v, k); printf("%8x%8x\n",v[0],v[1]); return 0; } ``` 因為主函式裡後續處理是把8 bit十六進位制的兩個結果往str裡填,所以輸出採用十六進位制形式。 ![image-20210328170941696](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161526814-1155786330.png) 得到`str="6308fe34b7fe6fdb"` 最後進行xor處理即可,上exp: ```python xtea="6308fe34b7fe6fdb" arr3=[2,5,4,13,3,84,11,4,87,3,86,3,80,7,83,3,0,4,83,94,7,84,4,0,1,83,3,84,6,83,5,80] flag="" for i in range(len(arr3)): flag+=chr(arr3[i]^ord(xtea[i%len(xtea)])) print("flag{"+flag+"}") ``` 得到flag:**flag{4645e180540ffa7a67cfa174cde105a2}** ## replace u1s1不知道這個標級怎麼標的,居然是比StrangeMine還高的困難,也有可能是我tcl吧( 主邏輯還是很簡單,輸入長度為24的flag並且以"flag{}"包裹(check在`sub_401550()`),然後經過某些處理(`sub_401AE7()`、`sub_401925()`還有可能的`IsDebuggerPresent()`)等於某已知字串(check在`sub_401883()`)即可。 ![image-20210328171544665](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161527052-500162551.png) ![image-20210328171908728](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161527298-1428087356.png) 最後check這裡`byte_4080E0`是輸入後經過處理存放的關鍵位置。 從頭開始捋處理函式,這裡`sub_401AE7()`的作用是把輸入和已知陣列進行xor以後放進`byte_4080E0`中。 ![image-20210328172304024](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161527526-1302278531.png) 真的有這麼簡單嗎? 當然不會,困難題目誒。(從這裡逆向解的話會得到假flag。 真正的奧祕在下一個函式`sub_401925()`這裡,看到`VirtualProtectEx()`這個改記憶體讀寫許可權的關鍵函式,DNA瞬間動了 ~~(別什麼奇怪的東西都往DNA裡刻啊喂~~ 。 ![image-20210328172535850](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161527818-338114653.png) 這裡很像之前做過的一個題([RE套路/從EASYHOOK學inline hook | c10udlnk_Log](https://c10udlnk.top/2020/12/16/reSkillsOn-Inline-hook/)),這種函式一般是搭配`WriteProcessMemory()`在執行時對記憶體進行修改(從而達到跳轉到某些函式的目的)。 靜態分析的話那篇blog也有講,但是為了做題方便直接動態除錯走起。 因為這裡有超——多的反除錯,懶得一個個patch了,直接把exit(0)裡的功能給patch掉(偷懶大法好。 ![image-20210328173054725](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161528105-1820041768.png) 即把這裡六個位元組直接patch成`90 90 90 90 90 90`,全部nop掉。 ![image-20210328173220088](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161528344-2052523497.png) 變成這個樣子: ![image-20210328173307881](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161528559-158699826.png) 別忘了動態除錯前要把patch的位元組儲存進exe裡(`Edit->Patch program->Apply patches to input file`)。 然後開調,斷點下到`sub_401925()`和`IsDebuggerPresent()`這裡,記得要隨便輸入一個符合前兩個check的字串。 ![image-20210328173823185](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161528777-1150895000.png) 從`sub_401925()`可以大致看到修改的是`IsDebuggerPresent()`的內容。 ![image-20210328174025489](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161529016-2098024667.png) 所以我們按F9直接走到`IsDebuggerPresent()`這裡,F7步入。 ![image-20210328174118284](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161529263-1795596254.png) 可以看到跳轉到了一個函式,而再往裡走可以看到這是個被花指令處理的函式所以靜態分析down掉了。 很容易就能找到三個跟第一題相同原理的花指令(注意+2那裡要nop多一個位元組),所以直接patch,然後把資料按c轉成code。 ![image-20210328174304181](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161529508-2013516347.png) ![image-20210328174410899](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161529836-1895337531.png) ![image-20210328174507812](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161530080-1733134000.png) 最後得到反編譯結果: ```c void sub_4015C3() { int v0[128]; // [rsp+30h] [rbp-50h] BYREF __int64 v1[5]; // [rsp+230h] [rbp+1B0h] char *v2; // [rsp+258h] [rbp+1D8h] int v3; // [rsp+264h] [rbp+1E4h] int j; // [rsp+268h] [rbp+1E8h] int i; // [rsp+26Ch] [rbp+1ECh] VirtualProtectEx(hProcess, IsDebuggerPresent_0, 0x10ui64, 0x40u, &flNewProtect); WriteProcessMemory(hProcess, IsDebuggerPresent_0, Destination, 0x10ui64, 0i64); VirtualProtectEx(hProcess, IsDebuggerPresent_0, 0x10ui64, flNewProtect, 0i64); if ( !IsDebuggerPresent() ) { j = 0; v3 = 0; v1[0] = 0i64; v1[1] = 0i64; v1[2] = 0i64; v1[3] = 0i64; memcpy(v0, &unk_404020, sizeof(v0)); v2 = Str; for ( i = 1; i <= 5; ++i ) { for ( j = 0; j <= 23; ++j ) v2[j] = v0[(unsigned __int8)v2[j]]; } for ( i = 0; i <= 5; ++i ) *((_DWORD *)v1 + i) = ((unsigned __int8)v2[i + 12] << 8) | ((unsigned __int8)v2[i + 6] << 16) | ((unsigned __int8)v2[i] << 24) | (unsigned __int8)v2[i + 18]; for ( i = 0; i <= 5; ++i ) sprintf(&byte_4080E0[8 * i], "%x", *((unsigned int *)v1 + i)); } } ``` 就是先把`IsDebuggerPresent()`裡的內容還原,然後在非除錯情況下將輸入經過一些處理放到`byte_4080E0`中。 這!才是真正的加密函式! 然後這個邏輯也很好逆啦,順著就是先在unk_404020盒裡換五次,然後柵欄密碼得到最後的字串。 寫出exp有: ```python from binascii import * from hashlib import md5 ans=unhexlify("416f6b116549435c2c0f1143174339023d4d4c0f183e7828") tmps=[0,6,12,18] seq=[] for i in range(6): tmpl=[x+i for x in tmps] seq=seq+tmpl arr1=[0 for i in range(24)] for i in range(24): arr1[seq[i]]=ans[i] box=[0x00000080, 0x00000065, 0x0000002F, 0x00000034, 0x00000012, 0x00000037, 0x0000007D, 0x00000040, 0x00000026, 0x00000016, 0x0000004B, 0x0000004D, 0x00000055, 0x00000043, 0x0000005C, 0x00000017, 0x0000003F, 0x00000069, 0x00000079, 0x00000053, 0x00000018, 0x00000002, 0x00000006, 0x00000061, 0x00000027, 0x00000008, 0x00000049, 0x0000004A, 0x00000064, 0x00000023, 0x00000056, 0x0000005B, 0x0000006F, 0x00000011, 0x0000004F, 0x00000014, 0x00000004, 0x0000001E, 0x0000005E, 0x0000002D, 0x0000002A, 0x00000032, 0x0000002B, 0x0000006C, 0x00000074, 0x00000009, 0x0000006E, 0x00000042, 0x00000070, 0x0000005A, 0x00000071, 0x0000001C, 0x0000007B, 0x0000002C, 0x00000075, 0x00000054, 0x00000030, 0x0000007E, 0x0000005F, 0x0000000E, 0x00000001, 0x00000046, 0x0000001D, 0x00000020, 0x0000003C, 0x00000066, 0x0000006B, 0x00000076, 0x00000063, 0x00000047, 0x0000006A, 0x00000029, 0x00000025, 0x0000004E, 0x00000031, 0x00000013, 0x00000050, 0x00000051, 0x00000033, 0x00000059, 0x0000001A, 0x0000005D, 0x00000044, 0x0000003E, 0x00000028, 0x0000000F, 0x00000019, 0x0000002E, 0x00000005, 0x00000062, 0x0000004C, 0x0000003A, 0x00000021, 0x00000045, 0x0000001F, 0x00000038, 0x0000007F, 0x00000057, 0x0000003D, 0x0000001B, 0x0000003B, 0x00000024, 0x00000041, 0x00000077, 0x0000006D, 0x0000007A, 0x00000052, 0x00000073, 0x00000007, 0x00000010, 0x00000035, 0x0000000A, 0x0000000D, 0x00000003, 0x0000000B, 0x00000048, 0x00000067, 0x00000015, 0x00000078, 0x0000000C, 0x00000060, 0x00000039, 0x00000036, 0x00000022, 0x0000007C, 0x00000058, 0x00000072, 0x00000068] arr2=[0 for i in range(24)] for i in range(5): for j in range(24): arr2[j]=box.index(arr1[j]) arr1=arr2 myInput=''.join(map(chr,arr1)) print(myInput) flag=myInput.encode() print(md5(flag).hexdigest()) # 別忘了最後提交md5 ``` 得到flag:**flag{Sh1t_you_dec0d3_it}** ## 奇怪的掃雷 這道題賽中沒怎麼做,賽後覆盤靜態硬剛+動態調沒搞出來,終於等到了帶掃雷玩的wp([MAR DASCTF明御攻防賽 PWN、RE Writeup - 安全客,安全資訊平臺](https://www.anquanke.com/post/id/236178#h3-8)),感覺思路大概沒錯、關鍵函式也找對了,但是忽略了一個小地方: ![image-20210331190818301](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161530339-870126702.png) 我當時還在納悶為什麼是用程式碼段的資料來計算md5值,原來是為了檢測有沒有patch啊( 以及請教隊裡大佬以後才發現還忽略了一個點,就是md5的update是拼接的,沒認真學md5的我一直以為是覆蓋的(慚愧,真的要好好學雜湊演算法。 編寫用來hook的dll檔案對我來說還是有點難(tcl),就試著用自己的方法做一下好了。 (啊其實就是直接靜態硬解,從頭捋捋怎麼發現函式的。 首先直接findcrypt,找到md5的常數。 ![image-20210401004127186](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161530601-688881945.png) 通過交叉引用找到上一層。 ![image-20210401004222831](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161530823-942393493.png) ![image-20210401004350757](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161531121-349595563.png) 發現這裡有一個前面題目提到的花指令,照例nop掉,轉函式,看虛擬碼。 ![image-20210401004429125](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161531358-878919593.png) ![image-20210401004614020](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161531622-31539804.png) 感覺這是個自己寫的處理異常的函式(`TopLevelExceptionFilter`這個名字引起警覺)。 然後往下看,先根據交叉引用+識別演算法給MD5的一系列函式命個名,方便靜態分析。 (也可以盲找`AfxMessageBox`的交叉引用,畢竟能出flag的地方一般都在MessageBox。) ~~更何況這題是賽後覆盤,根據大家群裡零零散散發出來的截圖可以判斷flag用`AfxMessageBox`給出,而查AfxMessageBox的交叉引用可以發現就這裡是放了變數的。~~ 主要邏輯在: ![image-20210401005119122](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161531901-735723996.png) ![image-20210401005144366](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161532178-1403747052.png) 很明顯地看到,v12裝著最後的md5(也就是flag。 整體邏輯是: 1. 先把`IUnknown::operator=()`函式首地址開始的12288位元組(0x402000-0x405000)放到`Src`裡,然後根據**觸發異常時的eax、ebx、ecx、edx的值**修改部分位置(`Src[4101]`、`Src[128]`、`Src[256]`、`Src[0x2000]`),最後把`Src`丟進MD5Update裡。 2. 把`&loc_404FFE + 2`開始的12288位元組(0x405000-0x408000)放到`Src`裡,再`Src`丟進MD5Update裡。 3. 最後算出MD5,此MD5值即為flag。 慶幸這個MD5演算法沒有被魔改,不然還得找魔改的地方(癱。 ![image-20210401012909017](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161532467-1347885539.png) ![image-20210401012927466](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161532728-393264750.png) 現在缺的資料就是**觸發異常時的eax、ebx、ecx、edx的值**了,只要找到check一切好說,可以從時間的增加入手找到操縱遊戲的邏輯部分。 遊戲類果斷上CE(Cheat Engine)找關鍵記憶體,先綁上執行的exe檔案,調整掃描設定後點擊“首次掃描”。 ![image-20210401010218715](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161532972-1461527960.png) 在介面隨便點一下開始遊戲(讓遊戲開始計時),然後調整CE右側設定,一直點“再次掃描”(注意點選間隔在1s以上,不然時間根本就沒動過就掃描不出來了),直到左側只剩少數地址(並且有一個地址的當前值和計時器的相同)為止。 ![image-20210401011044402](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161533260-93341396.png) ![image-20210401011126872](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161533508-131088686.png) 雙擊這條地址,選中記錄,按`F6`,檢視改寫這個地址的位置。 ![image-20210401011238839](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161533707-1173996000.png) 可以看到 ![image-20210401011300905](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161533920-2026117310.png) 在IDA的反彙編視窗中按`g`跳到這個地址(`0x408803`) ![image-20210401011357084](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161534208-1202942299.png) 再反編譯可以看到 ![image-20210401011416868](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161534445-1545217332.png) 這就是時間增加的函式。 依次查交叉引用,可以發現check應該存在於這個右鍵鬆開的函式裡,並且可以猜是`sub_403D30()`。 ![image-20210401011601925](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161534744-1245668841.png) 為什麼我篤定是這個呢,因為這個函式不僅是以if框著的形式呼叫,而且裡面還會觸發一個異常: ![image-20210401011729507](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161535160-1938117586.png) 也就是我們常說的CC斷點 ![image-20210401011744842](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161535395-990521116.png) 這邊的邏輯可以猜測是迴圈check,一旦有不對的地方就直接`return 0;`,全部通過以後可觸發斷點,進而走到`TopLevelExceptionFilter`並給flag。 而我們需要的正是這裡的**eax、ebx、ecx、edx的值**。 ![image-20210401012050906](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161535725-4347378.png) 而觀察整個check函式的彙編可以發現,最後是直接用變數給四個暫存器賦值的,並且在之前的程式碼中這些變數幾乎沒有變動(具體可以自己琢磨琢磨,看目的暫存器的位置;只可意會不可言傳.jpg),所以可以通過把函式開始的`jmp short loc_403D55`patch成`jmp short loc_403D9E`。 ![image-20210401012256792](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161536010-718497321.png) 這樣直接跳過迴圈來到關鍵位置`loc_403D9E`,動態除錯時即可在觸發int3斷點時看到四個暫存器的值。 ![image-20210401012453275](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161536385-2038275391.png) 然後開調,老規矩,記得除錯之前要把patch的位元組儲存進exe裡(`Edit->
Patch program->Apply patches to input file`)。 選高階以後隨便點個右鍵插旗子,然後放任程式走,走到斷點時有提示,關掉提示以後可以看到右上角: ![image-20210401012739096](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161536598-1235290881.png) 四個暫存器的值也拿到了! 接下來就差記憶體了。因為前面靜態分析的時候我們patch了不少地方(花指令+這波跳轉),所以要拿最最開始的附件來dump。 開啟最最開始的附件,選擇`File->Script command`,分別在IDC下輸入以下指令碼dump出兩塊記憶體: ![image-20210401013416221](https://img2020.cnblogs.com/blog/2183122/202104/2183122-20210401161536839-137990435.png) ```c static main(){ auto i,fp; fp=fopen("./dump_0x402000","wb"); auto start=0x402000; auto size=12288; for(i=start;i