Tải bản đầy đủ (.docx) (24 trang)

Phân tích cấu trúc PE file

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.15 MB, 24 trang )

Cấu trúc của PE file
và đọc PE file

Tháng 8/ 2018

Trang 1


Mục Lục

I.

MỤC TIÊU
• Tìm hiểu cấu trúc của File PE bao gồm:
o DOS MZ Header
o PE Header: Signature, File Header, Optional Header
o Section Header
o Section Table


Viết 1 chương trình đọc File PE gồm các chức năng:
o Kiểm tra có phải file PE hợp lệ hay không
o Hiển thị dữ liệu dưới dạng HEX
o Hiển thị các giá trị của DOS MZ Header
o Hiển thị các giá trị của PE Header
 Signature
 File Header
Trang 2


Optional Header: Phần Data Directory hiển thị dữ liệu của Import


Descriptor, Export Directory
o Hiển thị dữ liệu của Section Table


II.

CẤU TRÚC FILE PE
PE file là định dạng file riêng của Win32. Tất cả các file có thể thực thi được trên
Win32( ngoại trừ các tập tin VxDs và các file Dlls 16 bit) đều sử dụng định dạng file PE.
Các file Dlls 32 bit, các file Dlls 32 bit, các file COMS, các điều khiền OCX, các chương
trình ứng dụng nhỏ trong Control Pannel( .CPL files) và các ứng dụng .NET tất cả đều là
định dạng file PE. Thậm chí các chương trình điều khiển ở Kernel mode của các hệ điều
hành NT cũng sử dụng định dạng PE.
Cấu trúc cơ bản của 1 file PE:
DOS MZ Header
DOS stub
PE header
Section table
Section 1
Section 2

Section n

III.

MS-DOS HEADER
Tất cả các file PE đều bắt đầu bằng DOS HEADER, vùng này chiếm giữ 64 bytes đầu
tiên của file.
Nó bao gồm 19 thành phần có trong đó chỉ có 2 thành phần đáng chú ý nhất là :
e_magic và e_lfanew


Trang 3




Thành phần e_magic của DOS header chứa 2 giá trị là 4Dh và 5Ah( là 2 ký tự
“M” và “Z” ) của Mark Zbikowsky một trong những người sáng tạo chính của
MS-DOS (MZ là 2 bytes đầu tiên mà bạn sẽ nhìn thấy trong bất kỳ file PE nào).



Thành phần e_lfanew là một giá trị DWORD( 1 double word=4 bytes), nó nằm ở
vị trí cuối cùng của DOS Header và nằm ở vị trí bắt đầu của DOS Stub, chứa
offset của PE Header.



Các ánh xạ file thường được sử dụng vì chúng nạp vào bộ nhớ và sử dụng file
một cách dễ dàng.
Khi sử dụng win32 API đọc một file PE thì:

Trang 4


LBase

CreateFile():
//Open the Exe File
hFile = CreateFile("D:\\Test\\no.exe", GENERIC_READ, FILE_SHARE_READ,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("\nERROR : Could not open the file specified\n");
_getch();
}



Hàm này được sử dụng để tạp và mở các file. Nó có thể mở các file, pipe,
stream hoặc các thiết bị I/O có sẵn và tạo file mở.

Ban đầu file đọc được sẽ ở Disk sau đó chúng ta sẽ lưu trữ toàn bộ nội dung của
file PE vào Physical Memory sử dụng hàm sau:
hMapObject = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

Một File Mapping Object sẽ có thể Map tới nhiều file View sử dụng hàm:
lpBase = MapViewOfFile(hMapObject, FILE_MAP_READ, 0, 0, 0);

Memory>

 Hàm MapViewOfFile() sẽ trả về một con trỏ trỏ đến địa chỉ cơ sở của ánh xạ
FileView, pointer này sẽ được sử dụng để truy cập trong bộ nhớ, nhờ đó mà ta có
thể đọc và ghi tại bất kỳ vị trí nào trong file.
Ta sẽ gán vị trí con trỏ dosHeader =lpBase nó có giá trị default là 0x04000000
//Get the DOS Header Base
dosHeader = (PIMAGE_DOS_HEADER)lpBase;// 0x04000000

Kiểm tra xem đó có phải là file PE hợp lệ hay không:
if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {

//Dump the Dos Header info

Trang 5


dumMSDOSHeader(dosHeader);
}
else {
printf("\nGiven File is not a valid DOS file\n");
goto end;
}

Code hiển thị dữ liệu MS DOS:
void dumMSDOSHeader(PIMAGE_DOS_HEADER dosHeader) {
printf("\nValid Dos Exe File\n------------------\n");
printf("\nDumping DOS Header Info....\n---------------------------");
printf("\n%-36s%s ", "Magic number : ", dosHeader->e_magic ==
0x5a4d ? "MZ(Mark Zbikowski)" : "-");
printf("\n%-36s%#x", "Bytes on last page of file :", dosHeader>e_cblp);
printf("\n%-36s%#x", "Pages in file : ", dosHeader->e_cp);
printf("\n%-36s%#x", "Relocation : ", dosHeader->e_crlc);
printf("\n%-36s%#x", "Size of header in paragraphs : ", dosHeader>e_cparhdr);
printf("\n%-36s%#x", "Minimum extra paragraphs needed : ",
dosHeader->e_minalloc);
printf("\n%-36s%#x", "Maximum extra paragraphs needed : ",
dosHeader->e_maxalloc);
printf("\n%-36s%#x", "Initial (relative) SS value : ", dosHeader>e_ss);
printf("\n%-36s%#x", "Initial SP value : ", dosHeader->e_sp);
printf("\n%-36s%#x", "Checksum : ", dosHeader->e_csum);
printf("\n%-36s%#x", "Initial IP value : ", dosHeader->e_ip);

printf("\n%-36s%#x", "Initial (relative) CS value : ", dosHeader>e_cs);
printf("\n%-36s%#x", "File address of relocation table : ",
dosHeader->e_lfarlc);
printf("\n%-36s%#x", "Overlay number : ", dosHeader->e_ovno);
printf("\n%-36s%#x", "OEM identifier : ", dosHeader->e_oemid);
printf("\n%-36s%#x", "OEM information(e_oemid specific) :",
dosHeader->e_oeminfo);
printf("\n%-36s%#x", "RVA address of PE header : ", dosHeader>e_lfanew);

printf("\n===========================================
====================================\n");
}

IV.

PE HEADER
PE Header là thuật ngữ chung đại diện cho một cấu trúc được đặt tên là
IMAGE_NT_HEADERS. Cấu trúc này bao gồm 3 thành phần và được định nghĩa trong
file windows.inc như sau:

Trang 6


Con trỏ trỏ đến NT HEADER được tính bằng con trỏ đầu file + địa chỉ RVA của PE
Header
//Get the Base of NT Header(PE Header) = lpBase + RVA address of PE header
ntHeader = (PIMAGE_NT_HEADERS)((DWORD)lpBase + (dosHeader->e_lfanew));




Signature:
Là một biến DWORD chứa những giá trị sau: 50h, 45h, 00( các ký tự “PE” được
đi kèm bởi các giá trị tận cùng là 0)
Chúng ta cần kiểm tra giá trị này để xem đó có phải là file PE hợp lệ hay không:

if (ntHeader->Signature == IMAGE_NT_SIGNATURE) {
printf("NTHEADER: %x", ntHeader);
printf("\nValid PE file \n-------------\n");
…………………
}



FileHeader ( 20bytes)
IMAGE_FILE_HEADERS: chứa thông tin về sơ đồ bố trí vật lý và nhũng đặc
tính của file. Ví dụ: số lượng các section,…
o FileHeader được định nghĩa như sau:

Trong cấu trúc này thì thành phần chúng ta cần quan tâm là
NumberOfSections. Trong trường hợp muốn thêm hoặc xóa bất kỳ các
sections nào trong 1 file PE thì thành phần này phải thay đổi.
Characteristics bao gồm các cờ mà các cờ này xác định những thể hiện để
chúng ta biết được đang làm việc với 1 file dll hay file thực thi.
Code hiển thị thông tin File Header:
void DumFileHeader(PIMAGE_NT_HEADERS ntHeader) {
//Get the IMAGE FILE HEADER Structure
IMAGE_FILE_HEADER header = ntHeader->FileHeader;
//Determine Machine Architechture
printf("\n%-36s", "Machine Architechture :");
checkMachineOfFileHeader(header);

//Determine the characteristics of the given file
printf("\n%-36s %#x \n", "Characteristics:", header.Characteristics);
checkCharacteristics(header);
char buff[40];

Trang 7


printf("\n%-36s%x", "Time Stamp :", header.TimeDateStamp);
ctime_s(buff, sizeof buff, &(header.TimeDateStamp));
printf("- %s\n", buff);
//Determine Time Stamp
printf("%-36s%d",
"No.sections(size)
:",
header.NumberOfSections);
//Determine number of sections
printf("\n%-36s%d",
"No.entries
in
symbol
table
:",
header.NumberOfSymbols);
printf("\n%-36s%d",
"Size
of
optional
header
:",

header.SizeOfOptionalHeader);
}

Trên Lord thì nó được xác định như sau:



Optional Header( 224 bytes tiếp theo)
IMAGE_OPTINAL_HEADER32: chứa thông tin về sơ đồ logic bên trong của 1
file PE. Ví dụ: AddressOpEntryPoint. Kích thước của nó được quy định bởi
một thành phần của FileHeader. Các cấu trúc của những thành phần này cũng
được định nghĩa trong file windows.h.
o Optional Header được định nghĩa như sau:

Trang 8












AddressOfEntryPoint- RVA(địa chỉ tương đối ) của câu lệnh
đầu tiên mà sẽ được thực thi khi chương trình PE Loader sẵn sàng
để run PE File( thông thường nó trỏ tới section .text ) hay CODE.

Magic: Xác định xem nhị phân là 32 bit hay 64 bit.
• 0x10B: 32 bit
• 0x20B: 64 bit
ImageBase- Địa chỉ nạp được ưu tiên cho PE file. Lấy ví dụ: Nếu
như giá trị trong trường hợp này là 400000h, Pe Loader sẽ cố gắng
để nạp vào trong không gian địa chỉ ảo mà bắt đầu tại 4000000h.
Từ được ưu tiên vào đây có nghĩ là PE Loader không thể nạp file
tại địa chỉ đó nếu như có một module nào khác đã chiếm giữ vùng
địa chỉ này, 99% các trường hợp giá trị của ImageBase luôn là
400000h.
SectionAlignment- Phần liên kết của các Sections trong bộ nhớ.
Khi file thực thi được ánh xạ vào trong bộ nhớ thì mỗi section phải
bắt đầu tại một địa chỉ ảo mà là một bội số của giá trị này. Ví dụ:
Nếu giá trị tại trường này là 4096(1000h) thì mỗi section tiếp
theo sẽ phải bắt đầu tại vị trí mà section trước đó cộng với 4096
bytes.
Nếu section đầu tiên là tại 401000h và kích thước của nó là 10
bytes, vậy section tiếp theo tại 402000h cho dù không gian địa chỉ
giữa 401000h và 402000h sẽ hầu không được sử dụng.
FileAlignment- Phần liên kết của các Section trong file. Ví dụ:
nếu giá trị cụ thể của trường này là 512(200h) thì mỗi secti(tương
tự như SectionAlignment)
Trang 9








SizeOfImage- Toàn bộ kích thước của PE Image trong bộ nhớ. I
SizeOfHeader- Kích thước của tất cả các headers+section table.
Tóm lại nó bằng Kích thước file- kích thước được tổng hợp của
toàn bộ sections trong file. Có thể dùng giá trị này như một file
offset của section đầu tiên trong file PE.
DataDirectoryMột
mảng
của
16
IMAGE_DATA_DIRECTORY structures, mỗi một phần có liên
quan tới một cấu trúc dữ liệu quan trọng trong PE file chẳng hạn
như import address table.

Code hiển thị thông tin Optional Header:
void DumOptionalHeader(PIMAGE_NT_HEADERS ntHeader) {
printf("\n\nDumping PE Optional Header Info....\n-----------------------------------");
//Info about Optional Header
IMAGE_OPTIONAL_HEADER opHeader = ntHeader->OptionalHeader;
//printf("\n\nInfo of optional Header\n-----------------------");
printf("\n%-36s%#x", "Address of Entry Point : ", opHeader.AddressOfEntryPoint);
printf("\n%-36s%#x", "Base Address of the Image : ", opHeader.ImageBase);
printf("\n%-36s%s", "SubSystem type : ",
opHeader.Subsystem == 1 ? "Device Driver(Native windows Process)" :
opHeader.Subsystem == 2 ? "Windows GUI" :
opHeader.Subsystem == 3 ? "Windows CLI" :
opHeader.Subsystem == 9 ? "Windows CE GUI" :
"Unknown"
);
printf("\n%-36s%s", "Given file is a : ", opHeader.Magic == 0x20b ? "PE32+(64)" :
"PE32");

printf("\n%-36s%d", "Size of code segment(.text) : ", opHeader.SizeOfCode);
printf("\n%-36s%#x", "Base address of code segment(RVA) :",
opHeader.BaseOfCode);
printf("\n%-36s%d", "Size of Initialized data : ", opHeader.SizeOfInitializedData);
printf("\n%-36s%#x", "Base address of data segment(RVA) :",
opHeader.BaseOfData);
printf("\n%-36s%#x", "Section Alignment :", opHeader.SectionAlignment);
printf("\n%-36s%d", "Major Linker Version : ", opHeader.MajorLinkerVersion);
printf("\n%-36s%d", "Minor Linker Version : ", opHeader.MinorLinkerVersion);

Trang 10


}

Trên Lord thì nó được xác định như sau:

Cấu trúc DATA DIRECTORY
Data Directory là 128 bytes cuối cùng của OptionalHeader, và lần lượt là những
thành phần cuối cùng của IMAGE_NT_HEADERS.

Cấu trúc của Data Directory có 2 thành phần mà bao gồm thông tin về vị trí và
kích thước của cấu trúc dữ liệu.
o VirtualAddress: là một địa chỉ ảo tương đối (relative virtual address) của
cấu trúc dữ liệu
o isize: bao gồm kích thước theo bytes của cấu trúc dữ liệu
o 16 directories mà những cấu trúc này tham chiếu đến, bản thân chúng
được được nghĩa trong file window.inc:

Trang 11



Trong một chương trình đọc file PE thì 2 giá trị này được xác định như sau:

Virtual
Address

isize

Trên hình minh họa trên , vị trí được tô màu hồng chính là Import Table, với 8
bytes đầu chính là Virtual Address= 02D000h và 8 bytes cuối cùng thể hiện
kích thước của cấu trúc Import = 181Eh
Trong win32 fuction, 2 giá trị này được xác định như sau:
dwImportDirectoryVA = ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress;
ImportSize = ntHeader->OptionalHeader.DataDirectory[1].Size;

với ntHeader là con trỏ trỏ đến cấu trúc IMAGE_NT_HEADER.
Để xác định được vị trí của một directory đặc biệt, bạn xác định rõ địa chỉ tương
đối từ data directory. Sau đó sử dụng địa chỉ ảo để xác định section nào directory
ở trong. Một khi bạn phân tích section nào chứa directory, thì Section Header cho
section đó sau đó sẽ được sử dụng để tìm ra offset chính xác.

Ở đây chúng ta cần xác định 2 khái niệm đó là RVA( relative virtual address) và
VA( virtual address). VA chính là địa chỉ đầu tiên mà file được nạp. còn RVA
được xác định là địa chỉ đầu tiên mà file được nạp trừ đi Image Base.
RVA=VA- Image Base
Ví dụ: Nếu một File PE nạp tại địa chỉ 400000h trong virtual address(VA)
space và chương trình bắt đầu thực thi tại virtual address 401000h, chúng ta có
thể nói rằng chương trình bắt đầu thực thi tại RVA 1000h.
Trang 12



Tại sao lại sử dụng RVA? Đó là để giảm bởi quá trình nạp cả trình loader. Đó là
bởi vì nếu một module có thể được relocated bất kỳ vị trí nào trong không gian địa
chỉ ảo, nó sẽ gây trở ngại cho trình loader để fix mọi hardcode address trong
module. Nhưng ngược lại, nếu tất cả relocatable items trong file use RVA, there is
no need for the loader to fix anything: nó chỉ đơn giản relocates toàn bộ module
tới một new starting VA.
Chuyển đổi từ Virtual Offset đến RawOfsset:

Công thức chung là:
RVA= RawOffset_YouHaveRawOffsetOfSection+VirtualOffsetOfSection+ImageBase


IMAGE_EXPORT_DIRECTORY: trỏ tới 3 mảng và một bảng những
chuỗi ký tự ASCII. Mảng quan trọng là Export Address Table( EAT), vì
nó là một mảng con của các con trỏ hàm mà chứa địa chỉ của các export
functions. Hai mảng tiếp theo là Export Name table và Export Ordinal
Table chạy song song theo thứ tự sắp xếp tăng dần dựa theo tên của các
hàm để có thể được thực hiện và đưa ra kết quả là số thứ tự của hàm đó
được tìm thấy vào một mảng khác

Trang 13


Thông thường, NumberOfFunction phải ít nhất bằng NumberOfNames.
Tuy nhiên trong một số trường hợp thì NumberOfNames ít hơn
NumberFunctions. Khi một hàm được Export thông quan số thứ tự, nó
không có trong danh sách 2 mảng ENT và EOT- nó không có tên. Những
hàm không có tên này sẽ được Export thông qua số thứ tự.


Code hiển thị nội dung Export:
void DumpExportDirectory32(LPVOID lpBase, PIMAGE_NT_HEADERS32 ntHeader,
PIMAGE_DOS_HEADER dosHeader) {
DWORD dwExportDirectoryVA, ExportSize, dwRawOffset;
dwExportDirectoryVA = ntHeader>OptionalHeader.DataDirectory[0].VirtualAddress; //get address of
ExportDirectory
ExportSize = ntHeader->OptionalHeader.DataDirectory[0].Size; //get size of
ExportDirectory

Trang 14


if (dwExportDirectoryVA == 0) {
printf("Export is NULL!\n");
}
PIMAGE_SECTION_HEADER pSectionHeader =
IMAGE_FIRST_SECTION(ntHeader);
dwRawOffset = (DWORD)lpBase + pSectionHeader->PointerToRawData;
PIMAGE_EXPORT_DIRECTORY pExportDirectory =
(PIMAGE_EXPORT_DIRECTORY)(dwRawOffset + (dwExportDirectoryVA pSectionHeader->VirtualAddress));
printf("\nNumberOfFunctions: %d\n", pExportDirectory->NumberOfFunctions);
printf("Address Of Function: %#x\n", pExportDirectory->AddressOfFunctions);
printf("Address Of Name: %#x\n", pExportDirectory->AddressOfNames);
printf("Address Of Name Ordinals: %#x\n", pExportDirectory>AddressOfNameOrdinals);
printf("Ordinal Base: %d\n", pExportDirectory->Base);
printf("Name RVA: %s\n", pExportDirectory->Name);
char* buff[50];
ctime_s(buff, sizeof(buff), &pExportDirectory->TimeDateStamp);
printf("Time Date Stamp: %s\n",buff);


//get an array to store Name of Functions(Import from Name)
DWORD32* name = (DWORD32*)((DWORD32)lpBase +
(DWORD32)pExportDirectory->AddressOfNames + pSectionHeader>PointerToRawData - pSectionHeader->VirtualAddress);
//get an array to store Address of FUnctions
DWORD32* address = (DWORD32*)((DWORD32)lpBase +
(DWORD32)pExportDirectory->AddressOfFunctions + pSectionHeader>PointerToRawData - pSectionHeader->VirtualAddress);
//DWORD32* Ordinal = (DWORD32*)((DWORD32)lpBase +
(DWORD32)pExportDirectory->AddressOfNameOrdinals + pSectionHeader>PointerToRawData - pSectionHeader->VirtualAddress);
DWORD32 funcName, funcAddress, ordinal;
if (strlen(name) != 0) {
for (int i = 0; i < (DWORD32)pExportDirectory->NumberOfNames; i++)
{
funcName = name[i];
funcAddress = address[i];
ordinal = Ordinal[i];
printf("\nFunctions :%s \n", funcName + dwRawOffset pSectionHeader->VirtualAddress);
printf("Address: %#x \n", funcAddress );
}
else
{
printf("\nExport Directiory is Null.\n");
}

}

printf("\n=========================================
======================================\n");

Sau khi chạy ta được kết quả như sau( file lấy ra test là wincredui.dll trong

System32):

Trang 15


Trang 16


Đối chiếu với kết quả của PE View:

Trang 17




IMAGE_IMPORT_DESCRIPTOR(IID)

Các thành phần quan trọng nhất là các tên imported DLL và các mảng
cảu các cấu trúc IMAGE_THUNK_DATA. Mỗi cấu trúc
IMAGE_THUNK_DATA tương ứng với một imported function từ
DLL.
Số lượng các phần tử trong các mảng OriginalFirstThunk và FirstThunk
phụ thuộc vào số lượng của các hàm được imported từ file DLL.
Hai mảng song song, tương đương được gọi bởi các tên khác nhau nhưng
tên chung nhất là Import Address Table( cho một được gọi bởi
FirstThunk) và Import Name Table hay Import Lookup Table(cho một
được trỏ bởi OrginalFirstThunk).
Trong phần này chúng ta thử hiển thị dữ liệu của Import Table bằng cách
tính RVA của Import Table (chuyển đổi qua RawOffset).
Code hiển thị nội dung phần Import:

Trang 18


void DumpImportDirectory64(LPVOID lpBase, PIMAGE_NT_HEADERS64
ntHeader, PIMAGE_DOS_HEADER dosHeader) {
// Get Information of Data Diractory
DWORD dwImportDirectoryVA, dwSectionCount, dwSection = 0,
dwRawOffset, ImportSize;

dwImportDirectoryVA = ntHeader>OptionalHeader.DataDirectory[1].VirtualAddress;
ImportSize = ntHeader->OptionalHeader.DataDirectory[1].Size;
dwSectionCount = ntHeader->FileHeader.NumberOfSections;

printf("\n=======================================
========================================\n");
printf("RVA: %#x\n", dwImportDirectoryVA);
printf("SIZE: %#x\n", ImportSize);
//the pointer beginning of section=pointer of NtHeader+pointer of size of
Nt header
//size_of_IMAGE_NT_HEADERS64
PIMAGE_SECTION_HEADER pSectionHeader =
(PIMAGE_SECTION_HEADER)((DWORD)lpBase + (dosHeader->e_lfanew) +
sizeof(IMAGE_NT_HEADERS64));
for (; dwSection < dwSectionCount && pSectionHeader->VirtualAddress
<= dwImportDirectoryVA; pSectionHeader++, dwSection++);
pSectionHeader--;
dwRawOffset = (DWORD)lpBase + pSectionHeader>PointerToRawData;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor =
(PIMAGE_IMPORT_DESCRIPTOR)(dwRawOffset + (dwImportDirectoryVA pSectionHeader->VirtualAddress));
printf("Section Header Last: %#x\n", pSectionHeader);


for (; pImportDescriptor->Characteristics; pImportDescriptor++) {
int count = 0;
printf("\nDLL Name : %s\n\n", dwRawOffset +
(pImportDescriptor->Name - pSectionHeader->VirtualAddress));
PIMAGE_THUNK_DATA64 pThunkData =
(PIMAGE_THUNK_DATA64)( dwRawOffset + (pImportDescriptor>FirstThunk - pSectionHeader->VirtualAddress));

+) {
{

for (; pThunkData->u1.AddressOfData != 0; pThunkData+
if (pThunkData->u1.AddressOfData != 0x80000011)

printf("\tFunction : %s \n", (dwRawOffset +
(pThunkData->u1.AddressOfData - pSectionHeader->VirtualAddress +
2)));
count++;

Trang 19


}
else {

printf("\tFunction : %x \n", (dwRawOffset +
(pThunkData->u1.AddressOfData - pSectionHeader->VirtualAddress +
2)));
}
}

printf("So Function la: %d", count);
}

pSectionHeader = IMAGE_FIRST_SECTION(ntHeader);
if (pSectionHeader->SizeOfRawData != 0)
HexDump((char *)(dwRawOffset), pSectionHeader>SizeOfRawData, pSectionHeader->VirtualAddress);

printf("========================================
==================");
}

Sau khi lấy dữ liệu Import Data Directory ta có kết quả: ( file lấy ra test
là file Unikey.exe):

Trang 20


Đối chiếu với kết quả khi đọc file bằng PE View ta được:

V.

SECTION HEADER
Là thành phần tiếp theo ngay sau PE Header. Nó là một mảng của những cấu trúc
IMAGE_SECTION_HEADER, mỗi phẫn tử sẽ chứ thông tin về một seciton trong PE
File ví dụ như thuộc tính của nó và offset ảo( virtual offset). Số lượng các Section chính
là thành phần thứ 2 của File Header ( NumberOfSection). Nếu có 8 Sections trong PE
file thì sẽ có 8 bản sao của cấu trúc này trong table. Mỗi một cấu trúc Header là 40 bytes
và sẽ không có thêm padding giữa chúng( có nghĩa là nó sẽ không chèn thêm các bytes có
giá trị 00h).Cấu trúc này được định nghĩa trong file windows.inc như sau:


Trang 21










VirtualSize: Kích thước thật sự của section’s data theo bytes. Nó có thể là
kích thước của section trên đĩa (SizeOfRawData)
VirtualAddress- RVA của section. Trình PE loader sẽ phân tích và sử dụng
giá trị trong trường này khi nó ánh xạ section vào trong bộ nhớ. Vì vậy nếu
giá trị trong trường này là 1000h và PE file được nạp tại địa chỉ 4000000h
thì section sẽ được nạp tại địa chỉ là 401000h
SizeOfRawData: Kích thước của section data trong file trên đĩa, được làm
tròn lên bội số tiếp theo của sự liên kết bởi trình biên dịch
PointerToRawData( Raw Offset) thành phần này thực sự rất hữu dụng bởi vì
nó là offset từ vị trí bắt đầu của file cho tới phần section data. Nếu nó có giá
trị là 0, thì section’s data không được chứa trong file và sẽ không bị bó buộc
vào thười gian nạp (load time). Trình PE Loader sẽ sử dụng giá trị trong
trường này để tìm kiếm phần data trong section data trong section là ở đâu
trong file.
Characteristics: Bao gồm các cờ ví dụ như section này có thể chứa
executable code, initialized data, uninitialized data, có thể được ghi hoặc đọc.

Hiển thị thông tin Section:
void DumSectionHeader(PIMAGE_SECTION_HEADER pSecHeader,

PIMAGE_NT_HEADERS ntHeader) {
printf("\n\nDumping Sections Header Info....\n--------------------------------");
//Retrive a pointer to First Section Header(or Section Table Entry)
int i;
for (pSecHeader, i = 0; i < ntHeader->FileHeader.NumberOfSections; i++,
pSecHeader++) {
printf("\n\nSection Info (%d of %d)", i + 1, ntHeader>FileHeader.NumberOfSections);
printf("\n---------------------");
printf("\n%-36s%s", "Section Header name : ", pSecHeader->Name);
printf("\n%-36s%#x", "ActualSize of code or data : ", pSecHeader>Misc.VirtualSize);
printf("\n%-36s%#x", "Virtual Address(RVA) :", pSecHeader>VirtualAddress);
printf("\n%-36s%#x", "Size of raw data (rounded to FA) : ", pSecHeader>SizeOfRawData);
printf("\n%-36s%#x", "Pointer to Raw Data : ", pSecHeader>PointerToRawData);

Trang 22


printf("\n%-36s%#x", "Pointer to Relocations : ", pSecHeader>PointerToRelocations);
printf("\n%-36s%#x", "Pointer to Line numbers : ", pSecHeader>PointerToLinenumbers);
printf("\n%-36s%#x", "Number of relocations : ", pSecHeader>NumberOfRelocations);
printf("\n%-36s%#x", "Number of line numbers : ", pSecHeader>NumberOfLinenumbers);
printf("\n%-36s%s%#x", "Characteristics : ", "Contains ", pSecHeader>Characteristics);
checkCharacteristicsofSection(pSecHeader);
}
}

Giá trị của pSection được xác định là con trỏ trỏ đến offset đầu
SECTION_HEADER:

tiên của


pSecHeader = IMAGE_FIRST_SECTION(ntHeader);

chúng ta có thể sử dụng function win32, hoặc có thể tính như sau:
//the pointer beginning of section=pointer of NtHeader+pointer of size of Nt header
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)
((DWORD)lpBase + (dosHeader->e_lfanew) + sizeof(IMAGE_NT_HEADERS));

Trên Lord, các giá trị này được xác định như sau:

VI.

LƯU Ý
Trang 23


Trong phần DOS Header, chúng ta cần lưu ý tới 02 trường: e_magic và
e_lfanew.
o Trong phần COFF File Header, chúng ta cần lưu ý tới trường:
NumberOfSections.
o Trong phần Optional Header, chúng ta cần quan tâm tới một số trường:
AddressOfEntryPoint (RVA), ImageBase, SectionAlignment, FileAlignment,
SizeOfImage, SizeOfHeaders và DataDirectory.
o Cuối cùng, trong bảng section, một số trường cần quan tâm: VirtualSize,
VirtualAddress, SizeOfRawData, PointerToRawData, Characteristics.
Ngoài ra, chúng ta cũng cần quan tâm tới vị trí tương đối của các trường và kích
thước tương ứng của chúng trên PE file.
o

Trang 24




Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×