Hệ thống tự động tạo chữ ký cho các tập tin thực thi. Cấu trúc các thành phần phần mềm

Bộ làm xáo trộn

Trình gỡ lỗi

Trình gỡ lỗi hoặc trình gỡ lỗi(Trình gỡ lỗi tiếng Anh) là một mô-đun môi trường phát triển hoặc một ứng dụng riêng biệt được thiết kế để tìm lỗi trong chương trình. Trình gỡ lỗi cho phép bạn theo dõi từng bước, giám sát, đặt hoặc thay đổi giá trị của các biến trong quá trình thực thi chương trình, đặt và xóa điểm dừng hoặc điều kiện dừng, v.v.

Làm xáo trộn(từ tiếng Latin obfuscare - che khuất, làm tối; và tiếng Anh obfuscate - làm cho không rõ ràng, khó hiểu, khó hiểu) hoặc làm xáo trộn mã - đưa văn bản nguồn hoặc mã thực thi của chương trình sang dạng duy trì chức năng của nó, nhưng gây khó khăn cho việc phân tích, tìm hiểu các thuật toán vận hành và sửa đổi trong quá trình dịch ngược.

« Sự vướng víu» Mã có thể được thực hiện ở cấp độ thuật toán, văn bản nguồn và/hoặc văn bản tập hợp. Để tạo văn bản tập hợp khó hiểu, có thể sử dụng các trình biên dịch chuyên dụng sử dụng các khả năng không rõ ràng hoặc không có giấy tờ của môi trường thời gian chạy chương trình. Ngoài ra còn có các chương trình đặc biệt thực hiện chức năng che giấu, được gọi là obfuscators.

Mô-đun thực thi, tệp thực thi- một tệp chứa chương trình ở dạng mà nó có thể được máy tính thực thi (sau khi tải vào bộ nhớ và được cấu hình cục bộ).

Thông thường, nó chứa biểu diễn nhị phân của các lệnh máy cho một bộ xử lý cụ thể (vì lý do này, trong tiếng lóng lập trình, từ nhị phân được sử dụng liên quan đến nó), nhưng nó cũng có thể chứa các lệnh bằng ngôn ngữ lập trình được thông dịch, việc thực thi các lệnh trong đó yêu cầu một thông dịch viên. Liên quan đến cái sau, thuật ngữ "chữ viết" thường được sử dụng.

Việc thực thi các tệp nhị phân được thực hiện bởi các máy được cài đặt phần cứng và phần mềm. Loại đầu tiên bao gồm các bộ xử lý - ví dụ: họ x86 hoặc SPARC. Thứ hai là các máy ảo, ví dụ như máy ảo Java hoặc .NET Framework. Định dạng của tệp nhị phân được xác định bởi kiến ​​trúc của máy thực thi nó. Có những máy được triển khai cả phần cứng và phần mềm, ví dụ như bộ xử lý dòng x86 và máy ảo VMware.

Trạng thái thực thi của tệp thường được xác định bởi các quy ước được thông qua. Do đó, trong một số hệ điều hành, các tệp thực thi được nhận dạng nhờ quy ước đặt tên tệp (ví dụ: bằng cách chỉ định phần mở rộng tệp - . exe hoặc. thùng rác), trong khi ở các tệp khác, các tệp thực thi có siêu dữ liệu cụ thể (ví dụ: bit quyền thực thi trên các hệ điều hành giống UNIX).

Trong kiến ​​trúc máy tính hiện đại, các tệp thực thi chứa lượng lớn dữ liệu không phải là chương trình máy tính: mô tả về môi trường phần mềm trong đó chương trình có thể được thực thi, dữ liệu để gỡ lỗi chương trình, các hằng số được sử dụng, dữ liệu mà hệ điều hành có thể cần để chạy quy trình (ví dụ: vùng kích thước được đề xuất) và thậm chí cả các mô tả về cấu trúc cửa sổ hệ thống con đồ họa được chương trình sử dụng.



Thông thường, các tệp thực thi chứa các lệnh gọi đến các hàm thư viện, chẳng hạn như các lệnh gọi tới các hàm của hệ điều hành. Do đó, cùng với sự phụ thuộc vào bộ xử lý (phụ thuộc vào máy là bất kỳ tệp thực thi nhị phân nào chứa mã máy), các tệp thực thi có thể được đặc trưng bởi sự phụ thuộc vào phiên bản của hệ điều hành và các thành phần của nó.

bộ nhớ của trình tải hệ điều hành và sau đó được thực thi. Trong hệ điều hành Windows, các tệp thực thi thường có phần mở rộng là ".exe" và ".dll". Phần mở rộng ".exe" dùng để chỉ các chương trình mà người dùng có thể trực tiếp khởi chạy. Phần mở rộng ".dll" có cái gọi là thư viện liên kết động. Các thư viện này xuất các hàm được sử dụng bởi các chương trình khác.

Để bộ tải khởi động của hệ điều hành tải chính xác tập tin thực thi vào bộ nhớ, nội dung của tệp này phải tương ứng với định dạng tệp thực thi được chấp nhận trong hệ điều hành này. Nhiều định dạng khác nhau đã tồn tại và vẫn tồn tại trên các hệ điều hành khác nhau ở những thời điểm khác nhau. Trong chương này, chúng ta sẽ xem xét định dạng Portable Executable (PE). Định dạng PE là định dạng chính để lưu trữ các tệp thực thi trong hệ điều hành Windows. Hội đồng. Các tệp NET cũng được lưu trữ ở định dạng này.

Ngoài ra, định dạng PE có thể được sử dụng để thể hiện tập tin đối tượng. Các tệp đối tượng được sử dụng để tổ chức biên dịch chương trình riêng biệt. Điểm biên dịch riêng biệt là các phần của chương trình (mô-đun) được biên dịch độc lập thành các tệp đối tượng, sau đó được liên kết bởi trình liên kết thành một tập tin thực thi.

Và bây giờ - một chút lịch sử. Định dạng PE được tạo bởi các nhà phát triển Windows NT. Trước đây, hệ điều hành Windows sử dụng các định dạng Có thể thực thi mới (NE) và Có thể thực thi tuyến tính (LE) để thể hiện các tệp thực thi và để lưu trữ tập tin đối tượngĐịnh dạng mô-đun đối tượng (OMF) đã được sử dụng. Định dạng NE dành cho các ứng dụng Windows 16 bit, trong khi định dạng LE, ban đầu được phát triển cho OS/2, đã là 32 bit. Câu hỏi đặt ra: tại sao các nhà phát triển Windows NT lại quyết định từ bỏ các định dạng hiện có? Câu trả lời trở nên rõ ràng khi bạn biết rằng hầu hết nhóm nghiên cứu tạo ra Windows NT đều đã từng làm việc tại Digital Equipment Corporation. Họ đang phát triển các công cụ cho hệ điều hành VAX/VMS tại DEC và họ đã có sẵn các kỹ năng cũng như mã được tạo sẵn để làm việc với các tệp thực thi được trình bày ở Định dạng tệp đối tượng chung (COFF). Theo đó, định dạng COFF ở dạng sửa đổi một chút đã được chuyển sang Windows NT và nhận được tên PE.

"Bảng thuật ngữ .NET Framework" nói rằng PE là cách triển khai định dạng COFF của Microsoft. Đồng thời, có tuyên bố rằng PE là định dạng tệp thực thi và COFF là định dạng tập tin đối tượng. Nói chung, chúng ta có thể thấy có sự nhầm lẫn trong tài liệu của Microsoft về tên của định dạng. Ở một số nơi họ gọi nó là COFF và ở những nơi khác họ gọi nó là PE. Đúng, người ta có thể nhận thấy rằng trong các văn bản mới, tên COFF ngày càng ít được sử dụng. Hơn nữa, định dạng PE không ngừng phát triển. Ví dụ: vài năm trước, Microsoft đã ngừng lưu trữ thông tin gỡ lỗi bên trong tệp thực thi và do đó hiện nay nhiều trường trong cấu trúc định dạng COFF không được sử dụng. Ngoài ra, định dạng COFF là 32 bit và phiên bản mới nhất của định dạng PE (được gọi là PE32+) có thể được sử dụng trên nền tảng phần cứng 64 bit. Vì vậy, rõ ràng, mọi thứ đang tiến tới mức cái tên COFF sẽ không còn được sử dụng nữa.

Điều thú vị cần lưu ý là các tệp thực thi ở định dạng NE và LE cũ vẫn được Windows hỗ trợ. Các tệp thực thi ở định dạng NE có thể chạy trong NTVDM (NT Virtual DOS Machine) và định dạng LE được sử dụng cho trình điều khiển thiết bị ảo (

Định dạng tệp thực thi của hệ điều hành phần lớn phản ánh các giả định và hành vi được tích hợp trong hệ điều hành. Liên kết động, hành vi của bộ tải khởi động và quản lý bộ nhớ chỉ là ba ví dụ về các thuộc tính dành riêng cho hệ điều hành có thể được hiểu khi bạn nghiên cứu định dạng tệp thực thi.

Tệp thực thi trên đĩa và mô-đun nhận được sau khi tải rất giống nhau. Trình tải chỉ cần sử dụng các tệp Win32 được ánh xạ bộ nhớ để tải các phần thích hợp của tệp PE vào không gian địa chỉ của chương trình. Việc tải DLL cũng dễ dàng như vậy. Sau khi mô-đun EXE hoặc .DLL được tải, Windows sẽ xử lý nó giống như các tệp ánh xạ bộ nhớ khác.

Ngược lại, trong Win32, bộ nhớ được sử dụng cho chương trình, dữ liệu, tài nguyên, bảng đầu vào, bảng đầu ra và các phần tử khác là một mảng không gian địa chỉ tuyến tính liên tục. Tất cả những gì đủ để biết trong trường hợp này là địa chỉ nơi trình tải ánh xạ tệp thực thi vào bộ nhớ. Sau đó, để tìm bất kỳ phần tử nào của mô-đun, việc tuân theo các con trỏ được lưu trữ như một phần của ánh xạ là đủ.

Tiêu đề MS-DOS

Tiêu đề MS-DOS chiếm 64 byte đầu tiên của tệp PE. Cấu trúc biểu thị nội dung của tiêu đề MS-DOS như sau:


typedef struct _IMAGE_DOS_HEADER ( //Tiêu đề DOS .EXE
USHORT e_magic; //MZ
USHORT e_cblp; // Byte cuối cùng
//trang tập tin
USHORT e_cp; //Các trang trong tập tin
USHORT e_crlc; //Cài đặt
USHORT e_cparhdr; // Kích thước tiêu đề bằng
// đoạn văn
USHORT e_minalloc; // Bộ nhớ được phân bổ tối thiểu
USHORT e_maxalloc; // Bộ nhớ được cấp phát tối đa
USHORT e_ss; // Ban đầu (tương đối)
//giá trị SS
USHORT e_sp; // Giá trị SP ban đầu
USHORT e_csum; //Kiểm tra tổng
USHORT e_ip; // Giá trị IP ban đầu
USHORT e_cs; // Ban đầu (tương đối)
//giá trị CS
USHORT e_lfarlc; //địa chỉ của file bảng cấu hình
USHORT e_ovno; // Số lớp phủ
USHORT e_res ; //Từ dành riêng
USHORT e_oemid; // Mã định danh OEM (cho
//e_oeminfo)
USHORT e_oeminfo; // Thông tin OEM; e_oemid
//cụ thể
USHORT e_res2 ; //Từ dành riêng
LONG e_lfanew; //địa chỉ của phần bù tiêu đề PE
) IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;

Tiêu đề chính của tệp PE biểu thị cấu trúc kiểu IMAGE_NT_HEADERS, được xác định trong tệp WINNT.H. Cấu trúc IMAGE_NT_HEADERS trong bộ nhớ là cấu trúc mà Windows sử dụng làm cơ sở dữ liệu mô-đun trong bộ nhớ. Mỗi tệp EXE hoặc DLL được tải được thể hiện trong Windows bằng cấu trúc IMAGE_NT_HEADERS. Cấu trúc này bao gồm một từ kép và hai cấu trúc con, như được hiển thị bên dưới:

Chữ ký DWORD;
Tiêu đề tệp IMAGE_FILE_HEADER;
IMAGE_OPTIONAL_HEADERTiêu đề tùy chọn;

Chữ ký tệp PE

Trường Chữ ký, được biểu thị dưới dạng mã ASCII, là PE\0\0 (hai byte 0 sau PE). Nếu trường e_lfanew trong tiêu đề DOS chỉ ra ký hiệu NE ở vị trí này thay vì ký hiệu PE thì bạn đang làm việc với tệp Win16 NE. Tương tự, nếu ký hiệu LE được chỉ định trong trường Chữ ký thì đây là tệp VxD (VirtualDeviceDriver). Ký hiệu LX đề cập đến một tệp từ đối thủ cũ của Windows 95, OS/2.

Tiêu đề tệp PE

Từ kép - chữ ký PE - trong tiêu đề của tệp PE được theo sau bởi cấu trúc kiểu IMAGE_FILE_HEADER. Các trường trong cấu trúc này chỉ chứa thông tin chung nhất về tệp.
Sau đây là các trường IMAGE_FILE_HEADER:

cấu trúc typedef _IMAGE_FILE_HEADER
{
Máy USHORT;
USHORT SốPhần;
Dấu thời gian ULONG;
Con trỏ ULONGToSymbolTable;
Số biểu tượng ULONG;
USHORT SizeOfOptionalHeader;
Đặc điểm USHORT;
) IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER

Máy móc– đây là đơn vị xử lý trung tâm dành cho tệp này. Các mã định danh bộ xử lý sau được xác định:

Intel I386 0xl4C
Intel I860 0xl4D
MIPS R3000 0x162
MIPS R4000 0x166
Tháng 12 Alpha AXP 0x184
Power PC 0x1F0 (endian nhỏ)
Motorola 68000 0x268
PA RISC 0x290 (Kiến trúc chính xác)

SốPhần– số phần trong tệp EXE hoặc OBJ.

Thời gianNgàyNgàyTem– thời điểm tệp được tạo bởi trình liên kết (hoặc trình biên dịch, nếu đó là tệp OBJ). Trường này cho biết số giây đã trôi qua kể từ 16:00 ngày 31/12/1969

Con trỏ tớiBiểu tượngBảng– offset file của bảng ký hiệu COFF. Trường này chỉ được sử dụng trong các tệp OBJ và PE có thông tin trình gỡ lỗi COFF. Các tệp PE hỗ trợ nhiều định dạng gỡ lỗi khác nhau, vì vậy trình gỡ lỗi phải tham khảo mục IMAGE_DIRECTORY_ENTRY_DEBUG trong thư mục dữ liệu.

SốBiểu Tượng– số ký hiệu trong bảng ký hiệu COFF.

Kích thướcTiêu đề tùy chọn– kích thước của tiêu đề tùy chọn có thể tuân theo cấu trúc này. Trong các tệp thực thi, đây là kích thước của cấu trúc IMAGE_OPTIONAL_HEADER tuân theo cấu trúc này.

Đặc trưng– cờ chứa thông tin về tập tin. Một số lĩnh vực quan trọng được mô tả ở đây.
0x0001 – tệp không chứa chuyển động
0x0002 - tệp đại diện cho ánh xạ thực thi (nghĩa là nó không phải là tệp OBJ hoặc LIB)
0x2000 – tệp là thư viện liên kết động (DLL), không phải chương trình

Tiêu đề tùy chọn của tệp PE

Thành phần thứ ba của tiêu đề tệp PE là cấu trúc kiểu IMAGE_OPTIONAL_HEADER. Đối với các tệp PE, phần này là bắt buộc. Các trường quan trọng nhất là trường ImageBase và Subsystem.

ImageBase- Khi trình liên kết tạo một tệp thực thi, nó mong đợi tệp đó sẽ được ánh xạ tới một vị trí cụ thể trong bộ nhớ và chính địa chỉ này được lưu trữ trong trường này.

Hệ thống con– loại hệ thống con mà tệp thực thi này sử dụng cho giao diện người dùng của nó. WINNT.H xác định các giá trị sau:
NATIVE = 1 – không yêu cầu hệ thống con (ví dụ: đối với trình điều khiển thiết bị)
WINDOWS_GUI = 2 – chạy trong hệ thống con GUI của Windows
WINDOWS_GUI = 3 – chạy trong hệ thống con ký tự Windows (ứng dụng đầu cuối)
OS2_GUI = 5 – chạy trong hệ thống con OS/2 (chỉ ứng dụng OS/2 IJC)
POSIX_GUI = 7 – chạy trong hệ thống con Posix

Bảng phần

Ngay sau tiêu đề tệp PE trong bộ nhớ có một mảng 1MAGE_SECT10N_HEADER. Cái bàn này. chứa thông tin về từng phần hiển thị. Số phần tử của mảng này được chỉ định trong tiêu đề của tệp PE (trường IMAGE_NT_HEADER.FileHeader.NumberOfSections). Các phần trong màn hình được sắp xếp theo địa chỉ bắt đầu thay vì theo thứ tự bảng chữ cái.
Mỗi IMAGE_SECTION_HEADER đại diện cho một cơ sở dữ liệu hoàn chỉnh về một phần của tệp EXE hoặc OBJ và có định dạng sau.

#xác định IMAGE_SIZEOF_SHORT_NAME 8
cấu trúc typedef _IMAGE_SECTION_HEADER
{
Tên UCHAR;
liên hiệp (
Địa chỉ vật lý ULONG;
Kích thước ảo ULONG;
) Linh tinh;
Địa chỉ ảo ULONG;
ULONG SizeOfRawData;
Con trỏ ULONGToRawData;
ULONG PointerToRelocations;
Con trỏ ULONGToLinenumbers;
USHORT NumberOfRelocations;
USHORT NumberOfLinenumbers;
Đặc điểm ULONG;
) IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Tên– Tên ANSI (không phải Unicode) 8 byte để đặt tên cho phần.

linh tinh– Trường này có các mục đích khác nhau tùy thuộc vào việc nó xuất hiện trong tệp EXE hay OBJ. Trong tệp EXE, nó chứa kích thước ảo của một phần mã chương trình hoặc dữ liệu. Đối với các tệp OBJ, trường này chỉ định địa chỉ vật lý của phần.

Địa chỉ ảo– trong trường hợp tệp EXE, trường này chứa RVA nơi trình tải sẽ ánh xạ phần đó. Các công cụ của Microsoft đặt RVA mặc định của phần đầu tiên thành 0x101. Đối với các tệp đối tượng, trường này được đặt thành 0.

Kích thước của dữ liệu thô– trong tệp EXE, trường này chứa kích thước phần được căn chỉnh theo giới hạn trên gần nhất của kích thước tệp.
PointerToRawData – phần bù tệp của khu vực chứa dữ liệu nguồn cho phần đó. Nếu người dùng ánh xạ tệp PE hoặc COFF vào bộ nhớ (thay vì để hệ điều hành tải nó), trường này quan trọng hơn trong VirtualAddress.

Con trỏ tới Tái định vị– trong các tệp đối tượng, đây là phần bù của tệp thông tin chỉnh sửa theo sau dữ liệu nguồn cho một phần nhất định. Trong tệp EXE, trường này được đặt thành 0.

Con trỏ tớiLinenumhers– offset file của bảng số dòng. Bảng số dòng khớp số dòng của tệp nguồn với các địa chỉ nơi bạn có thể tìm thấy mã được tạo cho một dòng nhất định. Thông thường, chỉ các phần mã (chẳng hạn như .text hoặc CODE) mới có số dòng. Trong tệp EXE, số dòng được thu thập ở cuối tệp sau dữ liệu nguồn cho các phần. Trong các tệp đối tượng, bảng số hàng cho một phần tuân theo dữ liệu nguồn của phần đó và bảng định vị lại cho phần đó.

SốSố Tái Định Vị– số lượng chuyển động trong bảng hiệu chỉnh cho phần này (chỉ được sử dụng trong các tệp đối tượng).
NumberOfLinenumbers – số dòng trong bảng số dòng cho phần này.
Đặc điểm - một tập hợp các cờ chỉ ra các thuộc tính của phần (chương trình/dữ liệu, dành cho việc đọc, dành cho việc viết, v.v.).

Phần thường xuyên

Phần.text(hoặc MÃ SỐ

Phần này chứa tất cả mã chương trình có mục đích chung được tạo bởi trình biên dịch hoặc trình biên dịch mã. Trình liên kết kết hợp tất cả các phần .text từ các tệp đối tượng khác nhau thành một phần .text lớn trong tệp EXE.

Phần.data(hoặc DỮ LIỆU, nếu tệp PE được tạo bởi Borland C++)

Dữ liệu khởi tạo sẽ đi vào phần .data. Dữ liệu được khởi tạo bao gồm các biến toàn cục và biến tĩnh được khởi tạo tại thời điểm biên dịch. Chúng cũng bao gồm các chuỗi ký tự (ví dụ: chuỗi "Hello World" trong chương trình C/C++). Trình liên kết kết hợp tất cả các phần .data từ các tệp LIB và đối tượng khác nhau thành một phần .data trong tệp EXE. Các biến cục bộ được đặt trên ngăn xếp của chuỗi và không chiếm dung lượng trong phần .data và .bss.

Phần.bss

Phần .bss lưu trữ các biến tĩnh và toàn cục chưa được khởi tạo. Trình liên kết kết hợp tất cả các phần .bss từ các tệp LIB và đối tượng khác nhau thành một phần .bss trong tệp EXE.

Phần.CRT

Một phần khác dành cho dữ liệu khởi tạo, được thư viện thời gian chạy Microsoft C/C++ sử dụng. Dữ liệu trong phần này được sử dụng cho các mục đích như gọi các hàm tạo của lớp tĩnh C++ trước khi gọi main hoặc WinMain.

Mục.rsrc

Phần .rsrc chứa tài nguyên mô-đun.

Mục.idata

Phần.idata (hoặc bảng nhập) chứa thông tin về các hàm (và dữ liệu) mà mô-đun nhập từ các DLL khác. Bảng nhập bắt đầu bằng một mảng bao gồm IMAGE_IMPORT_DESCRIPTOR. Mỗi phần tử (IMAGE_IMPORT_DESCRIPTOR) tương ứng với một trong các tệp DLL mà tệp PE được liên kết ngầm. Số lượng phần tử trong mảng không được tính đến ở bất kỳ đâu. Thay vào đó, cấu trúc cuối cùng của mảng IMAGE_IMPORT_DESCRIPTOR có các trường chứa NULL.
Cấu trúc IMAGE_IMPORT_DESCRIPTOR có định dạng sau

cấu trúc typedef _IMAGE_IMPORT_DESCRIPTOR (
liên hiệp (
Đặc điểm DWORD;
DWORD Bản gốcFirstThunk;
};
DWORD TimeDateStamp;

Chuỗi chuyển tiếp DWORD;
Tên DWORD;
DWORD FirstThunk;
) IMAGE_IMPORT_DESCRIPTOR

Đặc điểm/Bản gốcFirstThunk– Trường này chứa phần bù (RVA) của mảng từ kép. Mỗi từ kép này thực sự là sự kết hợp của IMAGE_THUNK_DATA. Mỗi từ kép IMAGE_THUNK_DATA tương ứng với một hàm được nhập bởi tệp EXE hoặc DLL đó.

Thời gianNgàyNgàyTem– dấu thời gian và ngày cho biết thời điểm tệp được tạo.
ForwarderChain - trường này liên quan đến chuyển giao, khi một DLL chuyển một liên kết tới một số chức năng của nó tới một DLL khác.
Tên là RVA của chuỗi ký tự ASCII kết thúc bằng null chứa tên của các tệp DLL sẽ được nhập.

đầu tiênthunk– Độ lệch RVA của mảng từ kép IMAGE_THUNK_DATA. Trong hầu hết các trường hợp, từ kép được coi là con trỏ tới cấu trúc IMAGE_IMPORT_BY_NAME. Cấu trúc này trông như thế này:

cấu trúc typedef _IMAGE_IMPORT_BY_NAME (
Gợi ý TỪ;
Tên BYTE;
) IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

Gợi ý– số xuất của hàm nhập.
Tên– Một chuỗi ASCIIZ có tên của hàm được nhập vào.

Một đoạn chương trình đọc từ tệp PE danh sách các chức năng hệ điều hành được chương trình nhập vào.

Đoạn chương trình sau ghi danh sách các thư viện có hàm được chương trình nhập vào tệp FunctionList.txt.

  1. void ShowImportFunction()

    BYTE *pImage = (BYTE*) GetModuleHandle(NULL ) ;

    IMAGE_DOS_HEADER *idh;

    IMAGE_OPTIONAL_HEADER *ioh;

    IMAGE_SECTION_HEADER *ish;

    IMAGE_IMPORT_DESCRIPTOR *iid;

    IMAGE_IMPORT_BY_NAME *ibn;

    IMAGE_THUNK_DATA *bụp;

    int i = 0;

    DWORD j = 0 ;

    char lib = "Thư viện đã nhập:";

  2. Tệp HANDLE = CreateFile(TEXT("FunctionList.txt") ,GENERIC_READ|GENERIC_WRITE,

    0 ,0 ,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS,0 ) ;

    idh = (IMAGE_DOS_HEADER*) pImage;

    ioh = (IMAGE_OPTIONAL_HEADER*)

    (pImage + idh->e_lfanew + 4 +

    kích thước của (IMAGE_FILE_HEADER) );

    ish = (IMAGE_SECTION_HEADER*) ((DWORD) ioh + sizeof (IMAGE_OPTIONAL_HEADER) ) ;

    với (i = 0 ; tôi< 16 ; i++)

Typedef struct _IMAGE_FILE_HEADER ( WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; Đặc điểm WORD; ) IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Tôi sẽ chỉ mô tả một cách khô khan những lĩnh vực này, bởi vì... những cái tên mang tính trực quan và thể hiện ý nghĩa trực tiếp, chứ không phải VA, RVA, RAW và những điều đáng sợ, hấp dẫn khác mà cho đến nay chúng ta chỉ nghe nói đến từ những tên cướp biển cũ. Mặc dù chúng ta đã gặp RAW - đây chỉ là những phần bù so với phần đầu của tệp (chúng còn được gọi là con trỏ thô hoặc phần bù tệp). Nghĩa là, nếu chúng ta có địa chỉ RAW, điều này có nghĩa là chúng ta cần chuyển từ đầu tệp sang vị trí RAW ( ptrFile+ TÔ). Sau đó, bạn có thể bắt đầu đọc các giá trị. Một ví dụ nổi bật của loại này là e_lfmới- mà chúng ta đã thảo luận ở trên trong tiêu đề Dos.

*Máy móc: WORD - số này (2 byte) chỉ định kiến ​​trúc bộ xử lý mà ứng dụng này có thể chạy trên đó.
SốPhần: DWORD - số phần trong tệp. Các phần (sau đây chúng ta sẽ gọi là bảng các phần) nằm ngay sau tiêu đề (PE-Header). Tài liệu nói rằng số phần được giới hạn ở 96.
Thời gianNgàyNgàyTem: WORD - một số lưu trữ ngày và giờ tệp được tạo.
Con trỏ tớiBiểu tượngBảng: DWORD là phần bù (RAW) cho bảng ký hiệu và SizeOfOptionalHeader là kích thước của bảng này. Bảng này nhằm mục đích lưu trữ thông tin gỡ lỗi, nhưng biệt đội đã không nhận thấy sự mất mát của một người lính ngay từ khi bắt đầu phục vụ. Thông thường, trường này được xóa bằng số không.
Tiêu đề SizeOfOption: WORD - kích thước của tiêu đề tùy chọn (ngay sau tiêu đề hiện tại) Tài liệu nêu rõ rằng đối với tệp đối tượng, nó được đặt thành 0...
*Đặc trưng: WORD - đặc điểm của tập tin.

* - các trường được xác định bởi một phạm vi giá trị. Bảng các giá trị có thể có được trình bày trong phần mô tả kết cấu tại văn phòng. trang web và sẽ không được liệt kê ở đây, bởi vì Họ không mang theo bất cứ điều gì đặc biệt quan trọng để hiểu định dạng.

Chúng ta hãy rời khỏi hòn đảo này! Chúng tôi cần phải di chuyển trên. Điểm tham chiếu là một quốc gia có tên là Tiêu đề tùy chọn.

“Bản đồ đâu, Billy? Tôi cần một bản đồ.”
(Đảo kho báu)

Tiêu đề tùy chọn (IMAGE_OPTIONAL_HEADER)

Danh hiệu của lục địa này không hay lắm. Tiêu đề này là bắt buộc và có 2 định dạng PE32 và PE32+ (lần lượt là IMAGE_OPTIONAL_HEADER32 và IMAGE_OPTIONAL_HEADER64). Định dạng được lưu trữ trong trường Ảo thuật: TỪ. Tiêu đề chứa thông tin cần thiết để tải xuống tệp. Như mọi khi:

IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER ( WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD addressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase ; DWORD SectorAlignment; WORD MajorOperatingSystemVersion; MinorImageVersion; WORD MinorSubsystemValue DWORD; SizeOfHeaders; DWORD SizeOfHeapCommit; DWORD NumberOfRvaAndSizes;


*Như mọi khi, chúng tôi sẽ chỉ kiểm tra các trường chính có tác động lớn nhất đến việc hiểu quá trình tải xuống và cách tiếp tục với tệp. Hãy đồng ý - các trường của cấu trúc này chứa các giá trị có địa chỉ VA (Địa chỉ ảo) và RVA (Địa chỉ ảo tương đối). Đây không phải là địa chỉ RAW và bạn cần có khả năng đọc (hay đúng hơn là đếm) chúng. Chắc chắn chúng ta sẽ học cách thực hiện điều này, nhưng trước tiên chúng ta sẽ phân tích các cấu trúc nối tiếp nhau để không bị nhầm lẫn. Hiện tại, chỉ cần nhớ - đây là những địa chỉ mà sau khi tính toán sẽ trỏ đến một vị trí cụ thể trong tệp. Bạn cũng sẽ gặp phải một khái niệm mới - sự liên kết. Chúng tôi sẽ xem xét nó cùng với các địa chỉ RVA, bởi vì những điều này có liên quan khá chặt chẽ.

Địa chỉ của EntryPoint: DWORD - Địa chỉ RVA của điểm vào. Có thể trỏ đến bất kỳ điểm nào trong không gian địa chỉ. Đối với tệp .exe, điểm vào tương ứng với địa chỉ mà chương trình bắt đầu thực thi và không thể bằng 0!
Mã cơ sở: DWORD - RVA của phần đầu mã chương trình (phần mã).
cơ sở dữ liệu: DWORD - RVA của phần đầu mã chương trình (phần dữ liệu).
ImageBase: DWORD - địa chỉ cơ sở ưu tiên để tải chương trình. Phải là bội số của 64kb. Trong hầu hết các trường hợp, nó bằng 0x00400000.
PhầnCăn chỉnh: DWORD - kích thước căn chỉnh (byte) của phần khi dỡ vào bộ nhớ ảo.
Căn chỉnh tệp: DWORD - kích thước căn chỉnh (byte) của phần bên trong tệp.
Kích thướcHình ảnh: DWORD - kích thước của tệp (tính bằng byte) trong bộ nhớ, bao gồm tất cả các tiêu đề. Phải là bội số của PartAligment.
Kích thước của tiêu đề: DWORD - kích thước của tất cả các tiêu đề (DOS, DOS-Stub, PE, Mục) được căn chỉnh theo FileAligment.
SốCủaRvaVàKích Cỡ: DWORD - số lượng thư mục trong bảng thư mục (chính bảng ở bên dưới). Hiện tại, trường này luôn bằng hằng số ký hiệu IMAGE_NUMBEROF_DIRECTORY_ENTRIES, bằng 16.
Thư mục dữ liệu: IMAGE_DATA_DIRECTORY - thư mục dữ liệu. Nói một cách đơn giản, đây là một mảng (có kích thước 16), mỗi phần tử trong đó chứa cấu trúc gồm 2 giá trị DWORD.

Hãy xem cấu trúc IMAGE_DATA_DIRECTORY là gì:

Typedef struct _IMAGE_DATA_DIRECTORY ( Địa chỉ ảo DWORD; Kích thước DWORD; ) IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
Chúng ta có gì? Chúng tôi có một mảng gồm 16 phần tử, mỗi phần tử chứa một địa chỉ và kích thước (cái gì? thế nào? tại sao? tất cả chỉ trong một phút). Câu hỏi đặt ra là chính xác những đặc điểm này là gì. Đối với điều này, Microsoft có các hằng số đặc biệt để khớp. Chúng có thể được nhìn thấy ở phần cuối của mô tả cấu trúc. Trong lúc đó:

// Mục nhập thư mục #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Xuất thư mục #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Nhập thư mục #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Thư mục tài nguyên #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Thư mục ngoại lệ #define IMAGE_DIRECTORY_ENTRY _SECURITY 4 // Thư mục bảo mật #define ECTORY_ENTRY_BASERELOC 5 / / Bảng định vị lại cơ sở #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Thư mục gỡ lỗi // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (sử dụng X86) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Dữ liệu cụ thể về kiến ​​​​trúc #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA của GP #define IMAGE_D IRECTORY_ENTRY_TLS 9 // T Thư mục LS # định nghĩa IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Tải thư mục cấu hình #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Thư mục nhập giới hạn trong tiêu đề #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Nhập bảng địa chỉ #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Trì hoãn tải mô tả nhập #define COM_DESCRIPTOR 14 // COM Bộ mô tả thời gian chạy
Vâng! Chúng ta thấy rằng mỗi phần tử của mảng chịu trách nhiệm về bảng gắn liền với nó. Nhưng than ôi, chúng ta vẫn không thể tiếp cận được những bờ biển này, bởi vì... chúng tôi không biết cách làm việc với địa chỉ VA và RVA. Và để học được, chúng ta cần nghiên cứu xem phần đó là gì. Họ sẽ cho bạn biết về cấu trúc và công việc của họ, sau đó bạn sẽ hiểu rõ tại sao lại cần VA, RVA và sự sắp xếp. Trong bài viết này chúng ta chỉ đề cập đến xuất khẩu và nhập khẩu. Mục đích của các trường còn lại có thể được tìm thấy trong văn phòng. tài liệu hoặc trong sách. Vì vậy, nó ở đây. Các trường thực tế:

Địa chỉ ảo: DWORD - RVA cho bảng mà phần tử mảng tương ứng.
Kích cỡ: DWORD - kích thước bảng tính bằng byte.

Vì thế! Để đến được những bờ biển kỳ lạ như bảng nhập khẩu, xuất khẩu, tài nguyên và những thứ khác, chúng ta cần phải trải qua một nhiệm vụ theo từng phần. Nào, cậu bé phục vụ, hãy nhìn vào bản đồ chung, xác định xem chúng ta đang ở đâu và đi tiếp:

Và chúng tôi nằm ngay trước không gian rộng mở của các khu vực. Chúng tôi chắc chắn cần phải tìm ra những gì họ đang che giấu và cuối cùng tìm ra một kiểu địa chỉ khác. Chúng tôi muốn những cuộc phiêu lưu thực sự! Chúng tôi muốn nhanh chóng đến các nước cộng hòa như bảng xuất nhập khẩu. Những tên cướp biển già nói rằng không phải ai cũng có thể tiếp cận được chúng, nhưng những người đã trở về với vàng và những phụ nữ có kiến ​​thức thiêng liêng về đại dương. Chúng tôi khởi hành và hướng tới tiêu đề Phần.

“Ngươi đã bị phế truất, Silver! Xuống khỏi thùng đi!”
(Đảo kho báu)

Tiêu đề phần (IMAGE_SECTION_HEADER)


Ngay phía sau mảng Thư mục dữ liệu các phần nối tiếp nhau. Bảng phần thể hiện một quốc gia có chủ quyền, được chia thành SốPhần các thành phố. Mỗi thành phố có thủ công riêng, quyền riêng và kích thước 0x28 byte. Số phần được chỉ định trong trường SốPhần, được lưu trữ trong File-header. Vì vậy, hãy nhìn vào cấu trúc:

Typedef struct _image_section_header (tên byte; union (dword betficaddress; dword virtualSize;) misc; Tiêu đề;
Tên: BYTE - tên phần. Hiện tại nó dài 8 ký tự.
Kích thước ảo: DWORD - kích thước phần trong bộ nhớ ảo.
Kích thước của dữ liệu thô: DWORD - kích thước phần trong file.
Địa chỉ ảo: DWORD - Địa chỉ phần RVA.
Kích thước của dữ liệu thô: DWORD - kích thước phần trong file. Phải là bội số Căn chỉnh tệp.
Con trỏToRawData: DWORD - RAW offset ở đầu phần. Cũng phải là bội số Căn chỉnh tệp
Đặc trưng: DWORD - truy cập các thuộc tính của phần và quy tắc tải nó vào ảo. ký ức. Ví dụ: một thuộc tính để xác định nội dung của một phần (dữ liệu ban đầu, dữ liệu không ban đầu, mã). Hoặc truy cập các thuộc tính - đọc, viết, thực thi. Đây không phải là toàn bộ phạm vi của họ. Các đặc điểm được thiết lập bởi các hằng số từ cùng một WINNT.h, bắt đầu bằng IMAGE_SCN_. Bạn có thể làm quen với các thuộc tính của các phần một cách chi tiết hơn. Các thuộc tính trong sách của Chris Kaspersky cũng được mô tả rõ ràng - danh sách tài liệu tham khảo nằm ở cuối bài viết.

Về tên, bạn nên nhớ những điều sau - phần có tài nguyên phải luôn có tên.rsrc. Nếu không, tài nguyên sẽ không được tải. Đối với các phần còn lại, tên có thể là bất cứ điều gì. Thông thường có những tên có ý nghĩa, ví dụ .data, .src, v.v... Nhưng điều đó cũng xảy ra:

Các phần là một khu vực được tải vào bộ nhớ ảo và mọi công việc diễn ra trực tiếp với dữ liệu này. Địa chỉ trong bộ nhớ ảo, không có bất kỳ phần bù nào, được gọi là Địa chỉ ảo, viết tắt VA. Địa chỉ ưa thích để tải xuống ứng dụng, được đặt trong trường ImageBase. Đây giống như điểm bắt đầu của vùng ứng dụng trong bộ nhớ ảo. Và độ lệch RVA (Địa chỉ ảo tương đối) được đo tương ứng với điểm này. Tức là VA = ImageBase+ RVA; ImageBase chúng tôi luôn biết và có VA hoặc RVA tùy ý sử dụng, chúng tôi có thể thể hiện cái này qua cái kia.

Có vẻ như chúng tôi đã quen với việc ở đây. Nhưng đây là bộ nhớ ảo! Và chúng ta đang ở trong thể chất. Ký ức ảo đối với chúng ta bây giờ giống như một chuyến du hành đến các thiên hà khác mà chúng ta chỉ có thể tưởng tượng. Vì vậy, hiện tại chúng tôi không thể truy cập vào bộ nhớ ảo, nhưng chúng tôi có thể tìm hiểu những gì sẽ có ở đó vì nó được lấy từ tệp của chúng tôi.

Căn chỉnh


Để thể hiện chính xác việc tải lên virtual. bộ nhớ, cần phải hiểu một cơ chế như sự liên kết. Trước tiên, chúng ta hãy xem sơ đồ về cách các phần được phân trang vào bộ nhớ.

Như bạn có thể thấy, phần này không được tải vào bộ nhớ theo kích thước của nó. Đây là nơi sử dụng sự sắp xếp. Đây là giá trị phải là bội số của kích thước của phần trong bộ nhớ. Nếu nhìn vào sơ đồ, chúng ta sẽ thấy kích thước của phần là 0x28 và kích thước của phần là 0x50. Điều này là do kích thước căn chỉnh. 0x28 “không đạt” 0x50 và kết quả là phần này sẽ bị hủy tải và khoảng trống còn lại trong kích thước 0x50-0x28 sẽ bằng 0. Và nếu kích thước phần lớn hơn kích thước căn chỉnh thì sao? Ví dụ phầnKích thước= 0x78, một phầnCăn chỉnh= 0x50, tức là vẫn không thay đổi. Trong trường hợp này, phần này sẽ chiếm 0xA0 (0xA0 = 0x28 * 0x04) byte trong bộ nhớ. Nghĩa là, một giá trị là bội số của phầnCăn chỉnh và bao phủ hoàn toàn phầnKích thước. Cần lưu ý rằng các phần trong tệp được căn chỉnh theo cách tương tự, chỉ theo kích thước Căn chỉnh tệp. Sau khi nhận được cơ sở cần thiết, chúng tôi có thể tìm ra cách chuyển đổi từ RVA sang RAW.

“Đây không phải là đồng bằng, khí hậu ở đây rất khác.”
(V.S. Vysotsky)

Một bài học số học nhỏ


Trước khi bắt đầu thực thi, một số phần của chương trình phải được gửi đến không gian địa chỉ của bộ xử lý. Không gian địa chỉ là dung lượng RAM được bộ xử lý xử lý vật lý. “Mảnh” trong không gian địa chỉ nơi chương trình được tải xuống được gọi là ảnh ảo. Hình ảnh được đặc trưng bởi địa chỉ tải xuống cơ sở (Image base) và kích thước (Image size). Vì vậy VA (Địa chỉ ảo) là địa chỉ liên quan đến phần đầu của bộ nhớ ảo và RVA (Địa chỉ ảo tương đối) liên quan đến nơi chương trình được tải xuống. Làm cách nào để tìm ra địa chỉ tải xuống cơ sở của một ứng dụng? Với mục đích này, có một trường riêng biệt trong tiêu đề tùy chọn được gọi là ImageBase. Đây là một khúc dạo đầu nhỏ để làm mới trí nhớ của bạn. Bây giờ chúng ta hãy xem sơ đồ biểu diễn các địa chỉ khác nhau:

Vậy làm thế nào bạn vẫn có thể đọc thông tin từ một tập tin mà không lưu nó vào bộ nhớ ảo? Để làm điều này, bạn cần chuyển đổi địa chỉ sang định dạng RAW. Sau đó, chúng ta có thể bước vào bên trong tệp để đến khu vực chúng ta cần và đọc dữ liệu cần thiết. Vì RVA là địa chỉ bộ nhớ ảo mà dữ liệu được chiếu từ tệp lên đó nên chúng ta có thể thực hiện quy trình ngược lại. Để làm điều này, chúng ta cần một số học đơn giản chín x mười sáu. Dưới đây là một số công thức:

VA = ImageBase + RVA; RAW = RVA - phầnRVA + rawSection; // rawSection - offset cho phần từ đầu tệp // phầnRVA - RVA của phần (trường này được lưu trữ bên trong phần)
Như bạn có thể thấy, để tính RAW, chúng ta cần xác định phần RVA thuộc về. Để làm điều này, bạn cần phải xem qua tất cả các phần và kiểm tra các điều kiện sau:

RVA >= phầnVitualAddress && RVA< ALIGN_UP(sectionVirtualSize, sectionAligment) // sectionAligment - выравнивание для секции. Значение можно узнать в Optional-header. // sectionVitualAddress - RVA секции - хранится непосредственно в секции // ALIGN_UP() - функция, определяющая сколько занимает секция в памяти, учитывая выравнивание
Đặt tất cả các câu đố lại với nhau, chúng ta có được danh sách này:

Typedef uint32_t DWORD; typedef uint16_t WORD; typedef uint8_t BYTE; #define ALIGN_DOWN(x, căn chỉnh) (x & ~(align-1)) #define ALIGN_UP(x, căn chỉnh) ((x & (align-1))?ALIGN_DOWN(x,align)+align:x) // phần IMAGE_SECTION_HEADER; // init mảng phần int defSection(DWORD rva) ( for (int i = 0; i< numberOfSection; ++i) { DWORD start = sections[i].VirtualAddress; DWORD end = start + ALIGN_UP(sections[i].VirtualSize, sectionAligment); if(rva >= bắt đầu && rva< end) return i; } return -1; } DWORD rvaToOff(DWORD rva) { int indexSection = defSection(rva); if(indexSection != -1) return rva - sections.VirtualAddress + sections.PointerToRawData; else return 0; }
*Tôi không bao gồm khai báo kiểu hoặc khởi tạo mảng trong mã mà chỉ cung cấp các hàm sẽ giúp tính toán địa chỉ. Như bạn có thể thấy, mã này không phức tạp lắm. Chỉ là một chút bối rối. Điều này sẽ biến mất... nếu bạn dành thêm một chút thời gian mày mò với .exe thông qua trình dịch ngược.

HOAN HÔ! Chúng tôi đã tìm ra nó. Giờ đây chúng ta có thể đến vùng đất tài nguyên, xuất nhập khẩu thư viện và nói chung là bất cứ nơi nào mà trái tim chúng ta mong muốn. Chúng tôi vừa học cách làm việc với một loại địa chỉ mới. Hãy lên đường nào!

"-Không tệ không tệ! Tuy nhiên, họ vẫn nhận được khẩu phần ăn cho ngày hôm nay!”
(Đảo kho báu)

Xuất bảng


Ngay trong phần tử đầu tiên của mảng Thư mục dữ liệu RVA được lưu trữ trong bảng xuất, được biểu thị bằng cấu trúc IMAGE_EXPORT_DIRECTORY. Bảng này phổ biến đối với các tệp thư viện động (.dll). Mục đích chính của bảng là liên kết các hàm được xuất với RVA của chúng. Mô tả được trình bày trong văn phòng. Thông số kỹ thuật:

Typedef struct _IMAGE_EXPORT_DIRECTORY ( Đặc điểm DWORD; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; Tên DWORD; Cơ sở DWORD; Số DWORD của hàm; Số tên DWORD; Địa chỉ của hàm DWORD; Địa chỉ của tên DWORD; Địa chỉ của tên thứ tự DWORD ; ) IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
Cấu trúc này chứa ba con trỏ tới ba bảng khác nhau. Đây là bảng tên (chức năng) ( Địa chỉTên), Số thứ tự( Địa chỉOfNamesOrdinals), địa chỉ( Địa chỉ của chức năng). Trường Tên lưu trữ RVA của tên thư viện động. Ordinal giống như một trung gian giữa bảng tên và bảng địa chỉ, là một mảng các chỉ mục (kích thước chỉ mục là 2 byte). Để rõ ràng hơn, hãy xem xét sơ đồ:

Hãy xem một ví dụ. Giả sử phần tử thứ i của mảng tên cho biết tên của hàm. Sau đó, địa chỉ của hàm này có thể được lấy bằng cách truy cập phần tử thứ i trong mảng địa chỉ. Những thứ kia. tôi là một thứ tự.

Chú ý! Ví dụ: nếu bạn lấy phần tử thứ 2 trong bảng thứ tự, điều đó không có nghĩa là 2 - nó là thứ tự cho bảng tên và địa chỉ. Chỉ số là giá trị được lưu trữ trong phần tử thứ hai của mảng thứ tự.

Số giá trị trong bảng tên ( SốTên) và thứ tự bằng nhau và không phải lúc nào cũng trùng với số phần tử trong bảng địa chỉ ( Số lượng chức năng).

“Họ đến vì tôi. Cám ơn vì sự quan tâm của bạn. Bây giờ chắc chắn họ đang giết người!”
(Đảo kho báu)

Nhập bảng


Bảng nhập là một phần không thể thiếu của bất kỳ ứng dụng nào sử dụng thư viện động. Bảng này giúp tương quan các lệnh gọi đến các hàm thư viện động với các địa chỉ tương ứng. Quá trình nhập có thể diễn ra ở ba chế độ khác nhau: nhập tiêu chuẩn, nhập ràng buộc và nhập chậm. Bởi vì Chủ đề nhập khẩu khá nhiều mặt và xứng đáng có một bài viết riêng; tôi sẽ chỉ mô tả cơ chế tiêu chuẩn và phần còn lại tôi sẽ chỉ mô tả như một “bộ xương”.

Nhập khẩu tiêu chuẩn- V Thư mục dữ liệu Bảng nhập được lưu trữ dưới chỉ mục IMAGE_DIRECTORY_ENTRY_IMPORT(=1). Nó là một mảng các phần tử thuộc loại IMAGE_IMPORT_DESCRIPTOR. Bảng nhập lưu trữ (trong một mảng) tên của hàm/thứ tự và nơi trình tải sẽ ghi địa chỉ hiệu quả của hàm này. Cơ chế này không hiệu quả lắm vì Thành thật mà nói, tất cả đều bắt nguồn từ việc tìm kiếm trong toàn bộ bảng xuất cho từng chức năng được yêu cầu.

Nhập khẩu ràng buộc- với sơ đồ công việc này, -1 được nhập vào các trường (trong phần tử đầu tiên của bảng nhập tiêu chuẩn) TimeDateStamp và ForwardChain và thông tin về liên kết được lưu trữ trong ô Thư mục dữ liệu với chỉ mục IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(=11). Nghĩa là, đây là một loại cờ cho trình tải mà bạn cần sử dụng tính năng nhập ràng buộc. Ngoài ra, “chuỗi nhập khẩu ràng buộc” cũng có cấu trúc riêng. Thuật toán vận hành như sau: thư viện cần thiết được tải vào bộ nhớ ảo của ứng dụng và tất cả các địa chỉ cần thiết được “liên kết” ở giai đoạn biên dịch. Một trong những nhược điểm là khi biên dịch lại dll, bạn sẽ cần phải biên dịch lại chính ứng dụng đó, vì địa chỉ chức năng sẽ được thay đổi.

Trì hoãn nhập- với phương pháp này, người ta giả định rằng tệp .dll được gắn vào tệp thực thi, nhưng nó không được tải vào bộ nhớ ngay lập tức (như trong hai phương pháp trước), mà chỉ khi ứng dụng truy cập vào biểu tượng lần đầu tiên (đây là những gì đã được dỡ tải các phần tử từ thư viện động được gọi). Nghĩa là, chương trình được thực thi trong bộ nhớ và ngay khi quá trình đạt đến điểm gọi một hàm từ thư viện động, một trình xử lý đặc biệt sẽ được gọi để tải dll và phân phối địa chỉ hiệu quả của các hàm của nó. Để nhập hoãn lại, trình tải sẽ liên hệ với DataDirectory (mục số 15).

Sau khi trình bày một chút về các phương thức nhập, hãy chuyển thẳng đến bảng nhập.

“Đây là một thủy thủ! Quần áo của anh ấy là hàng hải. - Ừ? Bạn có nghĩ rằng bạn sẽ tìm thấy một giám mục ở đây không?
(Đảo Châu Báu - John Silver)

Bộ mô tả nhập (IMAGE_IMPORT_DESCRIPTOR)


Để tìm ra tọa độ của bảng nhập, chúng ta cần truy cập vào mảng Thư mục dữ liệu. Cụ thể là phần tử IMAGE_DIRECTORY_ENTRY_IMPORT (=1). Và đọc địa chỉ RVA của bảng. Dưới đây là sơ đồ chung về con đường cần thực hiện:

Sau đó, chúng tôi nhận RAW từ RVA, theo các công thức nêu trên, rồi “bước” qua tệp. Bây giờ chúng ta đang ở ngay trước một mảng cấu trúc có tên IMAGE_IMPORT_DESCRIPTOR. Sự kết thúc của mảng được biểu thị bằng cấu trúc “không”.

Typedef struct _IMAGE_IMPORT_DESCRIPTOR ( union ( Đặc điểm DWORD; DWORD OriginalFirstThunk; ) DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; Tên DWORD; DWORD FirstThunk; ) IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
Tôi không thể tìm thấy liên kết đến mô tả cấu trúc trên msdn, nhưng bạn có thể thấy nó trong tệp WINNT.h. Hãy bắt đầu tìm hiểu nó.

Bản gốcĐầu tiênThunk: DWORD - RVA của bảng tên nhập (INT).
Thời gianNgàyNgàyTem: DWORD - ngày và giờ.
Chuỗi giao nhận: DWORD - chỉ mục của ký tự được chuyển tiếp đầu tiên.
Tên: DWORD - Chuỗi RVA có tên thư viện.
đầu tiênthunk: DWORD - RVA của bảng địa chỉ nhập (IAT).

Mọi thứ ở đây đều có phần giống với xuất khẩu. Ngoài ra còn có một bảng tên (INT) và một loạt địa chỉ trên đó (IAT). Ngoài ra RVA của tên thư viện. Chỉ INT và IAT đề cập đến một mảng cấu trúc IMAGE_THUNK_DATA. Nó được trình bày dưới hai dạng - dành cho hệ thống 64 và 32 và chỉ khác nhau về kích thước của các trường. Hãy xem x86 làm ví dụ:

Typedef struct _IMAGE_THUNK_DATA32 ( union ( DWORD ForwarderString; Hàm DWORD; Thứ tự DWORD; Địa chỉ DWORD của dữ liệu; ) u1; ) IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
Điều quan trọng là phải trả lời rằng các hành động tiếp theo phụ thuộc vào phần quan trọng nhất của cấu trúc. Nếu nó được đặt thì các bit còn lại biểu thị số ký tự được nhập (nhập theo số). Mặt khác (bit quan trọng nhất bị xóa), các bit còn lại chỉ định RVA của ký hiệu được nhập (nhập theo tên). Nếu chúng ta nhập theo tên thì con trỏ sẽ lưu địa chỉ vào cấu trúc sau:

Typedef struct _IMAGE_IMPORT_BY_NAME ( Gợi ý WORD; Tên BYTE; ) IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Đây Gợi ý là số hàm và Tên- Tên.

Tất cả những điều này là để làm gì? Tất cả các mảng, cấu trúc này... Để rõ ràng, hãy xem xét một sơ đồ tuyệt vời với

Hiểu hệ thống tệp Linux, cấu trúc thư mục, cấu hình, vị trí tệp thực thi và tạm thời sẽ giúp bạn hiểu rõ hơn về hệ thống của mình và trở thành quản trị viên hệ thống thành công. Hệ thống tệp Linux sẽ không bình thường đối với người mới bắt đầu mới chuyển từ Windows, vì mọi thứ ở đây hoàn toàn khác. Không giống như Windows, chương trình không nằm trong một thư mục mà theo quy luật, được phân phối dọc theo hệ thống tệp gốc. Sự phân phối này tuân theo các quy tắc nhất định. Bạn có bao giờ thắc mắc tại sao một số chương trình lại nằm trong /bin, hoặc /sbin, /usr/sbin, /usr/local/bin, sự khác biệt giữa các thư mục này là gì?

Ví dụ: chương trình less nằm trong thư mục /usr/bin, nhưng tại sao lại không nằm trong /sbin hoặc /usr/sbin. Và các chương trình như ifconfig hoặc fdisk được đặt trong thư mục /sbin chứ không phải ở đâu khác.

Bài viết này sẽ trình bày đầy đủ về cấu trúc của hệ thống file Linux, sau khi đọc xong bạn sẽ hiểu được mục đích sử dụng hầu hết các thư mục trong thư mục gốc của Linux.

/ - nguồn gốc

Đây là thư mục chính trên hệ thống Linux. Về cơ bản, đây là hệ thống tập tin Linux. Không có đĩa hoặc bất cứ thứ gì tương tự trong Windows. Thay vào đó, địa chỉ của tất cả các tệp bắt đầu từ thư mục gốc và các phân vùng, ổ đĩa flash hoặc ổ đĩa quang bổ sung được gắn vào các thư mục của thư mục gốc.

Lưu ý rằng người dùng root có thư mục chính là /root chứ không phải chính nó.

/bin - (nhị phân) tệp nhị phân của người dùng

Thư mục này chứa các tập tin thực thi. Dưới đây là các chương trình có thể được sử dụng ở chế độ một người dùng hoặc chế độ khôi phục. Nói một cách dễ hiểu, những tiện ích có thể sử dụng đó vẫn chưa được kết nối với thư mục /usr/. Đây là các lệnh phổ biến như cat, ls, tail, ps, v.v.

/sbin - các tệp thực thi hệ thống (nhị phân hệ thống)

Giống như /bin, nó chứa các tệp thực thi nhị phân có sẵn trong giai đoạn đầu khởi động, khi thư mục /usr chưa được gắn kết. Nhưng có những chương trình ở đây chỉ có thể được thực thi với quyền siêu người dùng. Đây là những tiện ích khác nhau để bảo trì hệ thống. Ví dụ: iptables, khởi động lại, fdisk, ifconfig, swapon, v.v.

/etc - tệp cấu hình (etcetera)

Thư mục này chứa các file cấu hình của tất cả các chương trình được cài đặt trên hệ thống.

Ngoài các tệp cấu hình, hệ thống khởi tạo Init Scripts còn chứa các tập lệnh để khởi động và kết thúc các trình nền hệ thống, gắn hệ thống tệp và khởi động chương trình. Cấu trúc thư mục linux trong thư mục này có thể hơi khó hiểu nhưng mục đích của tất cả chúng là thiết lập và cấu hình.

/dev - tập tin thiết bị (thiết bị)

Trong Linux, mọi thứ, kể cả các thiết bị bên ngoài, đều là tệp. Do đó, tất cả các ổ flash, bàn phím, micrô, máy ảnh được kết nối chỉ là các tệp trong thư mục /dev/. Thư mục này chứa một hệ thống tập tin bất thường. Cấu trúc hệ thống tệp Linux và các tệp chứa trong thư mục /dev được khởi tạo khi hệ thống khởi động bởi dịch vụ udev. Tất cả các thiết bị được kết nối đều được quét và các tệp đặc biệt được tạo cho chúng. Đây là những thiết bị như: /dev/sda, /dev/sr0, /dev/tty1, /dev/usbmon0, v.v.

/proc - (tiến trình) thông tin về các tiến trình

Đây cũng là một hệ thống tệp khác thường, nhưng là một hệ thống con được tạo động bởi kernel. Nó chứa tất cả thông tin về các tiến trình đang chạy trong thời gian thực. Về cơ bản, nó là một hệ thống tệp giả chứa thông tin chi tiết về từng quy trình, Pid, ​​tên tệp thực thi, tham số khởi động, quyền truy cập vào RAM, v.v. Bạn cũng có thể tìm thấy thông tin về việc sử dụng tài nguyên hệ thống tại đây, chẳng hạn như /proc/cpuinfo, /proc/meminfo hoặc /proc/uptime. Ngoài các tệp trong thư mục này còn có một cấu trúc lớn các thư mục Linux, từ đó bạn có thể tìm hiểu nhiều thông tin về hệ thống.

/var (biến) - Tệp biến

Tên của thư mục /var là dễ hiểu; nó phải chứa các tập tin thay đổi thường xuyên. Kích thước của các tập tin này không ngừng tăng lên. Nó chứa các tệp nhật ký hệ thống, nhiều bộ đệm, cơ sở dữ liệu, v.v. Tiếp theo chúng ta sẽ xem xét mục đích của các thư mục Linux trong thư mục /var/.

/var/log - Tệp nhật ký

/var/lib - cơ sở dữ liệu

Một loại tệp khác được sửa đổi là tệp cơ sở dữ liệu, gói được lưu bởi trình quản lý gói, v.v.

/var/mail - thư

Máy chủ thư lưu trữ tất cả các email đã nhận hoặc gửi trong thư mục này; nhật ký và tệp cấu hình của nó cũng có thể được đặt ở đây.

/var/spool - máy in

Ban đầu, thư mục này chịu trách nhiệm về hàng đợi in trên máy in và hoạt động của bộ chương trình cpu.

/var/lock - khóa tập tin

Đây là nơi chứa các tập tin khóa. Các tệp này cho biết rằng một tài nguyên, tệp hoặc thiết bị cụ thể đang được sử dụng và không thể được sử dụng bởi một quy trình khác. Ví dụ: Apt-get khóa cơ sở dữ liệu của nó để các chương trình khác không thể sử dụng nó trong khi chương trình đang chạy trên đó.

/var/run - PID của tiến trình

Chứa các tệp có PID của các quy trình có thể được sử dụng để tương tác giữa các chương trình. Không giống như thư mục /run, dữ liệu được lưu sau khi khởi động lại.

/tmp (temp) - Tệp tạm thời

Thư mục này chứa các tập tin tạm thời được tạo bởi hệ thống, bất kỳ chương trình hoặc người dùng nào. Tất cả người dùng đều có quyền ghi vào thư mục này.

Các tập tin sẽ bị xóa mỗi khi bạn khởi động lại. Tương tự như Windows là thư mục Windows\Temp; tất cả các tệp tạm thời cũng được lưu trữ ở đây.

/usr - (ứng dụng người dùng) Chương trình người dùng

Đây là danh mục lớn nhất với nhiều tính năng. Đây là cấu trúc thư mục Linux lớn nhất. Tại đây bạn có thể tìm thấy các tệp thực thi, nguồn chương trình, nhiều tài nguyên ứng dụng, hình ảnh, âm nhạc và tài liệu.

/usr/bin/ - Các tập tin có thể thực thi được

Chứa các tệp thực thi của nhiều chương trình khác nhau không cần thiết trong giai đoạn khởi động hệ thống đầu tiên, chẳng hạn như trình phát nhạc, trình chỉnh sửa đồ họa, trình duyệt, v.v.

/usr/sbin/

Chứa các chương trình quản trị hệ thống nhị phân phải được chạy với quyền siêu người dùng. Ví dụ: chẳng hạn như Gparted, sshd, useradd, userdel, v.v.

/usr/lib/ - Thư viện

Chứa các thư viện cho các chương trình từ /usr/bin hoặc /usr/sbin.

/usr/local - Tệp người dùng

Chứa các tệp chương trình, thư viện và cài đặt do người dùng tạo. Ví dụ: các chương trình được biên dịch và cài đặt từ nguồn và các tập lệnh được viết thủ công có thể được lưu trữ tại đây.

/home - Thư mục chính

Thư mục này lưu trữ các thư mục chính của tất cả người dùng. Họ có thể lưu trữ các tệp cá nhân, cài đặt chương trình, v.v. trong đó. Ví dụ: /home/sergiy, v.v. So với Windows, đây là thư mục người dùng của bạn trên ổ C, nhưng không giống như WIndows, home thường nằm trên một phần riêng biệt. , để khi bạn cài đặt lại hệ thống, mọi dữ liệu và cài đặt chương trình của bạn sẽ được lưu lại.

/boot - Tệp bộ nạp khởi động

Chứa tất cả các tệp được liên kết với bộ tải khởi động hệ thống. Đây là kernel vmlinuz, ảnh initrd, cũng như các tập tin bootloader nằm trong thư mục /boot/grub.

/lib (thư viện) - Thư viện hệ thống

Chứa các tệp thư viện hệ thống được sử dụng bởi các tệp thực thi trong thư mục /bin và /sbin.

Thư viện có tên tệp có phần mở rộng *.so và bắt đầu bằng tiền tố lib*. Ví dụ: libncurses.so.5.7. Thư mục/lib64 trên hệ thống 64-bit chứa các phiên bản 64-bit của thư viện từ/lib. Thư mục này có thể được so sánh với WIndows\system32, tất cả các thư viện hệ thống cũng được tải xuống ở đó, chỉ có điều ở đó chúng được trộn với các tệp thực thi, nhưng ở đây mọi thứ đều riêng biệt.

/opt (Ứng dụng tùy chọn) - Các chương trình bổ sung

Các chương trình, trò chơi hoặc trình điều khiển độc quyền được cài đặt trong thư mục này. Đây là những chương trình được chính nhà sản xuất tạo ra dưới dạng các tệp thực thi riêng biệt. Các chương trình như vậy được cài đặt trong thư mục con /opt/, chúng rất giống với các chương trình Windows, tất cả các tệp thực thi, thư viện và tệp cấu hình đều nằm trong một thư mục.

/mnt (gắn kết) - Gắn kết

Quản trị viên hệ thống có thể gắn hệ thống tệp bên ngoài hoặc bổ sung vào thư mục này.

/media - Phương tiện di động

Hệ thống sẽ gắn tất cả các ổ đĩa ngoài được kết nối - ổ flash USB, ổ đĩa quang và các phương tiện lưu trữ khác - vào thư mục này.

/srv (máy chủ) - Máy chủ

Thư mục này chứa các tập tin máy chủ và dịch vụ. Ví dụ: nó có thể chứa các tệp từ máy chủ web apache.

/chạy - tiến trình

Một thư mục khác chứa các tệp PID tiến trình, tương tự như /var/run, nhưng không giống như vậy, nó nằm trong TMPFS và do đó tất cả các tệp sẽ bị mất sau khi khởi động lại.

/sys(system) - Thông tin hệ thống

Mục đích của các thư mục Linux từ thư mục này là lấy thông tin về hệ thống trực tiếp từ kernel. Đây là một hệ thống tệp khác được tổ chức bởi kernel và cho phép bạn xem và thay đổi nhiều tham số vận hành hệ thống, chẳng hạn như thao tác hoán đổi, điều khiển quạt, v.v.