Bài viết sẽ đi vào các vấn đi vào các vấn đề sau 1/ Cấu trúc PE của 1 file thực thi,bảng IAT,phân tích các lời gọi hàm API,các module được load…. 2/ Thêm 1 section vào file 3/ Pack và Unpack,cách fix lại IAT 4/ Calling Convention và Name Manling
1/ Cấu trúc PE của file Cấu trúc PE có rất nhiều tài liệu trên mạng,bao gồm cả tiếng việt. Các tài liệu có thể tham khảo : PE – langman -congdongcviet.com PE – kienmanowar – reaonline.net PE – FireDragon – virusvn.com
Tuy nhiên,một phần để lưu trữ,1 phần để mach lạc loạt seri bài viết Lập trình trên windows.Tôi sẽ trình bày sơ qua,cũng dựa vào bài viết khái quát trên blog của anh benina.
PE là từ viết tắt của Portable Executable. Nó là quy tắc định dạng (format) file của Win32. Hay nói rõ hơn nó là format của các chương trình binary (exe, dll, sys, scr) cho MS windows NT, windows 95 và win32s. Nó có thể cũng được dùng cho các file object ( bpl, dpl, cpl, ocx, acm, ax)…
Một module (một file sau khi load vào memory nó được gọi là một module) trong memory miêu tả code, data, và resource từ một file exe.Các phần khác của một file PE có thể được read, nhưng ko được xếp (mapping) vào vùng nhớ (ví dụ như relocations). Vài phần có thể ko được mapped vào trong memory hòan tòan, ví dụ, khi thông tin gỡ rối (debug) được đặt tại cuối một file. Một vùng trong PE header nói cho hệ thống biết cần bao nhiêu memory để set dự trữ cho việc sắp xếp exe vào memory. Dữ liệu ko được mapped vào memory được đặt cuối file, phần trước dữ liệu đó sẽ được mapped vào trong.
Khi hệ điều hành muốn thực thi 1 file PE , nó sẽ dùng 1 tiến trình (process) gọi là Windows Loader (hay còn gọi là PE Loader) để tải (hay là xếp đặt (mapping)) file PE vào vùng nhớ (memory). Việc tải này ko đơn giản như là việc copy y nguyên file vào vùng nhớ, mà nó thực sự là xếp đặt (mapping) dữ liệu file vào vùng nhớ. Tức là có thể có 1 số phần trong file PE trên đĩa (disk) sẽ ko được mapping(sắp đặt) vào vùng nhớ.Và cuối cùng , phần mở rộng tên file PE (đuôi của file PE) có dạng như sau: exe, dll, sys, scr, bpl, dpl, cpl, ocx, acm, ax.
Các thành phần trong cấu trúc PE : Cấu trúc minh họa của PE
I . MS-DOS header hay còn còn gọi là mz header : Vùng này chiếm 64 byte , với cấu trúc như sau :
II.Phần Header :
Gồm Signature(PE00) File Header Option Header File Header :20byte
Option Header
1 số điểm cần lưu ý ở phần này : AddressOfEntryPoint(RVA) là địa chỉ ảo tương đối,nơi mà câu lệnh đầu tiên được windows loader load lên bộ nhớ và thực thi.Đây cũng là 1 field rất quan trọng.Các virus dựa vào trường này,nhằm sửa lại trỏ đến 1 đoạn khác,thực thi xong phần “công việc” của chúng rồi cho nhảy lại vị trí gốc của chương trình.
Nếu là 1 file nguyên thể, ko pack, protect, compress gì cả thì nó sẽ trỏ đến điểm đầu tiên chạy thật của chương trình. thường là nằm trong section .TEXT ( nếu là sảm phẩm cháu của MS ), CODE (nếu là cháu của Borland). Điểm chạy này được gọi là OEP : Original Entry Point
Image Base : Địa chỉ ưu tiên nạp file vào memory
SectionAlignment Không gian của các section trong bộ nhớ ảo. Khi file thực thi được ánh xạ lên bộ nhớ ảo thì địa chỉ bắt đầu sẽ là bội số của giá trị này. Giá trị của trường này nhỏ nhất là 0x1000. Đối với các linker của Borland thì thường lấy 0x10000h==64KB (1 segment
FileAlignment : Độ rộng của section trên file sẽ là bội của trường này
… 1 số trường khác các bạn xem code tôi cung cấp,từ đó cũng dễ hiểu hơn
Ta sẽ đi phân tích các thành phần quan trọng hơn.Đó là DataDirectory Đó là 1 mảng chứa các thành phần quan trọng của PE,bao gồm :
RVAs : Một địa chỉ RVA (viết tắt của cụm từ Relative Virtual Address, đôi khi còn được gọi là Relative Virtual Offset), được sử dụng để mô tả một offset trên bộ nhớ, nếu như không xác định được ImageBase. RVA không phải là File Offset. Nếu bạn có một bảng section, thì rất dễ để tìm ra File Offset từ một RVA.
Công thức File Offset = RVA – Virtual Offset + Raw Offset
Xét bảng IAT : Ta thấy bảng này trỏ đến
nên ta tính ra được : File Offset = RVA – Virtual Offset + Raw Offset = 12B80 – 1000 + 400 = 11F80
Quan sát byte ở vị trí 11F80 + 0xC = 11F8C ta có giá trị 12E42 nằm trong .text đổi qua file offset : File Offset = RVA – Virtual Offset + Raw Offset = 12E42 -1000 +400 = 12242
Tại vị trí offset 12242 ta được :
Các section trong file PE
The .debug Section The .drectve Section (Object Only) The .edata Section (Image Only) The .idata Section The .pdata Section The .reloc Section (Image Only) The .tls Section The Load Configuration Structure (Image Only) The .rsrc Section The .cormeta Section (Object Only) The .sxdata Section
Cấu trúc từng section các bạn xem thêm tại file PE COFF download từ M$ về.Mình sẽ cung cấp ở phần tài liệu tham khảo.
Sau đây là code view các thành phần của PE file 1 cách đơn giản : Ta khai báo cấu trúc của từng thành phần PE
Lấy từng field trong cấu trúc đã khai báo : DOS header :
Header :
phần option :
Phần section :
2/Thêm 1 section vào file các bước thực hiện = tay : Xem bài viết của nhatphuongle tại đây : <link1>
Có 1 bài viết trên codeproject vừa hay vừa dễ hiểu.Có thời gian tôi sẽ translate bài này làm bài tut cho các bạn theo dõi. http://www.codeproject.com/Articles/1253…iginalOEP5
Ta nói sơ qua 1 số vấn đề tác giả đề cập :
Tôi muốn giải thích cách thay đổi Offset of Entry Point (OEP) tring cùng 1 file,lấy ví dụ là file CALC.EXE của Windows XP.Đầu tiên,sử dụng 1 PE Tool để tìm OEP,ở đây ta sử dụng PEID.Ta thấy Entrypoint có Offset là 0x00012475 và Image Base có Offset là 0x01000000.Giá trị của OEP là Relative Virtual Address,vì thế gá trị của Image Base dùng để chuyển qua giá trị của Virtual_Address
Virtual_Address = Image_Base + Relative_Virtual_Address
DynLoader(), in loader.cpp, is reserved for the data of the new section, in other words, the Loader. => jmp đến đoạn code thực thi
CPECryptor :
Tiếp theo :
Gán VirtualAddress và VirtualSize cho mỗi section bởi SectionAlignment:
Gán PointerToRawData và SizeOfRawData cho mỗi section bởi FileAlignment:
Fix lại SizeofImage
Hàm PEAlign:
Set Bound Import Directory header to zero(vì nó không quan trọng):
Bây giờ ta lưu OEP và Image Base lại.Ta sẽ giành 1 free space cho DynLoader()
Phục hồi các thanh ghi :
Fix lại IAT :
CITMaker class library trong itmaker.h sẽ build lại bảng IAT Khai báo bảng :
Sau đó thì ta thiết lập lại bảng IAT có hỗ trợ dll,vì thế ta phải build lại bằng code sau :
Inject your code
3/ Pack và Unpack,cách fix lại IAT
Để chống lại các cracker, các coder viết ra 1 chương trình packer, nó có nhiệm vụ nén file .exe lại thành 1 file mới cũng có đuôi là .exe ( khác với các file nén thông thường là .zip và .rar). Vì là file .exe nên nó có thể tự động run và khi run nó cũng tự động giải nén file lúc đầu bị pack vào memory. Sau khi giải nén xong, nó chuyển điều khiển control đến vùng memory chứa phần code giải nén và cho thực thi . Điểm đầu tiên trong memory giải nén sẽ thực thi gọi là OEP (Original Entry Point) . Các bạn nên phân biệt OEP và EP. EP là điểm đầu tiên trong memory mà Windows chuyển quyền điều khiển cho file exe thực thi sau khi Windows mapping file .exe vào vùng nhớ . Vậy EP là nói về Entry Point của file sau khi pack xong, còn OEP là Entry Point của file gốc bị pack sau khi được giải nén vào memory.
Các cracker cũng ko chịu thua các coder, để crack được, họ tìm cách xây dựng lại một file .exe từ code sau khi giải nén trong memory. Hành động này gọi là unpack. Vậy unpack tức là sau khi file pack giải nén vào memory và chuyển điều khiển đến OEP thì ta cho chương trình dừng lại tại OEP ( ko thực thi lệnh tại OEP) và copy vùng nhớ giải nén ra thành 1 file .exe. hành động copy này gọi là DUMP. Sau khi dump vùng nhớ ra thành file .exe, file này vẫn chưa run được, ta cần phải fix IAT (IAT là Import Address Table :bảng địa chỉ hàm nhập khẩu), các bạn sẽ tìm hiểu đề tài này sau, các bạn chỉ biết fix IAT tức là sửa chửa bảng quản lý các hàm “nhập khẩu” từ các file thư viện .dll thì file sau khi dump mới run được.
Cách kiểm tra chương trình có bị pack =upx không :
Hướng dẫn Unpack upx file : Tôi sẽ hướng dẫn các bạn cách unpack file được pack bằng upx,các kiểu pack khác các bạn tham khảo thêm tại tài liệu tôi cung cấp ở dưới.
Kiểm tra chương trình pack bằng gì bằng PEID
Tiếp tục chạy chương trình.Ấn F9 để debug.Ấn F7 để nhảy đến dòng tiếp.Vì UPX backup các thanh ghi nên ta sẽ lợi dụng việc này để tạo breakpoint và nhảy đến vị trí restore.
Ấn F9 để nhảy đến nơi POPAD các thanh ghi,sau đó đặt breakpoint(F2) tại 01020E5B và ấn F9 để nhảy đến đó,chuẩn bị vào đầu hàm,nơi thực hiện các code chính của chương trình.
Tắt breakpoint và tiếp tục ấn F7 để vào chương trình thật.Ta sẽ dump tại đây,xác định offset và sẽ tiến hành fix lại file PE.Đến đây,có nghĩa là khi load chương trình.file được pack đã giải nén và trả lại quyền thực thi cho chương trình chính.
Ta tiến hành dump như sau : Nhớ lấy địa chỉ trong ô modify,đây là địa chỉ quan trọng nhằm fix lại file sẽ cho address of entrypoint trỏ đến đó.
Ta tiếp tục fix lại file PE,fix lại IAT … để có thể chạy chương trình như bình thường ! Dùng ImpREC để fix. Ta nhập như hình vẽ.Sau đó bấm IAT Autosearch,Get Import rồi bấm Fix Dump.Lưu file lại thế là xong!!!
Cũng không quá phức tạp đúng không.Các kiểu pack khác sẽ khó chịu hơn rất nhiều vì nó mã hóa và làm rối code.Tôi xin cung cấp tài liệu để các bạn tập unpack.
tài liệu : http://www.hvaonline.net/hvaonline/posts/list/23682.hva Đây là cuốn ebook tổng hợp các bài unpack rất quý giá của REA.
4/ Calling Convention và Name Manling http://it08cl2.net/mybb/thread-Ham-trong-C-C
5/Kỹ thuật phân tích lời gọi hàm : bài article A Museum of API Obfuscation on Win32 của tác giả Masaki Suenaga có trình bày rất rõ ràng. anh quangrelight có dịch ra tại bài viết này,nhưng chưa xong http://diendan.congdongcviet.com/showthread.php?t=82874
Tài liệu tham khảo :
PE tutorial ——- kienmanowar Understanding IAT ——- lenhatluong Add new section ——- lenhatphuong pe_coff ——- m$ Inject-your-code-to-a-Portable-Executable-file ——- Ashkbiz Danehkar Unpack collection ——- REA Các tài liệu trích đoạn khác trên internet(không rõ ràng)
Tools : OllyDbg Exescope PEID ImpREC