1380 words
7 minutes
FLARE Learning Hub - Chapter 9: Reversing Windows Binaries

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.

mandiant
/
flare-learning-hub
Waiting for api.github.com...
00K
0K
0K
Waiting...

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 WindowsTiền tốVí dụÝ nghĩa
WORDwwYearsố nguyên 16-bit unsigned đại diện năm
DWORDdwdwSizesố nguyên 32-bit unsigned đại diện kích thước
LPCSTRlplpFileNamecon 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à __stdcall trê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ền NULL). 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):

  1. CreateFile mở file, trả về handle nếu thành công
  2. ReadFile dùng handle đó để đọc dữ liệu
  3. 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  →  CloseHandle

Nhó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)  →  FindClose

Windows 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 KeyViết tắtMô tả
HKEY_CURRENT_USERHKCUDữ liệu của user đang đăng nhập
HKEY_USERSHKUThông tin của mọi account trên máy
HKEY_CLASSES_ROOTHKCRLiên kết loại file, đăng ký COM object
HKEY_LOCAL_MACHINEHKLMCấu hình toàn hệ thống, mọi user
HKEY_CURRENT_CONFIGHKCCThô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)
  → RegCloseKey

Process 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ự:

  1. Parse PE header để xác thực file
  2. Dựng virtual memory space, map các section của file vào bộ nhớ theo đúng thông tin trong header
  3. Đệ 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 đó
  4. Chuyển quyền thực thi tới AddressOfEntryPoint khai 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:

  1. 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 đó)
  2. GetProcAddress(hModule, "printf") — trả về địa chỉ thật của hàm printf bên trong DLL vừa load
  3. Gọi printf gián tiếp qua con trỏ hàm vừa lấy được (không có CALL printf tường minh nào trong disassembly — chỉ thấy CALL [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 socket trước; client điền địa chỉ đích vào struct sockaddr rồi gọi connect; server gọi bind (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”.

FLARE Learning Hub - Chapter 9: Reversing Windows Binaries
https://stewmalwarehunter.id.vn/posts/flare-macc-ch9---reversing-windows-binaries/
Author
Stew
Published at
2026-10-02
License
CC BY-NC-SA 4.0