--- title: Malware Analysis description: AIS3 寒假課程,由 adr 主講 tags: security lang: zh_tw --- # Malware Analysis [TOC] # Tools - PEview - 開 COFF file - PEbear (v0.3.9.7) - 開 exe - PEstudio - 打包疑似惡意程式會用到 - AutoRuns - 可簡單檢查 Windows 註冊表 - Process Monitor - 紀錄以及查看所有讀寫註冊表、File system、創造子程序的活動 - 火絨劍 - 前幾個的合體 - 雖然是中國製造就是ㄌ # Compiler - .rdata 像是字串這種 const 會放在此 - .idata Import Address Table, 引用的外部函數會存在此表 組語中用 ds(data segment register) 指向此表 - .text 程式碼部分存放在此 - .data 可修改的全域變數 ## gcc - `gcc -S gg.c -masm=intel` Compile only 生出 assembly code - `gcc -c gg.s` Compile and assemble, but don't link 生出 object file ### look into assembly code - `.section .rdata,"dr"` 設定 .rdata 為唯讀 - `FLAT:LC1` TBD. 這 FLAT 是啥 ## COFF Obj File 可以用 PEview 觀察 COFF object file - IMAGE_FILE_HEADER Machine: 014c 表示 32bits NumberOfSections: 保存著有幾個 SECTION - IMAGE_SECTION_HEADER 是一個 IMAGE_SECTION_HEADER 陣列 用 IMAGE_FILE_HEADER 保存的幾個 SECTION 的資訊 可以簡單列舉所有 SECTION 若沒有此 SECTION (e.g. 沒有 .data 段) 則會 mapping address 0 - SECTION - IMAGE SYMBOL TABLE 可從此找到引用的外部函數 ## exe file - DOS Header MZ 字串開頭 - DOS stub - NT Headers - Signature - PE 字串開頭 - File Header - 就像 COFF 的 IMAGE_FILE_HEADER - Misc(VirtualSize): 要申請多大虛擬記憶體 - VirtualAddress: 預期擺放到哪個記憶體地址 - SizeOfRawData: 要擺多少資料上去記憶體 - PointerToRawData: 要從這 exe file 的哪裡把資料擺上記憶體 - 擺上記憶體的行為有個專有名詞 **`File Mapping`** - Optional Header - PE 結構特有欄位 - entry pointer, 32bits/64bits, Image base 都放在此 - Section Header Array - 就像 COFF 的 IMAGE_SECTION_HEADER ## Windows Process - [推薦閱讀1](https://www.cnblogs.com/qyun/p/6675062.html) - [推薦閱讀2](https://forcemz.net/windows/2018/12/01/PrivexecNew/) - Parent Process 權限多高, Child Process 權限就多高 ![](https://blog-10039692.file.myqcloud.com/1490956886477_3643_1490956887033.jpg) > 把 Process 當成挫冰碗 > [name=adr] - (B.) Child Process Created 剛創出執行緒, 並將 entry pointer 指向 RtlUserThreadStart 目的是修正 import function table - (C.) RtlUserThreadStart - LdrInitializeThunk - LdrpInitializeProcess 這些 function 不只修 import function table, 也有跟 **windows shim** 有關的機制 - [shim 衍生閱讀](https://www.freebuf.com/articles/system/114287.html) - (D.) Jmp to program ### Threads - TEB - 用 fs:[0x18] 存取 TEB - 一個 process 中可多個 threads, threads 資訊由 TEB 紀錄 - PEB - fs:[0x30] ### Process Hollowing 創好 process 後, 把 text 段替換為惡意程式 ``` CreateProcessA(path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &SI, &PI) ``` - 創造 Process 但先不執行 - 任何 memory mapping 操作都還沒做 ``` VirtualAllocEx ``` 分配虛擬記憶體 ``` WriteProcessMemory ``` 寫入 Memory ### IFEO (Image File Execution Options) 改註冊表 AeDebug 讓在 cmd 原本執行 a.exe 實際上變成執行 b.exe # IDA trick ## main / WinMain - [WinMain](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winmain) - Bonus Keyword: WinExec - nShowcmd: 是否有視窗 :::info main: 跳出小黑窗 terminal WinMain: 跳出現代視窗 or 什麼視窗都沒有 ::: 一開始入口點 start 會執行 CRTStartup 接著用 `cmdln` 獲取指令列 會執行 msvcrt.dll 提供的 `__getmainargs` 此 function 用來 parse 餵給 main 的三參數: argc argv envp 若是 WinMain 則會有四參數(請閱 M$ Manual) 找 main / WinMain 方式: 從 CRTStartup 追進去, 找到一個 function 符合以下 - 吃三個參數 (合理懷疑是 argc argv envp)(或四個) - 回傳值會丟給 exit 他八成是 main ## Info - 若看到 TlsCallback_0 TlsCallback_1 start => MinGW 編的 - main 中第一個 function 不用逆 第一個 function 是對 tlsCallback 做初始化 實際上是有張 tls 表存放 8 個 function pointer, 都是初始化 這 function 把 tls 表的 init functions 執行過一遍後就 return 若沒有 strip 掉, 則此 function 會叫做 `_main` - 只有 start => MS VS 編的 ## 挑重點看 > 你該不會要一行一行看吧 > [name=adr] > 惡意程式一定會有一些惡意行為 惡意行為一定要 call 外部函數 e.g. VirtualAlloc 從高風險的外部函數往回追 ## IDA Hot keys - About ASLR Edit -> Segments -> Rebase program 重新定義 IMAGE BASE - `x`: jump to xref to operand 跳到使用此變數的 code 段 - C Pseudocode to Asm 在 C Pseudocode 頁面空白處右鍵並按 Copy to asm - `y`: 修正函數、變數定義 - `alt+a`: set ASCII string style 若是寬字節字串, 卻被 IDA 已 byte char 形式表示, 可用 `alt+a` 調整 ## 動態模擬 > 建議使用 windows 10 > [name=adr] > 因為 windows 10 內建 Local Win32 debugger adr 習慣還會有 Windbg 接著就能動態 debug **靜態分析會出錯的部分, 跑過一次動態分析後就會修好ㄌ** ## Enum 比如說解出 OpenProcess 這 API 第一個參數, 根據 MSDN, 可以是幾個 flag 的 and 這些 FLAG 是個 enum, 取名規則前面都有 PROCESS 只要在 IDA 中把此參數的 Type 設定為 MACRO_PROCESS 就能在 decompiler 畫面看到 enum name, 而不是冷冰冰的數字 ## recon shellcode Hot key `d` 先改變結構長度 Hot key `u` 回到 undefined 狀態 Hot key `c` 當作指令解碼 Hot key `p` 從當下位址當作一個 code block 試圖反組譯 Hot key `f` 試圖將 code block 作為 function ## Plugin - Light in house - 將有執行到 code block 的高亮起來 # Patch ## Rellocation 符合以下條件則 99% 有開 ASLR 1. 有 .reloc 區段 2. 有個 Base Relocation Table 複習一下 File mapping 從躺在 Disk 的 program 變成躺在 ram 上的 process 記憶體轉換公式: **ImageBase + Virtual Address(VA) + offset = 實際躺在 ram 上的位址** relocation table 存放哪個 text 段用到什麼 function import function | text address (VA + offset) --- | --- **當程式 load 進 memory 後, 若有啟用 ASLR, 則會再從 relocation table 中將 text 段程式碼做替換, 換成正確的、加過 ASLR base 的 import function address** 小知識 1. x32/x64dbg 中有畫底線的就是由 relocation table 決定的地址 2. WinMain 吃的第一個參數就是 ImageBase patch 時注意這點, 不能 patch 到會被 relocate 的部分 整個過程是: - 躺在 disk 中的 program, text 段中用到外部函數的部分會先寫著 Origin ImgaeBase + VA + offset - Application loader 做 file mapping 時, 從 relocation table 中找到要修改的 text 段 - 將 text 段中原本的 Origin ImgaeBase + VA + offset 減去 Origin ImgaeBase 加上 ASLR ImageBase - 修改完畢 ## IDA 再來就是實際 patch 在 IDA 中, Edit > Patch Program > Assemble Patch 好後, Edit > Patch Program > Apply patches to input file ## x64dbg 按 space 修組合語言 之後 ctrl+P 修補程式即可 # 特殊 calling convention - __declspec(naked) - __fastcall - __cdecl - 由呼叫的函數收拾記憶體空間 - gcc 函數預設是這種類型 - ret 10 - 被呼叫的函數收拾記憶體空間, 此例是 ret 前 add esp, 0x10 - e.g. Win32 許多種 API 都屬於這類型 # 編譯器優化產生的指令 - [_mm_cvtsi32_si128](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi32_si128&expand=1879) - [_mm_cmpistrz](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpistrz&expand=960) # SEH Structured Exception Handling Visual Studio 關於 SEH 的參數 - EHsc: 編譯器時期就解決一些咚咚 - SEH: Windows 特有的解法 若 SEH 中找不到 Handler 就呼叫 SetUnhandledExceptionFilter 這 API 設定的 Callback - VS 中的 "錯誤報告" 這變數就是設定相關的參數 看到兩次 push 第二次 push 還是 push fs:[0] 就肯定是在註冊 SEH chain x64dbg 忽略例外要拔掉 配合單步步入(忽略例外) 單步步過(忽略例外) 才能 debug exception IDA 中的圖形看到兩個並排的 code block 沒有連線 就是 Try 跟對應的 catch exception # Others - x32dbg TLS Callbacks - IDA Local Win32 Debugger - Valut7 - 函數中 push ecx 之類的等同於 sub esp, 0x4 申請區域變數空間 - lea edx, dword ptr ds:[edx+ecx-0x30] - 等校於 edx = edx + ecx - 0x30 - 一種優化, 速度加快 4 倍 - PATHEXT 只要副檔名有在此環境變數被設定, 都是可執行ㄉ - .vbs/.js 會用 wscript.exe 執行 - .msc 會用 microsoft management console 執行 - 可以用 cheatengine hook dll ?!?!?! - [unicode 202e](https://isvincent.pixnet.net/blog/post/47138941-windows-%E7%94%B1%E5%8F%B3%E5%90%91%E5%B7%A6%E9%A1%AF%E7%A4%BA%E5%AD%97%E5%85%83%28%E9%97%9C%E6%96%BCunicode%E7%9A%84202e%E5%AD%97%E5%85%83%29) - os.rename('a.exe', u'a\u202etpp.exe') - 副檔名騙你 - [amsi.dll](https://tyranidslair.blogspot.com/2018/06/disabling-amsi-in-jscript-with-one.html) - 把 powershell 複製出來, 改名成 amsi.dll - 執行此 amsi.dll - 用這 powershell(名字已經變成 amsi.dll) 執行的 command 不會被 defender 防護 - SetUnhandled - https://github.com/x64dbg/x64dbg/issues/1685 - ZwQueryInformationProcess - Self-delete - http://www.delphitop.com/html/chengxu/1762.html - control flow guard # DLL 入口函數有兩種 - DllEntryPoint - DllMain LoadLibrary 時會執行一次 DLLMain ## Debug dll IDA 中動態 debug dll 設定 IDA 先開 dll, 之後將 Debugger > Process options > Application 改為 exe 路徑 即可 ## entrypoint 之前 在執行 exe 的 entrypoint 前, 會先執行其使用的 dll 的 entrypoint # 0xcc int3 Debugger 下斷點的方式 若先下斷點之後再 copy paste 過去, 會被影響到 要注意下斷點的時機, 先讓他 copy paste 完再斷點 # Windows Programming - SetWindowsHookExW - 註冊 hook - UnhookWindowsHookEx - 註銷 hook - ShellExecuteA - 執行 command - WinExec - 執行 process - CreateMutexA - ? - GetLastError - 獲取 error number - MessageBoxA - 創造 messagebox - GetTempPathA - 獲取 temp path - ExitProcess - 離開 - GetTickCount - 取得系統 counter - LoadLibraryA - LoadLibraryW - 載入 dll - GetProcAddress - 獲取 dll 中的特定 function address - CallNextHookEx - GetModuleFileNameW - VirtualAlloc - VirtualAllocEx - WriteProcessMemory - NtUnmapViewOfSection - CreateProcessA - GetThreadContext - SetThreadContext - ResumeThread