Overview
Mục tiêu chính của advanced static analysis là phát hiện các khả năng, các attack vector và các kỹ thuật tránh né của mã độc. Để thực hiện advanced static analysis thì thường người ta sử dụng IDA Pro, Binary Ninja và radare2.
Các bước thực hiện advanced static analysis:
- Xác định entry point của malware và các system call mà nó sử dụng.
- Xác định các code section của malware và phân tích chúng sử dụng debugger hoặc hex editor.
- Phân tích control flow graph của malware để xác định execution path của nó.
- Theo dõi hành vi của malware bằng cách phân tích các system call mà nó gọi khi thực thi.
- Sử dụng các thông tin trên để hiểu về các kỹ thuật tránh né và các thiệt hại mà malware có thể gây ra.
Ghidra: A Quick Overview
Là một công cụ dịch ngược mã nguồn mở giúp:
- Decompilation: decompile chương trình thành mã nguồn C.
- Disassembly: disassemble thành hợp ngữ.
- Debugging: cho phép chạy code step-by-step.
- Analysis: tự động xác định các hàm, biến, …
Layout của Ghidra:
Ý nghĩa của các phần:
- Program Trees: hiển thị các section của binary.
- Symbol Tree:
- Imports: chứa các hàm thư viện được gọi sử dụng. Khi click vào từng API call thì Ghidra sẽ hiển thị mã hợp ngữ tương ứng với API call đó.
- Exports: chứa các API/function call được export từ binary. Hữu ích khi phân tích file DLL vì nó sẽ giúp hiển thị tất cả các hàm trong DLL.
- Functions: chứa các hàm mà nó tìm thấy trong binary, bao gồm cả entry function. Khi click vào từng hàm thì Ghidra sẽ hiển thị mã hợp ngữ của hàm đó.
- Data Type Manager: cho biết các kiểu dữ liệu có trong binary.
- Listing: hiển thị mã hợp ngữ của binary. Gồm các giá trị sau:
- Virtual Address
- Opcode
- Assembly Instruction (PUSH, POP, ADD, XOR, etc.)
- Operands
- Comments
- Decompile: Ghidra sẽ dịch mã hợp ngữ thành mã giả C ở đây.
- Toolbar: bao gồm các option hỗ trợ việc phân tích.
Một số loại góc nhìn trong Ghidra:
Graph View: cho phép ta thấy đồ thị của mã hợp ngữ.
The Memory Map: hiển thị ánh xạ bộ nhớ của binary.
Chúng ta có thể tìm các chuỗi có trong binary bằng cách vào mục Search -> For Strings
:
Identifying C Code Constructs in Assembly
Có ba cách tiếp cận để phân tích disassembled code:
- Tìm main function ở trong Symbol Tree.
- Kiểm tra section
.text
trong Program Trees và tìm entry point. - Tìm các chuỗi khả nghi và đi đến code sử dụng các chuỗi đó.
Code: Hello World
Chương trình hello world viết bằng C:
#include <stdio.h>
int main() {
printf("HELLO WORLD!!");
return 0;
}
Chương trình hello world viết bằng hợp ngữ:
section .data
message db 'HELLO WORLD!!', 0
section .text
global _start
_start:
; write the message to stdout
mov eax, 4 ; write system call
mov ebx, 1 ; file descriptor for stdout
mov ecx, message ; pointer to message
mov edx, 13 ; message length
int 0x80 ; call kernel
Chương trình Hello_World.exe
ở trong Ghidra:
Có thể thấy, binary thực hiện push chuỗi “HELLO WORLD!” vào stack trước khi gọi hàm print.
Code: For Loop
Vòng lặp for trong C:
int main() {
for (int i = 1; i <= 5; i++) {
std::cout << i << std::endl;
}
return 0;
}
Vòng lặp for trong hợp ngữ:
main:
; initialize loop counter to 1
mov ecx, 1
; loop 5 times
mov edx, 5
loop:
; print the loop counter
push ecx
push format
call printf
add esp, 8
; increment loop counter
inc ecx
; check if the loop is finished
cmp ecx, edx
jle loop
Trong đoạn code trên:
- Hàm
main
khởi tạo loop counterecx
1 với giá trị là 1 và loop limitedx
là 5. - Label
loop
được sử dụng để đánh dấu bắt đầu vòng lặp. - Với mỗi lần lặp thì sẽ in ra giá trị của loop counter
ecx
bằng hàmprintf
từ thư viện C chuẩn. - Sau khi loop counter được in ra thì giá trị của nó sẽ được tăng lên 1.
- Vòng lặp sẽ tiếp tục nếu giá trị của loop counter nhỏ hơn hoặc bằng loop limit.
Chương trình for-loop.exe
ở trong Ghidra:
Code: Function
Hàm tính tổng ở trong C:
int add(int a, int b){
int result = a + b;
return result;
}
Hàm tính tổng ở trong hợp ngữ:
add:
push ebp ; save the current base pointer value
mov ebp, esp ; set base pointer to current stack pointer value
mov eax, dword ptr [ebp+8] ; move the value of 'a' into the eax register
add eax, dword ptr [ebp+12] ; add the value of 'b' to the eax register
mov dword ptr [ebp-4], eax ; move the sum into the 'result' variable
mov eax, dword ptr [ebp-4] ; move the value of 'result' into the eax register
pop ebp ; restore the previous base pointer value
ret ; return to calling function
Code: While Loop
Vòng lặp while ở trong C:
int i = 0;
while (i < 10) {
printf("%d\\n", i);
i++;
}
Vòng lặp while ở trong hợp ngữ:
mov ecx, 0 ; initialize i to 0
loop_start:
cmp ecx, 10 ; compare i to 10
jge loop_end ; jump to loop_end if i >= 10
push ecx ; save the value of i on the stack
push format ; push the format string for printf
push dword [ecx]; push the value of i for printf
call printf ; call printf to print the value of i
add esp, 12 ; clean up the stack
inc ecx ; increment i
jmp loop_start ; jump back to the start of the loop
loop_end:
Trong đoạn code trên:
- Chỉ thị
mov
khởi tạo giá trị cho thanh ghieax
(biếni
) là 0. - Label
loop_start
đánh dấu bắt đầu vòng lặp. - Chỉ thị
cmp
so sánheax
với 10. Nếueax
lớn hơn hoặc bằng 10 thì kết thúc vòng lặp. - Nếu không thì in ra giá trị của
eax
sử dụng hàmprintf
. - Chỉ thị
add
giúp dọn dẹp stack sau khi gọi hàmprintf
. - Sau đó, giá trị của
eax
được tăng lên 1 và vòng lặp được tiếp tục.
Vòng lặp while của chương trình While-Loop.exe
ở trong Ghidra:
An Overview of Windows API Calls
Ta sẽ xem xét hàm CreateProcessA
của Windows API. Hàm này giúp tạo ra một process mới có một primary thread. Function signature:
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
Ví dụ sử dụng:
#include <Windows.h>
#include <stdio.h>
int main() {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA(NULL, "C:\\Windows\\notepad.exe", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed (%d).\n", GetLastError());
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
Khi biên dịch thành hợp ngữ:
push 0
lea eax, [esp+10h+StartupInfo]
push eax
lea eax, [esp+14h+ProcessInformation]
push eax
push 0
push 0
push 0
push 0
push 0
push 0
push dword ptr [hWnd]
call CreateProcessA
Đoạn code hợp ngữ đẩy các đối số vào stack theo thứ tự nghịch đảo rồi gọi hàm CreateProcessA
. Hàm này sẽ trả về một handle trỏ đến process có một primary thread.
Common APIs Used by Malware
Các tác giả malware sử dụng rất nhiều Windows API.
Keylogger
Một vài hàm dùng cho keylogging:
- SetWindowsHookEx: cài đặt một application-defined hook vào hook chain. Malware sử dụng hàm này để giám sát và can thiệp các sự kiện hệ thống chẳng hạn như các tổ hợp phím và click chuột.
- GetAsyncKeyState: lấy trạng thái của một virtual key. Malware sử dụng hàm này để xác định xác định xem một phím được bấm hay thả ra.
- GetKeyboardState: lấy trạng thái của toàn bộ các virtual key. Malware sử dụng hàm này để xác định trạng thái của tất cả các key trên bàn phím.
- GetKeyNameText: lấy tên của key. Malware sử dụng hàm này để xác định tên của phím được bấm.
Downloader
Là một loại malware dùng để tải malware khác xuống máy của nạn nhân. Các API mà nó sử dụng:
- URLDownloadToFile: tải một file từ internet và lưu vào một local file. Malware sử dụng hàm này để tải thêm hoặc cập nhật mã độc.
- WinHttpOpen: khởi tạo WinHTTP API. Malware sử dụng hàm này để thiết lập kết nối HTTP.
- WinHttpConnect: kết nối đến server từ xa sử dụng WinHTTP API.
- WinHttpOpenRequest: mở HTTP request sử dụng WinHTTP API.
C2 Communication
Command and Control (C2) là một phương pháp malware sử dụng để liên lạc với một server từ xa hoặc attacker. Kênh giao tiếp này được dùng để nhận lệnh từ attacker, gửi dữ liệu ăn cắp được cho attacker hoặc tải thêm mã độc xuống máy nạn nhân. Các API mà malware sử dụng:
- InternetOpen: khởi tạo một session để kết nối đến internet.
- InternetOpenUrl: mở URL để download.
- HttpOpenRequest: mở một HTTP request.
- HttpSendRequest: gửi HTTP request đến C2 server.
Data Exfiltration
Mã độc sử dụng các API sau để trích xuất dữ liệu từ máy nạn nhân:
- InternetReadFile: đọc dữ liệu từ một handle của một tài nguyên internet. Malware sử dụng hàm này để ăn cắp dữ liệu và chuyển đến C2.
- FtpPutFile: upload một file lên FTP server.
- CreateFile: tạo hoặc mở một file hoặc một thiết bị. Malware dùng hàm này để đọc hoặc chỉnh sửa các tập tin có chứa dữ liệu nhạy cảm hoặc các cấu hình hệ thống.
- WriteFile: dùng để ghi dữ liệu vào một file hoặc một thiết bị. Malware sử dụng để ghi dữ liệu ăn cắp được vào file và gửi cho remote server.
- GetClipboardData: lấy dữ liệu từ clipboard.
Dropper
Là loại malware dùng để cài đặt malware khác xuống máy nạn nhân. Các API mà nó sử dụng:
- CreateProcess: tạo mới một tiến trình với một primary thread. Malware dùng hàm này để thực thi code trong ngữ cảnh của một tiến trình hợp lệ nhằm gây khó khăn cho việc phát hiện và phân tích.
- VirtualAlloc: cấp bộ nhớ trong không gian địa chỉ ảo của tiến trình. Malware sử dụng hàm này để cấp phát bộ nhớ lưu mã độc.
- WriteProcessMemory: ghi dữ liệu vào một vùng bộ nhớ trong không gian địa chỉ ảo của tiến trình.
API Hooking
Là phương pháp mã độc dùng để can thiệp các lời gọi sử dụng Windows API và chỉnh sửa hành vi của chúng. Điều này giúp mã độc ngăn cản được sự phát hiện của các phần mềm bảo mật và thực hiện các hành vi độc hại chẳng hạn như ăn cắp dữ liệu hoặc điều chỉnh cấu hình hệ thống. Các API sử dụng:
- GetProcAddress: lấy địa chỉ của exported function hoặc biến từ một DLL cụ thể. Malware dùng hàm này để xác định vị trí và các API call của các tiến trình khác.
- LoadLibrary: nạp một DLL vào không gian địa chỉ của tiến trình. Malware dùng hàm này để nạp và thực thi mã từ một DLL hoặc một module khác.
- SetWindowsHookEx: cài đặt một hook procedure giúp giám sát một sự kiện hệ thống hoặc các thông điệp gửi đến một cửa sổ. Malware dùng hàm này để can thiệp vào các lời gọi Windows APIs và chỉnh sửa hành vi của chúng.
Anti-debugging and VM Detection
Là các kỹ thuật được sử dụng bởi mã độc để tránh bị phát hiện và phân tích. Các API sử dụng:
- IsDebuggerPresent: kiểm tra xem tiến trình có đang chạy bằng debugger hay không.
- CheckRemoteDebuggerPresent: kiểm tra xem có debugger từ xa đang debug của tiến trình hay không.
- NtQueryInformationProcess: lấy thông tin về một tiến trình cụ thể.
- GetTickCount: lấy số mili giây đã trải qua kể từ khi hệ thống được khởi động. Malware dùng hàm này để xác định xem nó có đang chạy trong môi trường ảo hóa hay không.
- GetModuleHandle: lấy handle của một module cụ thể.
- GetSystemMetrics: lấy metric của hệ thống và các cấu hình.
Seealso
Tham khảo thêm MalAPI.io để biết các API mà malware sử dụng.
Process Hollowing: Overview
Là một kỹ thuật giúp inject mã độc vào một tiến trình hợp lệ đang chạy trên máy nạn nhân. Một cách tổng quát, mã độc tạo ra một tiến trình treo và thay thế không gian bộ nhớ của tiến trình đó bằng mã nguồn của chính nó. Sau đó, mã độc cho tiến trình chạy để thực thi mã nguồn.
Các bước thực hiện:
- Tạo mới một tiến trình hợp pháp sử dụng hàm
CreateProcessA
. - Hàm
NtSuspendProcess
dùng để treo tiến trình vừa tạo. - Cấp phát vùng nhớ trong tiến trình đang treo sử dụng hàm
VirtualAllocEx
. Vùng nhớ này sẽ được dùng để chứ mã độc. - Ghi mã độc và vào vùng nhớ đã được cấp phát sử dụng hàm
WriteProcessMemory
. - Điều chỉnh entry point của tiến trình để trỏ đến mã độc sử dụng hàm
SetThreadContext
và hàmGetThreadContext
. - Resume tiến trình đang treo sử dụng hàm
NtResumeProcess
. Điều này giúp tiến trình thực thi mã độc. - Dọn dẹp tiến trình và các tài nguyên đã được sử dụng.
Minh họa:
Analyzing Process Hollowing
Bên dưới là các phân tích cho malware Benign.exe
.
CreateProcess
Nếu ta đã biết malware sử dụng process hollowing thì có thể tìm các reference đến hàm CreateProcessA
ở phần imports của Ghidra:
Có thể thấy, binary đẩy các đối số vào stack theo thứ tự nghịch đảo. Đối số có giá trị là 0x4
chính là flag CREATE_SUSPENDED
dùng để tạo chương trình bị treo.
Khi chọn góc nhìn đồ thị, ta sẽ thấy nếu malware tạo tiến trình treo thành công thì nó sẽ đi đến block theo mũi tên màu xanh lá cây. Ngược lại, nó sẽ đi đến block theo mũi tên màu đỏ:
Open Suspicious File
Malware sử dụng hàm CreateFileA
để tạo file hoặc mở file evil.exe
:
Hollow the Process
Malware sử dụng hàm NtUnmapViewOfSection
để unmap bộ nhớ của target process:
Hàm NtUnmapViewOfSection
nhận vào hai đối số:
- Base address (virtual address) cần unmap.
- Handle đến process.
Allocate Memory
Khi target process đã bị unmap bộ nhớ (trở nên hollow - rỗng) thì mã độc sẽ thực hiện cấp phát vùng nhớ sử dụng VirtualAllocEx
:
Đối số truyền vào VirtualAllocEx
bao gồm:
- Handle đến process.
- Địa chỉ để cấp phát.
- Kích thước cần cấp phát.
- Loại cấp phát.
- Flag bảo vệ bộ nhớ.
Write Down the Memory
Mã độc thực hiện ghi tiến trình/mã nguồn vào vùng nhớ đã được cấp phát của target process sử dụng hàm WriteProcessMemory
:
Có thể thấy, mã độc thực hiện sao chép những đoạn code độc hại vào target process.
Resume Thread
Cuối cùng, mã độc thực hiện resume thread thông qua hàm SetThreadContext
và hàm ResumeThread
:
Related
list
from outgoing([[TryHackMe - Advanced Static Malware Analysis]])
sort file.ctime asc
Resources
Footnotes
-
xem thêm TryHackMe - x86 Architecture ↩