Xin chào mọi người, lần đầu tiên mình viết blog trên VNPT Cyber Immunity, lúc đầu nhận làm thì nghĩ cũng dễ thôi mà, mình vốn cũng là người hay viết lách. Nhưng khi bắt tay vào làm thì lại cảm thấy bí ý tưởng ☹ . Cả tuần phân vân mà không biết nên chọn chủ đề gì. Mình đã thử vài chủ đề nhưng cảm thấy chưa được ưng ý lắm, deadline dí tới mông rồi may mà nghĩ ra được chủ đề mình thấy khá thú vị mà phù hợp với phong cách của mình 😀
Mình dự định sẽ làm một loạt bài khám phá về format của các loại file cả trên Windows, linux hay mobile. Cố gắng mỗi loại file sẽ làm 1 bài lý thuyết và một bài thực hành (code C++, python, hoặc ngôn ngữ gì đó tùy hứng 😀). Với kế hoạch như vậy thì có thể seri này có thể lên tới chục hoặc vài chục bài, miễn là ae còn ủng hộ thì mình sẽ còn nghiên cứu và viết lách.
Nào, cùng bắt tay vào bài đầu tiên nhé!
Mình chọn bài đầu tiên sẽ viết về common object file format (COFF) vì mình thấy ít có tài liệu và ít người quan tâm tới định dạng file này, có lẽ là mọi người dung nhiều quá mà không để ý. Bắt đầu bằng một cái gì đó lạ, chắc là sẽ gây được chút ấn tượng 😅
COFF
Viết tắt của Common Object File Format, được sử dụng để lưu trữ các code đã được biên dịch. Ví dụ như output của các trình compiler hay linker.
Nếu là dân dev C, C++ thì bạn có thể dễ dàng tìm thấy các file với định dạng COFF này: nó là các lib trong windows sdk hay thư viện khác.COFF file cũng được sinh ra khi ta buid các code trong visual studio, gcc, g++, …
File COFF có thể chứa code của các hàm (code này đã được biên dịch sang mã máy nhé), các symbol, một phần hay toàn bộ code của một thư viện hoặc 1 file thực thi.
Thoạt nhìn cấu trúc COFF có nhiều nét tương đồng với PE file (Microsoft’s Portable Executable – một định dạng file rất phổ biến mà chúng ta sẽ phân tích nó ở bài nào đó sau này). Tuy nhiên COFF được đánh giá là phức tạp hơn. COFF trong nhiều trường hợp được thay thế bằng ELF và một số định dạng file thực thi khác.
Bắt đầu bằng một thứ cổ điển như COFF thì sau này chúng ta mở rộng ra một số định dạng khác sẽ dễ hơn rất nhiều 😋
COFF file structure
Structure |
Chức năng | Vị trí |
Kích thước |
File header | Lưu trữ các thông tin cơ bản về file và các con trỏ đến các bảng cấu trúc khác | Nằm ở đầu file | 20 byte |
Optional Header | Lưu trữ thông tin bổ sung về việc thực thi file | Cấu trúc này có thể có hoặc không có trong file tùy thuộc vào thông tin trong file header. Nếu có, nó sẽ nằm ngay sau File header | 0 hoặc 28 byte |
Section header | Lưu trữ thông tin về các section trong file | Nằm ngay tiếp sau Optional header | Có 2 version của COFF: COFF1 kích thước section header 40 byte, COFF2 là 48 byte. Như vậy khối section header có tổng kích thước là số section * kích thước section. |
Section Relocation table | Cung cấp thông tin về những địa chỉ cần thay đổi giúp tệp có thể load ở bất kỳ vùng nhớ nào. | Nằm sau khối các section header. Hầu như mỗi section đều có một relocation table tương ứng, địa chỉ được trỏ đến bởi 1 trường trong section header | Địa chỉ và số lượng entries được lấy từ thông tin section header tương ứng. Kích thước 1 relo entry là 10 bytes.
Tổng kích thước = Số lượng entries * 10 |
Section Line Number table | Cung cấp thông tin debug: ánh xạ code này ở dòng nào trong file mã nguồn (.c, .cpp). | Được trỏ tới từ thông tin trong section header | Kích thước 1 items là 6 byte, do đó kích thước table = Số items * 6 |
symbol table | Lưu trữ thông tin về các symbol được define hoặc định nghĩa trong code | Được trỏ đến từ file header | Kích thước 1 entry là 18 byte. Kích thước table = số entries * 18 |
String table | Lưu trữ tên section hoặc tên symbol mà dài hơn 8 ký tự | Được trỏ đến từ symbol table hoặc section header | 4 byte đầu tiên là kích thước của string table , tiếp đó là nội dung các string phân cách bằng. Do đó kích thước này không cố định và >= 4 |
Mục đích của chúng ta là tìm hiểu để code được một chương trình view được cấu trúc của một file COFF bất kỳ, do đó cần tiếp tục đi sâu vào chi tiết các table.
File header
Vị trí |
Kiểu dữ liệu |
Ý nghĩa |
0-1 | Unsigned short | magic number, COFF có 2 version nên sẽ có một số giá trị magic number khác nhau. Trong giới hạn code tool thì mình muốn đọc các file Microsoft COFF nên sẽ sử dụng version 1. |
2-3 | Unsigned short | Số lượng section |
4-7 | long | Time và date stamp, thời điểm mà file được tạo |
8-11 | long | File pointer đến Symbol table |
12-15 | long | số lượng entries trong symbol table |
16-17 | unsigned short | kích thước optional header, chỉ có 2 giá trị là 0 hoặc 28. Nếu là 0 thì không có optional header. |
18-19 | unsigned short | cờ, có rất nhiều thông tin của tệp được thể hiện trong đây, nhưng mình thực sự cũng chưa hiểu hết, có lẽ khi code tool chúng ta sẽ cố gắng tìm thể hiện thêm cái này. |
Optional File Header
Chỉ những file COFF có khả năng thực thi mới có optional header vì những thông tin trong này chủ yếu là phục vụ cho runtime.
Vị trí |
Kiểu dữ liệu |
Ý nghĩa |
0-1 | unsigned short | magic number |
2-3 | unsigned short | version stamp |
4-7 | unsigned long | kích thước của section code |
8-11 | unsigned long | kích thước dữ liệu đã được khởi tạo |
12-15 | unsigned long | kích thước dữ liệu chưa được khởi tạo |
16-19 | unsigned long | Entry point – vị trí bắt đầu thực thi |
20-23 | unsigned long | Địa chỉ bắt đầu của code thực thi |
24-27 | unsigned long | Địa chỉ bắt đầu của dữ liệu đã được khởi tạo |
Section Header
Vị trí |
Kiểu dữ liệu |
Ý nghĩa |
0-7 | char[] | Tên section. Nếu tên section < 8 ký tự, thì sẽ nằm luôn trọng trương này. Nếu không, trường này sẽ chứa con trỏ tới string table nơi chứa tên section |
8-11 | long | section physical address |
12-15 | long | section virtual address |
16-19 | long | kích thước section |
20-23 | long | Con trỏ tới section data |
24-27 | long | Con trỏ tới relocation table của section |
28-31 | long | Con trỏ tới line number table của section |
32-33 | unsigned short | số relocation entries |
34-35 | unsigned short | Số line number entries |
36-39 | long | cờ |
Ở đây có 2 trường Address là physical và virtual: physical là địa chỉ khi file nằm trên ổ cứng vật lý, virtual là địa chỉ khi file nằm trên không gian nhớ ảo, có thể hiểu và hình dung khi file được load lên ram. Với COFF file thì thường 2 trường này có giá trị giống nhau. Sau này với PE file thì nó sẽ khác nhau.
Relocation Entries
Relocation có ý nghĩa gì?
Khi trình biên dịch biến mã nguồn thành mã máy thì thường code sẽ nằm ở 1 section và các biến sẽ nằm ở 1 section khác, toàn bộ việc sử dụng biến sẽ quy về thao tác với các địa chỉ vùng nhớ. Lúc này khi load các section này lên thì việc tham chiếu đến các vùng nhớ này sẽ bị thay đổi và không thể chắc chắn rằng vùng nhớ cần thao tác sẽ có địa chỉ bao nhiêu.
bảng Relocation sẽ đánh dấu những vị trí nhạy cảm đó trong code để khi trình loader load các section lên vùng nhớ nào, nó sẽ đi fix lại các vị trí được đánh dấu đó phù hợp với vùng nhớ tương ứng. Như vậy file hay các section có thể được load một cách rất linh động và thoải mái, loader sẽ dùng bảng relocation để đi tinh chỉnh lại địa chỉ để mọi thứ chạy được.
Vị trí |
Kiểu dữ liệu |
Ý nghĩa |
0-3 | long | physical address tham chiếu |
4-7 | long | Tham chiếu đến symbol table index |
8-9 | unsigned short | phần này khá phức tạp, nó cho biết cách mà địa chỉ này được cập nhật |
Line Number Entries
Ý nghĩa có lẽ mình đã mô tả ở phần đầu, chứa thông tin dòng code trong file mã nguồn gốc.
Vị trí |
Kiểu dữ liệu |
Ý nghĩa |
0-3 | long | symbol index hoặc physical address |
4-5 | unsigned short | dòng bao nhiêu |
Symbol Table
Vị trí | Kiểu dữ liệu | Ý nghĩa |
0-7 | char[] | Nếu symbol name < 8 ký tự, nó sẽ nằm luôn ở đây. Ngược lại, vị trí này sẽ là con trỏ đến string table chứa symbol name. |
8-11 | long | Giá trị của symbol |
12-13 | short | section number |
14-15 | unsigned short | Loại symbol |
16 | char | storage class |
17 | char | auxiliary class |
Chi tiết rõ hơn về symbol table mình sẽ trình bày thêm khi demo tool 😀
Như vậy là chúng ta đã cấu trúc hóa các thành phần quan trọng nhất của COFF file. Lý thuyết thì mình cũng tham khảo từ 3 nguồn:
- https://wiki.osdev.org/COFF
- https://www.ti.com/lit/an/spraao8/spraao8.pdf?ts=1653923070006&ref_url=https%253A%252F%252Fwww.google.com%252F#:~:text=COFF%20is%20an%20implementation%20of,segments%20and%20target%20system%20memory.
- https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
Nếu lý thuyết suông thì chắc mình cũng chỉ là người Việt hóa và phân tích tài liệu thôi, do đó bài thực hành sẽ là điều mình muốn làm và hy vọng sẽ có ích cho cộng đồng.
Cảm ơn mọi người đã đọc và hẹn gặp lại ở bài tiếp theo.