# 0x07. Advanced topic - Anti-Debug / Anti-Analysis
[toc]
## Why?
惡意程式開發軍火商花費大量時間與資源辛苦開發出來的產品
如果輕輕鬆鬆就被看出來在做什麼那不是很虧嗎?
<br>

<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招數
在我看來根本是神仙打架

<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)

<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`