Simple Reverse - 0x30(2023 HW - Evil FlagChecker)
Background
Anti Disassembly - 這一部分可以看一下碩一修的malware reverse的anti disassembly的修復(就是d和c的交錯使用) Anti Debugging - 首推scylla hide
Source code
:::spoiler IDA main
1 |
|
::: :::spoiler IDA loc_401AE0
1 |
|
::: :::spoiler IDA notify_debugger
1 |
|
::: :::spoiler IDA sub_401220
1 |
|
::: :::spoiler IDA sub_401170
1 |
|
::: :::spoiler IDA InputFlag_Check
1 |
|
::: :::spoiler IDA check
1 |
|
:::
Recon
這一題沒有那麼難,難的是怎麼用工具寫出來,本來想要直接用z3或angr直接噴出來,但是不知道為啥就完全沒有奇蹟發生,所以還是硬幹
首先,先用ida看主要的流程,會發現有很多jmp系列的位址都跑掉了,此時就要修復,就是data(d)和code( c)之間交錯使用,並且把那些奇怪的data byte換成nop,修把patch好的部分,就會呈現上面的source code這樣
- 一樣由上而下,首先會先進到sleep睡眠兩分鐘,並且判斷進到下一行的時候,時間是否在範圍內,這也是time based的anti debugging手法,這部分可以動態直接patch掉
:::spoiler Patch Sleep Function Result
:::
- 接著會進到loc_401AE0,這部分應該是一個function但不知道為甚麼IDA翻譯不出來,不過看了一下source code也是蠻簡單的,就是一直跳到==sub_401220==,這個在動態也可以patch
:::spoiler Patch Anti-Debug Result
:::
- ==sub_401220==主要是在其他anti debug的部分,具體怎麼做不是很清楚,只知道大概是和exception handler有關係,不過我在開了scylla hide之後沒有出現甚麼特別的事情
- 接著會進到==sub_401170==,這一段蠻重要的,就是處理一些Exception Handler的事情,然後莫名其妙的會進到0x40120F中的==InputFlag_Check==,中間的一些操作可能是被scylla hide擋掉了,不過中間也確實有檢察==IsDebuggerPresent==這東西
- 到了這邊就可以大膽猜測一些常見的操作,諸如scanf或是printf的function,接著我們會進到check這個function,也就是實際把我們的輸入,進行cipher操作後和內部的data bytes進行對比的過程
- 所以到了這邊一切都很明瞭了,主要的code如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19iv = 0xE0C92EAB; memset(output, 0, 0x400u); block = 0; if ( len ) { mem_addr_gap = input - output; // v5代表我們輸入的flag的位址和他memset的位址的差距,從這支檔案為例就是0x418 mem_addr_gap_cp = input - output; do { cipher = iv ^ output[block + mem_addr_gap]; output[block] = cipher; iv = len + (cipher ^ __ROR4__(iv, 3)) - block; Sleep(1000u); printf(dot, new_line); mem_addr_gap = mem_addr_gap_cp; ++block; } while ( block < len ); }
其中,
output[block + mem_addr_gap]
其實就是我們的input,所以exploit的邏輯就是用brute force,把所有可能都丟一遍,然後嘗試去對比有沒有和built-in cipher bytes一樣,BTW,len
代表我們輸入的長度,合理猜測和built-in cipher bytes的長度一樣,也就是23個char,中間的sleep在動態也可以patch掉,就看自己方便 :::danger 在寫ROR的實作時有一個非常重要的重點要注意,也就是最後一個右旋的bit如果是0,在下一次右旋時會被忽略,也就是那個bit會消失,被當成0x的一部分,舉例來說,0x111001,右旋兩次後變成0x011110,但是最左邊的0會被當成0x的一部分,所以下一次再右旋兩次的結果會變成0x10111而不是0x100111,所以我的作法是在每次右旋之前都檢查bit length是不是都是32 bits,如果有少就padding 0在最左邊 :::
Exploit
另外說明一下,z3或angr的解法都沒辦法實作出來,不確定是甚麼原因,但有機會還是會想解看看,所以先放著看看
1 |
|
:::spoiler z3 solver
1 |
|
::: :::spoiler angr solver
1 |
|
:::
Flag: FLAG{jmp1ng_a1l_ar0und}