Trao đổi với tôi

http://www.buidao.com

10/14/10

[PE File] Portable Executable File Format

Hôm nay langman mặc dù rất bận nhưng xin được mạn phép trình bày sơ bộ về các tổ chức ct trong bộ nhớ như sau :

Bộ nhớ là gì ?

Bộ nhớ là 1 khái niệm chung, tiếng anh gọi là memory
Không gian nhớ ảo là gì ?
Chương trình của bạn được tổ chức trên đâu và như thế nào ?
Tại sao lại có từ công nghệ ảo , ảo hóa , ......?
eax,ebx là gì .... ?
Stack nằm ở đâu ???
.........................

Chap 1 : memory map


đầu tiên, ko đâu xa xôi langman check memory map của Unikey 4.0 RC, nó như sau :

(đoạn dưới còn nữa)

và thu gọn lại như sau


và 1 kết quả cũng gần như tương tự (ở 1 số điểm) khi ta tiến hành check memory map của những tiến trình khác


Sơ bộ :
+ Đầu tiên các bạn đã thấy được không gian nhớ rộng lớn từ 00000000h trải dài đến 7fffffffh........... và đây là ko gian nhớ ảo ( trình bày ở chap 2)

+ tiến trình của program thường bắt đầu tại address : 00400000 ( vì trường ImageBase trong PE file thường được thiết lập bằng 400000h )


+Tiến trình mất 00001000h byte cho PE header ( về PE header sẽ trình bày trong các chap sau) hay 4096 byte==1 section cho PE header
vậy câu lệnh run đầu tiên thực chất tại : địa chỉ bắt đầu + 00001000h





+ 2 byte đầu 00400000h và 00400001 luôn là 77==4Dh=="M" và 90==5A=="Z"
tại sao lại là MZ nhỉ ?


ngoài ra , nếu bạn dùng notepad mở file dll,exe,com,cpl, ocx, và các ứng dụng .NET khác đều có 2 byte đầu tiên là MZ.... Tại sao vậy nhỉ ????

Đây chính là viết tắt của Mark Zbikowsky, đây chính là tên của 1 trong những người quan trọng nhất sáng lập ra hệ điều hành MS-DOS đặt tiền đề cho Microsoft có được như ngày hôm nay.

+Stack của hàm main được nằm ở vùng có địa chỉ thấp hơn, với Unikey thì nó xin 2 section == 2000h bản thân tôi cũng chả hiểu sao Unikey nó lại cần stack rộng đến thế ....

+ ngoài ra bạn còn có thể thấy rõ các vùng
Executable Code Section: có tên là .text (micosoft) - code (borland)
Data section : .data , .rdata , .bss (micosoft) - data (borland)
Export Data Section : .edata
Import Data Section : .idata


+ Các thư viện liên kết động của hệ thống thì được nạp tại địa chỉ 77xxxxxh đổ lên trên
UKhook40.dll được nạp tại 10000000h


bài viết của langman, có nguồn từ Cộng đồng C việt, bạn nào vác thì làm ơn thêm từ cộng đồng c việt vô nhé

Chap 2 : Unikey

Hôm nay thứ 7, ko đi đâu chơi, ngồi viết tiếp chap 2 của cái tut này vậy. Ở chap 2 này chúng ta sẽ nghiên cứu về Unikey 4.0 RC .

Đánh giá sơ bộ về sảm phầm này :

Vậy file này này được tác giả viết với Visual C++ 7.1 hay còn được biết với cái tên Visual C++ .NET 2003



==> Visual C++ 7.0 ( Visual C++ .NET 2002 )

và ta thấy file này ko hề được pack bằng bất kì phần mềm pack chuyên dụng nào, thật tiện đề nghiên cứu.... Vậy chúng ta cùng đem nó ra làm vật thí nghiệm hôm nay nhé (sr tác giả ).

Ta bắt đầu nghiên cứu với file UnikeyNT nha
cấu trúc 1 file thực thi trong Windows os như sau :

PE là gì ?
Portable Executable File Format : 1 định dạng file thực thi. Các file dll, exe, com, cpl, ocx, và các ứng dụng .NET khác đều theo định dạng này.
Sảm phẩm cuối cùng của chúng ta... vậy chúng ta sẽ cùng nghiên cứu nó nha

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 :

PHP Code:
struct IMAGE_DOS_HEADER
{
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
DWORD e_lfanew; // File address of new exe header
};

giá trị của e_magic luôn luôn bằng 0x5a4b,byte đầu tiên sẽ là 0x4b=='M', byte tiếp theo 0x5A=='Z' (Little Endian Byte Order)
Đây chính là viết tắt của Mark Zbikowsky, đây chính là tên của 1 trong những người quan trọng nhất sáng lập ra hệ điều hành MS-DOS đặt tiền đề cho Microsoft có được như ngày hôm nay.
Đây là dấu hiệu thông báo cho biết đây là 1 file thực thi hợp lệ
Đây được MS coi là 1 magic number

+ Điều đáng quan tâm nhất ở trong cấu trúc này là e_lfanew, như ta đã biết, giờ ai còn dùng DOS nữa nên File address of new exe header là quan trong nhất. Đây là 1 DWORD (4byte). Giá trị nằm trong 4 byte này chưa offset đến PE file header.

II. DOS Stub
phần này có dung lượng khác nhau đối với mỗi PE file và do IDE dịch ra PE file quyết định. Thật ra đây chính là 1 ct dos nhỏ gọn với nội dung đơn giản . Thường trong đoạn code này, mục đích chính là gọi hàm 9 trong ngắt int 21h để viết lên chuỗi "This program ...."

III. PE header file
+ đầu tiên windows loader sẽ đọc ở phần MZ header giá trị e_magic, nhằm kiểm tra xem file có hợp lệ hay ko. Thật là độc quyền, thật là Microsoft...

+ tiếp theo windows loader sẽ đọc giá trị e_lfanew. windows loader sẽ bỏ qua luôn phần dos stub để nhảy sang PE header.

xin chỉ dẫn đôi chút thế này :
có nhiều tài liệu thì cho PE Signature ra 1 phần riêng, có tài liệu thì PE Signature lại nằm trong PE header, theo cá nhân tôi thì PE Signature là 1 phần của PE header vì e_lfanewtrỏ đến vùng PE Signature mà
demo nó như sau :
PHP Code:
struct PE header
{
tín hiệu
Image
// mô tả sơ bộ
Option
}

vùng màu xanh đó là dos stub, là 1 ct dos đơn giản viết lên màn hình câu : "This program cannot be run in DOS mode"...


A. PE Signature
Các bạn nhòn trong ảnh trên , 4 byte tôi khoanh tròn có giá trị là 0x00000108
đây chính là giá trị của e_lfanew
ngó đến offset 108, đó chính là PE Signature, là 4 byte tôi gạch chân,
giá trị của nó thì các bạn ngó sang màn hình bên phải, đơn giản là PE thôi,...
Các giá trị có thể có của vùng này : ý nghĩa thông báo
PE : file thực thi của windows NT - 32bit
LE : file thực thi của windows 3.x // đồ cổ ấy mà hệ điều hành cổ
NE : file thực thi của windows 16bit // đồ cổ ấy mà hệ điều hành cổ
LX : file thực thi của hệ điều hành OS/2 // đồ cổ hệ điều hành cổ
các món đồ cổ trên các bạn có thể tham khảo tài liệu lịch sử về hệ điều hành để biết thêm chi tiết.....

mở winnt.h lên ta có đoạn này
PHP Code:
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_OS2_SIGNATURE 0x454E // NE
#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
B. Image file header tạm dịch là bức tranh toàn cảnh ban đầu về file

vùng này là 20 byte tiếp theo với các yêu tố sau :
PHP Code:
struct IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections; // quan trọng cần chú ý
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics; // chứa các cờ , phục vụ cho việc debug
};
#define IMAGE_SIZEOF_FILE_HEADER 20
//vùng này là 20 byte bạn nhé

ở trong đây thì trường đáng quan tâm nhất là :
NumberOfSections : chúng ta phải thay đổi phần này nếu như muốn thêm hoặc xóa section trong file PE. Các virus rất quan tâm đến trường này, ví dụ như này, các virus tăng trường này thêm 1, sau đó chèn body của nó vào trong section nó khai báo thêm,...... như thế là virus đã chiếm đóng được file của mình rồi.
các packer cũng quan tâm đến nó, trong các file đã bị pack thì thường chỉ có 2 section, khi chạy sẽ tiến hành xả nén ra các section ( sẽ trình bày về pack ở các chap sau)
tất nhiên packer quan tâm đến nó thì c.r.a.c.k.er ( diễn đàn ko cho viết từ c r a c k ) cũng quan tâm đến nó vậy .

các trường khác trong vùng này chúng ta ko cần quan tâm lắm.....
ok?

bài viết của langman, có nguồn từ Cộng đồng C việt, bạn nào vác thì làm ơn thêm từ cộng đồng c việt vô nhé

Chap 3 : Unikey (tiếp )


IV. Cấu trúc Option
A. Cấu trúc
Phần tiếp theo là phần Option với cấu trúc như sau
( các bạn có thể thấy cấu trúc này trong file winnt.h)

PHP Code:
struct IMAGE_OPTIONAL_HEADER
{
// Các trường chuẩn
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;

//Các trường mở rộng của hệ điều hành windows NT
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Reserved1;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
}
B. Sơ lược về cấu trúc này :
+ magic : chả cần biết nó làm gì , nó cũng ko quan trọng lắm đâu...

+ MajorLinkerVersion, MinorLinkerVersion : đây là 2byte , trong các IDE cũ hơn thì nó định nghĩa theo kiểu unsigned char , nhưng dù gì thì cũng ko quan trọng, vì cái ý nghĩa của nó mới là quan trọng, 2 giá trị này chỉ ra phiên bản của linker đã linked nên nó

Chú ý thích về từ link này tí :

và 2 trường MajorLinkerVersion, MinorLinkerVersion sẽ nêu lên phiên bản của linker nói trên,

............
+MajorOperatingSystemVersion,MinorOperatingSystemVe rsion : 2 trường này nói nên phiên bản của hệ điều hành

Chú ý 2 : ví dụ version 7.01 thì : version này sẽ ko bị mã hóa theo chuẩn float nào cả, mà phần Major sẽ nêu nên số 7 và == 0x7
phần Minor sẽ == 0x1......


C. Các phần quan trọng
Vì tên ở trên cũng có khá rõ rồi , langman mạn phép trình bày thẳng vào các trường quan trọng bắt buộc phải nắm rõ nhé :

1. AddressOfEntryPoint : Nghe tên của trường này ta cũng biết là nó trỏ đến câu lệnh khởi đầu ( nó trỏ vào đâu thì đó là câu lệnh đầu tiên của chương trình được loader của hệ điều hành chạy).

+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 ct. 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



+Đối với 1 file bị virus quản lý, nó sẽ thêm 1 section .text vào, sau đó cho trường trỏ vào virus body của nó. sau khi nó chiếm được quyển, làm được những việc nó muốn nó mới nhảy đến OEP, ct mới được thực hiện.....
(mình ko có file nào bị nhiễm virus để debug cho bạn xem cả)



+Đối với 1 file bị pack,compress trường này sẽ trỏ đến đến section khác, thường là trỏ đến decompression stub để nhằm giải xả nén ra các các section khác lên bộ nhớ, sau đó mới cho nhảy về OEP, nói chung là nhay đển section khác, sau đó mới trả về OEP, ví dụ :

Các bạn xem ở ví dụ trên với game pikachu, ta thấy có 1 cái section tên là .aspack và trường trỏ đến vùng nhớ này !!! vậy là khi ta bật lên, ta sẽ nhảy ngay đến section này và thực hiện các code của aspack...


+Ở chế độ bảo vệ Starforce thì trường này sẽ trỏ đến 1 Vitual Address (địa chỉ ảo), vì lúc này code section (.text) vẫn có mặt trên ổ file nhưng lại ko được ánh xạ lên bộ nhớ ảo, mà sẽ được nạp lên trong quá trình thực thi (có thể là nạp đến đâu thực thi đến đấy, có thể là nạp hết , có thể là từng đoạn , còn tùy vào nhiều yếu tố ( sẽ được trình bày ở các phần sau )

Đây là 1 trường cực kì quan trọng, rất có ý nghĩa.


2. ImageBase : Địa chỉ nạp ưu tiên cho PE file. Windows loader sẽ cố gắng ánh xạ file vào vùng nhớ ảo có địa chỉ bắt đầu tại ImageBase. Ở đây langman dùng từ cố gắng và ưu tiên bởi vì nếu "đất đã có chủ" rồi thì windows loader sẽ đành phải dùng vùng lân cận.
99% trường này có giá trị bằng 00400000h điều này giải thích cho tại sao có nhận định ở chap1.
Mở rộng thêm đôi chút : đối với Visual Studio 2005 trở lên, thì giá trị 00400000h ko còn được dùng nhiều nữa .


Ta thấy :
+ OEP trỏ đến đến câu lệnh khởi đầu ( nhưng mà là ở trong file )
+ ImageBase cho ta địa chỉ ánh xạ ưu tiên.
vậy : câu lệnh khởi đầu trên bộ nhớ ảo sẽ có địa chỉ :
OEP + ImageBase
Chúng ta cùng nhau xem lại hình này sẽ thấy ngay điều đó :



hết chap 3 , dự kiến chap tiếp sẽ xoay quanh game pikachu .....

Chap 4 : Unikey (Tiếp)

C. Các phần quan trọng
3. 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).

Ví dụ : trường này có giá trị là 0x1000 , thì section tiếp theo (nếu đặt gần nhất) phải bắt đầu tại địa chỉ cũ + 0x1000.

Ví dụ cụ thể hơn :
Giả sử section đầu tiên bắt đầu tiên bắt đầu tại 0x00401000 và có độ rộng là 10byte. Thì section tiếp theo (nếu đặt gần nhất ) sẽ là 0x00402000 ( mặc dù không gian giữ 0x00401000 và 0x00402000 chả sử dụng làm gì cả)
Thực tế thì các section tiếp theo có thể phân bố rời rạc hơn ví dụ tại 0x0040300 hoặc 0x0040900, quan trọng nhất là khoảng cách phải chia hết cho 0x1000
Minh họa:


Các Virus 32 rất chú ý tến trường này phục vụ cho việc tính toán chính xác xem body của nó nằm ở đâu, nhưng chúng cũng ko mấy khi thay đổi trường này

Các bạn chú ý , giá trị trên kia liên quan đến bộ nhớ ảo , ko lại lầm lẫn với trường tiếp theo .

4. FileAlignment : Độ rộng của section trên file sẽ là bội của trường này, cũng có ý nghĩa giống như trường trên nhưng :
+ đây là ở trên file
+ Không gian rỗng ko được sử dụng hoặc ko được định nghĩa (thường thì có giá trị == 0 )


5. SizeOfImage : được hiểu bằng size của tất cả các header + dung lượng thật của các section liên kết với trường SectionAlignment

Ví dụ vui tí : tôi đọc trường này của game pikachu là 950272, sau khi tôi unpack game này thì được 1 file có dung lượng == 950272 byte
.........

6. SizeOfHeaders : nghe tên là biết rồi , độ lớn của total header, nó cũng là offset truy cập đến section đầu tiên, (trong file)

......... END ........

Vâng thưa các bạn, chúng ta tạm dừng việc nghiên cứu PE file format đối với Unikey tại đây. Tất nhiên chúng ta sẽ nghiên cứu tiếp,và loạt tut này vẫn còn nhiều chap, nhưng với đối tượng khác để dễ tiếp cận hơn, và nhiều thú vị hơn