decrypt các msil methods một cách thử công - bài dịch

11
Trang 1 Decrypt các MSIL methods một cách thủ công (Tài liệu viết bởi ubbelol cho http://rtn-team.cc) Người dịch: Levis Nickaster (http://ltops9.wordpress.com ). Đây là bài dịch MIỄN PHÍ, nhằm phục vụ mục đích nghiên cứu và chia sẻ kiến thức. Khi copy và đăng tải ở bất kì đâu cần ghi rõ nguồn để tôn trọng tác giả cũng như tôn trọng người dịch, cảm ơn. Đề xuất, đóng góp để bản dịch tốt hơn, hay gửi email cho tôi: levintaeyeon[at]live[dot]com. Trong bài viết này tôi sẽ trình bày với các bạn phương pháp sử dụng Windbg để decrypt hầu hết các MSIL method đã bị encrypt (bởi các trình obfuscator/protector). Công cụ sử dụng trong bài viết: - .NET Reflector - Windbg File để thực hành: http://www.multiupload.nl/DM4X60SQ8N (hiện tại link đã bị die, tác giả chưa update, cho nên bài viết này chỉ mang tính chất tham khảo, cách thực hiện đối với các file khác cũng tương tự) Nếu bạn thấy thích những bài viết như thế này, hãy ghé thăm blog của tôi, nơi mà tôi sẽ cố gắng đăng thêm nhiều bài viết mới : http://ubbecode.wordpress.com Lời giới thiệu Nếu bạn không rành về cách thức hoạt động của JIT compiler trong việc vận hành của CLR Runtime, bạn nên đọc bài viết sau: http://geekswithblogs.net/ilich/archive/2013/07/09/.net-compilation-part-1.- just-in-time-compiler.aspx hoặc một bài vài viết khác trên mạng về vấn đề này để hiểu được những gì cơ bản nhất. Lí do tôi gọi phương pháp này là chung nhất bởi vì tất cả các chương trình .NET được biên dịch thành mã IL đều cần phải biên dịch thêm thành mã máy lúc chương trình đó khởi chạy.. Công việc này được thực hiện bởi mscorjit.dll (.NET 3.5 trở xuống) hoặc clrjit(.NET 4.0 trở lên). 2 dll này đều có một hàm tên là “CompileMethod” để thực hiện việc biên dịch này. Về cơ bản thì các đoạn mã IL sẽ được truyền vào CompileMethod, và ở đây các đoạn mã máy được tạo ra. Điều này có nghĩa là, nếu một obfuscator hay protector nào đó có thể encrypt hoàn toàn IL code của 1 method, thì code vẫn sẽ được truyền vào trong CompileMethod dưới dạng một buffer của IL code một cách rõ ràng (các code bị obfuscated thì vẫn tồn tại, nên hiểu rõ khái niệm encrypt và obfuscate để tránh nhầm lẫn). Chúng ta có thể lợi dụng CompileMethod để dump code gốc (chưa bị encrypt). Một số obfuscator như Agile.NET sử dụng kĩ thuật “code vitualization”, có nghĩa là code sẽ được chuyển thành một dạng bytecode được tùy biến lại, chỉ dành cho obfuscator đó xử lý và vận hành, chứ không còn phải đi qua JIT compiler nữa. cho nên phương pháp tôi nêu ra trong bài viết này sẽ trở nên vô dụng. Tuy nhiên, nếu bạn có hứng thú với việc decrypt dạng bytecode tùy biến này (như Agile.NET đã làm), bạn nên tìm hiểu về de4dot.

Upload: levis-nickaster

Post on 27-May-2015

879 views

Category:

Software


8 download

DESCRIPTION

Decrypt các MSIL methods một cách thủ công Tác giả: ubbelol (http://ubbecode.wordpress.com) Người dịch: Levis Nickaster (http://ltops9.wordpress.com

TRANSCRIPT

Page 1: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 1

Decrypt các MSIL methods một cách thủ công

(Tài liệu viết bởi ubbelol cho http://rtn-team.cc)

Người dịch: Levis Nickaster (http://ltops9.wordpress.com ).

Đây là bài dịch MIỄN PHÍ, nhằm phục vụ mục đích nghiên cứu và chia sẻ kiến thức. Khi copy và đăng tải ở bất kì đâu cần ghi

rõ nguồn để tôn trọng tác giả cũng như tôn trọng người dịch, cảm ơn. Đề xuất, đóng góp để bản dịch tốt hơn, hay gửi

email cho tôi: levintaeyeon[at]live[dot]com.

Trong bài viết này tôi sẽ trình bày với các bạn phương pháp sử dụng Windbg để decrypt hầu hết các MSIL

method đã bị encrypt (bởi các trình obfuscator/protector).

Công cụ sử dụng trong bài viết:

- .NET Reflector

- Windbg

File để thực hành: http://www.multiupload.nl/DM4X60SQ8N (hiện tại link đã bị die, tác giả chưa update, cho

nên bài viết này chỉ mang tính chất tham khảo, cách thực hiện đối với các file khác cũng tương tự)

Nếu bạn thấy thích những bài viết như thế này, hãy ghé thăm blog của tôi, nơi mà tôi sẽ cố gắng đăng thêm

nhiều bài viết mới : http://ubbecode.wordpress.com

Lời giới thiệu

Nếu bạn không rành về cách thức hoạt động của JIT compiler trong việc vận hành của CLR Runtime, bạn nên

đọc bài viết sau: http://geekswithblogs.net/ilich/archive/2013/07/09/.net­compilation­part­1.-

just­in­time­compiler.aspx hoặc một bài vài viết khác trên mạng về vấn đề này để hiểu được những gì cơ bản

nhất. Lí do tôi gọi phương pháp này là chung nhất bởi vì tất cả các chương trình .NET được biên dịch thành mã

IL đều cần phải biên dịch thêm thành mã máy lúc chương trình đó khởi chạy.. Công việc này được thực hiện bởi

mscorjit.dll (.NET 3.5 trở xuống) hoặc clrjit(.NET 4.0 trở lên). 2 dll này đều có một hàm tên là “CompileMethod”

để thực hiện việc biên dịch này. Về cơ bản thì các đoạn mã IL sẽ được truyền vào CompileMethod, và ở đây các

đoạn mã máy được tạo ra. Điều này có nghĩa là, nếu một obfuscator hay protector nào đó có thể encrypt hoàn

toàn IL code của 1 method, thì code vẫn sẽ được truyền vào trong CompileMethod dưới dạng một buffer của IL

code một cách rõ ràng (các code bị obfuscated thì vẫn tồn tại, nên hiểu rõ khái niệm encrypt và obfuscate để

tránh nhầm lẫn). Chúng ta có thể lợi dụng CompileMethod để dump code gốc (chưa bị encrypt).

Một số obfuscator như Agile.NET sử dụng kĩ thuật “code vitualization”, có nghĩa là code sẽ được chuyển thành

một dạng bytecode được tùy biến lại, chỉ dành cho obfuscator đó xử lý và vận hành, chứ không còn phải đi qua

JIT compiler nữa. cho nên phương pháp tôi nêu ra trong bài viết này sẽ trở nên vô dụng. Tuy nhiên, nếu bạn có

hứng thú với việc decrypt dạng bytecode tùy biến này (như Agile.NET đã làm), bạn nên tìm hiểu về de4dot.

Page 2: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 2

Trong ví dụ này tôi sẽ sử dụng một chương trình mẫu phổ biến nhất của JIT hooking, được tạo bởi Daniel

Pistelli, bạn có thể tìm thấy ở đây: http://www.codeproject.com/Articles/26060/NET-Internals-and-Code-

Injection#JIT_Hooking_Example .

Dump Code thủ công từ JIT

Chúng ta cùng thử xem xét file thực hành trong .NET Reflector:

Nhìn rất giống các file .NET thông thường khác. Nhưng nếu ta xem code của method ở entrypoint, chúng ta sẽ

thấy một điều rất lạ:

và đây là IL code

Có một điều gì đó không đúng trong method này. Toàn bộ code của method đã bị encrypt, khiến cho chúng ta

không thể disassemble bằng .NET Reflector. File này còn có 1 loader để khởi động JIT hook, hãy xem thử loader

này trong .NET Reflector:

Page 3: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 3

Chương trình sẽ khởi động 1 OpenFileDialog để chúng ta có thể chọn file để load. Sau đó chương trình sẽ gọi

method HookJIT và sau đó khởi chạy file đã được load. Chúng ta cần chú ý đến method HookJIT này. Hãy xem

thử code của nó:

Vậy là chương trình sử dụng p/invoke để gọi 1 function tên là HookJIT được export ra từ trong 1 file dll tên là

“lgcoree.dll’. Dựa vào thông tin này, ta sẽ load file loader này vào Windbg và đặt 1 breakpoint khi chương trình

tiến hành load module lgcoree. Dùng lệnh:

0: 000>sxe ld:lgcoree

Sau đó dùng lệnh sau để chạy chương trình

0: 000>g

Cửa sổ Open File Dialog sẽ hiện ra, và chúng ta chọn file thực hành. Windbg sẽ dừng lại khi chương trình tiến

hành load module lgcoree, và sẽ có thông báo đại loại như thế này:

ModLoad: 682e0000 682f4000 C:\Users\Mattias\Desktop\jithook\release\lgcoree.dll

eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=7efdd000 edi=0041e100

eip=7745fc62 esp=0041dfd4 ebp=0041e028 iopl=0 nv up ei pl zr na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246

ntdll!ZwMapViewOfSection+0x12:

7745fc62 83c404 add esp,4

Chúng ta có thể xem tất cả các function được export trong module lgcoree:

0: 000>x lgcoree!*

Page 4: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 4

Chúng ta sẽ nhận được thông báo đại loại như sau:

682e1020 lgcoree!HookJIT (<no parameter info>)

Chúng ta có thể đoán rằng đây chính là method thực hiện việc hook CompileMethod của JIT compiler dựa trên

tên của function này. Thông thường chúng ta sẽ cần tiến hành disassemble và phân tích code của method này

để xem nó thực hiện những công việc gì. Nhưng trong bài viết này tôi sẽ bỏ qua phần đó, bởi vì chúng ta đã biết

đến phương pháp hook code trong bài viết tôi đã ghi link ở phần Giới thiệu ở phía trên. Function HookJIT có

cùng signature với CompileMethod. Chương trình sẽ lấy address của method này và thay thế pointer của

CompileMethod trong vtable của mscorjit::CILJit. Chương trình khác sẽ decrypt IL code và gọi CompileMethod

gốc. Tuy nheien chúng ta sẽ để cho method HookJIT hoạt động nhằm mục đích thay đổi pointer đến

CompileMethod trong vtable bằng pointer đến method mới đã được hook. Chúng ta sẽ đặt một breakpoint tại

HookJIT bằng lệnh:

0: 000>bu 682e1020

Và sau đó cho chương trình tiếp tục chạy:

0: 000>g

Ngay lập tức breakpoint sẽ hoạt động:

Breakpoint 0 hit

eax=682e0000 ebx=00000000 ecx=682e128d edx=0015e148 esi=00000001 edi=002ae16c

eip=682e1020 esp=002ae044 ebp=002ae048 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

lgcoree!HookJIT:

682e1020 55 push ebp

Chúng ta sẽ disassemble method HookJIT bằng lệnh:

0: 000>u

Cho đến khi chúng ta đến gần cuối của codemscorjit!CILJit::compileMethod:

Page 5: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 5

6fe95e1a 55 push ebp

6fe95e1b 8bec mov ebp,esp

6fe95e1d 51 push ecx

6fe95e1e 8b4510 mov eax,dword ptr [ebp+10h]

lgcoree!HookJIT+0x6e:

682e108e c70528eb2e6801000000 mov dword ptr [lgcoree!HookJIT+0xdb08 (682eeb28)],1

682e1098 5f pop edi

682e1099 5e pop esi

682e109a 8be5 mov esp,ebp

682e109c 5d pop ebp

682e109d c3 ret

Đặt breakpoint tại lệnh RET và tiếp tục chạy chương trình

0: 000>bu 682e109d

0: 000>g

Breakpoint này đã có tác dụng:

Breakpoint 1 hit

eax=00000001 ebx=00000000 ecx=560a0000 edx=0015e148 esi=00000001 edi=002ae16c

eip=682e109d esp=002ae044 ebp=002ae048 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

lgcoree!HookJIT+0x7d:

682e109d c3 ret

Đến đây thì vtable đã được thay đổi để sử dụng method CompileMethod mới. Ta tạm thời xóa bớt các

breakpoint để tránh nhầm lẫn:

0: 000>bc *

Để lấy address của vtable, chúng ta dùng lệnh:

0: 000>x mscorjit!CILJit::`vftable'

Page 6: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 6

Kết quả:

6fed9404 mscorjit!CILJit::`vftable' = <no type information>

Mở Memory Window từ Menu View -> Memory hoặc bấm ALT+5. Điền vào 6fed9404 trong phần tìm kiếm và

chỉnh Display Format thành “Pointer and Symbol”.

Chúng ta thấy method đã được thay đổi, nếu không thì ở dòng đầu tiên sẽ hiển thị “mscorjit!CompileMethod”

thay vì ‘lgcoree!HookJIT+0x80”. Chúng ta sẽ đặt 1 breakpoint tại address của lgcoree!hookJIT+0x80 và chạy

chương trình:

0: 000>bu 682e10a0

0: 000>g

Breakpoint sẽ hoạt động:

Breakpoint 0 hit

eax=6fee7268 ebx=0041a56c ecx=6fed9404 edx=0000000c esi=0088a168 edi=00000000

eip=682e10a0 esp=0041a3b0 ebp=0041a418 iopl=0 nv up ei pl nz na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206

lgcoree!HookJIT+0x80:

682e10a0 55 push ebp

Signature của CompileMethod có dạng như sau:

typedef int (__stdcall *compileMethod_def)(ULONG_PTR classthis, ICorJitInfo *comp,

CORINFO_METHOD_INFO *info, unsigned flags,

BYTE **nativeEntry, ULONG *nativeSizeOfCode);

Page 7: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 7

CORINFO_METHOD_INFO *info có cấu trúc như sau:

struct CORINFO_METHOD_INFO

{

CORINFO_METHOD_HANDLE ftn;

CORINFO_MODULE_HANDLE scope;

BYTE * ILCode; Pointer trỏ đến IL Code gốc

unsigned ILCodeSize;

unsigned short maxStack;

unsigned short EHcount;

CorInfoOptions options;

CORINFO_SIG_INFO args;

CORINFO_SIG_INFO locals;

};

Muốn xem IL Code gốc chúng ta mở Call Stack Window từ menu View -> Call Stack hoặc bấm ALT+6, bấm vào

“Raw args” và “Heading”, bạn sẽ nhìn thấy:

Danh sách argument hiển thị từ trái sang phải. Có nghĩa là 6fee7268 trở đến ULONG_PTR, 0041a56c trỏ đến

ICorjitInfo* và argument thứ 3 trỏ đến CORINFO_METHOD_INFO*. Nhưng trong cấu trúc của

CORINFO_METHOD_INFO có 2 phần ở trước pointer đến ILCode mà chúng ta cần bỏ qua. Vì vậy

00041a4e4+8 sẽ chứa pointer trỏ đến ILcode. Trong Memory Window chúng ta điền vào 00041a4e4+8:

Page 8: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 8

Dữ liệu trong memory tại 009e205c:

Đây chính là IL Code đã bị encrypt. Vậy decrypt bằng cách nào? Chúng ta chỉ cần để CompileMethod làm thay

việc đó. Disassemble lgcoree!HookJIT+0x80 bằng lệnh:

0: 000>u

Đến khi đến cuối của method:

lgcoree!HookJIT+0x80:

682e10a0 55 push ebp

682e10a1 8bec mov ebp,esp

682e10a3 56 push esi

682e10a4 90 nop

….

lgcoree!HookJIT+0x25b:

682e127b 7508 jne lgcoree!HookJIT+0x265 (682e1285)

682e127d 6a00 push 0

682e127f e8ee070000 call lgcoree!HookJIT+0xa52 (682e1a72)

682e1284 59 pop ecx

682e1285 33c0 xor eax,eax

682e1287 40 inc eax

682e1288 e8e8190000 call lgcoree!HookJIT+0x1c55 (682e2c75)

682e128d c20c00 ret 0Ch

Đặt 1 breakpoint tại lệnh RET và cho chương trình tiếp tục chạy:

0: 000>bu 682e128d

0: 000>g

Page 9: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 9

Kết quả:

Breakpoint 1 hit

eax=00000001 ebx=00000000 ecx=682e128d edx=0000000b esi=00000003 edi=00000001

eip=682e128d esp=078dfb7c ebp=078dfbb8 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202

lgcoree!HookJIT+0x26d:

682e128d c20c00 ret 0Ch

ILCode đã được decrypt. Mở Memory View lên và quay trở lại địa chỉ chứa ILCode bị encrypt lúc trước

(009e205c):

Lưu dữ liệu này lại :

0: 000> .writemem D:\cracking\raw4\ilbody.bin 009e205c L?0x46

Bây giờ việc còn lại cần làm là thay thế IL code bị encrypt trong file thực hành bằng IL code đã được decrypt

trong file ilbody.bin. Mở file thực hành trong CFF Explorer, và lấy địa chỉ RVA của method cần thay đổi:

Copy giá trị này lại và ghi vào phần Address Converter:

Page 10: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 10

Phần Code không bắt đầu ngay tại vị trí của RVA, có 1 phần header nằm ở đó. Tiếp tục tìm kiếm dấu hiện của

ilcode bị encrypt (15 07 1F 0D…, đã có ở phía trên). Trong trường hợp này là tại offset 0000025C. Lưu lại địa chỉ

này và mở file ilbody.bin trong CFF Explorer. Chuyến sang phần Hex Editor và copy tất cả những gì có trong đó,

quay trở lại file thực hành và paste đè vào phần ILCode bị encrypt:

Sau đó lưu file lại. Thế là xong, Main Method bây giờ đã được decrypt, và chúng ta có thể xem code dễ dàng

trong .NET Reflector và chương trình cũng có thể chạy được ổn định:

Page 11: Decrypt các MSIL methods một cách thử công - Bài dịch

Trang 11

Kết luận

Đây là một phương pháp rất nhàm chán để thực hiện 1 cách thủ công vì nó dài và đòi hỏi nhiều thao tác, nên

tôi không khuyến khích bạn làm trừ khi bạn chỉ muốn thử dump 1 hoặc 2 method. Tuy nhiên, tôi nghĩ nó rất

quan trọng để bạn biết được cách làm một cách tường tận. Có một vài công cụ thực hiện công việc tương tự

một cách tự động, ví dụ như JitDumper3 của yck1509. Có một điều cần chú ý là nếu như file bạn làm không

được biên dịch bằng .NET 3.5 hoặc thấp hơn thì bạn sẽ phải sử dụng clrjit.dll thay vì mscorjit.dll, nhưng các

thao tác thì vẫn tương tự. Tất nhiên, tất cả các function hook cũng không giống như thế này. Một số thì làm

method gốc chuyển hướng đến method compile mới thay vì thay đổi địa chỉ trong vtable, vậy nên bạn cần điều

chỉnh cho phù hợp với trường hợp của bạn, đọc thêm về hook tại: http://en.wikipedia.org/wiki/Hooking.

Cảm ơn vì đã đọc, nếu có thắc mắc hoặc thảo luận, phản hồi, hãy gửi cho tôi tại:

http://ubbecode.wordpress.com/2014/04/25/manual­global­decryption­of­msil­methods/

This article is originally created by ubbelol, and has been translated into Vietnamese by me (Levis), so I’m not

the author of it. All credits go to him. Any suggests for better translation in future, feel free to contact me:

levintaeyeon[at]live[dot]com or my personal blog: http://ltops9.wordpress.com

Enjoy and best Regards

Levis

Created Aug 3 2014