1. Tùy biến Confuser – Những điều đơn giản nhất
Trang 1
Tác giả: Levis Nickaster (http://ltops9.wordpress.com)
Lời mở đầu – Vài vấn đề cần nói
Trong quá trình tìm hiểu về Reverse Engineering .NET của tôi, tôi nhận thấy việc dịch ngược một chương trình viết bằng .NET rất
dễ dàng khi mà kiến trúc của .NET PE File lưu trữ rất nhiều thông tin quan trọng, từ đó người ta đã tạo ra rất nhiều các chương
trình decompiler/scanner mạnh mẽ đến mức có thể lấy code từ 1 file đã được compile, mang so sánh với mã nguồn gốc thì tỉ lệ
trùng khớp lên đến trên 95%. Có rất nhiều “người xấu” đã lợi dụng điểm yếu này để lấy trộm mã nguồn và xây dựng lại chương
trình (hay gọi là những kẻ ripper chuyên đi “rút source”). Nếu bạn là 1 developer, tôi đoán chắc rằng bạn sẽ không muốn 1 sản
phẩm mình bỏ rất nhiều công sức, thời gian để viết ra và biến thành bản thương mại, nhưng chỉ vài ngày sau đã xuất hiện một
sản phẩm khác với tính năng, giao diện giống ý đúc, nhưng được viết bởi một người khác. Chưa nói đến chuyện bẻ khóa phần
mềm, tôi mới chỉ bản đến việc bị đánh cắp mã nguồn.
Trong quá trình tìm hiểu, tôi đã thử phân tích hàng loạt các soft thương mại được viết bằng .NET của rất nhiều công ty, tổ chức,
cá nhân, cả ở nước ngoài lẫn ở Việt Nam, và nhận thấy hầu hết các phần mềm đó được bảo vệ rất kém. Chia thành hai trường
hợp:
- Không bảo vệ: các file .NET hoàn toàn không được bảo vệ bằng bất cứ chương trình nào. Và điều này khiến nhiều tên
ripper rất thích, chúng chỉ việc ném vào trong 1 trình decompiler và bấm vài nút, chúng đã có mã nguồn trong tay, và tiến
hành chỉnh sửa, xây dựng lại chương trình
- Được bảo vệ: Gọi là được bảo vệ nhưng các developer thường thường là hay phó mặc toàn bộ cho các obfuscator/packer
của bên thứ 3 họ sử dụng, họ chỉ biết đưa chương trình của mình vào, chọn và bấm nút, và không biết, cũng không chịu
tìm hiểu xem chương trình của mình đã được bảo vệ như thế nào, có an toàn hay không. Kèm vào đó các công ty cung
cấp các protector/obfuscator thường tung ra rất nhiều các câu chào mời, dụ dỗ đến đảm bảo như kiểu “sử dụng sản
phẩm của chúng tôi, bạn sẽ được an toàn”, “Sản phẩm mà chúng tôi cung cấp được đánh giá tốt nhất trên thị trường”,…
Những câu quảng cáo như thế dễ gây ra sự ảo tưởng về độ an toàn.
Trên thực tế, có rất nhiều công cụ, phương pháp được giới cracker/reverser/ripper sử dụng để vượt qua các lớp bảo vệ phần
mềm. Không có lớp bảo vệ nào là an toàn tuyệt đối cả, vậy nên không nên quá tin tưởng vào bất cứ một tuyên bố bảo vệ an toàn
nào của các công ty sản xuất protector/obfuscator. Có thể, bạn vẫn sẽ bị mất tiền để chi trả mua sản phẩm của các công ty đó,
nhưng sản phẩm của bạn không được bảo vệ một cách chắc chắn.
Trong .NET có 1 công cụ nổi tiếng là de4dot, có thể loại bỏ hầu hết các obfuscator/protector phổ biến của .NET. Rất mạnh nhưng
cách sử dụng lại hết sức đơn giản, vì vậy không mấy ngạc nhiên khi lớp bảo vệ phần mềm có thể vượt qua chỉ trong nháy mắt, và
người khác vẫn có thể tiếp cận đến code trong chương trình của bạn.
Hay có những phương pháp static analyze để phân tích chính code của các lớp bảo vệ (do đặc thù của .NET) để tìm ra phương
pháp loại bỏ chúng, lấy lại chương trình gốc, hoặc sử dụng dumping… Cracker/Reverser nhiều kinh nghiệm họ có vô vàn các cách
để tìm và loại bỏ các lớp bảo vệ đó. Mà các chương trình obfuscator/protector thường cập nhật rất chậm. Một bên thì phá rất
nhanh, còn một bên thì xây rất chậm, thì bạn hình dung mọi chuyện sẽ như thế nào?
Một sai lầm nữa của các developer là đặt niềm tin vào các commercial application. Vì các sản phẩm này thường thường là closed-source,
và chúng có độ tùy biến không cao. Một obfuscator/protector được nhiều người sử dụng -> nó trở nên phổ biến -> biến
thành đích ngắm của các reverser/cracker. Đến lúc nó bị hạ gục, thì bạn sẽ chẳng biết làm gì ngoài việc trông chờ vào một bản cập
nhật mới hơn, hay một giải pháp từ nhà phát triển của obfuscator/protector đó. Bạn sẽ không thể nào tự bảo vệ.
2. May mắn ở đây là có 1 obfuscator rất nổi tiếng, miễn phí và mã nguồn mở. Confuser, tôi nghĩ bạn biết đến cái tên này. Đây là
obfuscator được viết bởi yck1509, một reverser và .NET coder rất giỏi. Lý do khiến cho confuser trở nên mạnh mẽ là nó được xây
dựng dựa trên góc nhìn của 1 reverser có nhiều kinh nghiệm, nên nó bao gồm rất nhiều tính năng ưu việt. Bên cạnh đó có thể còn
là mối quan hệ thân thiết của yck1509 và 0xd4d (tác giả de4dot), nên 0xd4d không đưa phần deobfuscate Confuser vào bên trong
bản de4dot được release public. Thế nên Confuser có thể coi là an toàn hơn so với các sản phẩm thương mại khác. Tuy nhiên,
việc deobfuscator Confuser không phải không thực hiện được. Như tôi nói ở phía trên, không có kiểu bảo vệ nào là an toàn tuyệt
đối. 0xd4d đã có một bài viết rất chi tiết nói về việc unpack Confuser được đăng trên các forum nổi tiếng như tuts4you, hay
b@s,... Nếu làm theo bài hướng dẫn đó, thì có thể unpack/deobfuscate Confuser và đọc được code của chương trình gốc.
Nhưng Confuser có 1 ưu điểm nữa là nó là mã nguồn mở, vì vậy chúng ta có thể download source về, tiến hành cải tiến theo ý
riêng của chúng ta để khiến việc bảo vệ trở nên an toàn hơn, khiến cracker/reverser mất nhiều thời gian hơn để vượt qua.
Trong bài viết này, tôi sẽ nói về một cách cơ bản để tùy biến đôi chút mã nguồn trong Confuser, để tạo ra một lớp bảo vệ an toàn
hơn, có thể phần nào làm khó các cracker/reverser. Với những người có nhiều kinh nghiệm và kiến thức, họ hoàn toàn có thể
vượt qua, nhưng đối với những người ít kinh nghiệm, chỉ biết dập khuôn làm theo hướng dẫn có sẵn hoặc những kẻ chỉ biết sử
dụng tool, họ sẽ hoàn toàn bị bế tắc và không biết giải quyết thế nào. Điều đó khiến cho chúng ta cảm thấy yên tâm hơn, bởi
những người giàu kinh nghiệm thường sẽ chỉ động vào chương trình của bạn nếu như họ cảm thấy thú vị, và họ dành nhiều thời
gian để nghiên cứu nhiều hơn là việc đi bẻ khóa hoặc ăn cắp mã nguồn của chương trình. Việc tùy biến này giúp bạn có thể tạo ra
một obfuscator đặc biệt mà không có bất cứ một công cụ nào hỗ trợ vượt qua hay bài hướng dẫn nào nói đến. Điều này làm tăng
tính bảo vệ lên rất nhiều.
Thời điểm hiện tại M$ đang nghiên cứu phát triển một công nghệ khác, tôi cũng không có nhiều thông tin nhưng hình như nó có
tên là “native .NET”. Có nghĩa là các chương trình .NET thay vì được compile về IL code và chạy bằng CLR/JIT thì bây giờ sẽ được
compile thành các file native PE và chạy trực tiếp. Điều này loại bỏ hoàn toàn các công cụ Reverse Engineering .NET hiện đại và sẽ
khó khăn hơn trong việc dịch ngược để xem mã nguồn. Nhưng đây là công nghệ của tương lại, tất cả vẫn còn nằm trong phòng thí
nghiệm, tôi chỉ nói ở đây nhằm cung cấp cho các bạn một chút thông tin, chứ hiện tại công nghệ đó chưa được phổ biến và áp
dụng đại trà.
Trang 2
OK, hãy bắt đầu việc tùy biến Confuser
Tùy biến Confuser
Để bắt đầu việc tùy biến Confuser, bạn cần phải xác định được:
- Phương pháp nào được sử dụng để loại bỏ Confuser
- Các công cụ, kiến thức liên quan được sử dụng trong phương pháp đó
- Cách chống lại công cụ, kiến thức đó
Sau đó bạn cần phải có:
- Kiến thức về lập trình C# (Confuser được viết bằng C#)
- Kiến thức về cách hoạt động của .NET và Confuser
- .NET Framework và 1 IDE để có thể tiến hành tùy biến mã nguồn
Trong bài viết này, tôi sẽ tập trung vào phân tích và viết code để tìm cách chống lại phương pháp 0xd4d đã nhắc đến trong bài
viết của anh ta. Chỉ là một phương pháp hết sức cơ bản, như là một ví dụ cho việc tùy biến.
Trong bài viết của 0xd4d (https://forum.tuts4you.com/topic/30207-net-decrypt-confuser-19-methods/) , anh ta có nhắc đến việc
unpack Confuser Maximum Protection, bằng việc dump sử dụng WinDbg. Các bạn có thể tìm đọc bài viết đó để biết thêm. Chủ
3. yếu phương pháp này là dùng windbg set 1 breakpoint tại System.Runtime.InteropServices.Marshal.GetHINSTANCE
(là code đầu tiên được sử dụng bởi method Anti-tamper trong Confuser), nhằm mục đích tìm và dump file với tất cả code đã
dược decrypt bởi method Anti-tamper.
Vậy ở đây tôi cần chống lại phương pháp dump này. Có nghĩa là tôi phải tìm cách chống lại WinDbg. Muốn chống lại được WinDbg
thì phải xác định được WinDbg có đang được sử dụng hay không. Lang thang trên mạng 1 lúc, tôi tìm được thông tin ở đây:
http://www.codeproject.com/Articles/30815/An-Anti-Reverse-Engineering-Guide#WinDbgFW
Bởi vì WinDbg ít được sử dụng hơn cho nên người ta ít tìm hiểu các phương pháp anti-WinDbg. Tất cả những gì tôi tìm được là sử
dụng FindWindow với classname “WindbgFrameClass” của WinDbg. Nếu như tìm được WinDbg đang chạy thì thực hiện các
phương phấp nhằm tránh khỏi WinDbg (crash, thoát khỏi chương trình, disable WinDbg…). Như thế là có thể tránh khỏi việc bị
dump bởi WinDbg. Nhưng thêm code vào đâu, thêm code như thế nào?
Hãy xem thử source code của Confuser (download tại http://confuser.codeplex.com/SourceControl/latest) . Download toàn bộ
source về, và xem thử. Trong solution có một vài project con:
- Cecil : Thư viện mono.cecil được sử dụng bởi Confuser, dùng để thao tác với các file .NET
- Confuser: GUI của confuser
- Confuser.Console: CLI của Confuser
- Confuser.Core: Phần code các công việc chính của Confuser (cách hoạt động, các phương pháp obfuscation sẽ được áp
Trang 3
dụng lên file)
- Confuser.Core.Injections: phần code sẽ được thêm vào file Exe cần được bảo vệ, làm nhiệm vụ giúp chương trình sau khi
obfuscate vẫn có thể hoạt động tốt và các phương pháp bảo vệ khác (Anti Dump, anti debug, anti-tamper, code
decrypt….)
Như ta đã biết, phương pháp của 0xd4d tập trung vào việc tìm method Anti-tamper, vậy tôi sẽ thử thêm code vào trong method
anti-tamper này để phát hiện WinDbg và xử lý. Nếu không phát hiện ra WinDbg thì mới bắt đầu tiến hành chạy code anti-tamper
để decrypt method. Vậy thì tôi sẽ phải thêm code vào trong phần code anti-tamper sẽ được inject vào chương trình cần được
bảo vệ, nằm trong file AntiTamper.cs của project Confuser.Core.Injections. Mở file này lên, trong file có 2 class của Anti-tamper JIT
và Anti-tamper Mem. Theo như ubbelol (1 .NET guru khác) thì trong confuser chỉ có Anti-tamper Mem được sử dụng. Vậy tôi sẽ
tiến hành thêm code vào method Initalize() là method khởi đầu của class này.
Như trong hình là code mở đầu của method Initalize(). Việc tôi cần làm là sử dụng FindWindow để tìm ra Window Handler
của WinDbg. Mà FindWindow là native API, nằm trong user32.dll, nên tôi cần phải khai báo bằng pinvoke:
[DllImportAttribute(“user32.dll”)]
Static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Thêm code trên vào trước method initialize(). Sau đó trong method Initalize ta có thể sử dụng được FindWindow.
4. Trang 4
Public static unsafe void Initalize()
{
If(FindWindow(“WindbgFrameClass”,null) != (IntPtr)0) // Nếu như tìm thấy Windbg, kết quả trả về sẽ
khác 0
{
<Thêm các code xử lý ở đây>
}
Else //Nếu như không phát hiện ra Windbg thì mới chạy code của Anti-tamper để decrypt
{
<Copy code của Anti-tamper vào đây>
}
}
Như thế thì trước khi breakpoint tại System.Runtime.InteropServices.Marshal.GetHINSTANCE hoạt động thì phần
code phát hiện WinDbg sẽ được chạy trước, và sẽ giúp phát hiện kịp thời để có thể ngăn chặn việc dump file. Khi được biên dịch
bằng JIT sẽ trông như thế này:
Ebp-90h lưu trữ giá trị của kết quả trả về sau khi thực hiện FindWindow, epb-94h lưu trữ 0 (là giá trị dùng để so sánh. Các lệnh
phía dưới thực hiện việc so sánh và kiểm tra. Và kết thúc việc kiểm tra bằng lệnh jne.
Hãy thử compile Confuser, chọn file để bảo vệ, và sử dụng Anti-tamper, sau đó tiến hành debug trong windbg, set breakpoint
giống như trong bài hướng dẫn của 0xd4d để biết két quả. Vd như sau lệnh if tôi thêm vào Environment.Exit(0); thì khi
phát hiện ra Windbg đang chạy, chương trình sẽ quit ngay trước khi bp tại Marshal.GetHINSTANCE có thể hoạt động. Và
cracker/reverser sẽ phải mât thêm thời gian để có thể debug và mò ra được. Việc debug JIT .NET không mấy dễ dàng, đặc biệt
với 1 .NET file đã bị obfuscated.
Từ If(…) đến Exception Handler
Phần này nhằm mục đích mở rộng thêm 1 chút cho phần anti Windbg. Như tôi đã trình bày ở trên, có thể sử dụngj FindWindow
để tìm và chống lại WinDbg ở một mức nào đó, nhưng một lệnh If(…) khá là dễ bị vượt qua vì chỉ cần patch zf (khi debug JIT) hay
brfalse-brtrue (với IL code). Vậy tôi sẽ thay việc sử dụng If(…) bằng việc sử dụng try{..} catch(Exception){…}. Có nghĩa là cứ ngầm
mặc định là WinDbg đang hoạt động, và cho tiến hành các code anti-WinDbg. Chỉ khi xảy ra Exception trong các code đó, thì có
nghĩa là Windbg không hoạt động, và sẽ cho chạy các code của Anti-tamper. Như vậy, quyền quyết định hoạt động của code được
5. giao lại cho Exception Handler của system, thay vì người dùng tự định nghĩa và viết phần quyết định cho code. Như vậy có nghĩa
là phải tìm cách tạo ra 1 exception. Tôi có thể làm được gì?
Đọc trên MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx) thì FindWindow sẽ trả về
Window Handler nếu tìm thấy, và trả về null nếu không tìm thấy. Như vậy tôi có thể tạo ra exception từ kết quả null này (có nghĩa
là không tìm thấy Windbg). Nhưng không dùng If. Tôi sẽ viết 1 code như sau
Trang 5
Try
{
IntPtr WindbgHandle = FindWindow(“WindbgFrameClass”,null); //dùng FindWindow để tìm
và lưu hwnd vào 1 biến
Int DummyValue = (int) WindbgHandle / (int)WindbgHandle; // tiến hành 1 phép chia
//viết tiếp các code anti-windbg ở đây
}
Trong code trên cần chú ý đến:
Int DummyValue = (int) WindbgHandle / (int)WindbgHandle; // tiến hành 1 phép chia
Đây chính là phần code có thể tạo ra exception. Tôi sẽ giải thích:
- Nếu như FindWindow trả về là Window Handle của WinDbg, thì WindbgHandle sẽ chứa giá trị của Handle
này, khi thực hiện phép chia cho chính nó, kết quả lưu lại Dummyvalue sẽ là 1.
- Nếu như FindWindow không tìm thấy Windbg -> return null, thì WindbgHandle = null (0 trong int). Phép chia
sẽ trở thành 0 / 0 -> sinh ra DivideByZeroException.
Việc ta cần làm chỉ là tóm lấy cái DivideByZeroException đó để làm dấu hiệu cho việc khởi động Anti-tamper method. Viết 1 lệnh
catch dưới code block try… vừa rồi:
Catch(System.DivideByZeroException)
{
//Thực hiện code của Anti-tamper
}
Như vậy việc patch sẽ trở nên khó khăn hơn đôi chút. Và trong code đã được jIT sẽ trở thành như sau:
Code đã trở nên rối mắt hơn nhiều, và nếu không đọc hiểu được code thì chắc chắn sẽ patch sai, hoặc là không thể patch được để
vượt qua đoạn này. Như vậy chúng ta đã tăng độ an toàn lên một chút (rất nhỏ) nữa.
6. Trang 6
Kết luận và cảm ơn
Tôi viết bài viết này không phải để chỉ cho bạn cách để tùy biến một bản confuser mới hoàn toàn, mà chỉ nếu ra con đường cần
làm để có thể tự cải tiến độ an toàn trong việc bảo vệ của Confuser. Còn rất nhiều những thứ khác bạn có thể làm, ngoài ví dụ đã
nêu trong bài viết này, tất cả phụ thuộc vào khả năng sáng tạo của bạn mà thôi.
I want to send my “Thanks’ to firstly, all REPT members. You guys will always be my brother. Love you guy so much, whenever
and wherever.
Thanks to all members in tuts4you and Bl@ckStorm forum, also exetools forum, 3 great RCE forum ever, especially Bl@ckStorm. I
learned a lot from this forum.
Thanks to all .NET RCE Guru: 0xd4d, CodeCracker, Kurapica, whoknows, kao, yck1509, ubbelol… You guys are my teachers. And
also my good friend Asif (aka Love Three Day Graces/Death), you are great, too. :D
Xin gửi lời cảm ơn và lời chào đến tất cả các anh trong REA, Cin1 cũng như các anh em bạn bè trong giới RCE Việt Nam:
kienmanowar, TQN, hacnho, Computer_Angel, Elvis, Bolzano, quygia128, Vic, pnta… (và rất nhiều người nữa tôi không nhớ hết
:D) vì những đóng góp của họ cho cộng đồng RCE. Tôi cũng đã học hỏi từ họ rát nhiều điều bổ ích.
Cảm ơn bạn đã dành thời gian để đọc bài viết này của tôi. Hi vọng sẽ giúp ích được cho bạn chút gì đó.
Enjoy and Best Regards,
Levis
Created Sep 14 2014