Posts Tagged ‘crypter’


I. Cơ bản về Crypter

Link gốc của bài viết: https://reverse2learn.wordpress.com/2011/09/01/unprotecting-the-crypter/

Ai có kinh nghiệm hoặc đã nghiên cứu trong lĩnh vực malware hẳn sẽ biết về các công cụ được gọi là “Crypter” hoặc đã từng sử dụng các công cụ đó vào mục đích riêng. Mục đích của Crypter nói chung là nhằm bảo vệ các file thực thi (executable), làm khó khăn trong việc phân tích hoặc RE. Trong ngữ cảnh malware thì mục đích chính của các trình crypter là làm cho các malware trở nên không thể bị phát hiện bởi các trình AV hay còn gọi với thuật ngữ là Fully Undetectable (FUD).

Nguyên tắc để tạo ra một Crypter rất đơn giản. Crypter bao gồm hai phần:

  1. Builder
  2. Stub

crypter

Cách thức thực hiện của chúng như sau:

  1. Cung cấp file đầu vào cho trình crypter, nó sẽ được mã hóa bằng một thuật toán mã hóa (thông thường là RC4, AES). Bằng việc mã hóa file như vậy sẽ vượt qua được cơ chế phân tích tĩnh của các trình AV, bởi vì trong quá trình phân tích tĩnh các trình AV sẽ tìm kiếm các mẫu (pattern) trong file thực thi xem có khớp với các signatures không. Khi file đã bị mã hóa thì các trình AV sẽ không thể tìm kiếm / so mẫu được.
  2. Chèn stub trước đoạn code thực thi. Khi người dùng thực thi chương trình thì stub sẽ chạy và giải mã file đã được mã hóa. Chú ý rằng lúc đó file được giải mã vẫn nằm trong bộ nhớ.
  3. Thực thi file đã giải mã từ bộ nhớ. Đây được coi là điểm cốt lõi của trình crypter. Còn được gọi là “Run PE”, có nhiều cách thức khác nhau để cho thực thi PE. Tuy nhiên hầu hết các trình crypter đều sử dụng cùng một phương pháp để thực thi file từ bộ nhớ, phương pháp đó sẽ được trình bày dưới đây (nếu anh em nào đã nghiên cứu về malware chắc sẽ chẳng lạ gì phương pháp của tác giả Tan Chew Keong)

Phương pháp:

  1. Sử dụng hàm API là CreateProcess với tham số là CREATE_SUSPENDED để tạo ra một suspended process từ bất kỳ file EXE nào. (Ta gọi đây là file EXE thứ nhất).
  2. Gọi hàm API là GetThreadContext để lấy thông tin về giá trị thanh ghi (thread context) của suspended process. Thanh ghi EBX của suspended process sẽ trỏ tới process’s PEB. Thanh ghi EAX chứa entry point của process (file EXE thứ nhất)
  3. Lấy địa chỉ base-address của suspended process từ thông tin PEB có được, ví dụ tại [EBX+8]
  4. Tải file EXE thứ hai vào trong memory (sử dụng API ReadFile) và cần thực hiện việc alignment bằng tay.
  5. Nếu như file EXE thứ hai có cùng địa chỉ base-address như the suspended process và kích thước image-size của nó <= image-size của suspended process, việc thực hiện rất đơn già là sử dụng hàm API WriteProcessMemory để ghi ảnh (image) của file EXE thứ hai vào trong không gian bộ nhớ của suspended process, nơi ghi bắt đầu từ địa chỉ base-address.
  6. Cách khác, unmap image của file EXE đầu tiên bằng việc sử dụng hàm API ZwUnmapViewOfSection (exported by ntdll.dll) và sử dụng hàm API VirtualAllocEx để cấp pháp đủ bộ nhớ cho file EXE thứ hai bên trong không gian bộ nhớ của suspended process. Hàm API VirtualAllocEx phải được cấp địa chỉ base-address của file EXE thứ hai để đảm bảo rằng Windows sẽ cung cấp memory theo vùng yêu cầu. Tiếp theo, sap chép image của file EXE thứ hai vào trong không gian bộ nhớ của suspended process bắt đầu tại địa chỉ đã được cấp phát (allocated address) (thực hiện bằng cách sử dụng hàm WriteProcessMemory).
  7. Tiếp theo tiến hành Patch địa chỉ base-address của file EXE thứ hai trong PEB của suspended process tại [EBX+8]
  8. Thiết lập thanh ghi EAX của thread context thành entry point của file EXE thứ hai
  9. Sử dụng hàm API SetThreadContext để chỉnh sửa thread context của suspended process
  10. Cuối cùng sử dụng hàm API ResumeThread khôi phục lại thực thi của (resume execute) của suspended process.

Thông thường khi các bạn load một file bị packed (packed executable) vào trong Ollydbg thì OllyDBG sẽ bật các cảnh báo tương tự như sau: “The code section is compressed” hay “The entrypoint is outside the code section“, điều này có nghĩa là OllyDBG thông báo cho ta biết file đó đã bị packed. Tuy nhiên, đối với những file đã bị crypted bởi trình crypter (thông qua việc áp dụng phương pháp trên) thì OllyDBG sẽ không đưa ra bất kỳ cảnh báo nào và vẫn load file một cách bình thường.

II. Phân tích

Đầu tiên dùng PeiD để scan file:
PeID scan

Với thông tin của PEiD cung cấp thì mọi thứ hoàn toàn bình thường. Tiến hành load file vào trong Olly, không nhận được bất kỳ cảnh báo nào:

Giờ sẽ tiến hành kiểm tra chương trình xem nó là bình thường hay là file độc hại. Theo phương pháp đã mô tả ở trên nó phải tạo ra một tiến trình mới. Do vậy, đặt BP tại các hàm API là CreateProcessACreateProcessW. Nếu khi run trong Olly và break tại hàm thì quan sát xem tham số truyền vào của hàm có phải là SUSPENDED MODE hay không (tương ứng với dwCreationFlags = CREATE_SUSPENDED). Thực hiện đặt BP và run trong Olly sẽ break tại hàm CreateProcessA, quan sát các tham số truyền vào cho hàm tại màn hình Stack sẽ thấy được giá trị CREATE_SUSPENDED:

Như vậy, nó gọi hàm CreateProcess ở chế độ suspended mode (suspend its main thread) và sau đó giải mã encrypted malware vào không gian địa chỉ của tiến trình mới đã được tạo, sau khi xong việc nó sẽ gọi tới hàm API ResumeThread.

Do bước cuối cùng nó sẽ gọi tới hàm ResumeThread, vậy đặt một BP tại hàm này và nhấn F9 tại Olly:

Olly dừng lại tại hàm ResumeThread, nhấn F7 để trace vào trong hàm.

Có thể thấy rằng bên trong hàm ResumeThread lại gọi tới native API là NtResumeThread

Chú ý: NtResumeThread là một Undocumneted API. Hầu hết các hàm API của Windows đều hoat động theo cơ chế này. Chúng cung cấp một hàm chính với các thông tin cụ thể về hàm nhưng sau đó trong nội bộ của hàm lại gọi tới các hàm APIs được liệt vào dạng undocumented. Khái niệm này rất quan trọng bởi đôi lúc các tác giả của các trình Crypter sẽ sử dụng undocumented APIs thay vì các Documented APIs. Lấy ví dụ, họ có thể gọi trực tiếp luôn hàm NtResumeThread thay vì gọi đến hàm ResumeThread. Trong trường hợp này nếu như ta đặt BP tại hàm ResumeThread thì OllyDBG sẽ không thể break. Do vậy, khuyến nghị nên đặt BP tại undocumented APIs. Nếu như trên ta đặt BP tại NtResumeThread thì sẽ break tại địa chỉ 75A0C3D5 chứ không phải 75A0C3C9.

Hiện tại, ta đang dừng tại NtResumeThread, nhần F7 để trace vào trong hàm và tiếp tục nhấn cho tới khi dừng lại tại 778764F2:

Đây là nơi mà hàm ResumeThread thực sự được thực thi và suspended process cũng sẽ bắt đầu thực thi, tuy nhiên để tránh bị lây nhiễm (infected) thì ta không muốn điều này xảy ra. Do đó, chúng ta dừng lại tại đây. Giờ mở Process Explorer lên và tiến hành dump tiến trình (ở đây chính là child process). Lựa chọn tiến trình con và chọn full dump:

File sau khi dump sẽ được lưu dưới dạng .dmp, đổi tên file thành dump.exe và scan thử bằng PEiD:

Trắng trơn chả có gì và nhận được thông tin là “Not a valid PE file”. Tiến hành fix lại file, PE file thường bắt đầu với dấu hiệu “MZ”, do vậy các trình detect như PeiD đầu tiên sẽ kiểm tra xem file có chứa MZ ở đầu hay không … nếu không thì có nghĩa đây không phải là một PE file hợp lệ. Để fix ta sử dụng Hex Workshop để mở file dump.exe, tìm kiếm chuỗi “MZ”, sau đó xóa toàn bộ những gì phỉa trên “MZ”. Sau khi xóa xong thì lưu lại:

Giờ scan lại bằng PeiD, kết quả có được như sau:

Có vẻ OK rồi, thử chạy file thì nhận được thông báo:

File không chạy được, có vẻ ta đang mất quá nhiều thời gian vào việc fix nó thì phải? Việc fix ở trên mới chỉ là biến file dump trở thành một PE file hợp lệ, tiếp theo ta cần phải tìm OEP cho nó bằng cách load vào OllyDBG hoặc sử dụng các trình PE tools. OEP là Original Entry Point, nó là địa chỉ mà từ đó chương trình bắt đầu thực thi.

III. Tại sao cần OEP

Như đã thấy qua các bước ở trên, chúng ta dump chương trình trước khi hàm ResumeThread thực thi nhưng file dump lại không hoạt động. Giả sử rằng các crypted program là malware vì vậy ta không muốn chúng chạy, vậy thì sau đó làm cách nào để ta có thể khiến nó hoạt động. Ý tưởng ở đây là thay đổi hai bytes đầu tiên tại Program entry point để nó bị bẫy trong một vòng lặp vô tận (infinite loop), với cách này nó sẽ không thể được thực thi và tất cả mọi thứ sẽ được đặt một cách chính xác, do vậy lúc đó là cơ hội tốt để tiến hành dump file. (Cách patch vòng lặp vô tận này anh em nào unpack Armadillo nhiều sẽ biết).

Đầu tiên tìm OEP của file mà ta đã dump bằng cách load file vào OllyDBG. Lưu ý, nên ghi lại các bytes bắt đầu tại entry point.

Ta có: 0048847F <ModuleEntryPoint>    6A 60   PUSH 60

EntryPoint là: 0048847F. Hai bytes đầu tiên là:  6A 60

Giờ tiến hành fix, ta load file Crypted.exe trong Olly, thực hiện các bước như đã làm cho tới khi dừng lại tại địa chỉ 7C90EB8D  0F34   SYSENTER sau khi đã trace vào trong hàm ResumeThread. Đó là điểm mà tại đây quá trình thực thi thực sự sẽ diễn ra. Bây giờ, ta phải thay đổi hay bytes đầu tiên tại Entry Point để bẫy chương trình vào vòng lặp vô tận. Sử dụng công cụ PUPE để thực hiện:

Quan sát trên hình, trong Process Explorer có thể thấy tiến trình con Crypted.exe, tiến trình này có process id là 544. Chuyển qua dạng HEX sẽ có giá trị là 0x220. Dùng PUPE để tìm:

Lựa chọn file có Process ID tương ứng và chọn Patch. Tại màn hình Patch, tiến hành Patch như sau:

Nhập thông tin OEP vào Direction và nhấn Search sẽ có được các bytes là 6A 60 (hãy nhỡ những bytes này để sau này còn khôi phục lại). Tiếp theo điền EB FE vào To change by (EB FE là lệnh nhảy tới cùng một lệnh do vậy tạo ra một vòng lặp vô tận) và nhấn Patching. Lúc này, các bytes gốc đã được thay bằng EB FE. Quay trở lại Olly và nhấn F9 để run. Sau khi nhấn F9, quan sát sẽ thấy tiến trình của chúng ta đã bị terminated trong OllyDBG.

Đừng lo lắng, điều này không ảnh hưởng gì cả. Lúc này chỉ có tiến trình còn là đang chạy (bởi nó đang rơi vào vòng lặp vô tận). Giờ ta sẽ tiến hành dump nó, để dump ta sử dụng công cụ PE Tools:

Lựa chọn Dump Full và save với bất kỳ tên nào bạn muốn, ở đây đặt là final_dump.exe. Sau khi dump xong cũng kill luôn process đi. Mở file final_dump.exe trong OllyDBG:

Như trong hình, hai bytes đầu tiên đang là EB FE, chúng ta sẽ thay lại bằng 2 bytes gốc là 6A 60 tương tự như hình:

Sau khi patch xong thì save file lại, như vậy ta đã thành công trong việc unpack crypted file. Có thể kiểm tra bằng cách run thử file.

PS: Bài tiếp theo sẽ thực hành trên target là ntpacker.

Regards,

m4n0w4r