# 0x07. Advanced topic - Anti-Debug / Anti-Analysis [toc] ## Why? 惡意程式開發軍火商花費大量時間與資源辛苦開發出來的產品 如果輕輕鬆鬆就被看出來在做什麼那不是很虧嗎? <br> ![](https://i.imgur.com/0mj1wwN.gif) <br> 所以軍火商會使用一些Anti-Debug/Analysis的方法來增加分析的難度,想隱藏真正最重要的部分 惡意程式分析常用的就是三個工具:Sandbox / VM / Debugger 所以自然成為軍火商的目標 <br> 其實上一篇中的Traffic Obfuscation也算是Anti-Analysis的一種,但本篇介紹的會偏向偵測方法 <br> ## Anti-Sandbox 既然要對抗Sandbox,那必須找到Sandbox的特徵,下面列出一些容易被抓到的小辮子 1. Sandbox能分析的時間 2. Sandbox用來分析的主機 3. 重開機 分析時間,是Sandbox的致命傷,畢竟作為一種Service,Sandbox不能讓分析主機被佔用太久,通常都是10分鐘以內 軍火商看準這點,在執行開始後,先Sleep,或者塞一些無關緊要的無限迴圈讓他空轉15分鐘甚至更長時間 對應API: `Sleep()` --- 針對用來分析的主機,有些特徵是很容易被抓到的 1. CPU - 在VM內,CPU ID通常是 VMware 或是 VirtualBox 專屬的CPU ID 2. RAM - 通常分析VM不會給太多RAM,跟平常吃資源怪獸的Windows OS不太一樣 3. Username - 有可能會像是Cuckoo-VM/Sandbox這種嫌疑很大的使用者帳戶 4. 針對其他資源做檢查,系統時間/儲存空間/Registry/顯示器畫素..... 對應API: `GetUserName() GetComputerName() GetSystemMetrics() GetSysmtemInfo()` 簡而言之,相當多東西可以找碴 不過利用API這樣的check還是可以透過Hooking來抵擋(下一章會提到) --- 讓系統重開機,Sandbox直接無法再繼續分析而中斷,進而達到對抗Sandbox的目的 <br> ## Anti-VM VM的特徵,不外乎就像上面說的,檢查CPU/RAM/Username,並且加上一些很容易出現在VM中的process,這邊舉一些例子 >wireshark.exe >fiddler.exe >x32dbg.exe >x64dbg.exe >processhacker.exe >processexplorer.exe 或是一些其他VM常駐程式,要是一不小心很容易就會被抓包 下面列出常用來枚舉所有進行中process的API ``` CreateToolHelp32Snapshot() - 列出所有正在跑的process Process32First() - 顯示第一個剛剛列出的process Process32Next() - 顯示下一個剛剛列出的process ``` [這邊](https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes) 微軟直接教你怎麼找出所有running中的process 簡單的psuedo code大概是這樣 ``` analysis_program_list = ['wireshark.exe','fiddler.exe','processhacker.exe'] All_Process = CreateToolHelp32Snapshot() 1st_process = Process32First() if 1st_process in analysis_program_list: exit() while(Process32Next() != None){ if Process32Next() in analysis_program_list: exit() } real_malicious_program() ``` <br> ## Anti-Debugger Anti-Debugger一直是相對熱門的話題,常常又會看到有神人蹦出新的Anti-Debug招數 在我看來根本是神仙打架 ![](https://i.imgur.com/XxM5o45.png =60%x) <br> 接下來,帶大家看看一些常見基本的招數 --- ### Windows API **IsDebuggerPresent()**: 幾乎在每隻惡意程式裡面都會看到,骨灰級用法 實際上是Check PEB 中 BeingDebugged Flag > Determines whether the calling process is being debugged by a user-mode debugger. > > Return value > If the current process is running in the context of a debugger, the return value is nonzero. > If the current process is not running in the context of a debugger, the return value is zero. > 對抗方式:ScyllaHide 下列為PEB(Process Environment Block) 的架構 ``` typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; <-檢查這裡 BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved4[104]; PVOID Reserved5[52]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved6[128]; PVOID Reserved7[1]; ULONG SessionId; } PEB, *PPEB; ``` <br> **CheckRemoteDebuggerPresent()**:功能與IsDebuggerPresent() 一樣,同時也可檢測其他的process 是否被Debug > Determines whether the specified process is being debugged. > > If the function succeeds(), the return value is nonzero. > If the function fails, the return value is zero. 對抗方式:ScyllaHide <br> **GetTickCount**():得到目前時間 先執行一次得到現在時間A,執行一段時間後在GetTickCount一次得到時間B 兩者相減利用時間差判斷是否有可能被Debugged 簡單的psuedo code ``` Time_a = GetTickCount() .... malicious_code() .... Time_b = GetTickCount() if Time_b - Time_a > 1 second: debugger_detected() ``` 對抗方式:ScyllaHide <br> ### Dynamic API Resolution 為**LoadLibrary() + GetProcAddress()**的組合 這兩個API皆為kernel32.dll之下的function,可以用來動態載入所需要的DLL以及API 這樣做最大的好處是你一開始無法透過import table看到有哪些API被import來判斷功能 算是很大程度上堵死了靜態分析的方法,也同時大大增加動態分析的難度 只有當程式開始跑了之後,才會動態載入所需要的library 可以看到,途中重複呼叫同樣的sub_D12,並且以各個API為argument輸入(little endian) ![](https://i.imgur.com/LTgAbNB.png =50%x) <br> 對抗方式:等他動態載入完後,在dump process,或是紀錄載入完畢後的地方設定breakpoint <br> ### API Hashing 當我們想要隱藏被使用的Windows API名稱,我們可以先將API名字取Hash ``` MD5(VirtualAllocEx) = 0x12345678 ``` 透過計算所需要API的Hash值,我們可以在對應的code中,遍歷`kernal32.dll` ``` // 這個函數會遍歷指定的 DLL 並找到對應的 API 函數地址 FARPROC resolve_api_address(char *dll_name, DWORD api_hash) { HMODULE dll = LoadLibraryA(dll_name); if (dll == NULL) { return NULL; } FARPROC proc = NULL; char proc_name[256]; for (int i = 0; i < MAX_API_NUMBER; i++) { GetProcAddressName(dll, i, proc_name); // 假設這個函數可以獲取 API 名稱 if (hash_function(proc_name) == api_hash) { proc = GetProcAddress(dll, proc_name); break; } } return proc; } // 在程式中調用 API void call_api() { DWORD api_hash = 0x12345678; // Hashed VirtualAllocEx FARPROC virtualAlloc_api = resolve_api_address("user32.dll", api_hash); if (virtualAlloc_api != NULL) { virtualAlloc_api(); // 調用 API } } ``` 利用這樣的技術,我們可以成功隱藏`VirtualAllocEx`以及其他所需要API的名稱,也會增加分析難度 對抗方式:IDA使用plugin搜尋已知hashed API value - https://github.com/OALabs/hashdb-ida <br> ### Software Breakpoint Detect 看標題可能會好奇,這怎麼檢查? 方法很簡單,在Debugger中,我們常常會對某些API設下breakpoints 設下breakpoint時,debugger會把instruction改寫成INT 3 (opcode 0xCC) 前面有提到,一個檔案只要更改任一byte,程式的hash就會不一樣 所以只要在執行中檢查幾次hash value,發現不一樣就結束程式 也或者可以掃描程式是否有 0xCC byte 出現 對抗方式:手動找出檢查點,在可能跳轉的地方patch更改檢查條件 <br> ## TLS Thread Local Storage(TLS):每個Thread都有自己的TLS,來看一段介紹 >TLS (Thread Local Storage) callbacks are stored in the PE header and provided by the Windows operating system to support additional initialization and to terminate per-thread data structures. https://www.ringzerolabs.com/2019/08/analyzing-tls-callbacks.html 在TLS中,也可以塞入針對debugger偵測的程式 所以當你嘗試把惡意程式拖進debugger想分析時,再進入Entry Point之前,在執行TLS部分的時候就會被抓出來 對抗方式:x64dbg已經可以直接繞過 <br> ## More Resources 綜上所寫的還只是一部分,但目前大多數的Anti-Debug都可以透過ScyllaHide來繞過 如果對這方面很有興趣的,可以參考以下資源 https://www.apriorit.com/dev-blog/367-anti-reverse-engineering-protection-techniques-to-use-before-releasing-software https://www.deepinstinct.com/2017/12/27/common-anti-debugging-techniques-in-the-malware-landscape/ https://anti-reversing.com/Downloads/Anti-Reversing/The_Ultimate_Anti-Reversing_Reference.pdf [-0xbc](https://hackmd.io/@0xbc000) ###### tags: `Malware Analysis` `Reverse Engineering` `tutorials`