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:

  1. Xác định entry point của malware và các system call mà nó sử dụng.
  2. 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.
  3. Phân tích control flow graph của malware để xác định execution path của nó.
  4. 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.
  5. 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:

  1. Program Trees: hiển thị các section của binary.
  2. Symbol Tree:
    1. 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 đó.
    2. 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.
    3. 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 đó.
  3. Data Type Manager: cho biết các kiểu dữ liệu có trong binary.
  4. Listing: hiển thị mã hợp ngữ của binary. Gồm các giá trị sau:
    1. Virtual Address
    2. Opcode
    3. Assembly Instruction (PUSH, POP, ADD, XOR, etc.)
    4. Operands
    5. Comments
  5. Decompile: Ghidra sẽ dịch mã hợp ngữ thành mã giả C ở đây.
  6. 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 counter ecx1 với giá trị là 1 và loop limit edx 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àm printf 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 ghi eax (biến i) là 0.
  • Label loop_start đánh dấu bắt đầu vòng lặp.
  • Chỉ thị cmp so sánh eax với 10. Nếu eax 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àm printf.
  • Chỉ thị add giúp dọn dẹp stack sau khi gọi hàm printf.
  • 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:

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:

  1. Tạo mới một tiến trình hợp pháp sử dụng hàm CreateProcessA.
  2. Hàm NtSuspendProcess dùng để treo tiến trình vừa tạo.
  3. 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.
  4. Ghi mã độc và vào vùng nhớ đã được cấp phát sử dụng hàm WriteProcessMemory.
  5. Điều chỉnh entry point của tiến trình để trỏ đến mã độc sử dụng hàm SetThreadContext và hàm GetThreadContext.
  6. 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.
  7. 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:

list
from outgoing([[TryHackMe - Advanced Static Malware Analysis]])
sort file.ctime asc

Resources

Footnotes

  1. xem thêm TryHackMe - x86 Architecture