The Need for Advanced Dynamic Analysis

Một số phương pháp né tránh static analysis:

  • Thay đổi hash bằng cách thêm vào một sự thay đổi nhỏ nào đó trong malware chẳng hạn như thêm vào một chỉ thị nop1.
  • Thay đổi các pattern trong malware mà bị phát hiện bởi kỹ thuật signature-based2.
  • Malware thực hiện decode các chuỗi quan trọng chẳng hạn như URL, C2 domain, … lúc run time.
  • Nạp DLL lúc runtime để tránh bị phân tích thông qua các PE header3.
  • Packing và obfuscation.

Một số phương pháp né tránh dynamic anlysis:

  • Kiểm tra xem malware có được thực thi trong máy ảo hay không: malware có thể làm điều này bằng cách kiểm tra các registry key hoặc các driver của các phần mềm ảo hóa chẳng hạn như VMWare và Virtualbox. Tương tự, việc có ít tài nguyên được cấp phát chẳng hạn như chỉ có một CPU hoặc RAM bị giới hạn cũng là dấu hiệu cho thấy malware đang được chạy ở trong một máy ảo. Khi malware phát hiện ra thì nó sẽ thực thi theo một cách khác nhằm đánh lừa người phân tích.
  • Tấn công thời gian: malware thường cố gắng gây ra timeout cho các hệ thống phân tích tự động chẳng hạn như dùng thư viện Window Sleep để sleep một ngày. Các hệ thống phân tích tự động thường tự động tắt sau vài phút và không tìm được các dấu vết của malware. Các hệ thống phân tích mới có thể nhận diện các loại tấn công này và cố gắng làm giảm thời gian sleep của malware. Tuy nhiên, malware cũng có thể nhận biết việc bị rút ngắn thời gian sleep bằng cách ghi lại thời gian bắt đầu thực thi và so sánh với thời gian hiện tại sau khi thức dậy. Nếu tổng thời gian thực thi bị rút ngắn thì malware cũng sẽ thay đổi cách thực thi.
  • Dấu vết của người dùng: malware sẽ kiểm tra xem có dấu vết của người dùng đang hoạt động trên máy hay không. Nếu không có hoặc có rất ít thì có nghĩa là nó đang được thực thi trong một môi trường có kiểm soát và nó sẽ thực thi theo một cách khác. Các dấu vết của người dùng có thể là các chuyển động chuột và bàn phím, lịch sử trình duyệt, các file mở gần đây, uptime của hệ thống, …
  • Nhận diện các công cụ phân tích: malware sẽ truy vấn các tiến trình đang chạy sử dụng hàm Process32First, Process32Next hoặc các hàm tương tự để kiểm tra xem có các công cụ phân tích nào đang chạy hay không. Nếu có thì nó sẽ thay đổi cách thực thi. Một cách khác để phát hiện các công cụ phân tích là kiểm tra các cửa sổ đang mở.

Introduction to Debugging

Để giám sát và thay đổi luồng thực thi của malware lúc run time thì cần phải sử dụng debugger. Có một vài loại debugger:

  • Source-level: thường được dùng bởi các lập trình viên và debug trên mã nguồn. Khi debug, ta sẽ thấy các biến và giá trị của chúng.
  • Assembly-level: dành cho các binary nhằm debug ở mức hợp ngữ. Khi debug, ta sẽ thấy các giá trị của thanh ghi và bộ nhớ.
  • Kernel-level: thấp hơn cả assembly-level debugger.

Familiarization With a Debugger

Đối với malware, ta có thể dùng các debugger sau: Windbg, Ollydbg, IDA, Ghidra và x32dbg/x64dbg (tương ứng với 32-bit binary và 64-bit binary).

Giao diện của x32dbg khi mở một binary:

Có thể thấy ở góc trái bên dưới, chương trình đang bị tạm dừng bởi vì một system breakpoint. Trong hình trên, ở chính giữa công cụ là dạng disassembly của chương trình và có instruction pointer trỏ đến chỉ thị tiếp theo. Ở bên phải là các thanh ghi và giá trị của chúng. Ở bên dưới là dump view và stack view (từ trái qua phải).

Ở tab breakpoint là các breakpoint của chương trình:

Memory map hiển thị bộ nhớ của chương trình:

Chúng ta cũng có thể thấy call stack của chương trình:

Tab Threads hiển thị các tab có trong chương trình:

Các handle đến file, tiến trình hoặc các tài nguyên khác được liệt kê ở trong tab Handles:

Debugging in Practice

Các công cụ được khoanh vùng lần lượt là:

  • Mở file.
  • Restart quá trình thực thi của file.
  • Dừng quá trình thực thi.
  • Nút mũi tên sẽ thực thi chương trình đến khi nào bị dừng hoặc bị tạm dừng.
  • Nút tạm dừng.
  • Stepping into.
  • Stepping over.

Sau khi nhấn nút mũi tên thì chương trình sẽ chuyển sang trạng thái Running rồi Paused:

Lý do bị tạm dừng là “INT3 breakpoint …” Điều này có nghĩa là chương trình đã chạm đến một TLS callbacks.

Chúng ta có thể cài đặt các breakpoint tự động ở trong menu Options > Preferences:

Bởi vì các TLS callback thường được dùng để chống dịch ngược nên ta cần đi qua từng instruction. Sau một vài instruction thì ta đi đến instruction jne:

Nếu kết quả của phép so sánh cmp là không bằng nhau (zero flag khác 1) thì instruction jne sẽ nhảy đến địa chỉ D116E. Ở địa chỉ này, chương trình pop ebp và trả về.

Trong hình trên, debugger hiển thị thông điệp “Jump is not taken” ở trên dump view và ZF được gạch chân với giá trị là 1 nhằm cho ta biết điều kiện của instruction jne không được thỏa mãn (kết quả phép so sánh cmp là bằng nhau) và nó sẽ không nhảy đến D116E.

Khi đó, instruction jne sẽ nhảy đến địa chỉ D1000. Chúng ta có thể double-click vào địa chỉ để hiển thị code:

Trong đoạn code trên, ta thấy chương trình gọi sử dụng API CreateToolhelp32Snapshot cùng với LoadLibraryGetProcAddress (nếu kéo xuống dưới).

Hàm LoadLibrary nạp thư viện SuspendThread để treo thread:

Việc treo thread này có thể được kích hoạt khi chương trình phát hiện một tiến trình nào đó chẳng hạn như debugger (hàm CreateToolhelp32Snapshot được dùng để lấy các tiến trình đang chạy).

Như vậy, hành động của malware khi phát hiện ra dynamic analysis của malware chính là treo thread. Mục tiêu của ta là thay đổi luồng thực thi nhằm khiến cho chương trình không chạy vào execution path này.

Bypassing Unwanted Execution Path

Để làm cho instruction jne thỏa mãn thì ta cần sửa lại ZF thành 0 (tương ứng với việc kết quả của phép cmp là không bằng nhau) bằng cách double-click vào giá trị của ZF:

Để cho chương trình tự động nhảy đến địa chỉ D116E và thực hiện các hành vi độc hại một cách tự động mà không cần sự can thiệp vào giá trị của thanh ghi thì ta có thể thay instruction jne thành jn hoặc dùng unconditional jump (jmp). Đây được gọi là patching.

Ta nhấn chuột phải vào instruction và chọn Binary > Edit:

Công cụ sẽ hiển thị như sau:

Với 75 05 là opcode của jne.

Chúng ta cũng có thể thay instruction gọi đến địa chỉ D1000 bằng instruction nop:

Sau đó ta chọn File > Patch File để ghi sự thay đổi. Một instruction thông thường sẽ tương ứng với 5 instruction nop:

Sau khi patch thì chương trình trở thành như sau:

Lúc này, cho dù điều kiện của jne không thỏa mãn thì chương trình vẫn sẽ pop ebp và trả về.

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

Resources

Footnotes

  1. xem thêm The NOP Instruction

  2. xem thêm Signature-Based Detection

  3. xem thêm TryHackMe - Dissecting PE Headers