吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|www.2dw61quu.icu

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 2463|回復: 31

[CTF] 2019百越杯線上賽逆向endless_nightmare題目分析

[復制鏈接]
baymax0day 發表于 2019-11-21 11:24
本帖最后由 baymax0day 于 2019-11-21 22:04 編輯

[md]# 2019百越杯線上賽逆向endless_nightmare題目分析

上上周末的線上賽,一開始本來想的是想跟著題目走一下完整的流程的,發現亂七八糟的東西太多,就先放一下解題過程吧~ 主要是想請教一下表哥們前面的東西是如何設計的,主要是想學習一下`混淆機制`和`生成機制`,奈何自己太菜了,分析不出來,只想看解題過程的,移步[解題過程](###解題過程)。。 大白一枚,務必輕噴~

## 題目簡述

#### 基礎分析

1. 題目運行起來會要求輸入username和passwd,除了no以外沒有任何提示;
2. IDA打開搜索字符串,得出幾個可能比較重要的字符串
   

字符串搜索

字符串搜索

字符串搜索

字符串搜索


1. ida查看導入表,幾個比較有意思的函數,也是讓我最開始分析比較`頭冷`的(因為不知道是干嘛用的);a. 此處的互斥體不是讓程序互斥的,而是增加了一種加密方式?、b. 原子操作 居然會產生ERROR_FILE_NOT_FOUND錯誤,這個學到了

導入表

導入表


4. OD載入程序,最開始因為TLS斷下了,說明程序中可能用到了多線程(雖然解題的時候沒有用到,但是前面看到了就想分析一下來著),并且程序出錯或者結束的時候,上次錯誤居然是0x2錯誤,我還以為需要什么文件,但是看了一圈也沒發現文件操作,于是就學到了上面的原子會產生0x2錯誤
   

OD

OD


#### 反編譯查看

1. 直接指出了main函數,并且邏輯相對清晰,輸出username和passwd,兩個都接收完之后都進行了sub_487300函數進行處理
   

main函數

main函數


2. 接下來進入了sub_407960函數,ida反編譯之后函數太長,大約1000多行,這也是難住大多數人的地方吧應該,也是我想請教的`第一個地方`, 類似這種的ida混淆的實現方式,有文章推薦么?如果有的話,交流一下。。

ida_func.png

3. 于是轉入dbg進行分析,到調用sub_407960函數的地方,棧上有輸入的username和passwd作為參數,修改函數的返回值eax,程序直接輸出flag{我們輸入的passwd},即可以簡單認為這個函數是我們需要尋找的`校驗函數`、

校驗函數

校驗函數


4. 程序中經常調用CreateMutexA,FindAtomA等函數的函數sub_4188A0? 同樣sub_418120這個函數會調用上面的函數,并且傳入use_fc_key等字符串,我拿類似的字符串度搜了一下,發現別的程序中也有過這個東西,然后再在字符串中發現了多線程的包winpthreads-git20141130-src,所以上面的問題就是我第一次見tdm的多線程,難免大驚小怪,勿怪我~

atom

atom

多線程

多線程


5. 因為想學習加密和混淆機制來著,最開始嘗試過從末尾,即返回值開始回溯(`這里贊一下x64dbg的跟蹤機制,太帥了`),尋找校驗算法,這種思路糾結了好久但是失敗了,路徑太多了,所以就先解題吧。。。

## 解題過程

題目比較簡單,主要思路就是利用軟硬訪問斷點,對輸入的數據進行監控,即可追溯到對輸入的數據進行校驗的關鍵代碼處

#### 尋找 校驗一

   1. 接上得知sub_407960是關鍵的檢驗函數,并且在棧上有我們的輸入;
   2. 因此,第一步就是對這兩個地址下斷,發現第一此訪問我們的輸入是strlen函數就是求長度,說明程序應該有用到這個長度;
   3. 再對這兩個長度的地址下斷,來到兩個校驗長度的函數, 在這個地方走了個捷徑,在知道這個地方是校驗user的長度后,往上翻了一下,發現兩個類似,估計是passwd的長度,直接下斷分析。
   4. 最終得出user的長度得是0x6,passwd的長度得是0x24

user_len

user_len

pass_len

pass_len


#### 尋找username
   
    1. 我不會告訴你,username也是我在向上翻的過程中發現的
    2. 就算猜不出來也沒關系,因為我們輸入的username地址處下了斷,因此程序會在以下校驗處斷下

username1.png

user2

user2

   
    3. 匯編比較清晰,可以直接看出校驗方式為:
   
    u[0] * u[1] == 0x3250
    u[1] - u[2] == 0x2f
    u[2] ^ u[3] == 0x33
    u[3] + u[4] == 0xe6
    u[4] % u[5] == 0x33
    u[5] | u[0] == u[0]

   4. 接下來就是z3一把梭,得出用戶名, 代碼如下(一個小小的遞歸),同時我發現使用向量求結果的時候,結果居然是依據定義的向量位數來的??比如u[0]、u[1]定義了8位,但是要求的結果是0x3250,第一次求出來的居然是1和80,原因就是取了0x3250的低8位0x50,
```python
def get_user():

    flag = list(string.punctuation + string.ascii_letters + string.digits)

    solver = Solver()

    def reFind(Res):
        list_chr = [(r.name(), chr( int(Res[r].as_string()))) for r in Res.decls()]
        for r in Res.decls():
            r_chr = chr( int(Res[r].as_string()))
            if r_chr not in flag:
                t = int( Res[a].as_string() )
                solver.add( a!=t ) # 增加條件
                if solver.check() == unsat:
                    return
                new_Res = solver.model()
                return reFind(new_Res)

        tmp_a, tmp_b = int(Res[a].as_string()), int(Res.as_string())
        if(tmp_a * tmp_b != 0x3250):
            t = int(Res[a].as_string())
            solver.add(a != t) # 說明此時的值并非正確, 重新計算
            if solver.check() == unsat:
                return
            new_Res = solver.model()
            return reFind(new_Res)
        
        return list_chr

    a, b, c, d, e, f = BitVecs("a b c d e f", 8)

    solver.add(a * b == 0x3250)
    solver.add(b - c == 0x2f)
    solver.add(c ^ d == 0x33)
    solver.add(d + e == 0xe6)
    solver.add(e % f == 0x33)
    solver.add(f | a == a)
    print(solver.check())
    Res = solver.model()

    print(reFind(Res))
```

#### 尋找passwd

   因為沒有找到生成算法,因此這個部分就相對簡單了。
   
   1. 同樣依照之前的,向上翻,當然不往上尋找也是可以的,因為有訪問斷點,所以會斷下的;只不過向上翻可以直接看到校驗規則,由于匯編代碼很簡單可以直接得出計算規則:passwd 在 0x8、0xd、0x12、0x17處得有'-'字符
   
   

passwd

passwd


   2. 接著對輸入再進行一次比對'-'的操作,在401e50的地方會因訪問斷點斷下,猜測是去除‘-‘字符;單步跟蹤會在棧上看到去除'-'的字符串;

    del_.png
    trace_del.png

   3. 對新產生的即去除’-‘的字符串重新下訪問斷點,最后會在最后的校驗函數斷下;簡單分析發現函數在和輸入的passwd進行對比,拿到對比的字符,就是flag
   
    end.png

```python
def get_pass():

    tmp_p = list(string.ascii_letters[:0x24])
    tmp_p[0x8] = tmp_p[0xD] = tmp_p[0x12] = tmp_p[0x17] = chr(0x2d)
    print("".join(tmp_p))

    passwd = "89ab3210fedc98ba54761032d8badcfe"
    list_p = list(passwd)
    list_p.insert(0x8, '-')
    list_p.insert(0xD, '-')
    list_p.insert(0x12, '-')
    list_p.insert(0x17, '-')
    print("".join(list_p))
```



題目藍奏云鏈接:https://www.lanzous.com/i7hi9cd

免費評分

參與人數 11威望 +1 吾愛幣 +16 熱心值 +10 收起 理由
NanKeYM + 1 + 1 樓主,有線下的wp嗎
不掛科夫斯基 + 1 我很贊同!
CLX + 1 + 1 用心討論,共獲提升!
笙若 + 1 + 1 [email protected]
gaosld + 1 + 1 用心討論,共獲提升!
ArnoD + 1 + 1 用心討論,共獲提升!
三胖胖胖 + 1 + 1 用心討論,共獲提升!
Hmily + 1 + 7 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
劉樣andholiday + 1 + 1 用心討論,共獲提升!
smile5 + 1 + 1 熱心回復!
烏龍小八戒 + 1 [email protected]

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

liltn 發表于 2019-11-21 15:37
感謝樓主分享!
dongfang155 發表于 2019-11-21 13:00
混淆部分是一堆while1嗎 如果是的話 這是OLLVM的控制流平坦化,可以用去平坦化工具deflat.py來去除一些的
AIctiy 發表于 2019-11-21 11:36
txq18363001227 發表于 2019-11-21 12:07
不錯,謝謝分享
2Burhero 發表于 2019-11-21 12:12
感謝分享
處女-大龍貓 發表于 2019-11-21 13:08
支持,支持,收藏學習
Ginobili 發表于 2019-11-21 14:52
老哥題目文件能不能發一下
劉樣andholiday 發表于 2019-11-21 16:47
樓主有人牛,感謝分享啦
CORLEONEY 發表于 2019-11-21 19:24
網上原題,太氣人了
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:禁止回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2019-12-17 12:05

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表
快手网红