[原創]看雪CTF2017第二題lelfeiCM的writeup
主要有一下幾大點:
0. 定位演算法位置
由於是console程式,並且沒有隱藏字串,透過OD/IDA找到關鍵字串,所在函式就是關鍵演算法函式:
.data:00409058 aWellDone db 'WELL DONE!',0Ah,0 ; DATA XREF: _main:loc_401257o .data:00409064 aWrongKey___ db 'WRONG KEY...',0Ah,0 ; DATA XREF: _main+231o .data:00409072 align 4 .data:00409074 aKeyFormatError db 'key format error...',0Ah,0 ; DATA XREF: _main+9Ao
其實就在main函式中,然後看獲取輸入之後幹了什麼。
首先檢查輸入長度是不是在8到20之間,不是提示key len error
.text:00401066 cmp ecx, 8 .text:00401069 jl loc_40127A .text:0040106F cmp ecx, 14h .text:00401072 jg loc_40127A .text:00401078 xor esi, esi .text:0040107A xor edx, edx .text:0040107C test ecx, ecx
是不是都是數值,不是就提示key format error...
.text:00401082 jle short loc_4010AC .text:00401084 .text:00401084 loc_401084: ; CODE XREF: _main+94j .text:00401084 mov al, [esp+edx+4138h+key] .text:00401088 cmp al, 30h .text:0040108A jle short loc_401090 .text:0040108C cmp al, 39h .text:0040108E jle short loc_401091 .text:00401090 .text:00401090 loc_401090: ; CODE XREF: _main+8Aj .text:00401090 inc esi .text:00401091 .text:00401091 loc_401091: ; CODE XREF: _main+8Ej .text:00401091 inc edx .text:00401092 cmp edx, ecx .text:00401094 jl short loc_401084 .text:00401096 test esi, esi .text:00401098 jz short loc_4010AC .text:0040109A push offset aKeyFormatError ; "key format error...\n" .text:0040109F call f_printf_401BE0
下面接著就是演算法的重要部分了,一看到下面的函式,就知道有點小類結構了
.text:004012C0 ; KEY_OBJ1 *__thiscall f_keyobj_init_4012C0(KEY_OBJ1 *this) .text:004012C0 f_keyobj_init_4012C0 proc near ; CODE XREF: _main+B3p .text:004012C0 ; f_keyobj_calc_mul_401730+29p ... .text:004012C0 push esi .text:004012C1 mov esi, ecx .text:004012C3 mov dword ptr [esi], offset off_4080C8 .text:004012C9 call ds:GetTickCount .text:004012CF mov ecx, esi .text:004012D1 mov [esi+200Ch], eax .text:004012D7 mov [esi+2008h], eax .text:004012DD call f_keyobj_init_seed1_401A60 .text:004012E2 mov eax, esi .text:004012E4 pop esi .text:004012E5 retn .text:004012E5 f_keyobj_init_4012C0 endp
1. 演算法類結構分析,各類函式的功能分析
先把類結構大致整理出來,方面後續分析
00000000 KEY_OBJ1 struc ; (sizeof=0x2010) ; XREF: _mainr 00000000 ; f_keyobj_calc_mul_401730r 00000000 vtable_4080C8 dd ? 00000004 cur_calc_pos dd ? //結果長度 00000008 seed_array_1024_1 dd 1024 dup(?) //儲存key的值 00001008 seed_array_1024 dd 1024 dup(?) //儲存序號 00002008 TickCnt_key_seed dd ? 0000200C TickCnt1 dd ? 00002010 KEY_OBJ1 ends
然後就是幾個關鍵函式:
1.1 初始化資料
.text:00401A60 ; char *__thiscall f_keyobj_init_seed1_401A60(KEY_OBJ1 *this) ... .text:00401A8F call f_kyeobj_getindex_4019E0 //更加GetTickCount獲取隨機index,用於打亂序號的順序增加分析難度 ... .text:00401ABA mov esi, [ecx] .text:00401ABC sub ecx, 4 .text:00401ABF mov [eax], esi .text:00401AC1 add eax, 4 .text:00401AC4 dec edx
這個地方首先就想到了每次GetTickCount不一樣,那麼演算法怎麼保證結果相同呢,便想到肯定跟index順序無關,後面驗證果然是,我就把401A60給patch了一下,然初始化的序號結構沒有打亂順序,保持0-0x3ff,如下
//nop了00401A8F呼叫的迴圈部分 .text:00401A8F call f_kyeobj_getindex_4019E0 //這裡其實就是seed_array_1024[1023],不讓它倒過來賦值,修改為lea ecx, [esi+1008h] .text:00401AAE lea ecx, [esi+2004h]
這樣之後,就可以很方便檢視資料變換,觀察這兩個欄位即可
00000004 cur_calc_pos dd ? //結果長度 00000008 seed_array_1024_1 dd 1024 dup(?) //儲存key的值
後面所有相關函式中有關index轉換的也不用關注,因為他變來變去都是0-0x3ff,就只需要關注具體資料操作了。
然後其他函式功能分析也就簡單了。
下面簡單列一下,不做詳細說明了(很簡單,就是陣列操作過來過去的)
.text:004014E0 ; int __thiscall f_keyojb_key1_4014E0(void *this, const char *key) //將輸入的key儲存到seed_array_1024_1 中,字元轉為數值,每個值存一個dword .text:00401970 ; void __thiscall f_keyobj_key1_s2_401970(KEY_OBJ1 *this) //數值大於10,取餘存當前index位置,取商和index+1位置求和儲存,其實就是進位處理(後面才醒悟) .text:00401730 ; signed int __userpurge f_keyobj_calc_mul_401730@<eax>(int a1@<eax>, int keyobj0@<ecx>, signed int a3)//用a3取商做右位移,a3取餘做加法,其實就是做乘法運算 text:00401840 ; signed int __userpurge f_keyobj_mul2_401840@<eax>(int a1@<eax>, int a2@<ecx>, KEY_OBJ1 *a3)//兩個KEY_OBJ做乘法
2. 醒悟演算法究竟是個什麼玩意
輸入的key關鍵處理部分
.text:004010E0 push 9 .text:004010E2 lea ecx, [esp+413Ch+keyobj] .text:004010E9 call f_keyobj_calc_mul_401730 ; ... .text:0040110B lea eax, [esp+4138h+keyobj1] .text:00401112 lea ecx, [esp+4138h+keyobj] .text:00401119 push eax .text:0040111A mov byte ptr [esp+413Ch+var_4], 1 .text:00401122 call f_keyobj_mul2_401840 ... .text:00401127 push 9 .text:00401129 lea ecx, [esp+413Ch+keyobj] .text:00401130 mov esi, eax .text:00401132 call f_keyobj_calc_mul_401730 ;
先前想著輸入的key用9做位移,做加法,幹麼呢...一直繞不清,後來重新看f_keyobj_key1_s2_401970,覺得是進位處理,一下子就靈光了,這是實現乘法運算(1024位的乘法,真實折騰,nb)。
這樣演算法也基本清楚了。
key*9*key*9*(...) => result
怎麼校驗的呢?
1. 計算結果長度必須是奇數
.text:00401154 call f_keyobj_curpos_4013A0 .text:00401159 and eax, 80000001h .text:0040115E jns short loc_401165 .text:00401160 dec eax .text:00401161 or eax, 0FFFFFFFEh .text:00401164 inc eax .text:00401165 .text:00401165 loc_401165: ; CODE XREF: _main+15Ej .text:00401165 cmp eax, 1
2. result[len/2] == key[0]
.text:00401175 call f_keyobj_curpos_4013A0 .text:0040117A sar eax, 1 .text:0040117C push eax .text:0040117D lea ecx, [esp+413Ch+keyobj] .text:00401184 call f_keyobj_check1_4013B0 .text:00401189 push 0 .text:0040118B lea ecx, [esp+413Ch+keyobj1] .text:00401192 mov edi, eax .text:00401194 call f_keyobj_check1_4013B0 .text:00401199 cmp edi, eax .text:0040119B lea ecx, [esp+4138h+keyobj1] .text:004011A2 jnz short loc_40121C
3. 高位部分和key相同(跳過比較那個位元組)
.text:004011D0 lea ecx, [esp+4144h+keyobj1] .text:004011D7 push esi .text:004011D8 push ecx .text:004011D9 lea ecx, [esp+414Ch+keyobj] .text:004011E0 call f_keyobj_check2_4013E0
4. 低位部分和key逆序(跳過比較那個位元組)
text:004011F6 lea edx, [esp+413Ch+keyobj1] .text:004011FD push eax .text:004011FE push 1 .text:00401200 push 0 .text:00401202 push edx .text:00401203 lea ecx, [esp+414Ch+keyobj] .text:0040120A call f_keyobj_check2_4013E0
感覺結果應該是這一個樣子的:
1234567->1234567654321 //中間因為長度折騰了好久,後面查了才知道這是迴文數,翻半天沒有什麼演算法,指令碼已經跑起來了
怎麼求逆呢?演算法不好,那就指令碼跑吧!
3. 指令碼跑
i = 11111111# while True: break is1 = str(i) is2_len = len(is1) is1 = is1[:is2_len-1] is2 = is1[::-1] k = i*9*9*i ks1 = '' ks = '' while True: #print i, k #break ks1 = str(k) lll = len(ks1)/2 if len(ks1) > 2*is2_len: #print 'long out - 1', i, len(ks1), 2*is2_len break if (is2_len + len(ks1))>1024: #print 'long out - 1', i, is2_len + len(ks1) break if (len(ks1)%2!=0) and (is1[0:1] == ks1[lll:lll+1]): print 'get -success1 > ', i, is1, k break if len(ks1)>1024: #print 'long out', i break k = k * i*9 ls2_len1 = is2_len-1 ks = ks1[:ls2_len1] if ((is1 == ks) and (is2 == ks1[(-1*ls2_len1):])): print 'get -success > ', i, is1, k print '' i += 1 if i % 10000000 == 0: print '...', i if i > 99999999999999999999: break
4. 總結
結果最後跑出來是
get -success > 12345679 1234567 12345678987654321
因為程式碼中處理字元存為數值是倒著的,所以key應該是97654321
相關文章
- [原創]看雪CTF2017第六題 Ericky-apk詳細writeup(從一個安卓新手的角度)2019-02-25TF2APK安卓
- 看雪CTF.TSRC 2018 團隊賽 第二題 『半加器』 解題思路2018-12-23
- 看雪.紐盾 KCTF 2019 Q2 | 第二題點評及解題思路2019-07-01
- 看雪·深信服 2021 KCTF 春季賽 | 第二題設計思路及解析2021-05-12
- 『看雪眾測』這次的眾測物件是看雪!2018-03-21物件
- [投票] 看雪2018年度最佳原創技術文章落誰家?你說了算!2019-01-01
- 我和看雪的故事 -- by 有毒2022-01-03
- 今天,看雪19歲!2019-01-13
- 看雪安卓容器2019-01-14安卓
- 《看雪隱私政策》2021-11-24
- 原創:springIOC介紹第二講2020-04-06Spring
- 2018 第二屆看雪安全開發者峰會 | 徵集贊助商2018-03-07
- 網鼎杯-writeup-第二場-babyRSA2018-08-24
- XSS挑戰第二期 Writeup2020-08-19
- 《看雪服務條款》2021-11-26
- 2021看雪KCTF逆向WP2021-05-21
- 演算法分析:看雪CTF2019的一道逆向題目2022-03-24演算法TF2
- 《看雪課程 免責宣告》2021-11-26
- 雪佛蘭創界何時上市 雪佛蘭創界上市時間2022-03-04
- 關於成立看雪CTF戰隊的公告2018-08-10
- 看雪CTF.TSRC 2018 團隊賽 第九題『諜戰』 解題思路2018-12-19
- 看雪CTF.TSRC 2018 團隊賽 第七題 『魔法森林』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第十四題『 你眼中的世界』 解題思路2018-12-29
- 看雪應急響應服務2018-03-09
- 《看雪招聘使用者協議》2021-11-26協議
- 2020 看雪SDC 議題預告 | Android WebView安全攻防指南20202020-10-21AndroidWebView
- 2020 看雪SDC議題回顧 | DexVmp最新進化:流式編碼2020-10-28
- 2020 看雪SDC議題回顧 | Android WebView安全攻防指南20202020-10-29AndroidWebView
- 2020 看雪SDC 議題預告 | DexVmp最新進化:流式編碼2020-10-13
- 看雪CTF.TSRC 2018 團隊賽 第一題 『初世紀』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第五題 『交響曲』 解題思路2018-12-23
- 看雪CTF.TSRC 2018 團隊賽 第六題 『追凶者也』 解題思路2018-12-23
- 看雪.紐盾 KCTF 2019 Q3 | 第四題點評及解題思路2019-09-29
- 看雪.紐盾 KCTF 2019 Q3 | 第七題點評及解題思路2019-09-30
- 看雪.紐盾 KCTF 2019 Q3 | 第一題點評及解題思路2019-09-25
- 看雪.紐盾 KCTF 2019 Q3 | 第六題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第八題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第九題點評及解題思路2019-10-08