# 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
```cpp
int __cdecl main(int argc, const char **argv, const char **envp)
{
DWORD TickCount; // [esp+0h] [ebp-14h]
unsigned int v5; // [esp+8h] [ebp-Ch]
TickCount = GetTickCount();
Sleep(120000u);
v5 = GetTickCount() - TickCount;
if ( v5 < 119950 || v5 > 120050 )
ExitProcess(0);
((void (*)(void))loc_401AE0)();
return 0;
}
```
:::
:::spoiler IDA loc_401AE0
```
.text:00401AE0 loc_401AE0: ; CODE XREF: _main:loc_4014AB↑p
.text:00401AE0 push ebp
.text:00401AE1 mov ebp, esp
.text:00401AE3 lea esi, aHelloHacker ; "Hello Hacker"
.text:00401AE9 mov al, 48h ; 'H'
.text:00401AEB cmp [esi], al
.text:00401AED jz short loc_401AF0
.text:00401AED
.text:00401AED ; ---------------------------------------------------------------------------
.text:00401AEF db 0E8h
.text:00401AF0 ; ---------------------------------------------------------------------------
.text:00401AF0
.text:00401AF0 loc_401AF0: ; CODE XREF: .text:00401AED↑j
.text:00401AF0 nop word ptr [eax+eax+00000000h]
.text:00401AF9 jmp short loc_401B01
.text:00401AF9
.text:00401AF9 ; ---------------------------------------------------------------------------
.text:00401AFB db 48h ; H
.text:00401AFC db 65h ; e
.text:00401AFD db 6Ch ; l
.text:00401AFE db 6Ch ; l
.text:00401AFF db 6Fh ; o
.text:00401B00 db 0
.text:00401B01 ; ---------------------------------------------------------------------------
.text:00401B01
.text:00401B01 loc_401B01: ; CODE XREF: .text:00401AF9↑j
.text:00401B01 jmp short loc_401B0E
.text:00401B01
.text:00401B01 ; ---------------------------------------------------------------------------
.text:00401B03 db 0E8h
.text:00401B04 db 66h ; f
.text:00401B05 db 0Fh
.text:00401B06 db 1Fh
.text:00401B07 db 84h
.text:00401B08 db 0
.text:00401B09 db 0
.text:00401B0A db 0
.text:00401B0B db 0
.text:00401B0C byte_401B0C db 0
.text:00401B0D db 0E8h
.text:00401B0E ; ---------------------------------------------------------------------------
.text:00401B0E
.text:00401B0E loc_401B0E: ; CODE XREF: .text:loc_401B01↑j
.text:00401B0E jz short loc_401B13
.text:00401B0E
.text:00401B10 jnz short loc_401B13
.text:00401B10
.text:00401B10 ; ---------------------------------------------------------------------------
.text:00401B12 db 0E8h
.text:00401B13 ; ---------------------------------------------------------------------------
.text:00401B13
.text:00401B13 loc_401B13: ; CODE XREF: .text:loc_401B0E↑j
.text:00401B13 ; .text:00401B10↑j
.text:00401B13 push 1
.text:00401B15 jmp sub_401220
```
:::
:::spoiler IDA notify_debugger
```cpp
void __cdecl __noreturn notify_debugger()
{
if ( IsDebuggerPresent() )
ExitProcess(1u);
__debugbreak();
}
```
:::
:::spoiler IDA sub_401220
```
.text:00401220 sub_401220 proc near ; CODE XREF: .text:00401B15↓j
.text:00401220
.text:00401220 ms_exc= CPPEH_RECORD ptr -18h
.text:00401220
.text:00401220 push ebp
.text:00401221 mov ebp, esp
.text:00401223 push 0FFFFFFFEh
.text:00401225 push offset stru_403B40
.text:0040122A push offset ?notify_debugger@@YAXABUtagEXCEPTION_VISUALCPP_DEBUG_INFO@@@Z_SEH
.text:0040122F mov eax, large fs:0
.text:00401235 push eax
.text:00401236 sub esp, 8
.text:00401239 push ebx
.text:0040123A push esi
.text:0040123B push edi
.text:0040123C mov eax, ___security_cookie
.text:00401241 xor [ebp+ms_exc.registration.ScopeTable], eax
.text:00401244 xor eax, ebp
.text:00401246 push eax
.text:00401247 lea eax, [ebp+ms_exc.registration]
.text:0040124A mov large fs:0, eax
.text:00401250 mov [ebp+ms_exc.old_esp], esp
.text:00401253 mov [ebp+ms_exc.registration.TryLevel], 0
.text:0040125A call sub_401170
.text:0040125A
.text:0040125F ; ---------------------------------------------------------------------------
.text:0040125F test al, al
.text:00401261 jz short loc_40126B
.text:00401261
.text:00401263 push 1 ; uExitCode
.text:00401265 call ds:ExitProcess
.text:00401265
.text:0040126B ; ---------------------------------------------------------------------------
.text:0040126B
.text:0040126B loc_40126B: ; CODE XREF: sub_401220+41↑j
.text:0040126B int 3 ; Trap to Debugger
.text:0040126B
.text:0040126C ; ---------------------------------------------------------------------------
.text:0040126C jmp short loc_40127C
.text:0040126C
.text:0040126E ; ---------------------------------------------------------------------------
.text:0040126E
.text:0040126E loc_40126E: ; DATA XREF: .rdata:stru_403B40↓o
.text:0040126E mov eax, 1
.text:00401273 retn
.text:00401273
.text:00401274 ; ---------------------------------------------------------------------------
.text:00401274
.text:00401274 loc_401274: ; DATA XREF: .rdata:stru_403B40↓o
.text:00401274 mov esp, [ebp+ms_exc.old_esp]
.text:00401277 call ?notify_debugger@@YAXABUtagEXCEPTION_VISUALCPP_DEBUG_INFO@@@Z ; notify_debugger(tagEXCEPTION_VISUALCPP_DEBUG_INFO const &)
.text:00401277
.text:0040127C ; ---------------------------------------------------------------------------
.text:0040127C
.text:0040127C loc_40127C: ; CODE XREF: sub_401220+4C↑j
.text:0040127C mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00401283 mov ecx, [ebp+ms_exc.registration.Next]
.text:00401286 mov large fs:0, ecx
.text:0040128D pop ecx
.text:0040128E pop edi
.text:0040128F pop esi
.text:00401290 pop ebx
.text:00401291 mov esp, ebp
.text:00401293 pop ebp
.text:00401294 retn
.text:00401294
.text:00401294 sub_401220 endp
```
:::
:::spoiler IDA sub_401170
```
.text:00401170 sub_401170 proc near ; CODE XREF: sub_401220+3A↓p
.text:00401170
.text:00401170 var_1= byte ptr -1
.text:00401170
.text:00401170 push ebp
.text:00401171 mov ebp, esp
.text:00401173 push ecx
.text:00401174 mov [ebp+var_1], 1
.text:00401178 push offset TopLevelExceptionFilter ; lpTopLevelExceptionFilter
.text:0040117D call ds:SetUnhandledExceptionFilter
.text:0040117D
.text:00401183 ; ---------------------------------------------------------------------------
.text:00401183 int 3 ; Trap to Debugger
.text:00401183
.text:00401183 sub_401170 endp
.text:00401183
.text:00401184 ; ---------------------------------------------------------------------------
.text:00401184 jmp short loc_40118A
.text:00401184
.text:00401186 ; ---------------------------------------------------------------------------
.text:00401186 mov byte ptr [ebp-1], 0
.text:00401186
.text:0040118A
.text:0040118A loc_40118A: ; CODE XREF: .text:00401184↑j
.text:0040118A mov al, [ebp-1]
.text:0040118D mov esp, ebp
.text:0040118F pop ebp
.text:00401190 retn
.text:00401190
.text:00401190 ; ---------------------------------------------------------------------------
.text:00401191 align 10h
.text:004011A0
.text:004011A0 ; =============== S U B R O U T I N E =======================================
.text:004011A0
.text:004011A0 ; Attributes: library function noreturn static bp-based frame
.text:004011A0
.text:004011A0 ; void __cdecl __noreturn notify_debugger()
.text:004011A0 ?notify_debugger@@YAXABUtagEXCEPTION_VISUALCPP_DEBUG_INFO@@@Z proc near
.text:004011A0 ; CODE XREF: sub_401220+57↓p
.text:004011A0
.text:004011A0 ms_exc= CPPEH_RECORD ptr -18h
.text:004011A0
.text:004011A0 ; FUNCTION CHUNK AT .text:00401206 SIZE 0000000E BYTES
.text:004011A0
.text:004011A0 push ebp
.text:004011A1 mov ebp, esp
.text:004011A3 push 0FFFFFFFEh
.text:004011A5 push offset stru_403B20
.text:004011AA push offset ?notify_debugger@@YAXABUtagEXCEPTION_VISUALCPP_DEBUG_INFO@@@Z_SEH
.text:004011AF mov eax, large fs:0
.text:004011B5 push eax
.text:004011B6 sub esp, 8
.text:004011B9 push ebx
.text:004011BA push esi
.text:004011BB push edi
.text:004011BC mov eax, ___security_cookie
.text:004011C1 xor [ebp+ms_exc.registration.ScopeTable], eax
.text:004011C4 xor eax, ebp
.text:004011C6 push eax
.text:004011C7 lea eax, [ebp+ms_exc.registration]
.text:004011CA mov large fs:0, eax
.text:004011D0 mov [ebp+ms_exc.old_esp], esp
.text:004011D3 mov [ebp+ms_exc.registration.TryLevel], 0
.text:004011DA call ds:IsDebuggerPresent
.text:004011DA
.text:004011E0 test eax, eax
.text:004011E2 jz short loc_4011EC
.text:004011E2
.text:004011E4 push 1 ; uExitCode
.text:004011E6 call ds:ExitProcess
.text:004011E6
.text:004011EC ; ---------------------------------------------------------------------------
.text:004011EC
.text:004011EC loc_4011EC: ; CODE XREF: notify_debugger(tagEXCEPTION_VISUALCPP_DEBUG_INFO const &)+42↑j
.text:004011EC int 3 ; Trap to Debugger
.text:004011EC
.text:004011EC ?notify_debugger@@YAXABUtagEXCEPTION_VISUALCPP_DEBUG_INFO@@@Z endp
.text:004011EC
.text:004011ED ; ---------------------------------------------------------------------------
.text:004011ED mov dword ptr [ebp-4], 0FFFFFFFEh
.text:004011F4 mov ecx, [ebp-10h]
.text:004011F7 mov large fs:0, ecx
.text:004011FE pop ecx
.text:004011FF pop edi
.text:00401200 pop esi
.text:00401201 pop ebx
.text:00401202 mov esp, ebp
.text:00401204 pop ebp
.text:00401205 retn
.text:00401205
.text:00401206 ; ---------------------------------------------------------------------------
.text:00401206 ; START OF FUNCTION CHUNK FOR notify_debugger(tagEXCEPTION_VISUALCPP_DEBUG_INFO const &)
.text:00401206
.text:00401206 loc_401206: ; DATA XREF: .rdata:stru_403B20↓o
.text:00401206 mov eax, 1
.text:0040120B retn
.text:0040120B
.text:0040120C ; ---------------------------------------------------------------------------
.text:0040120C
.text:0040120C loc_40120C: ; DATA XREF: .rdata:stru_403B20↓o
.text:0040120C mov esp, [ebp+ms_exc.old_esp]
.text:0040120F call InputFlag_Check
```
:::
:::spoiler IDA InputFlag_Check
```cpp
void __noreturn InputFlag_Check()
{
flag_info flag_info; // [esp+0h] [ebp-408h] BYREF
printf(flag_info.Hello, flag_info.nonono);
memset(&flag_info, 0, sizeof(flag_info));
scanf(std::cin, (int)&flag_info);
check((int)&flag_info, strlen((const char *)&flag_info));
printf(flag_info.Hello, flag_info.nonono);
ExitProcess(0);
}
```
:::
:::spoiler IDA check
```cpp
void __fastcall check(char *input, unsigned int len)
{
unsigned int iv; // ebx
unsigned int block; // edi
int mem_addr_gap; // ecx
unsigned __int8 cipher; // cl
char *input_cipher_cp; // ecx
char *cipher_flag_cp; // edx
bool v9; // cf
unsigned int i; // esi
int dot; // [esp+0h] [ebp-41Ch]
int new_line; // [esp+4h] [ebp-418h]
int mem_addr_gap_cp; // [esp+Ch] [ebp-410h]
char output[1028]; // [esp+10h] [ebp-40Ch] BYREF
iv = 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 );
}
printf(dot, new_line);
input_cipher_cp = output;
cipher_flag_cp = cipher_flag;
v9 = len < 4;
for ( i = len - 4; !v9; i -= 4 )
{
if ( *(_DWORD *)input_cipher_cp != *(_DWORD *)cipher_flag_cp )
break;
input_cipher_cp += 4;
cipher_flag_cp += 4;
v9 = i < 4;
}
}
```
:::
## Recon
這一題沒有那麼難,難的是怎麼用工具寫出來,本來想要直接用z3或angr直接噴出來,但是不知道為啥就完全沒有奇蹟發生,所以還是硬幹
首先,先用ida看主要的流程,會發現有很多jmp系列的位址都跑掉了,此時就要修復,就是data(d)和code( c)之間交錯使用,並且把那些奇怪的data byte換成nop,修把patch好的部分,就會呈現上面的source code這樣
1. 一樣由上而下,首先會先進到sleep睡眠兩分鐘,並且判斷進到下一行的時候,時間是否在範圍內,這也是time based的anti debugging手法,這部分可以動態直接patch掉
:::spoiler Patch Sleep Function Result


:::
2. 接著會進到loc_401AE0,這部分應該是一個function但不知道為甚麼IDA翻譯不出來,不過看了一下source code也是蠻簡單的,就是一直跳到==sub_401220==,這個在動態也可以patch
:::spoiler Patch Anti-Debug Result

:::
3. ==sub_401220==主要是在其他anti debug的部分,具體怎麼做不是很清楚,只知道大概是和exception handler有關係,不過我在開了scylla hide之後沒有出現甚麼特別的事情

4. 接著會進到==sub_401170==,這一段蠻重要的,就是處理一些Exception Handler的事情,然後莫名其妙的會進到0x40120F中的==InputFlag_Check==,中間的一些操作可能是被scylla hide擋掉了,不過中間也確實有檢察==IsDebuggerPresent==這東西
5. 到了這邊就可以大膽猜測一些常見的操作,諸如scanf或是printf的function,接著我們會進到check這個function,也就是實際把我們的輸入,進行cipher操作後和內部的data bytes進行對比的過程
6. 所以到了這邊一切都很明瞭了,主要的code如下
```cpp
iv = 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的解法都沒辦法實作出來,不確定是甚麼原因,但有機會還是會想解看看,所以先放著看看
```python
from string import *
from tqdm import trange
def ror(n, rotations, width):
if rotations.bit_length() < 32:
rotations = '0' * (32 - rotations.bit_length()) + bin(rotations)[2:]
tmp = rotations[-width:] + rotations[:-width]
return int(tmp, 2)
tmp = int(bin(rotations << (n-width))[-n:-n+width] + bin(rotations >> width)[2:], 2)
return tmp
candidates = printable
targets = [0xED, 0x03, 0x81, 0x69, 0x7B, 0x84, 0xA6, 0xA0, 0x5B, 0x2B, 0xB6, 0xE6, 0x5C, 0x57, 0xC9, 0x99, 0xE8, 0xB2, 0x20, 0x72, 0x38, 0xF1, 0x58]
len = len(targets)
iv = 0xE0C92EAB
flag = ''
for byte in trange(len):
iv_xor = int(hex(iv)[2:][-2:], 16)
for candidate in candidates:
cipher = iv_xor ^ ord(candidate)
if cipher == targets[byte]:
flag += candidate
iv = ror(32, iv, 3)
iv = len + (cipher ^ iv) - byte
break
# print(flag)
print(flag)
```
:::spoiler z3 solver
```python
from z3 import *
target = [0xED, 0x03, 0x81, 0x69, 0x7B, 0x84, 0xA6, 0xA0, 0x5B, 0x2B, 0xB6, 0xE6, 0x5C, 0x57, 0xC9, 0x99, 0xE8, 0xB2, 0x20, 0x72, 0x38, 0xF1, 0x58]
len = len(target)
iv = 0xE0C92EAB
# 起手式 - 開一個Solver
s = Solver()
# 建立符號 - 以此HW來說就是建立23個符號對應每一個flag字元
bvs = [BitVec(f'bt_{i}', 8) for i in range(len)]
# 加上constraint - 以此lab來說每一個flag字元都應該限制在空白到0x7f之間
for bv in bvs:
s.add(And(bv >= 0x20, bv <= 0x7f))
for i in range(len):
iv = f'int(hex(iv)[2:][-2:], 16)'
bvs_formula = f'(({eval(iv)}) ^ bvs[{i}])'
s.add(eval(bvs_formula) == target[i])
RotateRight = f'int(bin({iv_formula} << (32-3))[-32:-29] + bin({iv_formula} >> 3)[2:], 2)'
iv_formula = f'{int(hex(iv)[2:][-2:], 16)}'
iv_formula = f'{len} + ({iv_formula} ^ {iv}) - {i}'
print(f'iv_formula = {iv_formula}')
# 如果有解的話就會做以下操作
if s.check() == sat:
print('Find ~~~')
print(s.model())
flag = ""
for bv in bvs:
flag += chr(s.model()[bv].as_long())
print(flag)
```
:::
:::spoiler angr solver
```python
import angr
import claripy
# 建立一個project
root = 'Reverse/HW3/Evil FlagChecker/'
proj = angr.Project(root + 'test.exe')
# 建立Claripy Symbol
sym_arg = claripy.BVS('sym_arg', 8 * 23) # 就像z3一樣要建立symbol
# 建立初始的state
state = proj.factory.entry_state(stdin=sym_arg)
simgr = proj.factory.simulation_manager(state)
# 有了proj/symbol/initial state之後就要開始讓他跑起來
# simgr.explore(find = lambda s: b'Good!' in s.posix.dumps(1))
simgr.explore(find = lambda s: b"Good!" in s.posix.dumps(1), avoid=lambda s: b"No no no..." in s.posix.dumps(1))
if len(simgr.found) > 0:
print(simgr.found[0].solver.eval(sym_arg, cast_to=bytes))
else:
print("NONONONO")
```
:::
Flag: `FLAG{jmp1ng_a1l_ar0und}`