700 words
4 minutes
FLARE Learning Hub - Chapter 6: Debugging Techniques

Chapter 6 của “Malware Analysis Crash Course” (FLARE/Mandiant) chuyển hướng từ static analysis (đọc code không chạy) sang vài kỹ thuật cơ bản khi debug — chủ động can thiệp vào luồng thực thi thay vì chỉ quan sát.

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

Forcing Code Execution — 3 cách can thiệp#

1. Chỉnh Register/Flag (tạm thời)#

Khi debugger đang dừng tại breakpoint, có thể sửa trực tiếp giá trị bất kỳ thanh ghi hay status flag nào. Cực kỳ hữu ích để ép một nhánh rẽ có điều kiện đi theo hướng mình muốn (ví dụ lật Zero Flag) mà không cần hiểu rõ logic phía trước tạo ra điều kiện đó như thế nào. Trong IDA: double-click vào giá trị thanh ghi ở panel General Register rồi nhập giá trị mới; các status flag hiện ở bên phải panel đó.

Giới hạn quan trọng: thay đổi này không tồn tại lâu dài — restart chương trình là mất hết, phải làm lại từ đầu mỗi lần debug.

2. Chỉnh Instruction Pointer (tạm thời, rủi ro cao hơn)#

Ép EIP nhảy thẳng tới địa chỉ khác để bỏ qua một đoạn code hoặc đổi hướng thực thi. Trong IDA: chuột phải vào lệnh đích → Set IP.

Rủi ro cần lưu ý: nếu đoạn code bị bỏ qua có chứa bước dọn stack (ví dụ sau một CALL), nhảy qua nó có thể làm stack bị lệch/hỏng — vì ESP không còn đúng như những gì các lệnh phía sau kỳ vọng. Phải chắc chắn không có phụ thuộc nào giữa đoạn bị bỏ qua và phần code chạy tiếp theo trước khi dùng kỹ thuật này.

3. Patch Code (vĩnh viễn)#

Sửa trực tiếp vào file binary — thay đổi tồn tại qua mọi lần chạy, cần thiết khi phải lặp lại cùng một thay đổi mỗi lần thực thi (ví dụ vô hiệu hóa một đoạn chống debug để lần nào mở lên cũng không bị chặn nữa).

Lưu ý quan trọng: patch file sẽ làm hỏng chữ ký số (digital signature) của binary và có thể kích hoạt cơ chế chống giả mạo (anti-tampering) nếu có.

Trong IDA, patch gồm đúng 2 bước tách biệt:

  1. Edit → Patch program → Assemble — sửa lệnh trong database (chưa ghi ra file thật)
  2. Edit → Patch program → Apply patches to input file — mới thực sự ghi thay đổi xuống file gốc

Jump Patching#

Lệnh nhảy có điều kiện là mục tiêu patch phổ biến nhất vì chúng quyết định hướng rẽ của toàn bộ nhánh thực thi. Các kiểu sửa hay gặp:

  • Đổi nhảy có điều kiện → vô điều kiện (thay JNZ bằng JMP)
  • Đảo ngược điều kiện (thay JNZ bằng JZ để lật hẳn logic)
  • Đổi đích nhảy sang một địa chỉ khác

Lưu ý bắt buộc khi patch: phải để ý số byte của lệnh thay thế so với lệnh gốc.

  • Nếu lệnh mới ngắn hơn lệnh gốc → phần byte dư ra không bị ghi đè, các byte rác đó sẽ bị decode thành lệnh vô nghĩa phía sau → crash.
  • Nếu lệnh mới dài hơn → sẽ đè luôn lên phần đầu của lệnh kế tiếp → cũng crash.

IDA có thể hiện số byte opcode ngay cạnh assembly để dễ kiểm tra độ dài — bật qua Options → General, chỉnh Number of opcode bytes thành 7.

NOP’ing-Out Instructions#

Xóa hẳn hành vi một lệnh bằng cách ghi đè từng byte của nó bằng NOP (0x90, 1 byte). Kỹ thuật này hay dùng để vô hiệu hóa hoàn toàn các tính năng chống phân tích (ví dụ một hàm anti-debug) — khác với jump patching (chỉ đổi hướng rẽ nhánh), NOP’ing xóa sạch tác dụng của lệnh gốc.

Vì NOP chỉ chiếm đúng 1 byte, bắt buộc phải biết chính xác độ dài byte của lệnh gốc để thay thế đủ số NOP tương ứng — thiếu sẽ để sót byte rác (giống vấn đề ở jump patching), thừa sẽ đè sang lệnh kế tiếp.


Ba kỹ thuật này là công cụ nền tảng khi debug, nhưng cần dùng có chủ đích — can thiệp sai chỗ có thể làm chương trình crash hoặc rơi vào trạng thái không hợp lệ. Chapter 7 mình quay lại static analysis với các cấu trúc phức tạp hơn: con trỏ, mảng, vòng lặp, struct.

FLARE Learning Hub - Chapter 6: Debugging Techniques
https://stewmalwarehunter.id.vn/posts/flare-macc-ch6---debugging-techniques/
Author
Stew
Published at
2026-09-10
License
CC BY-NC-SA 4.0