Chapter 9 — chapter cuối cùng mình ghi lại từ “Malware Analysis Crash Course” (FLARE/Mandiant). Đây là chapter mình thấy thực dụng nhất cho malware analysis, vì gần như mọi hành vi đáng chú ý của malware trên Windows (tạo file, sửa registry, giao tiếp mạng…) đều đi qua Windows API.
Windows API Functions
Windows API (Win32 API) là tập hàm lõi để ứng dụng tương tác với hệ điều hành — vì ứng dụng user-space không được truy cập phần cứng trực tiếp, gần như mọi thao tác cuối cùng đều quy về gọi API. Một điểm thú vị tài liệu nhắc tới: đoạn code CreateWindow từ cuốn Windows Programming Guide năm 1985 (Charles Petzold) tới giờ vẫn compile và chạy đúng — minh chứng cho độ ổn định hiếm có của Windows API qua 40 năm (ngoại lệ duy nhất: calling convention PASCAL ngày xưa dùng cho API import, nay thay bằng STDCALL).
Windows Data Types và Data Structures
Bảng kiểu cơ bản (giống Table 1.1 ở Chapter 1, nhắc lại dưới góc nhìn Windows API): BYTE/WORD/DWORD/QWORD. DWORD là kiểu phổ biến nhất cho giá trị số nguyên unsigned; QWORD tường minh chỉ xuất hiện khi source code chủ động dùng số 64-bit — phần lớn giá trị 64-bit trong code 64-bit thực chất là con trỏ, không phải số.
Windows định nghĩa kiểu riêng qua typedef thay vì dùng thẳng kiểu C chuẩn, để ngôn ngữ-trung lập. Ví dụ struct SYSTEMTIME:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;Một struct Windows thường sinh ra 3 cái tên: struct _SYSTEMTIME (tên C gốc, hiếm khi dùng trực tiếp), SYSTEMTIME (typedef, tên chuẩn hay dùng), và PSYSTEMTIME (con trỏ tới struct đó — tiền tố P/LP luôn có nghĩa “con trỏ tới kiểu này”).
Hungarian Notation
Tên field/biến Windows API được tiền tố hóa theo kiểu dữ liệu — ví dụ field năm trong SYSTEMTIME tên wYear (tiền tố w = WORD):
| Kiểu Windows | Tiền tố | Ví dụ | Ý nghĩa |
|---|---|---|---|
WORD | w | wYear | số nguyên 16-bit unsigned đại diện năm |
DWORD | dw | dwSize | số nguyên 32-bit unsigned đại diện kích thước |
LPCSTR | lp | lpFileName | con trỏ tới chuỗi ASCII hằng, kết thúc null |
Riêng LPCSTR là viết tắt gộp 3 ý: STR (chuỗi ASCII 8-bit), C (constant — hàm không sửa đổi chuỗi này), LP (con trỏ). Tách được 3 thành phần này giúp đoán ngay ý nghĩa tham số mà không cần tra doc.
Reading a Windows API Function Prototype
Prototype của Microsoft hay khác cách khai báo hàm C chuẩn — ví dụ GetSystemTime:
VOID WINAPI GetSystemTime(
_Out_ LPSYSTEMTIME lpSystemTime
);VOID— kiểu trả về, không có giá trị trả vềWINAPI— macro chỉ định calling convention (thường là__stdcalltrên 32-bit)_Out_— annotation SAL (Source-code Annotation Language) của Microsoft, báo tham số này nhận dữ liệu từ hàm (hàm ghi ra). Các annotation khác:_In_(caller cung cấp dữ liệu, hàm chỉ đọc),_Inout_(cả hai chiều),_In_opt_(tham số tùy chọn, được phép truyềnNULL). SAL chỉ là tài liệu/gợi ý cho tool phân tích tĩnh, bị preprocessor loại bỏ trước khi compile thật.
Ví dụ CreateFile minh họa rõ cách đọc:
HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);lpSecurityAttributes và hTemplateFile đánh dấu _In_opt_ — an toàn truyền NULL nếu không cần.
Handles
Handle là cơ chế thống nhất để Windows quản lý tài nguyên hệ thống dưới dạng object — ứng dụng user-land không được đụng trực tiếp vào object, chỉ thao tác qua handle. Về bản chất, handle là một index vào bảng handle riêng của từng process — vì mỗi process có bảng riêng, cùng một giá trị handle ở Process A có thể trỏ tới một object hoàn toàn khác ở Process B.
Vòng đời handle điển hình (minh họa qua thao tác đọc file):
- CreateFile mở file, trả về handle nếu thành công
- ReadFile dùng handle đó để đọc dữ liệu
- CloseHandle đóng lại khi xong — hàm này dùng chung cho hầu hết mọi loại handle, không riêng gì file
Windows API Naming Conventions
Hầu hết API xử lý chuỗi có 2 bản: ANSI và Unicode. CreateFile trong tài liệu chỉ là tên generic — thư viện hệ thống thực chất export cả CreateFileA (hậu tố A = ANSI) lẫn CreateFileW (hậu tố W = Wide/Unicode UTF-16LE). Code nguồn gọi macro CreateFile, lúc compile tự resolve thành A hoặc W tùy cấu hình project. Vì nhân Windows vốn dùng Unicode nội bộ, bản ANSI (CreateFileA) thực chất chỉ convert chuỗi sang wide rồi gọi lại đúng bản CreateFileW.
File API Workflows
Tài liệu nhóm API file thành 2 nhóm riêng biệt:
Nhóm I/O — đọc/ghi nội dung file:
CreateFile → ReadFile hoặc WriteFile → CloseHandleNhóm Enumeration — duyệt danh sách file theo pattern:
FindFirstFile (với search pattern) → FindNextFile (lặp lại cho tới hết) → FindCloseWindows Registry
Database cấu hình dạng cây key/subkey — mỗi key chứa ít nhất 1 value (value “Default”), mỗi value có kiểu dữ liệu riêng (REG_SZ cho chuỗi, REG_DWORD cho số 32-bit…). Malware hay sửa registry để thiết lập persistence hoặc giấu payload.
5 root key chính:
| Root Key | Viết tắt | Mô tả |
|---|---|---|
HKEY_CURRENT_USER | HKCU | Dữ liệu của user đang đăng nhập |
HKEY_USERS | HKU | Thông tin của mọi account trên máy |
HKEY_CLASSES_ROOT | HKCR | Liên kết loại file, đăng ký COM object |
HKEY_LOCAL_MACHINE | HKLM | Cấu hình toàn hệ thống, mọi user |
HKEY_CURRENT_CONFIG | HKCC | Thông tin hardware profile hiện tại |
Hai hive malware nhắm tới nhiều nhất: HKLM (ảnh hưởng toàn hệ thống) và HKCU (riêng user hiện tại).
Registry API Workflow
RegCreateKeyEx (tạo mới) hoặc RegOpenKeyEx (mở key có sẵn)
→ RegQueryValueEx (đọc) hoặc RegSetValueEx (ghi)
→ RegCloseKeyProcess Creation Overview
Khi 1 file .exe được mở lên, điều xảy ra đầu tiên không phải là code của chương trình, mà là Windows loader khởi tạo chính process đó.
Windows dùng mô hình virtual memory — mỗi process có không gian địa chỉ riêng tư, mặc định cô lập hoàn toàn. Khi file thực thi được chạy, ntdll.dll và kernel32.dll tự động được map vào process trước tiên. Code đầu tiên chạy không phải là entry point của chương trình — mà là code của Windows loader (nằm trong ntdll.dll), thực hiện theo trình tự:
- Parse PE header để xác thực file
- Dựng virtual memory space, map các section của file vào bộ nhớ theo đúng thông tin trong header
- Đệ quy resolve import table — load tất cả DLL cần thiết được liệt kê, rồi resolve tiếp import của chính các DLL đó
- Chuyển quyền thực thi tới
AddressOfEntryPointkhai báo trong file — lúc này code “thật” của chương trình mới bắt đầu chạy
Ngoài cơ chế import table tĩnh, LoadLibrary + GetProcAddress cho phép nạp và resolve hàm thủ công lúc runtime — gọi là Runtime Dynamic Linking. Malware rất hay dùng cặp này để né import table tĩnh: giấu luôn tên DLL/hàm thật (thường mã hóa), khiến công cụ phân tích tĩnh/tự động khó lần ra chương trình thực sự gọi gì.
DLL Function Usage
Ví dụ cụ thể resolve printf lúc runtime từ msvcrt.dll:
LoadLibraryA("msvcrt.dll")— map DLL vào process (nếu chưa có sẵn), trả vềHMODULE(chính là địa chỉ base của DLL đó)GetProcAddress(hModule, "printf")— trả về địa chỉ thật của hàmprintfbên trong DLL vừa load- Gọi
printfgián tiếp qua con trỏ hàm vừa lấy được (không cóCALL printftường minh nào trong disassembly — chỉ thấyCALL [con trỏ])
Windows Networking
Windows cung cấp 2 tầng API cho networking:
- Winsock — bám sát chuẩn Berkeley Sockets, cho TCP/UDP cấp thấp, tự lo phần giao thức bậc cao. Cả client lẫn server đều gọi
sockettrước; client điền địa chỉ đích vào structsockaddrrồi gọiconnect; server gọibind(gắn socket với port cục bộ) →listen(vào trạng thái chờ) →accept(nhận kết nối tới). - WinInet — trừu tượng cao hơn, có sẵn hỗ trợ HTTP/HTTPS/FTP. Luồng gửi 1 HTTP request điển hình:
InternetOpen -> khởi tạo thư viện WinInet
InternetConnect -> chỉ định server đích + port
HttpOpenRequest -> tạo request, khai báo verb (GET/POST) + path
HttpSendRequest -> gửi header + dữ liệu (nếu có)
InternetReadFile -> đọc response trả về
InternetCloseHandle -> giải phóng mọi handle đã mởMalware dùng C2 qua HTTP thường để lại dấu vết rất rõ qua chuỗi gọi InternetOpen → InternetConnect → HttpOpenRequest → HttpSendRequest này — lần theo import + xref là cách nhanh nhất để tìm ra domain/IP của C2 server.
Vậy là xong 9 chương lý thuyết của “Malware Analysis Crash Course”.