--- tags: CS 2022 Fall, 程式安全 --- # [0x06] Reverse III slide: https://drive.google.com/drive/folders/1ZC6mdxIzcLsnGJ7j8S4emGsiY_LRNlKt?usp=sharing lecture: https://youtu.be/4-hgyiCV3ZA ### Exception #### `main` ![](https://i.imgur.com/cUmQpdn.png) 跳到 `main` `xrefs` #### `.pdata` ![](https://i.imgur.com/j20exdI.png) ![](https://i.imgur.com/CNJqA1U.png) ```cpp typedef struct _RUNTIME_FUNCTION { ULONG BeginAddress; ULONG EndAddress; ULONG UnwindData; } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; ``` 來到 `main` 的 `UnwindData` 指的地方 #### `_UNWIND_INFO` 先做好 `SCOPE_RECORD` <!-- ^r use image base as offset base --> ![](https://i.imgur.com/MrWoyp8.png) 然後放到 `main` 的 `UnwindData` 的下面 ![](https://i.imgur.com/NxIRKCJ.png) 回到 `main_0` #### `main_0` ![](https://i.imgur.com/W30ZZCG.png) ![](https://i.imgur.com/WlOZ79T.png) 看到 2 個處以 0 的地方 那邊會有 `exception` 分別標上 `exception1` `exception2` 可以發現對應到上面 `_UNWIND_INFO` 的`SCOPE_RECORD` <!-- https://learn.microsoft.com/en-us/cpp/cpp/try-except-statement?view=msvc-170 --> ```c // win32.h #define EXCEPTION_EXECUTE_HANDLER 1 #define EXCEPTION_CONTINUE_SEARCH 0 #define EXCEPTION_CONTINUE_EXECUTION -1 ``` ![](https://i.imgur.com/6PMQoPC.png) 由以上資料 我們知道 `exception` 發生後會跳到那邊了 來看看 `exception1` 發生後會跳到的位置 #### `exception1` `jumpTarget` ![](https://i.imgur.com/cSSWkDw.png) 大概就是進行下面這樣的操作 ```c for (int i = 0; i < 38; i++) { flag_inp[i] ^= key + i; } ``` 至於 `key` 的值我們可以往上看 於 `main` `unwine_info` 的 `_C_specific_handler` ![](https://i.imgur.com/3VcMZpd.png) 我們了解到了 `key` 會是 `rax` ![](https://i.imgur.com/6wnRJRJ.png) `rax` 的值則會是 `0xBE` 實際跑起來也確實如此 ![](https://i.imgur.com/dAdpQ2e.png) 接著來看 `exception2` 的 `jumpTarget` #### `exception2` `continue_exec` ![](https://i.imgur.com/S4D9XlZ.png) ![](https://i.imgur.com/VN3Xen0.png) 做的事情大概像這樣 ```c for (int i = 0; i < 38; i++) { flag_inp[i] += key + i; } ``` 其中 `key` 會是 `0xEF` ![](https://i.imgur.com/4WNmCtF.png) 之後就是正常的執行後面 `IDA` 有成功反編譯出來的的迴圈 ![](https://i.imgur.com/B0zrAUb.png) 所以之後把 `flag_enc` 拉出來解密即可 #### `Exception/sol.cpp` ```cpp for (int i = 0; i < 38; i++) { unsigned char key = 0xEF; flag_enc[i] -= key + i; } for (int i = 0; i < 38; i++) { unsigned char key = 0xBE; flag_enc[i] ^= key + i; } // output flag for (int i = 0; i < 38; i++) { cout << flag_enc[i]; } // output: FLAG{__C_specific_handler_is_hooked:O} ``` <!-- https://learn.microsoft.com/en-us/cpp/cpp/writing-an-exception-filter?view=msvc-170 --> <!-- https://learn.microsoft.com/en-us/windows/win32/debug/exception-handler-syntax --> ### [LAB] TLS Callback 從 `IDA` 可以看到城市一開始會有三次的 `TLS Callback` ![](https://i.imgur.com/vTe4DPs.png) #### `TlsCalllback1` 這個做的是把 `key` 進行 `funcs` 的操作 ![](https://i.imgur.com/vVrLdmL.png) #### `TlsCalllback2` 這個做的事把 `funcs[3]` 的東西往左 `rotate` ![](https://i.imgur.com/M5sFMwN.png) ![](https://i.imgur.com/U5HCNhz.png) #### `main` ![](https://i.imgur.com/i8KjZiM.png) 由於上面的 `TlsCallback` 所以 `main` 裏面用到的 `funcs` `key` 都會跟一開始的不同 只要把 `TlsCallback` 操作後的 `funcs` `key` 拉出來然後就可以解密了 ![](https://i.imgur.com/sbqf3hH.png) #### `TLS Callback/sol.cpp` ```cpp for (int i = 0; i < 58; i++) { flag_enc[i] -= key[i & 3]; (funcs[i % 3u])(flag_enc[i]); } // output for (int i = 0; i < 58; i++) { cout << (unsigned char)flag_enc[i]; } // output: FLAG{The_first_TLS_callback_function_is_called_two_times!} ``` ### [LAB] Meow #### `main` ![](https://i.imgur.com/BtNWNT4.png) 由 `:9,10` 可以知道 `flag` 長度可能為 39 進入 `sub_401604` #### `sub_401604` ![](https://i.imgur.com/ufa2oVy.png) 看到了一些 `GetProcAddress` 整理一下 之後把 `sub_401604` 命名為 `getProcAddresses` 回到 `main` ![](https://i.imgur.com/bIDh1NL.png) 進入 `sub_401884` #### `sub_401884` ![](https://i.imgur.com/hjqNoDF.png) 進入 `sub_401550` #### `sub_401550` ![](https://i.imgur.com/fToMaw8.png) 已經知道這是把一個 `exe` 解密了 把這個 `exe` 透過動態分析直接取出解密後的樣子 把函式取名為 `decrypt_exe` 回到上一層 #### `sub_401884` 修一修可以看出 ![](https://i.imgur.com/AWB3tfO.png) ![](https://i.imgur.com/EDO2mYq.png) `:21` 取得了`自己這個執行檔`的位置 `:22` 把`自己這個執行檔`跑起來 第6個參數 `4` 表示 `CREATE_SUSPENDED` 之後用 `exe` 把 `自己這個執行檔` 記憶體中的東西換掉 所以到了 `:53` 會恢復執行的東西變成 `exe` 回到 `main` ![](https://i.imgur.com/MuUhF3K.png) 進入 `sub_401723` #### `sub_401723` `exe` ##### `sub_401723` ![](https://i.imgur.com/TRXZZkn.png) ![](https://i.imgur.com/tmjezr7.png) ##### `exe` ![](https://i.imgur.com/Yuyemc1.png) 可以看到 `sub_401723:28` 會先把 `flag_inp` 傳給 `exe:19` 然後 `exe:21` 會對 `flag_inp` 做一些事 (`sub_401550`) 然後 `exe:24` 會把 `flag_inp` 回傳給 `sub_401723:32` 來看看 `sub_401550` 對 `flag_inp` 做了什麼事 #### `sub_401550` ![](https://i.imgur.com/geT42WU.png) 很明顯的 這裡做的事加密 `flag_inp` 回到 `main` ![](https://i.imgur.com/XTEwYkE.png) 進入 `sub_401C37` #### `sub_401C37` ![](https://i.imgur.com/oNNrKfn.png) 是很一般的 `check_flag` 所以這裡只要把 `flag_enc` 拉出來 把上面的加密倒過來做就會得到 `flag` ##### `Meow/sol.cpp` 這邊的部分我先把 `encrypt` 的部分複製貼上 然後 `copilot` 會幫忙寫好 `decrypt` 然後把 `check_flag` 的部分複製貼上 然後 `copilot` 會幫忙寫好 `output_flag` 這邊只要呼叫 `output_flag` 出來的就會是答案 ```cpp int main () { output_flag(); // output: FLAG{pr0c355_h0ll0w1ng_4nd_n4m3d_p1p35} } ``` 順帶議題 這題的 `key` 是 `meow` 的 `base64` ### [HW] dropper #### DIE ![](https://i.imgur.com/x34shWU.png) 看到有被包殼 先解殼 ```sh Ultimate Packer for eXecutables Copyright (C) 1996 - 2022 UPX 4.0.0 Markus Oberhumer, Laszlo Molnar & John Reiser Oct 28th 2022 File size Ratio Format Name -------------------- ------ ----------- ----------- 39424 <- 16896 42.86% win64/pe dropper_43741eb13c4a767e.exe Unpacked 1 file. ``` #### `main` ![](https://i.imgur.com/ESjEd7f.png) ![](https://i.imgur.com/om3vrsg.png) 動態分析發現跑道這會跳到其他的地方 來看看是什麼地方 ![](https://i.imgur.com/pGUhAIg.png) 原來跑去 `kernel32.sleep(0x9AEC800)` 了 所以這裡才會跑這麼久 把他 patch 成 `sleep(1)` 往下走到 `00007FF60FE92DC5` 然後觀察一下參數 結果 `flag` 就在裡面 `5: [rsp+20]` 那就直接把 `flag` 拿走咯 ~~ ![](https://i.imgur.com/t9Zkhr9.png) ``` 5: [rsp+20] 000001D60364D2C0 000001D60364D2C0 "FLAG{H3r3_U_G0_iT_Is_UR_flAg}" ```