Chương 6. Phân tích một số cơ chế và hành vi thông thường của mã độ...
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
1. Trang 1
Phân tích Confuser 1.9.0.0
Method proxy và Constructor Proxy Confusion
Tác giả: ubbelol (http://ubbecode.wordpress.com)
Người dịch Levis Nickaster (http://ltops9.wordpress.com)
Đây là tài liệu 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, hãy gửi email cho tôi:
levintaeyeon[at]live[dot]com.
Method proxy là gì?
Rất đơn giản, ý đồ của việc sử dụng method proxy là tìm cách để giấu đi hàm call thật sự, và khiến cho việc xem và hiểu code
sau khi bị dịch ngược trở lên khó khăn hon, chúng ta sẽ không thể biết method nào được gọi. Các bạn có thể nhìn vào ảnh
dưới đây để thấy sự khác biệt của code một method bình thường và 1 method đã được áp dụng method proxy:
ở code phía bên trái, bạn có thể dễ dạng nhìn thấy method nào được sử dụng, nhưng ở code bên phải thì mọi thứ đã trở nên
rối rắm hơn rất nhiều. Và nếu bạn có gắng mò theo những hàm call đã bị obfuscate thì bạn sẽ chỉ nhìn thấy một vài delegate
nhìn giống như thế này:
Như bạn thấy đấy, code sẽ không dẫn trực tiếp đến method gốc sẽ được gọi khi phương pháp này được sử dụng.
2. Trang 2
Constructor proxy là gì?
Về bản chất, Constructor proxy giống với method proxy, nhưng constructor proxy chỉ có thể áp dụng với chỉ lệnh newobj
(trong IL Code) còn method proxy có thể áp dụng với chỉ lệnh call và callvirt. Dưới đây là ví dụ cho thấy một constructor call
bình thường (newobj) và khi đã được sử dụng constructor proxy:
Delegate của constructor proxy nhìn gần giống như delegate của method proxy
Chúng hoạt động thế nào?
Hãy cùng thử xem code của 1 hàm sử dụng proxy xem sao. Bạn sẽ thấy rằng, method này có các tham số truyền vào và kiểu
trả về giống y như hàm gốc. với phần code bên trong hàm như sau:
Chỉ lệnh đầu tiên (ldsfld) có nghĩa là load static field, load một trường kiểu tĩnh (static field) vào stack. Chỉ lệnh tiếp theo là
load argument tại ví trí 0. Sẽ có rất nhiều chỉ lệnh “ldarg” dựa trên số lượng tham số của method gốc. Sau đó chương trình
sẽ tiến hành invoke delegate và trả về 1 giá trị nào đó. Chỉ đơn giản là vậy, tuy nhiên có 1 constructor ở bên trong delegate
đó có code như sau:
Phàn code này làm nhiệm vụ chuyển metadata token (của delegate nói trên) thành 1 runtime handle, và sau đó gọi 1
method trong <Module> với handle đó. Có mấy thứ cần chú ý trong method được gọi. Thử nhìn vào mấy dòng code đầu tiên
để xem chương trình làm những gì:
Dòng đầu tiên thì không phải giải thích nữa. Dòng thứ 2 đọc signature của trường (field) từ trong #Blob sream ở metadata.
Mặc dù đó không phải là 1 signature thông thường, nhưng chúng ta sẽ phân tích phần này sau. Sauk hi làm được các bước
trên, chương trình sẽ tiến hành lấy method dựa trên signature có được.
3. Trang 3
Sau đó, chương trình sẽ tạo 1 dynamic method:
Phân này tương đối dễ hiểu. Chương trình sẽ tạo 1 dynamic method để gọi method vừa có được sau khi sử dụng signature ở
phía trên với các tham số gốc của method đó. Phần cuối code đặt giá trị cho trường static delegate để nó có thể sử dụng
được dynamic method vừa mới tạo. Đến đây thì delegate đã được hoàn thành và chương trình đã có thể sử dụng nó.
Như tôi đã nói ở trên, signature của method không bình thường. Confuser đã thêm vào signature gốc rất nhiều thông tin
khác. Hẫy thử lấy 1 delegate bất kì và xem thử signature của nó trong #Blob stream:
Byte màu đỏ là byte 0x06 mặc định
Bye màu da cam cho thấy fieldtype có phải là ValueType hay không.
Byte nằm giữa màu da cam và màu vàng (0x20) biểu diễn các kiểu của các trường (field type) cho metadata token của
những thành phần trong method (phần này có thể hiểu là thành phần trong method -> metadata token -> field type -> lưu
lại trong byte này, đọc thêm về .NET file format).
Byte màu vàng là 1 byte đêm (không có tác dụng cụ thể)
Các byte màu xanh dương là byte ngẫu nhiên
Byte màu đen biểu hiện cho tokentype (thường là giá trị của MemberRef)
Các byte màu xanh lục là bản ghi định danh kiểu Uint32 (RID – Record Indentifier) được xor với một key được tạo ngẫu
nhiên. Xem thêm ở đây: http://msdn.microsoft.com/en-us/library/ms404456.aspx
4. Trang 4
Làm thể nào để dịch ngược?
Bạn có thể nghĩ rằng việc dịch ngược các proxy kiểu này rất phức tạp, nhưng thực tế nó là một quá trình rất rõ ràng. Tất cả
nhứng thông tin chúng ta cần đều nằm trong signature của proxy tôi đã nói ở phía trên. Việc đầu tiên bạn cần làm là dùng
de4dot để clean, chuyển tên của các thành phần trong file về dạng có thể đọc được.
Sauk hi sử dụng de4dot, mở file vừa tạo được ra trong 1 trình decompiler nào đó và mở xem code của 1 method bất kì. Bạn
sẽ nhìn thấy giống như thế này:
Hãy thử xem proxy call đầu tiên (Delegate48.method_0()) sẽ làm gì. Bấm vào method _0() để chuyển tới nơi gọi là proxy
bridge ( hàm trung gian). Tiếp tục mò theo lệnh call đầu tiên trong proxy bridge đó:
Trong ví dụ này, chương trình sử dụng delegate48. Mở CFF Explorer lên, chuyền vào .NET Directory -> Metadata streams ->
#- -> Tables -> Fields. Tìm delegate đó trong danh sách hiện ra:
Copy lấy giá trị ở cột “Value” của Signature, và chuyển đến .NET Directory -> Metadata Streams ->#Blob. Bấm vào biểu
tượng “Goto offset” và điền giá trị copy được lúc nãy vào. Ta sẽ cùng phân tích signature này để tìm ra hàm call gốc.
Ghi lại các byte màu xanh lục, theo đúng thứ tự từ trái sang phải (0x94, 0xCC, 0xF7, 0x1D)
Ghi thêm lại byte màu đen (0x0A)
5. Trang 5
Bây giờ ta cần tìm thêm key được sử dụng để xor các byte màu xanh. Trong decompiler hãy xem code của method
constructor (.cctor) của một method proxy bất kì, sau đó lần theo chỉ lệnh call đầu tiên:
Key nằm ở trong method đó :
Bây giờ tạo 1 chương trình C# đơn giản để thực hiện công việc. Chúng ta có RID là 0x94, 0xCC, 0xF7,0x1D. và key để xor là
502779056.
Để lấy metadata token thì sau khi thực hiện xor, ta thực hiên or (phép toán bitwise) với 0x0A (byte màu đen):
Bây giò chúng ta đã có được metadata token, thực hiện phép toán sau để lấy RID:
6. Trang 6
Trong ví dụ này. RID = 36. Mở CFF Explorer lên, chuyển đến .NET Directory -> Metadata streams -> #- -> Tables ->
MemberRef. Tìm đến phần tử có số 36 trong danh sách hiện ra:
Bây giờ hiến hành thay thế lệnh call đến proxy của method thành method gốc này (EnableVisualStyles). Thực hiện công việc
với các proxy còn lại nếu bạn thấy cần thiết.
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: lvintaeyeon[at]live[dot]com or
my personal blog: http://ltops9.wordpress.com
Enjoy and best regards
Levis
Created Aug 8 2014