Archive for January, 2014

PeStudio 8.01

Posted: January 21, 2014 in PeStudio 8.01
Tags:

PeStudio is a free tool performing the static investigation of any Windows executable binary. A file being analyzed with PeStudio is never launched. Therefore you can evaluate unknown executable and even malware with no risk. PeStudio runs on any Windows Platform and is fully portable, no installation is required. PeStudio does not change the system or leaves anything behind.Among very famous security tools, PeStudio has proudly obtained Rank 4 on the Best 2013 Security Tools.

Indicators

PeStudio shows Indicators as a human-friendly result of the analyzed image. Indicators are grouped into categories according to their severity. Indicators show the potential and the anomalies of the application being analyzed. The classifications are based on XML files provided with PeStudio. By editing the XML file, one can customize the Indicators shown and their severity. Among the indicators, PeStudio shows when an image is compressed using UPX or MPRESS. PeStudio helps you to define the trustworthiness of the application being analyzed.

Virus Detection

PeStudio can query Antivirus engines hosted by Virustotal for the file being analyzed. This feature only sends the MD5 of the file being analyzed. This feature can be switched ON or OFF using an XML file included with PeStudio. PeStudio helps you to determine how suspicious the file being analyzed is.

And more…

Download here:

http://www.winitor.com/tools/PeStudio801.zip

Regards,

Advertisements

1. PE File Format Offsets

Như các bạn biết, có rất rất nhiều tài liệu viết về định dạng của PE file, các tài liệu này cung cấp các thông tin giải thích chi tiết về cấu trúc cũng như ý nghĩa của từng giá trị trong PE file. Vào thời kỳ đỉnh cao của phong độ, tôi đã dành thời gian dịch một mạch tài liệu “PORTABLE EXECUTABLE FILE FORMAT” của tác giả Goppit đăng tải trên diễn đàn ARTeam. Tài liệu dài 76 trang, không quá dày nhưng cũng là thành quả mà tôi đã dành thời gian và tâm huyết để dịch, sau này thấy cũng có một số bạn trẻ lấy làm “Reference” trong phần tài liệu tham khảo khi làm đồ án kết thúc đời sinh viên :). Giờ nhiều khi đọc lại tài liệu thấy có nhiều chỗ ngây ngô, đúng là cái thủa ban đầu ngơ ngác ấy.

Tài liệu thì nhiều, nhưng đôi khi chúng ta chỉ muốn biết offset của một giá trị đặc biệt trong file, do đó phần đầu tiên của bài viết này tôi xin liệt kê lại các offsets của dữ liệu. Xin nhắc lại thông tin chi tiết về các cấu trúc các bạn có thể xem tài liệu dịch của tôi hoặc các nguồn tài liệu khác trên Internet.

DOS MZ Header:

+00
WORD
e_magic
Magic Number MZ ($5A4D)
+02
WORD
e_cblp
Bytes on last page of file
+04
WORD
e_cp
Pages in file
+06 WORD e_crlc Relocations
+08 WORD e_cparhdr Size of header in paragraphs
+0A  (10) WORD e_minalloc Minimum extra paragraphs needed
+0C  (12) WORD e_maxalloc Maximum extra paragraphs needed
+0E  (14) WORD e_ss Initial (relative) SS value
+10  (16) WORD e_sp Initial SP value
+12  (18) WORD e_csum Checksum
+14  (20) WORD e_ip Initial IP value
+16  (22) WORD e_cs Initial (relative) CS value
+18  (24) WORD e_lfarlc File address of relocation table
+1A  (26) WORD e_ovno Overlay number
+1C  (28) Array[4] of WORD e_res Reserved words
+24  (36) WORD e_oemid OEM identifier (for e_oeminfo)
+26  (28) WORD e_oeminfo OEM information; e_oemid specific
+28  (40) Array[10] of WORD e_res2 Reserved words
+3C  (60) DWORD e_lfanew File address of new exe header

PE Header:

+00
DWORD
Signature ($00004550)
+04 WORD Machine
+06 WORD Number of Sections
+08 DWORD TimeDateStamp
+0C  (12) DWORD PointerToSymbolTable
+10  (16) DWORD NumberOfSymbols
+14  (20) WORD SizeOfOptionalHeader
+16  (22) WORD Characteristics

Optional Header:

  – standard fields-  
+18  (24)
WORD
Magic
+1A  (26) BYTE MajorLinkerVersion
+1B  (27) BYTE MinorLinkerVersion
+1C  (28) DWORD SizeOfCode
+20  (32) DWORD SizeOfInitializedData
+24  (36) DWORD SizeOfUnitializedData
+28  (40) DWORD AddressOfEntryPoint
+2C  (44) DWORD BaseOfCode
+30  (48) DWORD BaseOfData
  -NT additional fields-  
+34  (52) DWORD ImageBase
+38  (56) DWORD SectionAlignment
+3C (60) DWORD FileAlignment
+40  (64) WORD MajorOperatingSystemVersion
+42  (66) WORD MinorOperatingSystemVersion
+44  (68) WORD MajorImageVersion
+46  (70) WORD MinorImageVersion
+48  (72) WORD MajorSubsystemVersion
+4A  (74) WORD MinorSubsystemVersion
+4C  (76) DWORD Reserved1
+50  (80) DWORD SizeOfImage
+54  (84) DWORD SizeOfHeaders
+58  (88) DWORD CheckSum
+5C  (92) WORD Subsystem
+5E  (94) WORD DllCharacteristics
+60  (96) DWORD SizeOfStackReserve
+64  (100) DWORD SizeOfStackCommit
+68  (104) DWORD SizeOFHeapReserve
+6C  (108) DWORD SizeOfHeapCommit
+70  (112) DWORD LoaderFlags
+74  (116) DWORD NumberOfRvaAndSizes
+78  (120) DWORD ExportDirectory VA
+7C  (124) DWORD ExportDirectory Size
+80  (128) DWORD ImportDirectory VA
+84  (132) DWORD ImportDirectory Size
+88  (136) DWORD ResourceDirectory VA
+8C  (140) DWORD ResourceDirectory Size
+90  (144) DWORD ExceptionDirectory VA
+94  (148) DWORD ExceptionDirectory Size
+98  (152) DWORD SecurityDirectory VA
+9C  (156) DWORD SecurityDirectory Size
+A0  (160) DWORD BaseRelocationTable VA
+A4  (164) DWORD BaseRelocationTable Size
+A8  (168) DWORD DebugDirectory VA
+AC  (172) DWORD DebugDirectory Size
+B0  (176) DWORD ArchitectureSpecificData VA
+B4  (180) DWORD ArchitectureSpecificData Size
+B8  (184) DWORD RVAofGP VA
+BC  (188) DWORD RVAofGP Size
+C0  (192) DWORD TLSDirectory VA
+C4  (196) DWORD TLSDirectory Size
+C8  (200) DWORD LoadConfigurationDirectory VA
+CC  (204) DWORD LoadConfigurationDirectory Size
+D0  (208) DWORD BoundImportDirectoryinheaders VA
+D4  (212) DWORD BoundImportDirectoryinheaders Size
+D8  (216) DWORD ImportAddressTable VA
+DC  (220) DWORD ImportAddressTable Size
+E0  (224) DWORD DelayLoadImportDescriptors VA
+E4  (228) DWORD DelayLoadImportDescriptors Size
+E8  (232) DWORD COMRuntimedescriptor VA
+EC  (236) DWORD COMRuntimedescriptor Size
+F0  (240) DWORD 0
+F4  (244) DWORD 0

Section Header:
Section đầu tiên sẽ bắt đầu ngay sau Optional Header (+F8: tức là ta có thể tìm tới các section header bắt đầu tại offset F8 kể từ PE header). Section thứ hai sẽ bắt đầu liền sau Section thứ nhất và cứ tiếp tục cho đến Section cuối cùng. Số Section được định nghĩa tại giá trị NumberOfSections tại offset 06 kể từ PE header.

+0
Array[8] of BYTE
Name
+08 DWORD PhysicalAddress / Virtual Size
+0C DWORD VirtualAddress
+10  (16) DWORD SizeOfRawData
+14  (20) DWORD PointerToRawData
+18  (24) DWORD PointerToRelocations
+1C  (28) DWORD PointerToLineNumbers
+20  (32) WORD NumberOfRelocations
+22  (34) WORD NumberOfLineNumbers
+24  (36) DWORD Characteristics

Export Directory:

+0
DWORD
Characteristics
+04 DWORD TimeDateStamp
+08 WORD MajorVersion
+0A WORD MinorVersion
+0C DWORD Name
+10  (16) DWORD Base
+14  (20) DWORD NumberOfFunctions
+18  (24) DWORD NumberOfNames
+1C  (28) DWORD *AddressOfFunctions
+20  (32) DWORD *AddressOfNames
+24  (36) DWORD *AddressOfNameOrdinals

Import Directory:

+0
DWORD
OriginalFirstThunk
+04 DWORD TimeDateStamp
+08 DWORD ForwarderChain
+0C DWORD Name
+10 DWORD FirstThunk

2. Thêm một section mới vào PE file

Sau khi tổng hợp thông tin về các offset như trên, phần này tôi sẽ trình bày cách làm thế nào để thêm một section mới vào trong một PE file, đồng thời chỉnh sửa lại một số giá trị quan trọng tại PE Header để đảm bảo cho file vẫn hợp lệ và section mới sẽ được nạp vào bộ nhớ. Có thể nói, việc thêm được một section mới rất hữu ích và quan trọng khi chúng ta không đủ không gian để thêm một đoạn code của chúng ta ở một nơi nào đó trong PE file. Các bạn có thể tìm thấy nhiều công cụ có khả năng cho phép bạn thực hiện điều này, tuy nhiên việc thực hiện bằng tay luôn đem lại nhiều điều thú vị, nó giúp ta học và hiểu được nhiều hơn về định dạng PE file, đặc biệt trong trường hợp cụ thể này là về các sections. Các công cụ và kiến thức cần thiết để chúng ta có thể thực hiện được công việc này bao gồm:

  • Một PE file để thực hành: trong bài này tôi sử dụng target nhỏ là AddSection.exe.
  • Công cụ Hex editor: có thể sử dụng bất kỳ chương trình nào mà bạn thông thạo. Tôi sử dụng Hex Workshop
  • Công cụ hỗ trợ xem các sections table: ví dụ LordPE, wARK v.v.., trong bài tôi dùng PEditor cho nó đang dạng hóa.
  • Kiến thức về PE Header, Section Header.

Quá trình thực hiện thêm một section vào PE file như sau:

  • Bổ sung thêm các bytes cho section mới:

Việc bổ sung các bytes này chính là xác định kích thước cho section mới sẽ được thêm vào PE File. Đầu tiên, ta sẽ xem thông tin về các section hiện có của AddSection.exe. Load file vào trong PEditor:

Như hình, ta thấy file AddSection.exe hiện đang có 3 sections cùng với các thông tin liên quan tới từng section này. Tiếp theo ta dùng Hex Workshop để mở file AddSection.exe. Giả dụ, tôi cần section mới có độ dài 110h bytes thì phải làm thế nào. Rất dễ dàng, cuộn chuột xuống cuối cùng của file ta sẽ thấy có rất nhiều vùng 00 xuất hiện bắt đầu từ offset AE0h. Để có được 110h bytes, tôi sẽ lựa chọn các bytes từ offset AE0h cho đến khi tại Status Bar thông tin Sel hiển thị 000110h thì dừng, lúc đó điểm kết thúc là offset BEFh:


Sau khi chọn xong, nhấn chuột phải và chọn Copy. Sau khi Copy xong, nhấn chuột vào phần cuối của file, chuột phải và chọn Paste:

Nhấn Yes để chấp nhận, kết quả có được như sau:

Như vậy chúng ta đã có thông tin cho section mới, nó sẽ được bắt đầu tại offset C00h và kết thúc tại offset D0Fh. Độ dài của nó (hay kích thước) là 110h bytes.

  • Sửa PE Header:

Bước tiếp theo ta sẽ phải sửa thông tin tại PE Header. Có 3 thứ ta cần phải chỉnh tại PE Header bao gồm:

  • Tăng số sections (tại offset 06 bắt đầu từ PE Header).
  • Tăng Image Size.
  • Thêm section mới vào section table.

Như chúng ta thấy, dấu hiệu PE (Signature) bắt đầu tại offset B0. Thông tin Number of Sections bắt đầu tại offset 06 tính từ PE Header, tức là tại B0h + 06h = B6h. Và tại offset B6 ta sẽ thấy giá trị sau: 03 00 (đảo ngược lại trật tự các bytes ta sẽ có 00 03), tương ứng với số lượng section ban đầu của file là 3. Như vậy, để bổ sung section mới ta phải tăng giá trị này thêm 1, tức là thành 04 00, như hình minh họa dưới đây:

Bước tiếp theo ta sẽ tăng image size. Quay trở lại PEditor, ta thấy trường Section Alignment có giá trị 1000h và trường Image Size là 4000h.

Vì Section Alginment là 1000h nên section mới của chúng ta ít nhất cũng phải có độ dài 1000h. Do đó, chúng ta sẽ cộng thêm 1000h vào Image Size, tức là 4000h + 1000h = 5000h. Theo thông tin về offset ở trên thì Image Size nằm tại offset 50h trong PE Header, mà PE Header bắt đầu tại B0 nên Image Size sẽ nằm tại B0h + 50h = 100h. Ta chuyển tới offset 100h và sửa các bytes 0040 thành 0050, tương tự như hình:


Vậy là xong bước tăng Image Size, bước cuối chúng ta phải thêm section mới vào trong Section table. Ta cũng biết Section table bắt đầu tại offset F8h trong PE Header. Và một section sẽ có độ dài 28h bytes, vậy section mới của chúng ta sẽ có thông tin như sau:

+0 Array[8] of BYTE Name; Tên của Section là .REA --> 2E 52 45 41 00 00 00 00
+08 DWORD PhysicalAddress / Virtual Size; Virtual size là 110h-> 10 01 00 00
+0C DWORD VirtualAddress; Bắt đầu tại 4000 do .data là 3000 -> 00 40 00 00
+10 (16) DWORD SizeOfRawData; là 110h-> 10 01 00 00
+14 (20) DWORD PointerToRawData; Section mới bắt đầu tại C00 -> 00 0C 00 00
+18 (24) DWORD PointerToRelocations; -> 00 00 00 00
+1C (28) DWORD PointerToLineNumbers; -> 00 00 00 00
+20 (32) WORD NumberOfRelocations; -> 00 00
+22 (34) WORD NumberOfLineNumbers; -> 00 00
+24 (36) DWORD Characteristics; -> C0000040 (tương tự .data section) -> 40 00 00 C0

Với thông tin có được như trên, ta sẽ thêm các dự liệu mới này đằng sau section cuối cùng (là .data) vào trong section table, địa chỉ offset nơi ta bắt đầu thêm sẽ là B0h + F8h + 3*28h = 220h. Thực hiện chỉnh sửa ta sẽ có được như hình minh họa dưới đây:

Sau khi chỉnh sửa xong, lưu lại file đã chỉnh sửa và kiểm tra lại kết quả bằng PEditor:

Như vậy là xong, mọi việc có vẻ không quá khó 🙂 .

3. Tìm hiểu RVAs và Import Tables

Ở phần trên, ta đã tìm hiểu về Section Table cũng như cách thêm một Section vào PE file. Phần này, ta sẽ tìm hiểu về Import Table. Trong Import Table sẽ lưu trữ các hàm từ các DLLs được sử dụng bởi chương trình. Phần này khá thú vị và sẽ phức tạp hơn phần Section Table bởi vì chúng ta sẽ phải làm quen và sử dụng tới RVA. Công cụ sử dụng vẫn chủ yếu là một trình Hex Editor (tôi dùng Hex Workshop như phần trước). Tôi sẽ mô tả qua về Import Table và sau đó sẽ có phần thực hành nhỏ để trực quan.

  • RVA

RVA, tên tiếng anh đầy đủ là Relative Virtual Offset, dịch tiếng Việt nôm na là địa chỉ ảo tương đối. RVA được sử dụng để mô tả một memory offset nếu không biết địa chỉ base address, vì vậy nó không giống như là một file offset. Nếu như bạn biết thông tin về Section Table thì sẽ dễ dàng để tính được một file offset từ một địa chỉ RVA. Để dễ hiểu tôi mở file ví dụ là Utility.exe trong PEditor và tìm thông tin về Section table:

Đầu tiên chúng ta sẽ phải tìm hiểu section nào chứa thông tin RVA, từ đó ta sẽ tính toán ra file offset theo công thức sau:

File Offset := RVA - Virtual Offset + Raw Offset

Lấy ví dụ như sau: Giả sử chúng ta có một RVA là 0x11A0h. Theo quan sát thì ta có thể thấy RVA này nằm trong section .text (vì  1000h < 0x11A0h < 2000h). Raw Offset của section .text là 0x400h. Vì vậy theo công thức trên thì File Offset là 0x11A0 – 0x1000 + 0x400 = 0x5A0.

Ví dụ khác, giả sử RVA là 0x30D2h. Vậy RVA sẽ nằm trong section .data. Do đó, File Offset là 0x30D2 – 0x3000 + 0xA00 = 0xAD2.

Để đỡ phải mất công tính toán bằng tay, ta có thể sử dụng các công cụ hỗ trợ tính toán RVA. Bản thân các trình PE Editor như LordPE hay PEditor cũng có. Ví dụ, đối với PEditor:

  • Import Table:

Như đã biết, tại Offset +0x80 sau PE Signature là một RVA tới Import Directory. Import Directory là một mảng của cấu trúc còn được gọi là IMAGE_IMPORT_DESCRIPTORs. Mỗi một DLL sẽ có một IMAGE_IMPORT_DESCRIPTOR tương ứng được sử dụng bởi PE file. Một cấu trúc IMAGE_IMPORT_DESCRIPTOR sẽ bao gồm:

+0

DWORD

OriginalFirstThunk

+04

DWORD

TimeDateStamp

+08

DWORD

ForwarderChain

+0C

DWORD

Name

+10

DWORD

FirstThunk

  • OriginalFirstThunk: là một RVA trỏ tới một mảng của cấu trúc IMAGE_THUNK_DATAs. Đây cũng là những RVAs, mỗi cấu trúc tương ứng với một imported function. Mảng này không bao giờ thay đổi. Chú ý: Có một số trình linker thiết lập OriginalFirstThunk là các giá trị 0, do vậy ta sẽ sử dụng FirstThunk.
  • TimeDateStamp và ForwarderChain: Chúng ta sẽ không quan tâm đến hai giá trị này.
  • Name: là một RVA trỏ tới tên của DLL.
  • FirstThunk: là một RVA trỏ tới một mảng của cấu trúc IMAGE_THUNK_DATAs. Có thể gọi là một bản sao của mảng đầu tiên. Mảng này sẽ thay đổi!

Chi tiết các thông tin mô tả các bạn có thể xem lại tài liệu mà tôi đã dịch hoặc các nguồn tài liệu khác. Giờ quay trở lại với ví dụ của chúng ta, sử dụng Hex Workshop để mở file Utility.exe. Quan sát sẽ thấy PE signature bắt đầu tại offset 0xB0h. Tìm tới Import Table bằng cách cộng thêm 0x80h ta có 0xB0h + 0x80 = 0x130h, như vậy tại offset 0x130h là một RVA trỏ tới Import Directory:

Như thấy trên hình, ImportDirectory VA có giá trị 0x44200000, đảo ngược lại ta có 0x00002044. Với RVA 0x2044 ta tính ra file offset là 0x844.

Tại Hex Workshop ta tìm tới offset này:

Như vậy, Import Table của chúng ta bắt đầu tài offset 0x0844. Như đã biết, một IMAGE_IMPORT_DESCRIPTOR có độ dài 0x14 bytes (tức là 20 bytes). Do vậy, IMAGE_IMPORT_DESCRIPTOR đầu tiên sẽ bắt đầu từ 0x844 cho tới 0x858 và IMAGE_IMPORT_DESCRIPTOR thứ hai sẽ bắt đầu từ 0x858 cho tới 0x86B. Tới lúc nào bạn thấy có 0x14 bytes có giá trị 0x00 thì đó chính là kết thúc của mảng IMAGE_IMPORT_DESCRIPTORs. Với thông tin như vây, ta biết rằng file của chúng ta import các hàm từ 2 file DLL khác nhau.

Ta hãy xem mảng đầu tiên. Giá trị RVA tới tên của file DLL là tại offset 0x844 + 0x0C = 0x850. Tại offset 0x850 ta có được giá trị 0x0000218E, tính ra file offset sẽ là 0x98E.

Với thông tin trong Hex WorkShop cung cấp, ta có được DLL đầu tiên là USER32.DLL. Giá trị RVA của OriginalFirstThunk là tại offset 0x844, nó có giá trị là 0x2090 -> file offset là 0x890.

Như trên hình, ở đây chúng ta có tới 12 RVA giữa offset 0x890 và 0x8C0 (điều này có nghĩa là file của chúng ta sử dụng 12 hàm của USER32.DLL). Giá RVA tại offset 0x8C0 là 0x0, điều này có nghĩa là đây là kết thúc của mảng. Địa chỉ RVA đầu tiên là 0x2118 -> file offset là 0x918. Chuyển tới offset 0x918 ta có:

Như trên hình, giá trị Hint là 0x019B và tên của hàm là LoadIconA. Đó chính là hàm đầu tiên được chương trình import từ user32.dll. Địa chỉ RVA thứ hai là tại offset 0x894, có giá trị là 0x2124 -> file offset là 0x924. Theo trên hình thì Hint có giá trị 0x01DD và tên của hàm là PostQuitMessage. Cứ tương tự như vậy ta sẽ có được thông tin của 10 hàm còn lại được chương trình Import từ user32.dll.

Thực hiện tương tự như đã làm với DLL tiếp theo.

Giá trị Name sẽ nằm tại offset 0x844 + 0x14 + 0x0C = 0x864. Địa chỉ RVA tại đây là 0x21CE -> file offset có giá trị 0x9CE. Trong Hex Workshop ta sẽ có được DLL thứ hai là KERNEL32.DLL.

Giá trị OriginalFirstThunk kernel32.dll là tại offset 0x844 + 0x14 = 0x858. Giá trị RVA tại đây là 0x2080 -> file offset sẽ là 0x880.

Như trên hình, có 3 RVAs nghĩa là có 3 hàm được sử dụng từ kernel32.dll. Làm tương tự như đã làm với user32.dll ta sẽ có được tên của các hàm:

Tóm tắt các bước thực hiện:

  1. Tìm tới offset 0x80 trong Optional Header để lấy thông tin về địa chỉ RVA của Import Directory.
  2. Tại đó tìm ra mảng của IMAGE_IMPORT_DESCRIPTORs, mỗi mảng có độ dài là 0x14 bytes. Dấu hiệu kết thúc của mảng là 0x14 bytes giá trị 0x00. Số mảng IMAGE_IMPORT_DESCRIPTORs tương đương với số dll được import.
  3. Tại ví trí 0x0C của mỗi mảng ta sẽ có được thông tin RVA, từ địa chỉ RVA này trỏ tới tên của dll đã được import.
  4. Tại vị trí bắt đầu của mỗi mảng IMAGE_IMPORT_DESCRIPTOR, ta có được thông tin về OriginalFirstThunk RVA. Nếu giá trị này là 0x00, ta sẽ sử dụng giá trị FirstThunk RVA tại offset 0x10 trong mảng để thay thế.
  5. Tìm tới đó ta sẽ có được thông tin về mảng các DWORDS, mỗi giá trị trong mảng này sẽ trỏ tới giá trị Hint (WORD) và tên của hàm đã được import. Dấu hiệu kết thúc của mảng này là 8 giá trị 0x00.

Nguồn tham khảo:

Best Regards

m4n0w4r

[Leaked]Hiew v8.40

Posted: January 5, 2014 in [Leaked]Hiew v8.40
Tags:

Features of release VIII:

  • view and edit files of any length in text, hex, and decode modes
  • x86-64 disassembler & assembler (AVX instructions include)
  • physical & logical drive view & edit
  • support for NE, LE, LX, PE/PE32+ and little-endian ELF/ELF64 executable formats
  • support for Netware Loadable Modules like NLM, DSK, LAN,…
  • following direct call/jmp instructions in any executable file with one touch
  • pattern search in disassembler
  • built-in simple 64bit decrypt/crypt system
  • built-in powerful 64bit calculator
  • block operations: read, write, fill, copy, move, insert, delete, crypt
  • multifile search and replace
  • keyboard macros
  • unicode support
  • Hiew Extrenal Module (HEM) support
  • ArmV6 disassembler

Regards,


1. Target

Link download Ntpacker: http://tuts4you.com/download.php?view.980

2. Thực hành unpack

2.1. Cách dùng vòng lặp vô tận

Đầu tiên scan target bằng các trình PE detector. Thử với PEiD trước xem thế nào:

null

Tiếp theo dùng RDG để scan:

Với việc scan bằng PeiD và RDG như trên và với kết quả mà RDG đưa ra ta thấy có vẻ như đây là một dạng Crypter. Nếu đúng là Crypter thì theo phương pháp đã mô tả ở phần trước nó phải tạo ra một tiến trình mới. Do vậy, ta sẽ thử đặt BP tại hàm API là CreateProcessA, nếu khi run trong OllyDBG và break tại hàm, 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). Nếu đúng thì có thể nói nó là Crypter.

Load target vào OllyDBG, ta dừng tại đây:

 photo 1-4-20141-40-09PM.png

Đặt BP tại hàm CreateProcessA, sau đó nhấn F9:

 photo 1-4-20141-42-25PM.png

OllyDBG break tại hàm CreateProcessA, quan sát tại cửa sổ Stack ta thấy :

0012FE2C   00000004  |CreationFlags = CREATE_SUSPENDED

Như vậy, phán đoán của ta đã đúng, với việc dwCreationFlags có giá trị CREATE_SUSPENDED thì nó sẽ tạo process nhưng không được start. Process sẽ không thực thi cho đến chừng nào tiến trình chính (main process) được start thông qua hàm API ResumeThread.

Theo phần trước, 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 OllyDBG:

 photo 1-4-20141-51-54PM.png

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

 photo 1-4-20141-53-34PM.png

Bên trong hàm ResumeThread lại gọi tới native API là được export bởi ntdll.dll là NtResumeThread. Tiếp tục trace tiếp và trace vào trong hàm NtResumeThread cho tới khi dừng lại tại lệnh Sysenter:

 photo 1-4-20141-58-44PM.png

Giờ mở Process Explorer lên và tiến hành dump tiến trình con (child process). Lựa chọn tiến trình con và chọn full dump:

 photo 1-4-20142-01-22PM.png

Save file dump lại dưới tên là UnPackMe_NtPacker1_dump.exe. Scan thử bằng PEID xem thế nào:

 photo 1-4-20142-06-24PM.png

Tiến hành fix lại file dump bằng cách dùng Winhex để mởi file. Tại màn hình Winhex, nhấn tổ hợp phím tắt là Ctrl+Alt+X(Find Hex Values), trong ô text box nhập thông tin cần tìm là 4D 5A (tương ứng với “MZ”):

 photo 1-4-20142-10-43PM.png

Nhấn OK để thực hiện tìm kiếm, kết quả sẽ dừng lại tại đây:

 photo 1-4-20142-11-36PM.png

Tiến hành xóa toàn bộ các giá trị trước đó đi và lưu lại:

 photo 1-4-20142-13-46PM.png

 photo 1-4-20142-14-17PM.png

Đóng Winhex, dùng PEiD để scan lại xem kết quả thế nào:

 photo 1-4-20142-16-15PM.png

Có vẻ OK, tuy nhiên để ý vùng khoanh đỏ sẽ thấy giá trị First Bytes tại EP có vẻ hơi lạ, không giống bình thường đối với một target được code bằng VC++ (bình thường đối với những Target code bằng VC++ 6 trở về trước thường có bytes đầu tiên là 55 8B). Đương nhiên nếu ta run thử file dump sẽ lỗi ngay:

 photo 1-4-20142-22-24PM.png

Với thông tin First bytes như trên, có vẻ nhưng việc Patch vòng lặp vô tận không khả thi vì ta không biết rõ 2 bytes gốc là thế nào để mà khôi phục lại. Tuy nhiên, như đã nói dựa vào kinh nghiệm thì 2 bytes đầu sẽ là 55 8B và ta cứ thử xem thế nào. Giờ restart lại OllyDBG (nhớ kill cái child process bằng Process Explorer), thực hiện các bước như đã làm cho tới khi dừng lại tại địa chỉ lệnh SYSENTER.Chuyển qua Process Explorer để tìm thông tin của child process:

 photo 1-4-20142-53-48PM.png

Chuyển Process ID của child process là 768 về dạng HEX, ta có được giá trị là 0x300. Giờ ta chạy PUPE và tìm đến child process để tiến hành patch:

 photo 1-4-20142-58-02PM.png

Tại màn hình Patch, điền thông tin về EP mà ta có được ở trên vào ô Direction và nhấn Search (PUPE sẽ hiện thị kết quả tìm kiếm là 55 8B J). 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.

 photo 1-4-20143-02-09PM.png

Thoát PUPE, 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.

 photo 1-4-20143-03-12PM.png

Đừ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 tôi sử dụng công cụ SirPE (của tác giả Guan de Dio). Chạy SirPE và chọn child process, sau đó chọn Dump All:

 photo 1-4-20143-06-45PM.png

 photo 1-4-20143-09-56PM.png

Save với bất kỳ tên nào bạn muốn, ở đây tôi đặt là final_dump.exe. Sau khi dump xong cũng kill luôn process đi. Kết quả file dump ta có được như sau:

 photo 1-4-20143-11-54PM.png

Mở file final_dump.exe trong OllyDBG:

 photo 1-4-20143-12-41PM.png

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à 55 8B tương tự như hình:

 photo 1-4-20143-14-37PM.png

Sau khi patch xong thì save file lại, kiểm tra bằng cách run thử file:

 photo 1-4-20143-16-33PM.png

2.2. Cách dump trực tiếp từ OllyDBG

Cách này khả đơn giản, đầu tiên ta load file vào trong OllyDBG. Đi vào lệnh call tại địa chỉ : 10001E13   .  E8 70FCFFFF   call    10001A88 ta sẽ thấy được đoạn code bên dưới như sau:

 photo 1-4-20144-27-38PM.png

Như vậy, theo lý thuyết mô tả ở phần trước thì hàm API VirtualAllocEx dùng để cấp pháp đủ bộ nhớ cho file EXE thứ hai bên trong không gian bộ nhớ của suspended process. Tiếp theo, sau khi bộ nhớ đã được cấp phát sẽ thực hiện gọi hàm API WriteProcessMemory ghi dữ liệu từ điểm bắt đầu của PE file (file EXE thứ hai) vào trong vùng nhớ vừa được cấp phát.

Với cách phân tích này, ta sẽ tìm tới vùng buffer chứa thông tin về file thứ hai rồi dump ra ngay trong OllyDBG. Tiến hành đặt BP tại hàm API WriteProcessMemory, nhấn F9 ta dừng lại tại đây:

 photo 1-4-20144-35-32PM.png

Chú ý vùng buffer, đây là nơi chứa thông tin về PE file. Follow in dump tại địa chỉ này ta có được như sau:

 photo 1-4-20144-37-36PM.png

Ok tại cửa số dump, chuột phải chọn Backup > Save data to file:

 photo 1-4-20144-43-56PM.png

Lưu lại dưới tên là dumped.exe. File này sẽ chứa toàn bộ dữ liệu bắt đầu từ 00160000. Do vậy để biến nó thành một valid PE file thì phải tìm đến đoạn chứa “MZ” và xóa dữ liệu trước “MZ” đi và lưu lại. Dùng winhex để xóa, kết quả sau khi xóa như sau:

 photo 1-4-20144-51-23PM.png

Scan file dumped.exe bằng PEiD:

 photo 1-4-20144-53-49PM.png

Có thể thấy là mặc dù đã được fix thành valid PE file tuy nhiên thì file dumped.exe không có đầy đủ icon như file final_dump.exe, và chắc chắn một điều là file dumped.exe khi run sẽ bị crash. Vậy ta làm thế nào tiếp theo? Tôi mở đại LordPE để rebuild thử xem có được không. Kết quả rebuild bằng LordPE như sau:

 photo 1-4-20144-57-04PM.png

Sau khi rebuild xong thì kết quả đã có icon. Chạy thử OK:

 photo 1-4-20144-57-53PM.png

Regards,

m4n0w4r


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