SlideShare une entreprise Scribd logo
1  sur  12
Télécharger pour lire hors ligne
Trang 1
.NET Reverse Engineering – Advanced Patching, Playing with IL
Viết bởi: Levis Nickaster (http://ltops9.wordpress.com)
LỜI MỞ ĐẦU
Advanced Patching? Cái tên này do tôi tự đặt ra, để phân biệt với phương pháp Basic Patching trong
.NET. “Basic Patching” là gì? Cũng là do tôi tự gọi thế :D. Basic Patching đơn giản chỉ là trong quá trình
Cr@cking thì thường có các câu lệnh so sánh kiểu điều kiên (if..else...) và việc đa số các bạn thường làm
là chuyển brtrue thành brfalse trong IL code, và ngược lại để vượt qua quá trình kiểm tra. Phương pháp
basic patching này rất hiệu quả trong khá nhiều trường hợp. “Khá nhiều” chứ không phải tất cả.
Vậy nên phương pháp advanced patching này giải quyết được mọi thứ mà Basic patching không làm
được. Advanced Patching có thể thay đổi một phần hoặc toàn bộ chức năng của 1 chương trình, thêm
các đoạn code mới (code injection), hoặc là làm gì cũng được, tùy theo ý thích của các bạn. Nghe rất hay
phải không? Tất nhiên là hay, nhưng để thực hiện được advanced patching thì các bạn sẽ cần nhiều kiến
thức hơn, mà tôi sẽ cố găng trình bày + giới thiệu những tài liệu hữu ích có liên quan, trong bài viết này.
Và nên nhớ là, Advanced Patching chưa hẳn là cái tôi muốn các bạn nắm được ngay sau khi đọc bài viết
này, mà cái các bạn cần là những kiến thức tôi sẽ nói sau đầy.
Ở cuối bài viết tôi sẽ lấy ví dụ trong việc patch 1 ứng dụng thương mại (commercial application). Nhưng
theo đúng tiêu chí của bài viết này, việc patch chỉ mang tính chất nghiên cứu và học tập
Bạn cần có những gì? Chắc chắn là phải biết lập trình và có hiểu biết về .NET, ở mức basic cũng tạm đủ
rồi. Bên cạnh đó các bạn sẽ cần vài công cụ sau:
- 1 .NET Decompiler: .NET Reflector cũng ổn, tôi thích dùng SimpleAssemblyExplorer hơn vì nó
miễn phí và rất mạnh. Trong tut này tôi sẽ dùng cả 2. Riêng với Reflector thì các bạn cần có thêm
1 plugin nữa là ByteViewer viết bới CodeCracekr
- 1 Hex Editor: Tôi khuyến khích các bạn nên dùng CFF Explorer, vì CFF Explorer cho đến hiện tại là
chương trình support việc phân tích cấu trung file .NET tốt nhất mà tôi có thể tìm thấy được.
- 1 IDE và .NET Framework: Phần này thì tùy chọn, không bắt buộc các bạn phải có. Tìm hiểu về
.NET thì nên là cài vào trong máy những thành phần này. Tôi không thích Visual Studio lắm, và
để demo thì tôi thường dùng SharpDevelop
OK, hãy cùng bắt đầu nào.
Trang 2
MỘT VÀI ĐIỀU CŨ
Advanced Patching là kĩ thuật thay đổi một hay nhiều các IL code, và không chỉ dừng lại ở việc patch các
IL code thường thấy như brtrue, brtrue.s, brfalse, brfalse.s.... Vì vậy cần phải có kiến thức về MSIL/CIL/IL
nhiều hơn.
Đối với nhiều người mới, thì họ thường không có sự phân biệt giữa .NET PE file và native PE file. Bởi vì
trong mắt họ chúng đều là file PE cả. Thế nên tôi sẽ nhắc lại 1 chút về .NET PE file, và tại sao chúng ta
phải biết và sử dụng IL thay vì đọc ASM code giống như đối với native PE.
.NET PE file thường được coi là PE file “nửa mùa” vì chúng không chứa mã máy thực sự, mà chứa thứ
mà chúng ta cần tìm hiểu – IL Code. IL code là 1 dạng ngôn ngữ được dùng làm input cho JIT Compiler
trong quá trình vận hành, JIT Compiler chuyển IL Code này thành machine code, từ đó hệ điều hành mới
có thể thực thi được chương trình. Chứ hệ điều hành không thể đọc và hiểu được IL Code. IL Code là 1
dạng ngôn ngữ được chuyển về dạng đọc được từ các IL bytecode, là phần chính để tạo lên một .NET PE
File. Quá trình hoạt động của 1 chương trình .NET được mô tả trong hình dưới đây:
Các Decompiler .NET hoạt động dựa trên việc đọc IL bytecode và chuyển chúng về dạng của các ngôn
ngữ bậc cao, thế nên việc dịch ngược .NET tương đối là thuận lợi vì có thể đọc code dễ dàng, không mấy
khó khăn như đối với native PE. Các ngôn ngữ bậc cao là output này thực tế chỉ là 1 lớp bọc ngoài cùng
để khiến việc dịch ngược trở nên dễ dàng hơn, còn thực tế mọi thao tác của chúng ta đều hướng đến
các IL code/IL bytecode.
MSIL/CIL/IL CODE?
Về IL code, có rất nhiều sách nói riêng về ngôn ngữ này, cho nên tôi sẽ không đi vào nói chi tiết về nó,
mà sẽ chỉ cố gắng tóm tắt được những đặc điểm quan trọng của IL code. Các cuốn sách và link nên đọc:
- .NET IL Assembler ebook
- Expert .NET 2.0 IL Assember ebook
- Common Intermediate Language at Wikipedia
- Introduction to IL Assembly Language at CodeProject
Trang 3
Bên cạnh đó thì các bạn có thể tìm kiếm thêm trên Google với từ khóa “IL code” hoặc “MSIL language”.
MSIL, CIL, hay IL đều là 1 cả, cho nên chúng ta có thể dùng tên nào cũng được. Tôi thì hay gọi là IL vì dễ
nhớ và viết cũng nhanh :D.
1 đoạn IL code hiển thị trong SAE
Những thứ đầu tiên mà chúng ta cần biết khi đọc IL code là:
- IL là 1 ngôn ngữ thực hiện các biểu thức hay thao tác đều dựa nhiều vào stack, và cách hoạt
động cũng tương tự như cách hoạt động của stack, có nghĩa là LIFO (last-in, first-out). Dữ liệu
được đẩy vào cuối cùng sẽ là dữ liệu nằm trên cùng, và được lấy ra đầu tiên.
o Tôi lấy luôn ví dụ trên wikipedia như sau:
ldloc.0 //Load giá trị tại biến cục bộ đầu tiên vào stack
ldloc.1 //Load giá trị tại biến cục bộ thứ hai vào stack
add //Thực hiện phép cộng
stloc.0 //Lưu lại kết quả vào biến cục bộ đầu tiên
Ta có thể thấy được chương trình thực hiện tuần tự từ trên xuống dưới, để thực hiện
một biểu thức a=a+b. Tất cả mọi dữ liệu đều phải đưa vào trong stack trước khi có thể
thực hiện xử lý dữ liệu.
- IL mang đặc điểm của một ngôn ngữ lập trình hướng đối tượng (OOP). Chúng ta có thể khởi tạo
object mới, hoặc là invoke các method có sẵn của object khác,....
o Cũng lại lấy luôn ví dụ trong Wikipedia. Có 1 class Foo() như sau:
Trang 4
- class public Foo
{
- . method public static int32 Add(int32, int32) cil managed
- {
- . maxstack 2 // Khai báo độ lớn tối đa mà stack có thể
dùng
- ldarg.0 // Lấy tham số đầu tiên đưa vào stack
- ldarg.1 // lấy tham số thứ 2 đưa vào stack
- add // Thực hiện phép cộng
- ret // trở về
- }
}
Code dưới đâu có thể gọi được method Add() trong class Foo ở trên:
ldc.i4.2 //Đưa số nguyên có giá trị = 2 vào stack
ldc.i4.3 //Đưa số nguyên có giá trị = 3 vào stack
call int32 Foo::Add(int32, int32) //Gọi method Add() của Foo
stloc.0 //Lưu kết quả vào trong biến cục bộ đầu tiên
Code trên tương đương với việc thực hiên code int r = Foo.Add(2,3)
VD cho việc thực hiện tạo object mới:
ldc.i4.1 //Đưa số nguyên có giá trị = 1 vào stack
ldc.i4.4 //Đưa số nguyên có giá trị = 4 vào stack
//newobject gọi method ctor của class Car
newobj instance void Car::.ctor(int, int)
stloc.0 // myCar = new Car(1, 4); Lưu vào biến cục bộ
ldc.i4.1 //Đưa số nguyên có giá trị = 1 vào stack
ldc.i4.3 //Đưa số nguyên có giá trị = 2 vào stack
//newobject gọi method ctor của class Car
newobj instance void Car::.ctor(int, int)
stloc.1 // yourCar = new Car(1, 3); Lưu vào biến cục bộ
- IL là ngôn ngữ bao gồm rất nhiều các chỉ lệnh (instruction) khác nhau. Các chỉ lệnh này là 1 dạng
viết tắt của các từ khóa trong tiếng Anh. Vì vậy cách tốt nhất để ghi nhớ ý nghĩa các chỉ lệnh đó
là hãy đọc các chỉ lệnh này dưới dạng tiếng Anh. Danh sách các chỉ lệnh này các bạn có thể đọc
tại trang Wikipedia, hoặc đọc các sách phía trên tôi đã đề cập. Có một công cụ rất gọn nhẹ và
hữu ích, các bạn nên có công cụ này trong gói “đồ nghề” của mình, đó là MSIL Opcode Table,
công cụ này sẽ liệt kê các IL instrcution và các thông tin li
VD: brtrue = branch (if) true, ldloc = load (from) local variable, stloc = store (into) local
variable, ldarg = load argument, newobj = new object, callvirt = call virtual...
OK, vậy là tạm đủ để giới thiệu sơ qua về IL code, các bạn nên đọc các tài liệu trong link tôi đã viết phái
trên để tìm hiểu sâu hơn về ngôn ngữ này. Về cơ bản, các bạn cần phải nắm được 3 đặc điểm tôi đã nêu
ra. Bây giờ chúng ta sẽ đi đến phần khó hơn một chút, đó là phần tìm hiểu xem IL bytecode của 1
method được lưu trữ nhue thế nào trong 1 .NET PE file.
Trang 5
IL BYTECODE
IL bytecode chính là IL Code được biểu diễn dưới dạng nhị phân (binary) và được lưu giữ trong .NET PE
file. Nói thế này có vẻ hơi khó hiểu, tôi cứ nói nôm nà là các bạn mở 1 file .NET trong 1 hex editor, nhìn
thấy các byte trong đó, thì IL code là những byte nằm ở vị trí nhất định, thường thì nằm trong section
text của file. Trong 1 file .NET thường chỉ có 3 section: .text (chứa metadata và bytecode cũng như
resource (.NET Resource)), .rsrc (chứa resource - ở đây là native resource như Icon hay VERSIONINFO),
.reloc (relocation section). Ở đây tôi sẽ không tập trung vào việc phân tích cấu trúc của file .NET cũng
như cấu trúc của 1 .NET Metadata vì nó rất dài. Tôi đang dịch một bài viết về .NET PE File Format của
Daniel Pistelli (tác giả của CFF Explorer) trên CodeProject, và hi vọng sẽ hoàn thành sớm. Bây giờ thì bạn
có thể vào link trên để đọc bản tiếng Anh, rất chi tiết và đầy đủ.
Thế nên, ở phần này tôi sẽ tập trung đến cách xác định vị trí của bytecode của 1 method nhất định trong
chương trình, và tìm hiểu qua về cấu trúc của method dưới dạng bytecode.
Trong .NET Reflector, các bạn sẽ thấy rất dễ dàng khi đọc code vì code được hiển thị dưới dạng của các
ngôn ngữ bậc cao. Reflector cũng hỗ trợ việc hiển thị dưới dạng IL code nhưng lại không hiển thị các IL
bytecode. Chính vì vậy nên chúng ta cần đến plugin ByteViewer mà tôi đã nhắc đến ở phần đầu bài viết
này. Hãy cài đặt vào Reflector và chúng ta sẽ có thêm 2 lựa chọn ở phần Code Display:là BV_IL&Byte để
hiển thị cả IL code và bytecode dương ứng, và BV_Bytes để chỉ hiển thị IL Bytecode.
BV_IL&Bytes với IL code và bytecode được viết dưới dạng chú thích ngay bên cạnh
Trang 6
BV_Bytes chỉ hiển thị các bytecode
OK, trong Reflector là thế, vậy thì tôi muốn tìm code trong 1 Hex Editor thì sẽ làm thế nào?
Để làm điều này, chúng ta cần biết được Token của method đó. Token nôm na là 1 giá trị DWORD lưu
giữ index của Table xác định trong Metadata, và 1 index khác trỏ đến phần tử trong table đó. Có nghĩa là
ở đây có 2 index, và index thường được biểu diễn dưới dạng hexadecimal. VD: tôi có 1 token như sau:
0600002B, thì “06” ở đầu là index của Method Table, còn 2B là index đến method nằm trong method
Table đó (ta có 1 cái table tên là Methods, trong table này chứa tất cả các method có trong chương
trình, các method này đều được đánh số thứ tự). Về các table và metadata, các bạn hãy đọc trong bài
của Daniel Pistelli.
OK, từ method token này ta có thể tìm được RVA của method, và từ RVA có thể lấy được offset của
method đó. Có nghĩa là Method -> Token -> RVA -> Offset.
Tôi sẽ post lại cái ảnh phía trên ở đây, kèm theo 1 chút thay đổi:
Mặc định thì Reflector không hiển thị được Method token, nhưng với plugin trên thì chúng ta có thể lấy
token của method rất dễ dàng. Trong các chương trình khác (ILDASM, SAE,...) thì cũng rất dễ để lấy được
token.
Trang 7
Sau khi có được token thì chúng ta mở file .NET đó trong CFF Explorer, VD tôi có là 06000003, có nghĩa là
index 3 trong Method Table. Trong CFF Explorer, ở panel bên trái, ta chọn .NET Directory -> Metadata
Stream -> #` -> Tables. Sau đó ở k panel bên phải sẽ hiện ra 1 loạt các Table, ta tìm table có tên Methods
và expand nó ra sẽ thấy các method nằm trong đó, có đánh số thứ tự rõ ràng kèm theo tên. Tìm đến
method index 3, nhìn sang bên phải là sẽ hiện lên các thông tin về method đó, trong đó có giá trị RVA
mà ta cần tìm:
OK, RVA của method đó là 00002074. Bây giờ ta lại dùng tiếp tính năng khác của CFF Explorer là Address
Converter (nằm ngay phía dưới của panel bên trái). Điền RVA tìm được vào khung RVA, và ấn Enter,
chúng ta sẽ đến được với offset chứa bytecode:
Trang 8
Có 1 điều cần phải lưu ý là ở đây không chỉ có IL bytecode, mà phần binary đầu tiên là còn chứa 1 đoạn
dât của Method Header, đoạn method header này giúp jit compiler (và cả cách trình decomplie xác định
được thuộc tính của toàn bộ method đó).
Có 2 dạng method Header, 1 là Tiny Header, 2 là Fat Header. Theo blog của Zsozso có mô tả rất chi tiết
về 2 dạng method header này. Tôi sẽ tóm tắt lại những gì quan trọng:
Tiny Header là 1 byte nằm đầu tiên tại offset chứa method. 1 byte = 8 bit, 2 bit đầu tiên để xác định đó
là Tiny header, và 6 bit tiếp theo xác định độ dài của toàn bộ IL bytecode của method. 2 bit đầu tiên cố
định là 0x02.
Tiny Header chỉ được sử dụng trong các trường hợp sau:
- Trong method không sử dụng biến cục bộ
- Trong method không sử dụng exception handler.
- Trong method không có data section bổ sung
- Độ dài của toàn bộ IL bytecode của method không vượt quá 64 byte.
- Maxstack của method không được vượt quá 8.
Vì tiny header bị giới hạn trong 1 byte, cho nên không thường gặp, Đa phần các trường hợp đều là Fat
Header. Fat Header thì phức tạp và chứa nhiều thông tin hơn Tiny Header.
Trang 9
Fat Header gồm có:
2 byte đầu: thường thấy là 0x3013 (Little Endian). Trong đó chia thành các bit nhỏ, chứa các giá trị như
sau:
- Bit 0- bit 11: Fat format được set ở vị trí bit 0, giá trị là 1 -> Fat Header =0x3 (010b=0x2,
110b=0x3, bit đầu chuyển từ 0 thành 1). Bit thứ 8 để xác định xem có extra section trong
method không, Bit thứ 10 xác định là các constructor method luôn được gọi cho các biến cục bộ.
- Bit 12-15: Kích thước của toàn bộ Fat Header, Ksoos này được nhân với 4 để ra toàn bộ kích
thước cần dùng của Fat Header. Số này luôn là 3, 3x4=12, kích thước của toàn bộ Fat Header =
12 byte.
Byte thứ 2: kích thước 1 WORD. Ghi giá trị của maxstack.
Byte thứ 4: kích thước 1 DWORD: Ghi kích thước của toàn bộ IL bytecode trong method
Byte thứ 8: Kích thước 1 DWORD, Metadata Token trỏ đến một signature nằm trong metadata table,
tham chiếu đến 1 mục nhất định trong #Blob String, mô tả kiểu giá trị của các biến cục bộ có trong
method (viết tắt là LocalVarSigTok). Nếu giá trị này bằng 0, có nghĩa là trong method không có biến cục
bộ.
OK, thế là xong về 2 method header. Tôi sẽ lấy ví dụ về 1 Fat Header, cũng là method đã chụp ở trong
các hình trên. Data của Toàn bộ Method này:
133002002C0000000100001100032C0B027B0100000414FE012B0117000A062D0E0002
7B010000046F1200000A00000203281300000A002A
Trong đó:
- Màu đỏ: Fat Header
- Màu cam: Maxstack. Maxstack=3, có nghĩa là sẽ có tối đa 3 giá trị có thể được push vào stack
- Màu xanh lục: Size of code. 0x2C. Method này có 44 bytecode
- Màu xanh dương: LocalValSigTok
- Màu tím: IL bytecode
Thế là chúng ta đã biết được cách xác định thông tin và cách biểu diễn của một method trong .NET file.
Thực tế là phần này không được trực tiếp sử dụng trong Advance Patching, nhưng rất quan trọng để bạn
có thể hiểu mình đang làm gì trong khi thực hiện Advanced Patching
ADVANCED PATCHING
Mấy ngày trước, tôi đi kiếm một chương trình Disk Defraggement, và tình cờ tôi đọc được 1 bài Review
về Diskeeper nên kéo về xem thử để mong “kiếm chác” được gì đó. Và tình cờ nó được viết bằng .NET.
Về cách tôi tìm ra tử huyệt để patch, tôi sẽ không nói ở đây, các bạn có thể tự tìm. Tử huyệt nằm ở file
DkUserCtrl.dll, method GetData(), class ProductManager. Ta sẽ thấy 1 đoạn code như thế này:
Trang 10
1 biến kiểu bool có tên là IsTrialware được gán giá trị bằng việc đọc thông tin từ trong 1 file XML nào đó.
Nếu biến này có giá trị true, thì chương trình sẽ chạy ở trial mode, nếu mang giá trị false, thì chương
trình sẽ là Full :p. Vậy thì tôi muốn set cố định cho isTrialware luôn là false, thì tôi sẽ phải thay đổi code
nhiều hơn là Basic Patch, và ở đây cũng ko có cái if...else... nào để tôi có thể patch cả. Tôi muốn 1 code
như thế này:
This.DkProductData.isTrialware = false
Đoạn code trong IL sẽ như sau:
Chi tiết:
115 L_018d: ldloc.2 //Load local variable 2 to stack
116 L_018e: ldstr "isTrialware" //Load string to stack
//Call method
117 L_0193: call System.Boolean
DkUserCtrl.XmlHelper::GetXmlBoolValue(System.Xml.XmlNode,System.String)
118 L_0198: stfld System.Boolean
DkUserCtrl.DK_PRODUCT_DATA::isTrialware //Store value to object
119 L_019d: ldarg.0 //load the first argument
120 L_019e: ldfld DkUserCtrl.DK_PRODUCT_DATA
DkUserCtrl.ProductManager::DkProductData //load the field to stack
Code ở line 115,116 và 117 tương ứng với code sau: XmlHelper.GetXmlBoolValue(node, "isTrialware");
Trang 11
Bây giờ tôi sẽ xóa 3 lines này đi và thêm vào 1 dòng code mới để gán luôn cho giá trị của isTrialware =
false. Với kiểu boolean thì chỉ có 2 giá trị là 0 (false) và 1 (true). Vì thế cho nên chúng ta cần push 1 giá trị
0 vào stack. Vậy thì tôi sẽ dùng instruction ldc.i4.0, có nghĩa là LoaD Constant Integer (with) 4 bytes (size)
(and value is) 0. Vậy là trong stack có giá trị = 0. Chỉ lệnh stfld ở line 118 sẽ save vào isTrialware. Các bạn
nên sử dụng SAE để edit IL code sẽ dễ hơn rất nhiều so với sử dụng các công cụ khác. Cuối cùng, chúng
ta có được:
IL code sau khi được thay đổi
Hiển thị dưới dạng high level language
Kết quả (Full Edition)
Trang 12
Job done!
Ví dụ phía trên chỉ 1 ví dụ rất cơ bản cho việc Advance Patching. Chúng ta có thể làm nhiều hơn, thậm
chí xóa toàn bộ IL code của 1 method và thay thế lại bằng code mới của chúng ta. Điều này rất hữu ích
và có thể nói là luôn thành công trong các trường hợp.
OK, Tôi nghĩ thế là cũng tạm ổn để kết thúc bài viết này rồi.
KẾT LUẬN
Qua bài viết này, hi vọng các bạn sẽ nhận được 1 thứ gì đó có ích cho bản thân mình đẻ có thể áp dụng
trong việc thực hiện Reverse Engineering .NET. Advanced Patching chỉ là ứng dụng rất nhỏ, quan trọng là
việc đọc hiểu được IL code và hiểu được cấu trúc của method cũng như 1 phần cấu trúc của .NET PE file.
Và cũng như thường lệ, cuối cùng là lời cảm ơn. Đầu tiên là Daniel Pistelli, tác giả của CFF Explorer và rất
nhiều tutorial liên quan đến .NET File format. Tiếp đến là 0xd4d, tác giả của de4dot và WICKY Hu, tác giả
của SAE. Cũng xin cảm ơn rất nhiều anh em, bạn bè, tiền bối và hậu bối trong giới “giang hồ mạng”
Hacking & Security tại Việt Nam. Họ đã giúp đỡ và chia sẻ với tôi rất nhiều, tạo cho tôi nhiều cơ hội để
tiếp tục đi xa hơn trong con đường “vọc” và “vạch” này. Cụ thể ư? Nhiều quá tôi không kể hết được, rất
nhiều anh trong REA, HVA, Cin1, REPT, đại gia đình Mi2 JSC (đặc biệt là sếp CISSP luannt  ) cũng như
các bạn trẻ khác (nhiều quá, tôi không kể hết, những ai có cùng đam mê, đã gặp tôi, hoặc nói chuyện,
thảo luận với tôi thì đều được liệt vào đây cả, và đặc biệt nhất,(luôn luôn vậy) là em Elvis.. Cảm ơn các
bạn, những người đang đọc bài viết này, đã bỏ thời gian quý báu ra để nghe tôi “chém gió”. Hẹn gặp lại
trong các bài viết tiếp theo. Tôi sẽ viết nhiều bài viết nữa bởi với tôi, “learning is sharing” mà.
Best regards
Levis
Jan 28 2015

Contenu connexe

Tendances

Clean code - Trở thành một lập trình viên tốt hơn
Clean code - Trở thành một lập trình viên tốt hơnClean code - Trở thành một lập trình viên tốt hơn
Clean code - Trở thành một lập trình viên tốt hơnNhật Nguyễn Khắc
 
Code Refactoring: Thay đổi nhỏ - Lợi ích lớn
Code Refactoring: Thay đổi nhỏ - Lợi ích lớnCode Refactoring: Thay đổi nhỏ - Lợi ích lớn
Code Refactoring: Thay đổi nhỏ - Lợi ích lớnNhật Nguyễn Khắc
 
OOP interview questions & answers.
OOP interview questions & answers.OOP interview questions & answers.
OOP interview questions & answers.Questpond
 
Solid principles, Design Patterns, and Domain Driven Design
Solid principles, Design Patterns, and Domain Driven DesignSolid principles, Design Patterns, and Domain Driven Design
Solid principles, Design Patterns, and Domain Driven DesignIrwansyah Irwansyah
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features OverviewSergii Stets
 
Refactoring: Improve the design of existing code
Refactoring: Improve the design of existing codeRefactoring: Improve the design of existing code
Refactoring: Improve the design of existing codeValerio Maggio
 
Quản lý dự án phần mềm bằng SVN
Quản lý dự án phần mềm bằng SVN Quản lý dự án phần mềm bằng SVN
Quản lý dự án phần mềm bằng SVN Lương Bá Hợp
 
Python Interview Questions And Answers
Python Interview Questions And AnswersPython Interview Questions And Answers
Python Interview Questions And AnswersH2Kinfosys
 
Dependency injection presentation
Dependency injection presentationDependency injection presentation
Dependency injection presentationAhasanul Kalam Akib
 
Architectural styles and patterns
Architectural styles and patternsArchitectural styles and patterns
Architectural styles and patternsdeep sharma
 
API Testing & SoapUI
API Testing & SoapUIAPI Testing & SoapUI
API Testing & SoapUITran Bich
 
Introduction to laravel framework
Introduction to laravel frameworkIntroduction to laravel framework
Introduction to laravel frameworkAhmad Fatoni
 
JAVA-PPT'S-complete-chrome.pptx
JAVA-PPT'S-complete-chrome.pptxJAVA-PPT'S-complete-chrome.pptx
JAVA-PPT'S-complete-chrome.pptxKunalYadav65140
 

Tendances (20)

Clean code - Trở thành một lập trình viên tốt hơn
Clean code - Trở thành một lập trình viên tốt hơnClean code - Trở thành một lập trình viên tốt hơn
Clean code - Trở thành một lập trình viên tốt hơn
 
Code Refactoring: Thay đổi nhỏ - Lợi ích lớn
Code Refactoring: Thay đổi nhỏ - Lợi ích lớnCode Refactoring: Thay đổi nhỏ - Lợi ích lớn
Code Refactoring: Thay đổi nhỏ - Lợi ích lớn
 
OOP interview questions & answers.
OOP interview questions & answers.OOP interview questions & answers.
OOP interview questions & answers.
 
laravel.pptx
laravel.pptxlaravel.pptx
laravel.pptx
 
Solid principles, Design Patterns, and Domain Driven Design
Solid principles, Design Patterns, and Domain Driven DesignSolid principles, Design Patterns, and Domain Driven Design
Solid principles, Design Patterns, and Domain Driven Design
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features Overview
 
Laravel Eloquent ORM
Laravel Eloquent ORMLaravel Eloquent ORM
Laravel Eloquent ORM
 
Refactoring: Improve the design of existing code
Refactoring: Improve the design of existing codeRefactoring: Improve the design of existing code
Refactoring: Improve the design of existing code
 
Quản lý dự án phần mềm bằng SVN
Quản lý dự án phần mềm bằng SVN Quản lý dự án phần mềm bằng SVN
Quản lý dự án phần mềm bằng SVN
 
Python Interview Questions And Answers
Python Interview Questions And AnswersPython Interview Questions And Answers
Python Interview Questions And Answers
 
Clean Method
Clean MethodClean Method
Clean Method
 
Dependency injection presentation
Dependency injection presentationDependency injection presentation
Dependency injection presentation
 
Jdk,jre,jvm
Jdk,jre,jvmJdk,jre,jvm
Jdk,jre,jvm
 
C#.NET
C#.NETC#.NET
C#.NET
 
Laravel 101
Laravel 101Laravel 101
Laravel 101
 
Architectural styles and patterns
Architectural styles and patternsArchitectural styles and patterns
Architectural styles and patterns
 
API Testing & SoapUI
API Testing & SoapUIAPI Testing & SoapUI
API Testing & SoapUI
 
Java Collections Framework
Java Collections FrameworkJava Collections Framework
Java Collections Framework
 
Introduction to laravel framework
Introduction to laravel frameworkIntroduction to laravel framework
Introduction to laravel framework
 
JAVA-PPT'S-complete-chrome.pptx
JAVA-PPT'S-complete-chrome.pptxJAVA-PPT'S-complete-chrome.pptx
JAVA-PPT'S-complete-chrome.pptx
 

En vedette

Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịch
Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịchPhân tích Confuser 1.9.0.0 - Constant Protection - Bản dịch
Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịchLevis Nickaster
 
Reverse Engineering in Linux - The tools showcase
Reverse Engineering in Linux - The tools showcaseReverse Engineering in Linux - The tools showcase
Reverse Engineering in Linux - The tools showcaseLevis Nickaster
 
Hướng dẫn tạo Loader trong .NET - bản dịch
Hướng dẫn tạo Loader trong .NET - bản dịchHướng dẫn tạo Loader trong .NET - bản dịch
Hướng dẫn tạo Loader trong .NET - bản dịchLevis Nickaster
 
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịchPhân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịchLevis Nickaster
 
Decrypt các MSIL methods một cách thử công - Bài dịch
Decrypt các MSIL methods một cách thử công - Bài dịchDecrypt các MSIL methods một cách thử công - Bài dịch
Decrypt các MSIL methods một cách thử công - Bài dịchLevis Nickaster
 
Xây dựng nhóm phân tích mã độc
Xây dựng nhóm phân tích mã độcXây dựng nhóm phân tích mã độc
Xây dựng nhóm phân tích mã độcLevis Nickaster
 
Hướng dẫn lập trình quản lý c#
Hướng dẫn lập trình quản lý c#Hướng dẫn lập trình quản lý c#
Hướng dẫn lập trình quản lý c#An Nguyen
 

En vedette (8)

Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịch
Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịchPhân tích Confuser 1.9.0.0 - Constant Protection - Bản dịch
Phân tích Confuser 1.9.0.0 - Constant Protection - Bản dịch
 
Reverse Engineering in Linux - The tools showcase
Reverse Engineering in Linux - The tools showcaseReverse Engineering in Linux - The tools showcase
Reverse Engineering in Linux - The tools showcase
 
Hướng dẫn tạo Loader trong .NET - bản dịch
Hướng dẫn tạo Loader trong .NET - bản dịchHướng dẫn tạo Loader trong .NET - bản dịch
Hướng dẫn tạo Loader trong .NET - bản dịch
 
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịchPhân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
Phân tích Confuser 1.9.0.0 - method proxy confusion - Bản dịch
 
Decrypt các MSIL methods một cách thử công - Bài dịch
Decrypt các MSIL methods một cách thử công - Bài dịchDecrypt các MSIL methods một cách thử công - Bài dịch
Decrypt các MSIL methods một cách thử công - Bài dịch
 
Xây dựng nhóm phân tích mã độc
Xây dựng nhóm phân tích mã độcXây dựng nhóm phân tích mã độc
Xây dựng nhóm phân tích mã độc
 
Hướng dẫn lập trình quản lý c#
Hướng dẫn lập trình quản lý c#Hướng dẫn lập trình quản lý c#
Hướng dẫn lập trình quản lý c#
 
Applied linguistics
Applied linguisticsApplied linguistics
Applied linguistics
 

Similaire à Reverse Engineering .NET - Advanced Patching, Playing with IL

Lập trình C cho VĐK 8051
Lập trình C cho VĐK 8051Lập trình C cho VĐK 8051
Lập trình C cho VĐK 8051Mr Giap
 
Haiphongit.com.tai lieu-learning-php-my sql
Haiphongit.com.tai lieu-learning-php-my sqlHaiphongit.com.tai lieu-learning-php-my sql
Haiphongit.com.tai lieu-learning-php-my sqlGiang Nguyễn
 
Chap 5 6 - The Art of Readable Code
Chap 5 6 - The Art of Readable CodeChap 5 6 - The Art of Readable Code
Chap 5 6 - The Art of Readable Codeminhnc91
 
Câu hỏi phỏng vấn.pdf
Câu hỏi phỏng vấn.pdfCâu hỏi phỏng vấn.pdf
Câu hỏi phỏng vấn.pdfGrowup Work
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#Hihi Hung
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#LanLT2011
 
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]leduyk11
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#LanLT2011
 
How to write good code
How to write good code How to write good code
How to write good code Minh Hoang
 
Lập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệLập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệTrần Thiên Đại
 
PMMNM.docx
PMMNM.docxPMMNM.docx
PMMNM.docxNgnHng26
 
Sinh vienit.net --57669587-c-dhkh-hue
Sinh vienit.net --57669587-c-dhkh-hueSinh vienit.net --57669587-c-dhkh-hue
Sinh vienit.net --57669587-c-dhkh-hueTuấn Nguyễn Văn
 
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?ITguru.vn
 

Similaire à Reverse Engineering .NET - Advanced Patching, Playing with IL (20)

Lập trình C cho VĐK 8051
Lập trình C cho VĐK 8051Lập trình C cho VĐK 8051
Lập trình C cho VĐK 8051
 
Haiphongit.com.tai lieu-learning-php-my sql
Haiphongit.com.tai lieu-learning-php-my sqlHaiphongit.com.tai lieu-learning-php-my sql
Haiphongit.com.tai lieu-learning-php-my sql
 
Asp.net 3.5 _1
Asp.net 3.5 _1Asp.net 3.5 _1
Asp.net 3.5 _1
 
Hdsd eclipse
Hdsd eclipseHdsd eclipse
Hdsd eclipse
 
Chap 5 6 - The Art of Readable Code
Chap 5 6 - The Art of Readable CodeChap 5 6 - The Art of Readable Code
Chap 5 6 - The Art of Readable Code
 
Câu hỏi phỏng vấn.pdf
Câu hỏi phỏng vấn.pdfCâu hỏi phỏng vấn.pdf
Câu hỏi phỏng vấn.pdf
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
C# cơ bản hay
C# cơ bản hayC# cơ bản hay
C# cơ bản hay
 
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
Lập trình c# 2008 cơ bản (nhất nghệ) [thủ thuật it 360]
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
Nhat nghe c#
Nhat nghe   c#Nhat nghe   c#
Nhat nghe c#
 
005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf
 
005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf005. LAP TRINH C#.pdf
005. LAP TRINH C#.pdf
 
Python
PythonPython
Python
 
How to write good code
How to write good code How to write good code
How to write good code
 
Lập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất NghệLập trình C# 2008 cơ bản_Nhất Nghệ
Lập trình C# 2008 cơ bản_Nhất Nghệ
 
PMMNM.docx
PMMNM.docxPMMNM.docx
PMMNM.docx
 
Sinh vienit.net --57669587-c-dhkh-hue
Sinh vienit.net --57669587-c-dhkh-hueSinh vienit.net --57669587-c-dhkh-hue
Sinh vienit.net --57669587-c-dhkh-hue
 
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?
Bạn cần chuẩn bị gì khi đi phỏng vấn vị trí DevOps?
 

Reverse Engineering .NET - Advanced Patching, Playing with IL

  • 1. Trang 1 .NET Reverse Engineering – Advanced Patching, Playing with IL Viết bởi: Levis Nickaster (http://ltops9.wordpress.com) LỜI MỞ ĐẦU Advanced Patching? Cái tên này do tôi tự đặt ra, để phân biệt với phương pháp Basic Patching trong .NET. “Basic Patching” là gì? Cũng là do tôi tự gọi thế :D. Basic Patching đơn giản chỉ là trong quá trình Cr@cking thì thường có các câu lệnh so sánh kiểu điều kiên (if..else...) và việc đa số các bạn thường làm là chuyển brtrue thành brfalse trong IL code, và ngược lại để vượt qua quá trình kiểm tra. Phương pháp basic patching này rất hiệu quả trong khá nhiều trường hợp. “Khá nhiều” chứ không phải tất cả. Vậy nên phương pháp advanced patching này giải quyết được mọi thứ mà Basic patching không làm được. Advanced Patching có thể thay đổi một phần hoặc toàn bộ chức năng của 1 chương trình, thêm các đoạn code mới (code injection), hoặc là làm gì cũng được, tùy theo ý thích của các bạn. Nghe rất hay phải không? Tất nhiên là hay, nhưng để thực hiện được advanced patching thì các bạn sẽ cần nhiều kiến thức hơn, mà tôi sẽ cố găng trình bày + giới thiệu những tài liệu hữu ích có liên quan, trong bài viết này. Và nên nhớ là, Advanced Patching chưa hẳn là cái tôi muốn các bạn nắm được ngay sau khi đọc bài viết này, mà cái các bạn cần là những kiến thức tôi sẽ nói sau đầy. Ở cuối bài viết tôi sẽ lấy ví dụ trong việc patch 1 ứng dụng thương mại (commercial application). Nhưng theo đúng tiêu chí của bài viết này, việc patch chỉ mang tính chất nghiên cứu và học tập Bạn cần có những gì? Chắc chắn là phải biết lập trình và có hiểu biết về .NET, ở mức basic cũng tạm đủ rồi. Bên cạnh đó các bạn sẽ cần vài công cụ sau: - 1 .NET Decompiler: .NET Reflector cũng ổn, tôi thích dùng SimpleAssemblyExplorer hơn vì nó miễn phí và rất mạnh. Trong tut này tôi sẽ dùng cả 2. Riêng với Reflector thì các bạn cần có thêm 1 plugin nữa là ByteViewer viết bới CodeCracekr - 1 Hex Editor: Tôi khuyến khích các bạn nên dùng CFF Explorer, vì CFF Explorer cho đến hiện tại là chương trình support việc phân tích cấu trung file .NET tốt nhất mà tôi có thể tìm thấy được. - 1 IDE và .NET Framework: Phần này thì tùy chọn, không bắt buộc các bạn phải có. Tìm hiểu về .NET thì nên là cài vào trong máy những thành phần này. Tôi không thích Visual Studio lắm, và để demo thì tôi thường dùng SharpDevelop OK, hãy cùng bắt đầu nào.
  • 2. Trang 2 MỘT VÀI ĐIỀU CŨ Advanced Patching là kĩ thuật thay đổi một hay nhiều các IL code, và không chỉ dừng lại ở việc patch các IL code thường thấy như brtrue, brtrue.s, brfalse, brfalse.s.... Vì vậy cần phải có kiến thức về MSIL/CIL/IL nhiều hơn. Đối với nhiều người mới, thì họ thường không có sự phân biệt giữa .NET PE file và native PE file. Bởi vì trong mắt họ chúng đều là file PE cả. Thế nên tôi sẽ nhắc lại 1 chút về .NET PE file, và tại sao chúng ta phải biết và sử dụng IL thay vì đọc ASM code giống như đối với native PE. .NET PE file thường được coi là PE file “nửa mùa” vì chúng không chứa mã máy thực sự, mà chứa thứ mà chúng ta cần tìm hiểu – IL Code. IL code là 1 dạng ngôn ngữ được dùng làm input cho JIT Compiler trong quá trình vận hành, JIT Compiler chuyển IL Code này thành machine code, từ đó hệ điều hành mới có thể thực thi được chương trình. Chứ hệ điều hành không thể đọc và hiểu được IL Code. IL Code là 1 dạng ngôn ngữ được chuyển về dạng đọc được từ các IL bytecode, là phần chính để tạo lên một .NET PE File. Quá trình hoạt động của 1 chương trình .NET được mô tả trong hình dưới đây: Các Decompiler .NET hoạt động dựa trên việc đọc IL bytecode và chuyển chúng về dạng của các ngôn ngữ bậc cao, thế nên việc dịch ngược .NET tương đối là thuận lợi vì có thể đọc code dễ dàng, không mấy khó khăn như đối với native PE. Các ngôn ngữ bậc cao là output này thực tế chỉ là 1 lớp bọc ngoài cùng để khiến việc dịch ngược trở nên dễ dàng hơn, còn thực tế mọi thao tác của chúng ta đều hướng đến các IL code/IL bytecode. MSIL/CIL/IL CODE? Về IL code, có rất nhiều sách nói riêng về ngôn ngữ này, cho nên tôi sẽ không đi vào nói chi tiết về nó, mà sẽ chỉ cố gắng tóm tắt được những đặc điểm quan trọng của IL code. Các cuốn sách và link nên đọc: - .NET IL Assembler ebook - Expert .NET 2.0 IL Assember ebook - Common Intermediate Language at Wikipedia - Introduction to IL Assembly Language at CodeProject
  • 3. Trang 3 Bên cạnh đó thì các bạn có thể tìm kiếm thêm trên Google với từ khóa “IL code” hoặc “MSIL language”. MSIL, CIL, hay IL đều là 1 cả, cho nên chúng ta có thể dùng tên nào cũng được. Tôi thì hay gọi là IL vì dễ nhớ và viết cũng nhanh :D. 1 đoạn IL code hiển thị trong SAE Những thứ đầu tiên mà chúng ta cần biết khi đọc IL code là: - IL là 1 ngôn ngữ thực hiện các biểu thức hay thao tác đều dựa nhiều vào stack, và cách hoạt động cũng tương tự như cách hoạt động của stack, có nghĩa là LIFO (last-in, first-out). Dữ liệu được đẩy vào cuối cùng sẽ là dữ liệu nằm trên cùng, và được lấy ra đầu tiên. o Tôi lấy luôn ví dụ trên wikipedia như sau: ldloc.0 //Load giá trị tại biến cục bộ đầu tiên vào stack ldloc.1 //Load giá trị tại biến cục bộ thứ hai vào stack add //Thực hiện phép cộng stloc.0 //Lưu lại kết quả vào biến cục bộ đầu tiên Ta có thể thấy được chương trình thực hiện tuần tự từ trên xuống dưới, để thực hiện một biểu thức a=a+b. Tất cả mọi dữ liệu đều phải đưa vào trong stack trước khi có thể thực hiện xử lý dữ liệu. - IL mang đặc điểm của một ngôn ngữ lập trình hướng đối tượng (OOP). Chúng ta có thể khởi tạo object mới, hoặc là invoke các method có sẵn của object khác,.... o Cũng lại lấy luôn ví dụ trong Wikipedia. Có 1 class Foo() như sau:
  • 4. Trang 4 - class public Foo { - . method public static int32 Add(int32, int32) cil managed - { - . maxstack 2 // Khai báo độ lớn tối đa mà stack có thể dùng - ldarg.0 // Lấy tham số đầu tiên đưa vào stack - ldarg.1 // lấy tham số thứ 2 đưa vào stack - add // Thực hiện phép cộng - ret // trở về - } } Code dưới đâu có thể gọi được method Add() trong class Foo ở trên: ldc.i4.2 //Đưa số nguyên có giá trị = 2 vào stack ldc.i4.3 //Đưa số nguyên có giá trị = 3 vào stack call int32 Foo::Add(int32, int32) //Gọi method Add() của Foo stloc.0 //Lưu kết quả vào trong biến cục bộ đầu tiên Code trên tương đương với việc thực hiên code int r = Foo.Add(2,3) VD cho việc thực hiện tạo object mới: ldc.i4.1 //Đưa số nguyên có giá trị = 1 vào stack ldc.i4.4 //Đưa số nguyên có giá trị = 4 vào stack //newobject gọi method ctor của class Car newobj instance void Car::.ctor(int, int) stloc.0 // myCar = new Car(1, 4); Lưu vào biến cục bộ ldc.i4.1 //Đưa số nguyên có giá trị = 1 vào stack ldc.i4.3 //Đưa số nguyên có giá trị = 2 vào stack //newobject gọi method ctor của class Car newobj instance void Car::.ctor(int, int) stloc.1 // yourCar = new Car(1, 3); Lưu vào biến cục bộ - IL là ngôn ngữ bao gồm rất nhiều các chỉ lệnh (instruction) khác nhau. Các chỉ lệnh này là 1 dạng viết tắt của các từ khóa trong tiếng Anh. Vì vậy cách tốt nhất để ghi nhớ ý nghĩa các chỉ lệnh đó là hãy đọc các chỉ lệnh này dưới dạng tiếng Anh. Danh sách các chỉ lệnh này các bạn có thể đọc tại trang Wikipedia, hoặc đọc các sách phía trên tôi đã đề cập. Có một công cụ rất gọn nhẹ và hữu ích, các bạn nên có công cụ này trong gói “đồ nghề” của mình, đó là MSIL Opcode Table, công cụ này sẽ liệt kê các IL instrcution và các thông tin li VD: brtrue = branch (if) true, ldloc = load (from) local variable, stloc = store (into) local variable, ldarg = load argument, newobj = new object, callvirt = call virtual... OK, vậy là tạm đủ để giới thiệu sơ qua về IL code, các bạn nên đọc các tài liệu trong link tôi đã viết phái trên để tìm hiểu sâu hơn về ngôn ngữ này. Về cơ bản, các bạn cần phải nắm được 3 đặc điểm tôi đã nêu ra. Bây giờ chúng ta sẽ đi đến phần khó hơn một chút, đó là phần tìm hiểu xem IL bytecode của 1 method được lưu trữ nhue thế nào trong 1 .NET PE file.
  • 5. Trang 5 IL BYTECODE IL bytecode chính là IL Code được biểu diễn dưới dạng nhị phân (binary) và được lưu giữ trong .NET PE file. Nói thế này có vẻ hơi khó hiểu, tôi cứ nói nôm nà là các bạn mở 1 file .NET trong 1 hex editor, nhìn thấy các byte trong đó, thì IL code là những byte nằm ở vị trí nhất định, thường thì nằm trong section text của file. Trong 1 file .NET thường chỉ có 3 section: .text (chứa metadata và bytecode cũng như resource (.NET Resource)), .rsrc (chứa resource - ở đây là native resource như Icon hay VERSIONINFO), .reloc (relocation section). Ở đây tôi sẽ không tập trung vào việc phân tích cấu trúc của file .NET cũng như cấu trúc của 1 .NET Metadata vì nó rất dài. Tôi đang dịch một bài viết về .NET PE File Format của Daniel Pistelli (tác giả của CFF Explorer) trên CodeProject, và hi vọng sẽ hoàn thành sớm. Bây giờ thì bạn có thể vào link trên để đọc bản tiếng Anh, rất chi tiết và đầy đủ. Thế nên, ở phần này tôi sẽ tập trung đến cách xác định vị trí của bytecode của 1 method nhất định trong chương trình, và tìm hiểu qua về cấu trúc của method dưới dạng bytecode. Trong .NET Reflector, các bạn sẽ thấy rất dễ dàng khi đọc code vì code được hiển thị dưới dạng của các ngôn ngữ bậc cao. Reflector cũng hỗ trợ việc hiển thị dưới dạng IL code nhưng lại không hiển thị các IL bytecode. Chính vì vậy nên chúng ta cần đến plugin ByteViewer mà tôi đã nhắc đến ở phần đầu bài viết này. Hãy cài đặt vào Reflector và chúng ta sẽ có thêm 2 lựa chọn ở phần Code Display:là BV_IL&Byte để hiển thị cả IL code và bytecode dương ứng, và BV_Bytes để chỉ hiển thị IL Bytecode. BV_IL&Bytes với IL code và bytecode được viết dưới dạng chú thích ngay bên cạnh
  • 6. Trang 6 BV_Bytes chỉ hiển thị các bytecode OK, trong Reflector là thế, vậy thì tôi muốn tìm code trong 1 Hex Editor thì sẽ làm thế nào? Để làm điều này, chúng ta cần biết được Token của method đó. Token nôm na là 1 giá trị DWORD lưu giữ index của Table xác định trong Metadata, và 1 index khác trỏ đến phần tử trong table đó. Có nghĩa là ở đây có 2 index, và index thường được biểu diễn dưới dạng hexadecimal. VD: tôi có 1 token như sau: 0600002B, thì “06” ở đầu là index của Method Table, còn 2B là index đến method nằm trong method Table đó (ta có 1 cái table tên là Methods, trong table này chứa tất cả các method có trong chương trình, các method này đều được đánh số thứ tự). Về các table và metadata, các bạn hãy đọc trong bài của Daniel Pistelli. OK, từ method token này ta có thể tìm được RVA của method, và từ RVA có thể lấy được offset của method đó. Có nghĩa là Method -> Token -> RVA -> Offset. Tôi sẽ post lại cái ảnh phía trên ở đây, kèm theo 1 chút thay đổi: Mặc định thì Reflector không hiển thị được Method token, nhưng với plugin trên thì chúng ta có thể lấy token của method rất dễ dàng. Trong các chương trình khác (ILDASM, SAE,...) thì cũng rất dễ để lấy được token.
  • 7. Trang 7 Sau khi có được token thì chúng ta mở file .NET đó trong CFF Explorer, VD tôi có là 06000003, có nghĩa là index 3 trong Method Table. Trong CFF Explorer, ở panel bên trái, ta chọn .NET Directory -> Metadata Stream -> #` -> Tables. Sau đó ở k panel bên phải sẽ hiện ra 1 loạt các Table, ta tìm table có tên Methods và expand nó ra sẽ thấy các method nằm trong đó, có đánh số thứ tự rõ ràng kèm theo tên. Tìm đến method index 3, nhìn sang bên phải là sẽ hiện lên các thông tin về method đó, trong đó có giá trị RVA mà ta cần tìm: OK, RVA của method đó là 00002074. Bây giờ ta lại dùng tiếp tính năng khác của CFF Explorer là Address Converter (nằm ngay phía dưới của panel bên trái). Điền RVA tìm được vào khung RVA, và ấn Enter, chúng ta sẽ đến được với offset chứa bytecode:
  • 8. Trang 8 Có 1 điều cần phải lưu ý là ở đây không chỉ có IL bytecode, mà phần binary đầu tiên là còn chứa 1 đoạn dât của Method Header, đoạn method header này giúp jit compiler (và cả cách trình decomplie xác định được thuộc tính của toàn bộ method đó). Có 2 dạng method Header, 1 là Tiny Header, 2 là Fat Header. Theo blog của Zsozso có mô tả rất chi tiết về 2 dạng method header này. Tôi sẽ tóm tắt lại những gì quan trọng: Tiny Header là 1 byte nằm đầu tiên tại offset chứa method. 1 byte = 8 bit, 2 bit đầu tiên để xác định đó là Tiny header, và 6 bit tiếp theo xác định độ dài của toàn bộ IL bytecode của method. 2 bit đầu tiên cố định là 0x02. Tiny Header chỉ được sử dụng trong các trường hợp sau: - Trong method không sử dụng biến cục bộ - Trong method không sử dụng exception handler. - Trong method không có data section bổ sung - Độ dài của toàn bộ IL bytecode của method không vượt quá 64 byte. - Maxstack của method không được vượt quá 8. Vì tiny header bị giới hạn trong 1 byte, cho nên không thường gặp, Đa phần các trường hợp đều là Fat Header. Fat Header thì phức tạp và chứa nhiều thông tin hơn Tiny Header.
  • 9. Trang 9 Fat Header gồm có: 2 byte đầu: thường thấy là 0x3013 (Little Endian). Trong đó chia thành các bit nhỏ, chứa các giá trị như sau: - Bit 0- bit 11: Fat format được set ở vị trí bit 0, giá trị là 1 -> Fat Header =0x3 (010b=0x2, 110b=0x3, bit đầu chuyển từ 0 thành 1). Bit thứ 8 để xác định xem có extra section trong method không, Bit thứ 10 xác định là các constructor method luôn được gọi cho các biến cục bộ. - Bit 12-15: Kích thước của toàn bộ Fat Header, Ksoos này được nhân với 4 để ra toàn bộ kích thước cần dùng của Fat Header. Số này luôn là 3, 3x4=12, kích thước của toàn bộ Fat Header = 12 byte. Byte thứ 2: kích thước 1 WORD. Ghi giá trị của maxstack. Byte thứ 4: kích thước 1 DWORD: Ghi kích thước của toàn bộ IL bytecode trong method Byte thứ 8: Kích thước 1 DWORD, Metadata Token trỏ đến một signature nằm trong metadata table, tham chiếu đến 1 mục nhất định trong #Blob String, mô tả kiểu giá trị của các biến cục bộ có trong method (viết tắt là LocalVarSigTok). Nếu giá trị này bằng 0, có nghĩa là trong method không có biến cục bộ. OK, thế là xong về 2 method header. Tôi sẽ lấy ví dụ về 1 Fat Header, cũng là method đã chụp ở trong các hình trên. Data của Toàn bộ Method này: 133002002C0000000100001100032C0B027B0100000414FE012B0117000A062D0E0002 7B010000046F1200000A00000203281300000A002A Trong đó: - Màu đỏ: Fat Header - Màu cam: Maxstack. Maxstack=3, có nghĩa là sẽ có tối đa 3 giá trị có thể được push vào stack - Màu xanh lục: Size of code. 0x2C. Method này có 44 bytecode - Màu xanh dương: LocalValSigTok - Màu tím: IL bytecode Thế là chúng ta đã biết được cách xác định thông tin và cách biểu diễn của một method trong .NET file. Thực tế là phần này không được trực tiếp sử dụng trong Advance Patching, nhưng rất quan trọng để bạn có thể hiểu mình đang làm gì trong khi thực hiện Advanced Patching ADVANCED PATCHING Mấy ngày trước, tôi đi kiếm một chương trình Disk Defraggement, và tình cờ tôi đọc được 1 bài Review về Diskeeper nên kéo về xem thử để mong “kiếm chác” được gì đó. Và tình cờ nó được viết bằng .NET. Về cách tôi tìm ra tử huyệt để patch, tôi sẽ không nói ở đây, các bạn có thể tự tìm. Tử huyệt nằm ở file DkUserCtrl.dll, method GetData(), class ProductManager. Ta sẽ thấy 1 đoạn code như thế này:
  • 10. Trang 10 1 biến kiểu bool có tên là IsTrialware được gán giá trị bằng việc đọc thông tin từ trong 1 file XML nào đó. Nếu biến này có giá trị true, thì chương trình sẽ chạy ở trial mode, nếu mang giá trị false, thì chương trình sẽ là Full :p. Vậy thì tôi muốn set cố định cho isTrialware luôn là false, thì tôi sẽ phải thay đổi code nhiều hơn là Basic Patch, và ở đây cũng ko có cái if...else... nào để tôi có thể patch cả. Tôi muốn 1 code như thế này: This.DkProductData.isTrialware = false Đoạn code trong IL sẽ như sau: Chi tiết: 115 L_018d: ldloc.2 //Load local variable 2 to stack 116 L_018e: ldstr "isTrialware" //Load string to stack //Call method 117 L_0193: call System.Boolean DkUserCtrl.XmlHelper::GetXmlBoolValue(System.Xml.XmlNode,System.String) 118 L_0198: stfld System.Boolean DkUserCtrl.DK_PRODUCT_DATA::isTrialware //Store value to object 119 L_019d: ldarg.0 //load the first argument 120 L_019e: ldfld DkUserCtrl.DK_PRODUCT_DATA DkUserCtrl.ProductManager::DkProductData //load the field to stack Code ở line 115,116 và 117 tương ứng với code sau: XmlHelper.GetXmlBoolValue(node, "isTrialware");
  • 11. Trang 11 Bây giờ tôi sẽ xóa 3 lines này đi và thêm vào 1 dòng code mới để gán luôn cho giá trị của isTrialware = false. Với kiểu boolean thì chỉ có 2 giá trị là 0 (false) và 1 (true). Vì thế cho nên chúng ta cần push 1 giá trị 0 vào stack. Vậy thì tôi sẽ dùng instruction ldc.i4.0, có nghĩa là LoaD Constant Integer (with) 4 bytes (size) (and value is) 0. Vậy là trong stack có giá trị = 0. Chỉ lệnh stfld ở line 118 sẽ save vào isTrialware. Các bạn nên sử dụng SAE để edit IL code sẽ dễ hơn rất nhiều so với sử dụng các công cụ khác. Cuối cùng, chúng ta có được: IL code sau khi được thay đổi Hiển thị dưới dạng high level language Kết quả (Full Edition)
  • 12. Trang 12 Job done! Ví dụ phía trên chỉ 1 ví dụ rất cơ bản cho việc Advance Patching. Chúng ta có thể làm nhiều hơn, thậm chí xóa toàn bộ IL code của 1 method và thay thế lại bằng code mới của chúng ta. Điều này rất hữu ích và có thể nói là luôn thành công trong các trường hợp. OK, Tôi nghĩ thế là cũng tạm ổn để kết thúc bài viết này rồi. KẾT LUẬN Qua bài viết này, hi vọng các bạn sẽ nhận được 1 thứ gì đó có ích cho bản thân mình đẻ có thể áp dụng trong việc thực hiện Reverse Engineering .NET. Advanced Patching chỉ là ứng dụng rất nhỏ, quan trọng là việc đọc hiểu được IL code và hiểu được cấu trúc của method cũng như 1 phần cấu trúc của .NET PE file. Và cũng như thường lệ, cuối cùng là lời cảm ơn. Đầu tiên là Daniel Pistelli, tác giả của CFF Explorer và rất nhiều tutorial liên quan đến .NET File format. Tiếp đến là 0xd4d, tác giả của de4dot và WICKY Hu, tác giả của SAE. Cũng xin cảm ơn rất nhiều anh em, bạn bè, tiền bối và hậu bối trong giới “giang hồ mạng” Hacking & Security tại Việt Nam. Họ đã giúp đỡ và chia sẻ với tôi rất nhiều, tạo cho tôi nhiều cơ hội để tiếp tục đi xa hơn trong con đường “vọc” và “vạch” này. Cụ thể ư? Nhiều quá tôi không kể hết được, rất nhiều anh trong REA, HVA, Cin1, REPT, đại gia đình Mi2 JSC (đặc biệt là sếp CISSP luannt  ) cũng như các bạn trẻ khác (nhiều quá, tôi không kể hết, những ai có cùng đam mê, đã gặp tôi, hoặc nói chuyện, thảo luận với tôi thì đều được liệt vào đây cả, và đặc biệt nhất,(luôn luôn vậy) là em Elvis.. Cảm ơn các bạn, những người đang đọc bài viết này, đã bỏ thời gian quý báu ra để nghe tôi “chém gió”. Hẹn gặp lại trong các bài viết tiếp theo. Tôi sẽ viết nhiều bài viết nữa bởi với tôi, “learning is sharing” mà. Best regards Levis Jan 28 2015