Liên kết, tải và tách Binary: PLT, GOT, nhập khẩu, sửa chữa và xác định chức năng
Type: Build
Languages: Python, C, x86-64 and arm64 assembly
Prerequisites: Phase 13 · 32 (ELF from scratch), 33 (disassembly and control flow), 38 (calling conventions)
Time: ~160 minutes
Mục tiêu học tập
- Quyết định từ một hồ sơ di chuyển và bảng biểu tượng những gì trình chỉnh sửa liên kết làm cho nó: một khoảng cách cố định, một mục PLT với một khe GOT, hoặc một di chuyển động.
- Đọc phần động lực, PLT và GOT của một ELF thực thi độc lập vị trí và theo dõi một cuộc gọi thông qua chúng bằng các công thức psABI.
- Đọc một đối tượng COFF (20 bit file header, 40 bit mục phần, 18 bit biểu tượng, 10 bit di chuyển) và các thư mục nhập khẩu, xuất khẩu và di chuyển cơ sở của một hình ảnh PE32+.
- Hãy giải mã chuỗi sửa chữa được nối chuỗi Mach-O từ từ và so sánh từng từ với nhau .
dyld_info- Tôi không biết. - Hãy nói gì
stripxóa và tồn tại trong mỗi định dạng, và chạy chương trình bị tước để chứng minh nó vẫn hoạt động. - Khôi phục chức năng khởi động của một bộ arm64 bị tẩy tay với
LC_FUNCTION_STARTS, quét tiền đề, và gọi mục tiêu, đánh giá mỗi phương pháp so với bảng biểu tượng của cặp đôi không bị cắt, và đặt tên các chức năng với chữ ký hình FLIRT.
Vấn đề
Anh liên kếtmain.cchống lạilibledger.dylibVà hỏi một nhân viên làm việc gì maingọi vào 0x1000005a0Cảnh sát đã giải thể hướng dẫn và đọcbl 0x100000694Địa chỉ đó chứa 12 byte:adrp x16- ldr x16, [x16, #0x38]- br x16- Cảnh sát theo dõi hàng đến0x100004038, đọc tám byte 00 00 00 00 00 00 10 80, và báo cáo một cuộc gọi qua một chỉ dẫn bị hỏng vào không gian lõi.
Sau đó, bạn cởi bỏ cấu trúc tĩnh của cùng một chương trình và hỏi nó có bao nhiêu chức năng.nm, thấy một tên,__mh_execute_header, và trả lời một.
Cả hai câu trả lời đều đọc file như thể đó là quá trình chạy.0x100004038Bit 63 nói liên kết, 12 bit nói nơi liên kết tiếp theo là, và các bit thấp nói về nhập khẩu để liên kết._putstrước đâymainCác tập tin bị tước mất bảng biểu tượng của nó và được giữ lạiLC_FUNCTION_STARTS, liệt kê tất cả 5 hàm bắt đầu trong 9 byte.
Bài học này xây dựng năm độc giả trong thư viện Python tiêu chuẩn. Một độc giả COFF cho các đối tượng của chúng tôi và một PE32+ cho các thư mục nhập khẩu, xuất khẩu và chuyển chỗ cơ sở. Một độc giả Mach-O cho các lệnh tải, các đoạn, bảng biểu tượng gián tiếp, chuỗi sửa chữa chuỗi, và các thử nghiệm xuất khẩu, kiểm tra từ từ với từ dyld_info. Một trình đọc ELF cho phần động, PLT, và GOT, cộng với một trình lập kế hoạch cho biết một liên kết PIE của các đối tượng của chúng tôi sẽ tạo ra gì. Một trình tìm hàm cho một xây dựng bị tẩy chay với bốn phương pháp và điểm số cho mỗi người. Một trình tạo chữ ký hình FLIRT, với các byte di chuyển wildcard và một CRC16 của đuôi.
Chỉ có Mach-O liên kết trên máy này. các thiết bị thực thi arm64 và dylib đã được kết nối, khép ra và chạy. các đối tượng ELF và COFF được biên soạn ở đây với clang -target, và không có trình chỉnh sửa liên kết ELF hoặc PE được cài đặt, vì vậy hai hình ảnh liên kết trong các định dạng đó đã được viết byte theo byte từ các thông số kỹ thuật bởi hai kịch bản Python trong thư mục cố định. llvm-objdumpvà cũng như các thư viện thăm dò. chúng không được chạy, và văn bản nói như vậy khi nó quan trọng.
Điều gì biên tập liên kết làm với một di chuyển
Bài 32 đọc hồ sơ di chuyển và bài 33 áp dụng S + A - Pđến một leaMột bản ghi tên một nơiP, một kiểu, một biểu tượng S, và một phụ kiệnA. Bảng 4.9 của x86-64 psABI cho thấy một công thức cho mỗi loại, với Lcho vị trí nhập PLT của biểu tượng, GVì sự đền bù của nó,GOTcho địa chỉ GOT, và B3 kết quả bao gồm các đối tượng của chúng ta.
Các liên kết biên tập ghi giá trị vào trường và ghi lại đã biến mất. R_X86_64_PC32đối với một biểu tượng được xác định trong các đầu vào là trường hợp này. Khoảng cách giữa hai byte của một hình ảnh không bao giờ thay đổi, bất kể địa chỉ tải.
Các biên tập liên kết tạo ra một mục PLT và một khe GOT. R_X86_64_PLT32với một hàm không xác định là trường hợp này.L + A - P, khoảng cách đến lối vào.R_X86_64_JUMP_SLOTghi lại trong .rela.pltnói với động lực liên kết để lấp đầy khoảng trống.
Bộ chỉnh sửa liên kết giữ một di chuyển cho bộ tải. R_X86_64_64là một địa chỉ tuyệt đối tám byte, và một vị trí độc lập thực thi không biết địa chỉ của nó cho đến khi nó tải.R_X86_64_RELATIVEvới công thức B + Atrong .rela.dyn- Tôi không biết.
main-x86_64-O1.ocó 18 hồ sơ: 14 trong .text, 2 trong .data.rel.ro, và 2 trong .rodata- plan_dynamic_link()trong code/main.pyáp dụng ba quy tắc cho họ cùng với ledger-x86_64-O1.o- Có thể là:
| Record | Count | Decision | Why |
|---|---|---|---|
R_X86_64_PC32 to .rodata, .L.str, fns, the table in .rodata | 16 | in place | the symbol is defined in the inputs |
R_X86_64_PLT32 to percent_change, grade, total, grade_label | 4 | direct, no PLT | defined in ledger-x86_64-O1.o, so the link editor reduces PLT32 to PC32 |
R_X86_64_PLT32 to puts twice and printf twice | 4 | PLT entry and GOT slot | undefined, provided by a shared library |
R_X86_64_64 to grade in fns[] | 2 | R_X86_64_RELATIVE | an absolute field in a PIE |
Hai nhập khẩu cho một .pltcủa 48 byte (PLT0 cộng với hai mục của 16), a .got.pltcủa 40 byte (ba khe được đặt lại cộng với hai), và một .rela.plthai ghi chép tương đối cho một .rela.dynNhững con số này đến từ các thông số được áp dụng cho các hồ sơ của chúng tôi.tiny-dyn-x86_64thực hiện phần PLT của kế hoạch này cho hai nhập khẩu tương tự, và kích thước của nó phù hợp với kế hoạch.
Người biên tập viết PLT32trên mỗi cuộc gọi, được xác định hoặc không được xác định, kể từ khi binutils 2.31 (H.J. Lu, 2018-02-13) và LLVM 7.0.0 (D43383, 2018-02-20).PLT32trong một đối tượng vì vậy nói "một cuộc gọi", và không nói gì về việc liệu mục PLT sẽ tồn tại hay không. Hai loại khác xuất hiện trong các cấu trúc khác. R_X86_64_GOTPCRELXvà R_X86_64_REX_GOTPCRELX(G + GOT + A - P) yêu cầu một khe GOT mà không có PLT, mà -fno-pltsản xuất và mà người biên tập liên kết có thể thư giãn cho một trực tiếp leahoặc callkhi biểu tượng là địa phương (psABI Dùng B). R_X86_64_32và R_X86_64_32Skhông thể giữ địa chỉ trong PIE và liên kết thất bại.
Bộ phận năng động, PLT và GOT
Chương 5 của gABI nói rằng một trình thực hiện tham gia vào kết nối động có một PT_INTERPchương trình tiêu đề, phải đi trước bất kỳ phân đoạn tải, và một PT_DYNAMIC, bao gồm:.dynamicphần. phần là một mảng của Elf64_Dyncặp, thẻ được ký và giá trị, kết thúc bằng DT_NULL- Tôi không biết.
tiny-dyn-x86_64là 5.184 byte được viết bởi build_tiny_dyn_elf.pytừ gABI và psABI.0x1000- Có thể là:
| Tag | Number | Value | Meaning |
|---|---|---|---|
DT_NEEDED | 1 | libc.so.6 | a string table offset, 0xb, the library to load |
DT_HASH | 4 | 0x1a0 | the SysV hash table |
DT_STRTAB | 5 | 0x188 | .dynstr |
DT_SYMTAB | 6 | 0x140 | .dynsym, 24 bytes per entry |
DT_STRSZ | 10 | 0x15 | 21 bytes of strings |
DT_SYMENT | 11 | 0x18 | 24 |
DT_PLTGOT | 3 | 0x10c0 | .got.plt |
DT_PLTRELSZ | 2 | 0x30 | two 24-byte records |
DT_PLTREL | 20 | 7 | the records are DT_RELA |
DT_JMPREL | 23 | 0x1b8 | .rela.plt |
DT_FLAGS_1 | 0x6ffffffb | 0x08000000 | DF_1_PIE, a GNU tag |
DT_NULL | 0 | 0 | end |
GABI nói tại sao DT_JMPRELlà riêng biệt: nó "có địa chỉ các mục di chuyển liên kết với bảng liên kết quy trình. Việc tách các mục di chuyển này cho phép các liên kết động bỏ qua chúng trong quá trình khởi tạo quy trình, nếu liên kết lười biếng được bật". DF_BIND_NOWtrong DT_FLAGS- DF_1_NOWtrong DT_FLAGS_1, hoặcLD_BIND_NOWtrong môi trường tắt liên kết lười biếng và các liên kết năng động xử lý chúng khi khởi động.
PLT0 và mục cho các điểm
Phần 5.2 của psABI cho thấy hai hình dạng trong Hình 5.2. Đây là các byte của tệp của chúng tôi:
text 1f0: ff 35 d2 0e 00 00 pushq 0xed2(%rip) # 0x10c8 = GOT+8
1f6: ff 25 d4 0e 00 00 jmpq *0xed4(%rip) # 0x10d0 = GOT+16
1fc: 0f 1f 40 00 nopl (%rax)
200: ff 25 d2 0e 00 00 jmpq *0xed2(%rip) # 0x10d8 = GOT+24, the slot for puts
206: 68 00 00 00 00 pushq $0x0 # index 0 into DT_JMPREL
20b: e9 e0 ff ff ff jmp 0x1f0 # PLT0Làm toán học một lần. Phác dịch tương đối với RIP tương đối với hướng dẫn tiếp theo. 0x1f6 + 0xed2 = 0x10c8, đó là .got.plt + 8- 0x1fc + 0xed4 = 0x10d0, đó là .got.plt + 16- 0x206 + 0xed2 = 0x10d8, khe thứ tư. 0x210 - 0x20 = 0x1f0Mỗi mục là 16 byte, vì vậy mục cho exitbắt đầu từ 0x210, đẩy$0x1, và đọc 0x10e0- Tôi không biết.
Chính phủ Ấn Độ có 3 khe cắm. psABI 5.2: mục 0 chứa địa chỉ của _DYNAMIC, và "nghệ nhất và hai mục trong bảng bù trừ toàn cầu cũng được đặt..got.pltđọc 0x1000, 0, 0, 0x206, 0x216. glibc điền vào khe 1 với nó struct link_mapvà khe hai với _dl_runtime_resolveKhi nó tải tập tin.0x206trong hồ sơ, đó là pushqbên trongputs@plt. psABI: khe "trước đầu ... chứa địa chỉ của hướng dẫn đẩy tiếp theo".
Vậy là cuộc gọi đầu tiên được thực hiệncall 0x200- jmpq *0x10d8đất trên pushq $0- jmp 0x1f0đẩy GOT+8 và nhảy qua GOT+16 vào giải quyết, đọc ghi 0 của DT_JMPREL- R_X86_64_JUMP_SLOT puts, trông putslên trong libc.so.6, viết địa chỉ của nó vào 0x10d8, và nhảy lên đó.call- jmpq *0x10d8Drepper viết rằng "giá phí trên cho tất cả ngoại trừ cuộc gọi đầu tiên do đó 'chỉ' một bước nhảy gián tiếp".%r11giải thích một lựa chọn đăng ký trong bài học 38: đó là một đăng ký cào vì vậy "điều mã trong PLT không cần phải đổ ra bất kỳ đăng ký".
Trang web gọi trong .textđọc e8 d4 ff ff ff0x227, và0x22c - 0x2c = 0x200- llvm-objdumpdấu vân taycallq 0x200 <puts@plt>cho nó và đặt tên vào mục từ .rela.pltghi lại, đó là cách plt_entries()- Đặt tên cho nó.
Những gì trong tệp không có
Tài liệu không có PT_GNU_RELRO. Jakub Jelinek đề xuất RELRO trong danh sách các công cụ trong năm 2004-01-06: di chuyển các phần được viết chỉ trong quá trình di chuyển đến đầu của phân đoạn viết, để các liên kết động có thể mprotectGNU ld mặc định đặt.init_array- .fini_array- .data.rel.ro- .dynamic, và.gotbên trong, và chỉ giữ ba khe cắm được đặt trong .got.pltbên trong, 24 byte, bởi vì các khe khác được viết lười biếng.-z nowbộ DF_BIND_NOWvà di chuyển toàn bộ .got.pltbên trong, đầy RELRO, bởi vì không có gì được viết sau khi khởi động._dl_protect_relrogọimprotect(start, end - start, PROT_READ)trên phạm vi trang-định hướng. -fno-pltđi một bước xa hơn ở phía biên dịch: các cuộc gọi đi qua jmpq *name@GOTPCREL(%rip), liên kết lười biếng là không thể, và hướng dẫn của GCC nói rằng "tất cả các biểu tượng bên ngoài được giải quyết tại thời điểm tải".
Dữ liệu cũng không có phần phiên bản.libc.so.6thêm .gnu.version, một Elfxx_Halfmỗi.dynsymnhập cảnh, và.gnu.version_r, một Verneedcho mỗi thư viện với Vernauxghi lại cho mỗi tên phiên bản. printf@GLIBC_2.2.5phương tiện: mục nhập của printftrong .gnu.versioncó một chỉ số, Vernauxcủa ai vna_otherbằng chỉ số đó có vna_name"GLIBC_2.2.5", trong Verneedcủa ai vn_filelà "libc.so.6". Chỉ số 0 là địa phương, 1 là toàn cầu, và bit 15 (VERSYM_HIDDEN- 0x8000) ẩn một phiên bản từ trình biên tập liên kết.
Bảng hash là SysV DT_HASHvới một cái thùng và một chuỗi 3 bước, 24 byte.DT_HASHhoặc DT_GNU_HASHđể tìm ra các biểu tượng trong tệp. tệp này không xuất khẩu gì, và nó không được chạy ở đây, vì vậy liệu glibc chấp nhận một bảng một thùng không được xác minh.
.dynsym, .symtab, và strip
GABI làm.dynsymvà .dynstrCác phần được phân bổ và.symtabvà .strtabDrepper: "Thếch biểu tượng bình thường không được sử dụng trong thời gian chạy và do đó không cần thiết để loại bỏ một bộ nhị phân của nó. Nó không ảnh hưởng đến hiệu suất".
GNU strip --strip-allloại bỏ .symtab- .strtab- .debug_*, và.commentMỗi phần được phân bổ sẽ ở lại:.interp- .dynsym- .dynstr- .gnu.hash- .gnu.version*- .rela.dyn- .rela.plt- .plt- .text- .rodata- .eh_frame- .init_array- .dynamic- .got- .got.plt- .data- .bssCác chủ đề của bộ phận sẽ ở lại trừ khi bạn thêm vào--strip-section-headers(bản 2.41) hoặc chạy llvm-strip --strip-sections- --strip-debuggiữ các biểu tượng. --only-keep-debugviết file debug mà --add-gnu-debuglinktên từ một người được tước đi, với một CRC32 trong .gnu_debuglink, vàld --build-idviết .note.gnu.build-idđể một bộ xử lý lỗi có thể phù hợp với hai.
Vật vật COFF
clang -target x86_64-pc-windows-msvc -O1 -fno-asynchronous-unwind-tables -csản xuất main-x86_64.objvà ledger-x86_64.obj. Trang định dạng PE xác định đối tượng: một tiêu đề tập tin 20 byte, không có tiêu đề tùy chọn, một bảng mục phần gồm 40 byte, một bảng biểu tượng gồm các bản ghi 18 byte và các bản ghi di chuyển 10 byte cho mỗi phần.
CoffObjecttrong code/main.pyĐọc cả bốn.0x8664, 10 phần, 37 ký hiệu ghi lại,SizeOfOptionalHeader0. Tên bảng phần .text- .data- .bss, sáu phần gọi là .rdata, và/16Một cái tên bắt đầu bằng/là một số phân số chuyển đổi vào bảng chuỗi, bắt đầu với kích thước bốn byte của riêng nó; /16quyết định .llvm_addrsig, đó là tên llvm-objdump -h- Nhìn vân tay.
Các 37 hồ sơ bao gồm các hồ sơ phụ trợ. Một biểu tượng phần (tầng lưu trữ 3) mang một trong những chứa chiều dài phần, số lượng di chuyển, một số tiền kiểm tra và một lựa chọn COMDAT..rdataCác phần có chọn 2, "bất kỳ", và chứa một chuỗi chữ nghĩa mỗi bên dưới một tên MSVC-mangled: ??_C@_04JDDJEAHN@even?$AA@là chuỗi "even". trình chỉnh sửa liên kết giữ một bản sao của mỗi..filevới ghi chú phụ trợ "main.c". llvm-objdump -tin những cái này như [ 0]- AUX scnlen ...- [ 2], và vv, bỏ qua các chỉ số phụ trợ, và thử nghiệm so sánh chỉ số và cặp tên của chúng tôi với văn bản đó.
Các cuộc di chuyển: 14 IMAGE_REL_AMD64_REL32trong .textvà 4 IMAGE_REL_AMD64_ADDR64trong .rdata- REL32là "địa chỉ tương đối 32 bit từ byte sau khi di chuyển".R_X86_64_PC32với -4được tích hợp: hồ sơ ELF mang thêm -4vì hồ sơ có một trường phụ gia, và hồ sơ COFF không có. ledger-x86_64.objcó ba REL320x36- 0x84, và0x93, và bài học 32 ledger-x86_64-O1.ocó ba R_X86_64_PC32Các mã là giống nhau, và các định dạng khác nhau chỉ trong nơi bốn được viết. ADDR64là một địa chỉ tuyệt đối tám byte, labelsbảng của các chỉ dẫn chuỗi, và nó trở thành một di chuyển cơ sở trong hình ảnh, như R_X86_64_64trở thành R_X86_64_RELATIVE- Tôi không biết.
ELF Elf64_Rela | COFF relocation | Mach-O relocation_info | |
|---|---|---|---|
| record size | 24 bytes | 10 bytes | 8 bytes |
| fields | offset, type and symbol in r_info, addend | offset, symbol index, type | address, then a 32-bit word with symbolnum:24, pcrel:1, length:2, extern:1, type:4 |
| addend | explicit | none, lives in the field | none, or an ARM64_RELOC_ADDEND record before the real one |
the call to puts | R_X86_64_PLT32 puts-0x4 | IMAGE_REL_AMD64_REL32 puts | ARM64_RELOC_BRANCH26 _puts |
Hình ảnh PE: nhập khẩu, xuất khẩu và di chuyển cơ sở
tiny-x86_64.exelà 2.560 byte được viết bởi build_tiny_pe.pyNó có nhiệm vụ tương tự như ELF nhỏ bé: gọiputsvà exittừ msvcrt.dllNó cũng xuất khẩu một tên và mang theo một chuyển đổi cơ sở, vì vậy ba thư mục cần thiết bài học này đều có mặt. pefile2024.8.26 và llvm-objdump -pCả hai đều đọc nó, và cả hai hình ảnh đều là thiết bị.
Đi qua các tiêu đề.MZvà trong khi trần gian0x3cgiữ e_lfanew- 0x40- Tại 0x40chữ ký PE\0\0Sau đó, cái tên tập tin COFF: máy0x8664, 4 phần,SizeOfOptionalHeader240, đặc điểm 0x22(hình ảnh thực thi, địa chỉ lớn biết).Magictrường, 0x20b, nghĩa là PE32+. AddressOfEntryPointlà 0x1000- ImageBaselà 0x140000000- SectionAlignment 0x1000- FileAlignment 0x200- SizeOfImage 0x5000- Subsystem3 (console). DllCharacteristics 0x8160mã hóa để HIGH_ENTROPY_VA 0x0020- DYNAMIC_BASE 0x0040- NX_COMPAT 0x0100, vàTERMINAL_SERVER_AWARE 0x8000Sau đó 16 thư mục dữ liệu của RVA và kích thước.0x2080kích thước0x4d, nhập khẩu tại 0x2000kích thước0x28, Đời căn cứ tại 0x4000kích thước0xc, IAT tại 0x2040kích thước0x18- Tôi không biết.
Các địa chỉ trong một hình ảnh là RVAs, bù đắp từ cơ sở tải. Bảng phần lập bản đồ chúng để thay đổi tệp: .textRVA 0x1000trong hồ sơ0x200- .rdataRVA 0x20000x400- .dataRVA 0x30000x600- .relocRVA 0x40000x800- PeImage.rva_to_offset()là cái bàn đó.
Chỉ mục nhập khẩu
Tại RVA 0x2000nằm trong một mảng các mô tả nhập khẩu 20 byte kết thúc bằng mô tả không.0x2028, timestamp 0, chuỗi vận chuyển 0 , tên 0x2068, Bảng địa chỉ nhập khẩu 0x2040. Tên RVA chỉ ra "msvcrt.dll". Bảng tìm kiếm chứa các mục 64 bit kết thúc bằng số không. Bộ Bit 63 có nghĩa là nhập bằng thứ tự, với thứ tự trong bit 15 đến 0. Bit 63 rõ ràng có nghĩa là bit 30 đến 0 là RVA của một mục gợi ý / tên: một gợi ý 16-bit, "một chỉ số vào bảng chỉ số tên xuất khẩu", sau đó tên ASCII. Chúng tôi là 0x2058(số 0, "đặt") và 0x2060(khí dụ 0, "từ").
Các thông số kỹ thuật nói rằng IAT là "tương tự với nội dung của bảng tìm kiếm nhập khẩu cho đến khi hình ảnh được gắn".0x2040chứa hai RVAs tương tự, và kiểm tra kiểm tra rằng 24 byte ở 0x2028bằng với 24 byte ở 0x2040. "Trong thời gian liên kết, các mục trong bảng địa chỉ nhập khẩu được ghi lại với địa chỉ 32 bit (cho PE32) hoặc 64 bit (cho PE32+) của các biểu tượng đang được nhập khẩu. " Các trình tải Windows làm điều đó trước khi điểm nhập khẩu chạy. Không có liên kết lười biếng và không có cột. Mã đạt đến khe trực tiếp:
text 100b: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1011 + 0x102f = 0x2040, the IAT slot of puts
1013: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1019 + 0x102f = 0x2048, the IAT slot of exitFF /2là cuộc gọi gần gián tiếp. Hai hướng dẫn có cùng các byte bởi vì mỗi người ngồi tám byte sau người khác và đọc một khe tám byte sau người khác. iat_calls()quét phần mã cho ff 15, thêm 6 và sự di chuyển, và đặt tên cho khe trong thư mục nhập khẩu.
Chỉ mục xuất khẩu
Tại RVA 0x2080bảng thư mục xuất khẩu 40byte: cờ, dấu thời gian, phiên bản, một tên RVA chỉ ra "tiny-x86_64.exe", cơ sở thứ tự 1, mục 1 bảng địa chỉ, chỉ số tên 1. Ba bảng sau.0x20a8có một RVA, 0x1000. Bảng chỉ số tên xuất khẩu tại 0x20acchứa một RVA cho chuỗi "tiny_entry".0x20b0có một chỉ số 16 bit không thiên vị, 0. Tên kbản đồ để địa chỉ nhập ordinals[k], và đơn lệnh xuất khẩu là ordinals[k] + ordinal base. Các thông số đặc trưng thêm một quy tắc mà chúng tôi không sử dụng: một mục địa chỉ chỉ vào bên trong phần xuất khẩu là một chuỗi vận chuyển như MYDLL.expfunc, và bảng chỉ dẫn tên là "định nghĩa theo từ ngữ để cho phép tìm kiếm nhị phân".
Việc di chuyển căn cứ
.relocchứa các khối. Mỗi khối bắt đầu với một trang RVA và kích thước khối, sau đó các mục 16-bit với loại trong bốn bit trên cùng và một trang bù trừ trong mười hai thấp.0x3000, kích thước 12, nhập 0xa000, loại 10, IMAGE_REL_BASED_DIR64, trục xuất 0, và một mục đệm kiểu 0, IMAGE_REL_BASED_ABSOLUTE. 8 byte ở RVA0x3000Đứng yên0x1400020d0, cơ sở hình ảnh cộng với RVA của chuỗi "đối kết bằng tay". Nếu tải đặt hình ảnh ở bất cứ nơi nào khác, nó thêm sự khác biệt vào trường đó. Mô tả: "Các tệp hình ảnh không chứa COFF di chuyển ... Một hình ảnh chứa thông tin di chuyển dưới dạng di chuyển cơ sở trong phần .reloc (trừ khi hình ảnh có thuộc tính IMAGE_FILE_RELOCS_STRIPPED). " /DYNAMICBASEđược bật mặc định trong link.exe hiện tại, vì vậy hình ảnh mang .relocvà DYNAMIC_BASE- Tôi không biết.
Một hình ảnh PE không có bảng biểu tượng để thoát ra.PointerToSymbolTable"đáng lẽ phải là không cho một hình ảnh vì thông tin chỉnh sửa COFF đã lỗi thời", và các tên sống trong một PDB riêng biệt..pdata, người RUNTIME_FUNCTIONcác mục đặt tên cho sự bắt đầu và kết thúc của mỗi hàm phân bổ hàng hoặc gọi một hàm khác.
Hình ảnh Mach-O: Load Commands, Stubs, và bảng biểu tượng gián tiếp
ledger_mainlà linked arm64 exectable. otool -hin 19 lệnh tải và cờ 0x00200085- MH_NOUNDEFS 0x1- MH_DYLDLINK 0x4- MH_TWOLEVEL 0x80, vàMH_PIE 0x20000019 lệnh là 4LC_SEGMENT_64(__PAGEZERO- __TEXT- __DATA_CONST- __LINKEDIT), hai LC_LOAD_DYLIB(@rpath/libledger.dylibVậy thì/usr/lib/libSystem.B.dylib), và một trong mỗi LC_DYLD_CHAINED_FIXUPS- LC_DYLD_EXPORTS_TRIE- LC_SYMTAB- LC_DYSYMTAB- LC_LOAD_DYLINKER- LC_UUID- LC_BUILD_VERSION- LC_SOURCE_VERSION- LC_MAIN- LC_RPATH- LC_FUNCTION_STARTS- LC_DATA_IN_CODE, vàLC_CODE_SIGNATURE- MachoFile.command_names()trả lại cùng một danh sách theo cùng một thứ tự như otool -l- Tôi không biết.
__TEXTbắt đầu từ 0x100000000- __textlà 256 byte ở 0x100000558- mainVà không có gì khác, bởi vì bốn chức năng sổ cái sống trong dylib. __stubslà 84 byte ở 0x100000658, 7 đoạn của 12 byte.__DATA_CONSTbắt đầu từ 0x100004000với __got, 64 byte, tám điểm chỉ số kiểu phần S_NON_LAZY_SYMBOL_POINTERS, và__const, 32 byte, fns[]và names[]bảng của main.c- Tôi không biết.
Một cây cối, bằng tay
Chuyện này là..._putslà ở 0x100000694- Có thể là:
text 30 00 00 90 adrp x16, 0x100004000
10 1e 40 f9 ldr x16, [x16, #0x38]
00 02 1f d6 br x160x90000030là adrpThư ký là 5 bit thấp nhất, 16.immlolà 30 đến 29, 0. immhilà 23 bit đến 5, 1, giá trị là immhi << 2 | immlo = 4, trong các trang của 4,096 byte, do đó trang mục tiêu là trang của hướng dẫn, 0x100000000, cộng với 0x4000- 0xf9401e10là ldrvới một sự thay thế 12 bit không ký kết được quy mô bằng 8: bit 21 đến 10 là 7, nhân 8 là 0x38, để tải được đọc0x100004038- 0xd61f0200là br x16. Bộ phận Arm64 trong nguồn ID64 là chỉ dẫn chính xác này 3StubAtom- Tôi không biết.
Tên nào thuộc về khe là từ bảng biểu tượng gián tiếp. LC_DYSYMTABCác điểm ở 15 chỉ số vào bảng biểu tượng.reserved1nơi mà chuỗi các chỉ số của nó bắt đầu và trong reserved2kích thước của mỗi cây cối (12).reserved1và tám byte mỗi khe. __stubsbắt đầu từ chỉ số 0 và __gotở chỉ số 7, do đó, thu thập 5 bản đồ để nhập bảng biểu tượng 10, _puts, và khe 7 của __gotBản đồ đến cùng một mục. otool -Ivin cả hai chạy, và indirect_table()tái tạo mỗi hàng.
Không gian tên hai cấp
nm -mdấu vân tay(undefined) external _puts (from libSystem)và (undefined) external _grade (from libledger)Thư viện được lấy từ bản thân ký hiệu.nlist.hđịnh nghĩa GET_LIBRARY_ORDINAL(n_desc)như 15 đến 8 bit của n_desc, 1 cho lần đầu LC_LOAD_DYLIB, 2 cho thứ hai, 0xfecho tìm kiếm động lực, 0xffcho cái được thực thi. ld(1): "Với -two_levelnamespace (đặc định), linker chỉ tìm kiếm các dylib trên dòng lệnh cho các biểu tượng, và ghi lại dylib mà chúng được tìm thấy. " nm_lines()cho lại 12 bản ghi từ n_type- n_sect- n_desc, vàn_value, và thử nghiệm so sánh dòng sản xuất của nó cho dòng với các chụp nm -m- Liên kết lại với -Wl,-flat_namespacevà đọc otool -hđể xem MH_TWOLEVELmột chút biến mất.
Các Fixups chuỗi, Byte cho Byte
LC_DYLD_CHAINED_FIXUPSlà một linkedit_data_commanddataoff 0x8000- datasize 208208 byte tiếp theofixup-chains.h- Tôi không biết.
Header là bảy trường 32 bit: fixups_version 0- starts_offset 32- imports_offset 80- symbols_offset 112- imports_count 8- imports_format 1(DYLD_CHAINED_IMPORT), symbols_format 0(không bị nén). llvm-objdump --macho --chained-fixupsin bảy giá trị này và thử nghiệm so sánh chúng.
Ở điểm 32dyld_chained_starts_in_imageseg_count 4và bốn lần bù đắp, một lần cho từng phân đoạn, không cho các phân đoạn không có sự cố. Chỉ có __DATA_CONSTCó một cái.dyld_chained_starts_in_segmentđọc size 24- page_size 0x4000- pointer_format 6(DYLD_CHAINED_PTR_64_OFFSET, " mục tiêu là vm bù đắp"), segment_offset 0x4000- max_valid_pointer 0- page_count 1, và một page_startcủa 0. DYLD_CHAINED_PTR_START_NONE- 0xffff, có nghĩa là một trang mà không có sự cố.
Dòng bắt đầu tại file offset 0x4000 + 0Mỗi từ 64 bit mang theo hai thứ: những gì để viết ở đó, và nơi từ tiếp theo là.
text dyld_chained_ptr_64_bind ordinal:24 addend:8 reserved:19 next:12 bind:1 = 1
dyld_chained_ptr_64_rebase target:36 high8:8 reserved:7 next:12 bind:1 = 0Đọc từ đầu tiên, 0x8010000000000000Bit 63 được thiết lập, kết nối.nextlà bit 62 đến 51, 0x002, trong đơn vị của 4 byte, do đó, sửa chữa tiếp theo là 8 byte trên.0x4058- 0x00000000000006db- 63 điểm, 1 rebase.nextlà 0, cuối chuỗi. mục tiêu là 0x6db, vm offset, và hình ảnh cơ sở 0x100000000làm cho nó thành công0x1000006db, chuỗi "odd" trong __cstringTừ trước đó,0x00100000000006d6, các cơ sởnames[0]"đối với nhau".
Cuộc đi bộ thăm mười hai từ: tám liên kết trong __gotvới số lệnh 0 đến 7, hai liên kết trong __constvới số số 0 (cả hai mục của fns[]điểm ở _grade), và hai cơ sở của names[]- chained_fixups()trả lại chúng với các file của họ bù đắp và từ thô, và thử nghiệm so sánh mười hai (offset, raw)cặp với dyld_info -fixup_chain_details, những gì in raw: 0x8010000000000000 bind: (next: 002, bindOrdinal: 0x000000)lần đầu tiên.
Bảng nhập khẩu tại ofset 80 có tám mục 32 bit: lib_ordinal:8- weak_import:1- name_offset:23, với tên ở số 112 ._grade- _grade_label- _percent_change- _totaltừ thư viện 1, và 4 đến 7 là ___stack_chk_fail- ___stack_chk_guard- _printf- _putstừ thư viện 2. fixup_rows()kết hợp hai bảng vào các dòng dyld_info -fixupsNhận dạng:__DATA_CONST __got 0x100004038 bind libSystem/_puts, và bài kiểm tra so sánh cả hai.
Vậy là "đạo chỉ dẫn tham nhũng" ở 0x100004038là nhập khẩu 7. dyld đi bộ chuỗi này một lần, khi tải, và viết địa chỉ của _putsvào khe cắm. Cuộc họp WWDC22 của Apple đã đưa ra như sau: "trong vị trí chỉ dẫn 64 bit trong DATA, một số bit chứa sự thay thế đến vị trí sửa chữa tiếp theo. Ngoài ra, có một bit nói rằng nếu sửa chữa là một liên kết hoặc một rebase. " Xcode 13 đã làm cho định dạng mặc định cho mục tiêu triển khai của macOS 12 hoặc iOS 15, và Xcode 13.3 mở rộng nó đến macOS 11 và iOS 13.4. Không có sự liên kết lười biếng trong định dạng này. Mỗi khe đều được kết nối trước.mainchạy.
Layout nó thay thế
ledger_main_nochainlà liên kết tương tự với -Wl,-no_fixup_chains- Nó đã làm vậy.LC_DYLD_INFO_ONLYthay vào đó, với bốn bảng có mã oct: rebase 8 bytes, bind 72, lười tie 120, xuất 64. Nó cũng có ba phần file chuỗi thiếu: __TEXT,__stub_helper(108 byte), một __gotchỉ có 16 byte giữ ___stack_chk_guardvà dyld_stub_binder, và__DATA,__la_symbol_ptr(56 byte, bảy khe). dyld_info -fixupsdanh sách bảy lazy-bindhàng và bảy rebases chỉ mỗi khe lười biếng vào trampoline của riêng nó trong __stub_helperTrampoline đẩy một chỉ số và nhảy lêndyld_stub_binder, giải quyết tên và viết khe. Đó là PLT và GOT của phần trước trong hình Mach-O: __la_symbol_ptrlà .got.plt- __stub_helperlà PLT0 và pushq $index, vàdyld_stub_binderlà _dl_runtime_resolve- code/main.pybáo cáo các bản sao và phần của tệp này và không giải mã mã mã mã mã liên kết; phòng thí nghiệm thực hành yêu cầu điều đó.
Các xuất khẩu tri
LC_DYLD_EXPORTS_TRIElà một cây tiền tố. Mỗi nút là một kích thước cuối uleb128, sau đó, nếu không bằng 0 thì uleb128 cờ và một uleb128 bù trừ, sau đó một byte của con số và, cho mỗi đứa trẻ, một cạnh chuỗi và một uleb128 nút bù trừ. exports_trie()Đi đi.libledger.dylibNó quay lại._percent_change 0x410- _grade 0x428- _total 0x464- _grade_label 0x498, bốn sự bù đắp dyld_info -exportsNhận được dấu vân tay.__mh_execute_header- _fns, và_main- Tôi không biết.
Những gì dải Quay
Apple strip(1)nói rằng không có tùy chọn trên một thực thi liên kết động nó cư xử như -u -r: lưu các biểu tượng không xác định và các biểu tượng tham chiếu động. -xloại bỏ các biểu tượng địa phương. -Nloại bỏ toàn bộ bảng biểu tượng và bảng chuỗi. "Đối với các thư viện chia sẻ động, mức tối đa của việc loại bỏ thường là -x", vì "Chỉ các biểu tượng toàn cầu được sử dụng bởi quá trình liên kết động".
Được quan sát trên các thiết bị của chúng tôi.strip ledger_main- Tôi đã lấyLC_SYMTABtừ 12 đến 10 hồ sơ và xóa _main- _fns, và_names. tám nhập khẩu không xác định vẫn còn, bởi vì DILD cần tên của họ để kết nối, và__mh_execute_headerở lại vì nó là REFERENCED_DYNAMICALLY- Lần thứ 10 là một đâm kiểu như vậy.N_OPTtên radr://5614542, mà các công cụ của Apple viết để bàn không bao giờ trống rỗng;nmchương trình bị tẩy ra in cùng ba dòng như bản gốc và thoát ra 0. LC_FUNCTION_STARTSvẫn còn một khởi đầu,main- Tôi không biết.
strip libledger.dylibKhông có lựa chọn nào đi xa hơn bình thường của người đàn ông trang -xLC_SYMTABcó 0 hồ sơ và nmDữ liệu "không có biểu tượng". Trie xuất vẫn liệt kê bốn hàm ở cùng một sự bù đắp, bởi vì Dyld liên kết qua trie và không bao giờ qua.LC_SYMTAB- LC_FUNCTION_STARTSLab C liên kết một cái mớimain.cchống lại Dyllib bị tước đi để chứng minh rằng trình biên tập liên kết cũng được phục vụ bởi Trie.
| What | ELF after --strip-all | PE image | Mach-O after strip |
|---|---|---|---|
| static symbol table | gone | never present in an image | local and defined names gone, undefined imports stay |
| names the loader binds | .dynsym and .dynstr stay | import directory stays | imports stay in LC_SYMTAB and in the chained imports table |
| exported names | .dynsym stays | export directory stays | exports trie stays |
| function starts | .eh_frame FDEs, unless built without unwind tables | .pdata for non-leaf functions | LC_FUNCTION_STARTS stays |
| debug information | .debug_* gone | in the PDB, never in the image | in the dSYM, never in the image |
| section or load command headers | stay unless --strip-section-headers | stay | stay |
Tìm các chức năng mà không có biểu tượng
static-arm64-O2liên kếtmain-arm64-O2.ovà ledger-arm64-O2.othành một tính năng thực thi với năm chức năng trong __text- nm -ncủa các tập tin không bị cắt là sự thật cơ bản: _main0x100000500(244 byte), _percent_change0x1000005f4(24), _grade0x10000060c(60), _total0x100000648(248), _grade_label0x100000740(72). static-arm64-O2.strippedKhông có N_SECTbiểu tượng bên trong __text- FunctionFinderchạy bốn phương pháp trên nó và score()so sánh từng danh sách với sự thật.
LC_FUNCTION_STARTS
Chỉ huy chỉ định ở 9 byte trong __LINKEDIT80 0a f4 01 18 3c f8 01 00. định dạng là uleb128 delta, đầu tiên từ __TEXTĐịa chỉ.80 0alà 0x500, nên khởi đầu đầu là0x100000500- f4 01là 0xf4, vậy 0x1000005f4- 18cho 0x10000060c- 3ccho 0x100000648- f8 01cho 0x100000740, và005 trong 5 không có dương tính sai, ở-O0và ở-O2. biên tập viên liên kết viết bảng này cho máy tháo và phóng viên vụ tai nạn, và stripGhidra đọc nó trong phân tích "Mach-O Function Starts" của nó.dyld_info -function_startsin nó, và LIEF phơi bày nó như function_starts- Tôi không biết.
Hình ảnh đầu tiên
Các mô hình khởi động AARCH64 của Ghidra bắt đầu với fd 7b bf a9, đó là stp x29, x30, [sp, #-0x10]!Trên các tập tin của chúng tôi nó tìm thấy 0 của 5 ở cả hai cấp độ.sub sp, sp, #NĐầu tiên và sau đóstp x29, x30, [sp, #M]với một sự thay đổi tích cực, do đó hình thức pre-index không bao giờ xảy ra.sub sp, sp, #imm- (word & 0xff0003ff) == 0xd10003ff, tìm thấy 5 trong 5 tại -O0, nơi mỗi hàm có khung, và 1 trong 5 tại -O2, nơi percent_change- grade- total, vàgrade_labellà các chức năng lá mà không có khung gì cả. percent_changebắt đầu với cbz w0, ...Một quét đầu tiên báo cáo những gì người biên dịch đã chọn phát ra, và bài học 38 cho thấy bao nhiêu lần đó là không có gì.
Không có scan nào tạo ra dương tính sai ở đây, bởi vì chương trình nhỏ.55của push rbpNhập vào bên trong các hướng dẫn khác, đó là lý do tại sao các tệp mẫu x86-64 của Ghidra kết hợp một mẫu sau như 55 48 89 e5với một mẫu trước như rethoặc nopĐiều đó phải chấm dứt chức năng trước đó.
Các mục tiêu gọi
Mỗi ngườibltrong __textđặt tên một hàm bắt đầu, bởi vì blvào giữa một hàm là một cái gì đó một trình biên dịch không phát ra cho C. Việc mã hóa là 0x94000000với chữ ký 26 bit từ thay thế.bl0x1000005a0đọc 3d 00 00 940x3dlà 61, nhân 4 là 0xf4, nên mục tiêu là0x100000694- Trongledger_mainĐó là cái cột của _putsTrong cấu trúc tĩnh,call_targets()thu thập các mục tiêu bên trong __text, thả những người đổ bộ vào __stubs, và thêm LC_MAINKhông có gì gọi.main. Năm trong năm ở cả hai cấp độ. Nucleus (Andriesse, Slowinska, Bos, 2017) xây dựng phát hiện chức năng của mình dựa trên ý tưởng này cho mã được tạo bởi trình biên dịch, và Andriesse và những người khác đo lường vào năm 2016 rằng ranh giới chức năng là phần khó khăn nhất của việc tách ra, khó hơn ranh giới hướng dẫn.
Một chữ ký hình như một cái lăng
Hex-Rays mô tả FLIRT trong tài liệu của nó: một mẫu là "32 byte đầu tiên của một hàm nơi tất cả các byte biến thể được đánh dấu", các byte biến thể đến từ các cố định và tham chiếu bên ngoài, các mẫu sống trong một cây, và ngoài mẫu "chúng tôi tính toán CRC16 của các byte bắt đầu từ vị trí 33 cho đến byte biến thể đầu tiên" và lưu trữ chiều dài đó. Hai hàm với cùng một mô hình và CRC được chia tách bởi một byte đuôi ở một vị trí nơi chúng khác nhau, sau đó bởi các tên mà chúng tham khảo. Một vụ va chạm mà không có gì tách rời được "được giải quyết bởi trí thông minh tự nhiên". Các chức năng ngắn hơn bốn byte mà không có tham chiếu bên ngoài được loại trừ, và "tệp chữ ký không chứa bất kỳ byte nào từ thư viện ban đầu, ngoại trừ tên của các chức năng".
signatures_from_object()xây dựng từ ledger-arm64-O2.ovà main-arm64-O2.o, cách mà FLAIR xây dựng nó từ các vật thể của thư viện.relocation_infoghi lại bao gồm một từ bốn byte, và những byte trở thành biến thể..patbố cục đường, được ghi lại trong trình phân tích của rizin, là mô hình, chiều dài CRC, CRC16, chiều dài hàm,:0000và tên, và ^offset namecho mỗi tên tham chiếu:
textA00000342800004B890C8052087D091B000DC01AC0035FD6 00 0000 0018 :0000 _percent_change
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 003C :0000 _grade ^0000 l_switch.table.grade ^0001 l_switch.table.grade
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 0048 :0000 _grade_label ^0000 l_switch.table.grade_label ^0001 l_switch.table.grade_label ^0002 _labels ^0003 _labels_percent_changelà 24 byte, do đó mô hình của nó là toàn bộ hàm và chiều dài CRC là 0. _gradelà 60 byte với việc di chuyển đầu tiên ở byte 36, vì vậy CRC bao gồm bốn byte..patsử dụng các parsers: polynomial 0x8408, giá trị ban đầu 0xffff, bổ sung, sau đó trao đổi hai byte. giá trị của nó cho chuỗi ASCII "123456789" là 0x6e90, và các con trục thử nghiệm đó.
match_signatures()thử tất cả các chữ ký tại mỗi khởi đầu chức năng của cấu trúc bị tẩy, mẫu trước, sau đó là CRC.-O0Tất cả 5 chức năng đều có tên của họ và không có gì va chạm.-O2Hai đường trên đụng độ.grade_labelgrade, vì vậy hai hàm chia sẻ 36 byte đầu tiên của họ và bốn byte CRC.0x10000060cvà ở0x100000740Chân chia chúng: ở 44 byte_grade- Có 20và _grade_label- Có 28, đó là ldr w0, [x9, w8, uxtw #2]chống lạildr x8, [x9, w8, uxtw #3]Báo cáo ghi lại cả đụng độ và số byte quyết định chúng.
Thử nghiệmtest_object_bytes_equal_linked_bytes_outside_relocated_wordskiểm tra tiền đề: mỗi byte không biến của mỗi hàm đối tượng bằng với byte tại cùng một trục xuất trong trình thực được liên kết, do đó trình chỉnh sửa liên kết chỉ thay đổi các từ được chuyển. _totalkhông có chuyển đổi và không có thay đổi byte.
Những công cụ làm gì với cùng một ý tưởng
IDA Pro 9.4 gửi FLIRT chữ ký cho mỗi bộ xử lý và các sigmakeCông cụ để xây dựng chúng; IDA Free chỉ bao gồm x86 và x86-64 và là cho việc sử dụng phi thương mại. Lumina là một máy chủ chìa khóa metadata chức năng bằng md5 của chức năng và bỏ qua các chức năng dưới 32 byte. Ghidra 12.1.4 (Apache-2.0) có ID chức năng: một hash đầy đủ trên mnemonics và các chế độ giải quyết "rút mạnh chống lại thay đổi do liên kết" và một hash cụ thể thêm các operand liên tục, được ghi theo chỉ dẫn phù hợp, với các chức năng cha mẹ và con như tie-breakers; cơ sở dữ liệu được gửi của nó chỉ bao gồm các thư viện Visual Studio, và bạn lấp đầy của riêng bạn từ các nhị phân của riêng bạn. Binary Ninja 6.0 Krypton (thị mại, với phiên bản miễn phí) sử dụng WARP (Apache-2.0): hàm GUID là một UUIDv5 trên các GUID khối cơ bản, mỗi một UUIDv5 trên các byte khối sau khi đánh giá "tất cả các hướng dẫn có thể di chuyển" và bỏ ra NOP, với gọi, người gọi và các hàm lân cận như là hạn chế. BinDiff 8 (Apache-2.0) phù hợp các chức năng giữa hai bộ nhị phân với một thứ tự các bước cố định, từ sự phù hợp hash thông qua chỉ số MD đồ thị dòng chảy đến gọi sự phù hợp chuỗi, và cân nhắc sự tương đồng từ các cạnh, khối, hướng dẫn phù hợp và chỉ số MD đồ thị dòng chảy. Diaphora 3.4.3 (AGPL-3.0, chỉ IDA) thêm sản phẩm số nguyên tố nhỏ, nơi mỗi bản đồ mnemonic đến một số nguyên tố và một hàm là sản phẩm, một dấu vân tay đa bộ không đặt hàng. radar2 6.2.4 (LGPL-3.0) có chữ ký (zg- z/- zb) và đọc là FLIRT .sigcác tệp với zfs; rizin 0.9.1 (LGPL-3.0) tạo ra .pathoặc .sigtừ một binary mở với Fcvà áp dụng một với Fs- Tôi không biết.
IDA và Binary Ninja đã đóng cửa và được mô tả ở đây từ tài liệu của họ. radar2 và rizin không được cài đặt trên máy này, và các lệnh của họ được trích dẫn từ sách và nguồn của họ.clang- ld- llvm-objdump- otool- nm- strip, vàdyld_infotừ các công cụ dòng lệnh, và LIEF 1.0.0, pyelftools 0.33, và hồ sơ 2024.8.26 thông qua thăm dò. LIEF đọc tất cả các định dạng ở đây, bao gồm cả các đối tượng COFF, mà nó đã hỗ trợ từ 0.17.0, và báo cáo mười liên kết và hai cơ sở trên ledger_main, cùng 12 sửa chữa. các công cụ đọc các tập tin ELF chỉ. hồ sơ từ chốimain-x86_64.objvới PEFormatError: 'DOS Header magic not found.'và đọc hình ảnh nhỏ bé.
Hãy xây dựng nó
Chạy demo:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.pyKết quả là một báo cáo JSON với năm phần. elfgiữ số lượng di chuyển của hai đối tượng, kế hoạch liên kết và phần động lực, PLT và GOT của tiny-dyn-x86_64- coffgiữ các phần, di chuyển, chức năng và tên không xác định của hai đối tượng. pegiữ các tiêu đề, thư mục, nhập khẩu, xuất khẩu, di chuyển cơ sở và các cuộc gọi IAT của tiny-x86_64.exe- machogiữ ledger_mainVới những mảnh cây, các chuỗi sửa chữa, các hàng sửa chữa, và xuất khẩu, bố cục di sản của ledger_main_nochain, Dylib, và hai báo cáo về tình trạng.identificationCó sự thật, bốn phương pháp với điểm số, và các chữ ký với sự va chạm của chúng, tại -O0và -O2- Tôi không biết.
Hình dạng dự kiến:
json{
"elf": {"linkPlan": {"pltSymbols": ["puts", "printf"], "sizes": {".plt": 48, ".got.plt": 40, ".rela.plt": 48, ".rela.dyn": 48}, "...": "..."}, "tiny-dyn-x86_64": {"...": "..."}},
"coff": {"main-x86_64.obj": {"...": "..."}},
"pe": {"imports": [{"dll": "msvcrt.dll", "iat": "0x2040", "...": "..."}], "...": "..."},
"macho": {"ledger_main": {"flags": "0x200085", "fixupRows": ["__DATA_CONST __got 0x100004000 bind libledger/_grade", "..."], "...": "..."}, "strip": {"...": "..."}},
"identification": {"O2": {"methods": {"functionStarts": {"recall": "5/5"}, "prologueSub": {"recall": "1/5"}, "...": "..."}, "correctNames": 5}}
}Đọc code/main.pytheo thứ tự này:
ElfFileđọc tiêu đề, phần, cả hai bảng biểu tượng, và di chuyển, vàdynamic_entries()-jump_slots()-plt_entries(), vàplt0()đọc hình ảnh liên kết.plan_dynamic_link()áp dụng bảng 4.9 và phần 5.2 đối với hồ sơ di chuyển của các vật thể có thể di chuyển.CoffObjectđọc tiêu đề tập tin, bảng phần, các biểu tượng 18 byte với các hồ sơ phụ trợ của chúng, và chuyển đổi 10 byte.PeImageđọc các đoạn MS-DOS, chữ ký PE, tiêu đề tùy chọn, thư mục dữ liệu, phần, và các thư mục nhập khẩu, xuất khẩu và di chuyển cơ sở, vàiat_calls()decodesff 15- Tôi không biết.MachoFileđọc lệnh tải, phân đoạn,nlistghi, bảng biểu tượng gián tiếp, các đoạn, các sửa chữa chuỗi, các thử nghiệm xuất khẩu và di chuyển đối tượng.FunctionFinderchạy bốn người tìm kiếm bắt đầu vàscore()so sánh mỗi điều với sự thật.signatures_from_object()vàmatch_signatures()xây dựng và áp dụng các chữ ký hình FLIRT.- -
parse_*các chức năng đọc văn bản công cụ được ghi lại mà các thử nghiệm so sánh với.
Các lệnh khác:python3 code/main.py elf FILE- coff FILE- pe FILE, vàmacho FILEin một báo cáo. python3 code/main.py plan OBJECT...in kế hoạch liên kết cho bất kỳ bộ vật thể ELF nào. python3 code/main.py functions STRIPPED UNSTRIPPED OBJECT...ghi điểm của người tìm và chữ ký trên một cặp khác của bạn xây dựng.
Sử dụng nó
Sử dụng các độc giả theo thứ tự này trên một chương trình bạn xây dựng.
- Đọc tiêu đề và đặt tên định dạng và loại hình: vật thể có thể di chuyển, thể hiện hoặc thư viện chia sẻ. Người đọc từ chối những gì họ không biết, và từ chối đó là thông tin.
- Đăng danh nhập khẩu với các nhà cung cấp:
DT_NEEDEDvà.dynsym, danh mục nhập khẩu hoặc bảng nhập khẩu chuỗi vàLC_LOAD_DYLIBMỗi lần gọi tên trong danh sách đó đều bỏ qua hình ảnh. - Theo dõi một cuộc gọi bằng tay đến khe cắm của nó. Viết ra các byte gọi, các stub hoặc byte PLT, địa chỉ khe cắm, từ trong tệp và những gì tải ở đó. Nếu từ trong tệp trông giống như rác, hãy giải mã nó như một sửa chữa trước khi bạn gọi nó là rác.
- So sánh bảng biểu tượng với xuất khẩu và chức năng bắt đầu.
stripTên chỉ cần một người sửa lỗi không cần. - Trong một cấu trúc bị tước, chạy các trình tìm kiếm bạn có thể chạy và ghi điểm chúng chống lại cặp đôi không bị tước khi bạn vẫn còn một.
LC_FUNCTION_STARTS-.pdata, hoặc.eh_frame, trên các mẫu byte, và sử dụng mục tiêu gọi để lấp đầy khoảng trống. - Xây dựng chữ ký từ các đối tượng của riêng bạn và áp dụng chúng. ghi lại mọi vụ va chạm và các byte chia nó. Một tên mà hai chữ ký tuyên bố là một người không biết đến cho đến khi đuôi hoặc một tên tham chiếu quyết định nó.
Giữ văn bản công cụ được ghi lại với các tệp. Khi chuỗi công cụ thay đổi, lấy lại, bởi vì một danh sách thay đổi là một vật cố định thay đổi.
Phòng thí nghiệm tương tác
Phòng thí nghiệm A: PLT bằng tay
Từ codethư mục, mở Python:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code
python3 -qĐi chạy:
pythonfrom main import *
elf = load_elf("tiny-dyn-x86_64")
[(e.tag_name, e.text) for e in elf.dynamic_entries()]
elf.plt0()
[(e.symbol, hex(e.address), hex(e.slot), hex(e.slot_initial)) for e in elf.plt_entries()]
[hex(w) for w in elf.got_plt_words()]Trước mỗi cuộc gọi, tính toán câu trả lời từ các byte trong văn bản trên. Sau đó thay đổi IMPORTStrong build_tiny_dyn_elf.pyđể ba tên, xây dựng lại, và dự đoán các kích thước mới của .plt- .got.plt, và.rela.plttrước khi đọc chúng.
Phòng thí nghiệm B: một sự sửa chữa dây chuyền
pythonexe = load_macho("ledger_main")
stub = next(s for s in exe.stubs() if s.symbol == "_puts")
stub.lines()
raw = int.from_bytes(exe.read(stub.slot, 8), "little")
hex(raw), raw >> 63, (raw >> 51) & 0xfff, raw & 0xffffff
[(i.index, i.name, i.library) for i in exe.chained_fixups()["imports"]]Tự giải mã từ liên kết trên giấy trước tiên: bit liên kết, nexttrường trong đơn vị bốn byte, đơn vị thứ tự. sau đó đọc từ tám byte hơn nữa và xác nhận nó là fixup tiếp theo. sau đó đọc từ cuối cùng của chuỗi tại0x100004058và giải thích tại sao nó nextlà không.
Phòng thí nghiệm C: trượt và nối lại
Trong thư mục đồ đạc, liên kết một chương trình mới với các dilib hoàn toàn khô:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code/fixtures
clang -target arm64-apple-macos -O1 main.c -L. -lledger.stripped -Wl,-rpath,@executable_path -o /tmp/relinked
nm /tmp/relinked | grep ' U '
dyld_info -exports libledger.stripped.dylibKết nối thành công với một thư viện mà LC_SYMTABlà trống, bởi vì trình chỉnh sửa liên kết đọc các xuất trie.python3 ../main.py macho libledger.stripped.dylibvà so sánhsymtab- exports, vàfunctionStartsSau đó, hãy dỡ bỏ cấu trúc tĩnh bằng-Nthay vì và xem cái gì dyld_info -function_startsvẫn còn dấu vân tay.
Phòng thí nghiệm D: vụ va chạm
pythonfrom main import *
stripped = load_macho("static-arm64-O2.stripped")
sigs = signatures_from_object(load_macho("ledger-arm64-O2.o"))
[s.pat_line() for s in sigs]
starts = FunctionFinder(stripped).function_starts()
match_signatures(sigs, stripped, starts)Hãy đọc 2 hồ sơ vụ va chạm rồi tạo ra chữ ký từledger-arm64-O0.o, so sánh chúng với static-arm64-O0.stripped, và giải thích từ .patđường tại sao không xảy ra va chạm ở -O0Rồi rút ngắn đi.PATTERN_LENGTHđến 16 và đếm số lượng các hàm va chạm.
Phòng thí nghiệm thực hành
Khóa mã các bảng liên kết cũ và thêm bộ tìm PE.
Yêu cầu:
- - Thử giải mã
LC_DYLD_INFO_ONLYrebase, bind, và lười tieo liên kết các dòng opcode củaledger_main_nochainthành hàng với phân đoạn, phần, địa chỉ, loại và mục tiêu, và làm cho chúng bằng với 21 hàng trong các hình ảnh được chụpdyld_info -fixups- Thư. - Thêm một
PT_GNU_RELROtiêu đề chương trình và mộtDT_FLAGSvớiDF_BIND_NOWđếnbuild_tiny_dyn_elf.py, di chuyển.got.pltbên trong phạm vi bao phủ, và làmllvm-objdump -pcho thấy tiêu đề mới. nói trong các thiết bị README phần nào của RELRO đầy đủ này tái tạo và phần nào chỉ có thể thực hiện glibc. - Hãy viết một
.pdatađọc cho hình ảnh PE32+ và tìm hàm sử dụngRUNTIME_FUNCTIONbắt đầu địa chỉ. Xây dựng một đối tượng vớiclang -target x86_64-pc-windows-msvc -cKhông có-fno-asynchronous-unwind-tables, đọc nó.pdataCác chức năng này được chuyển đổi theo quy định tại bảng này. - Viết một bài kiểm tra cho mỗi người đọc so sánh với văn bản công cụ được ghi lại.
Thành công là nghĩa củapython3 code/main.py macho ledger_main_nochainin cùng 21 hàng sửa chữa như dyld_infoNhìn vân tay, ELF nhỏ gọn mang theo một PT_GNU_RELROĐó làllvm-objdumpdanh sách, và các .pdatatìm kiếm tên các chức năng nó có thể và liệt kê các chức năng lá nó không thể.
Bài học: Cây tạo vật
Bài học này bao gồm:outputs/skill-linking-loading-and-stripped-binaries.mdSử dụng nó để biến một chương trình bạn xây dựng thành một danh sách nhập khẩu của nó với các nhà cung cấp của họ, một cuộc gọi theo dõi đến khe cắm tải điền, một tuyên bố về những gì stripNó đòi hỏi cặp đôi không bị cắt như sự thật căn bản, một danh sách công cụ bắt giữ cho mỗi yêu cầu, và một hồ sơ của mỗi va chạm.
Hãy kiểm tra
Chạy demo và bộ xác định:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.py
python3 code/tests/test_main.pyViệc kiểm tra phải chứng minh:
- phần năng động của
tiny-dyn-x86_64llvm-objdump -pTag cho tag, vàDT_FLAGS_1mangDF_1_PIE - Hai người
R_X86_64_JUMP_SLOTHồ sơ phù hợpllvm-objdump -Rvà tênputsvàexit - PLT0 đẩy GOT+8 và nhảy qua GOT+16, mỗi mục đọc khe và đẩy chỉ số của nó, và mỗi khe chứa địa chỉ của riêng nó
pushq - Các phần và tiêu đề chương trình của trận đấu ELF nhỏ
llvm-objdump -h -p - Các hồ sơ di chuyển của hai vật thể ELF phù hợp
llvm-objdump -r - kế hoạch liên kết tìm thấy hai biểu tượng PLT, bốn cuộc gọi trực tiếp, hai di chuyển tương đối, và kích thước của hình ảnh nhỏ
- bảng phần COFF, 37 ký hiệu ghi lại với các ký hiệu phụ trợ, và di chuyển phù hợp
llvm-objdump -h -t -r, và/16quyết định.llvm_addrsig - ba COFF
REL32ghi chép củaledgerngồi ở 3 ELFPC32bồi thường - Các tiêu đề PE,
DllCharacteristics, và các thư mục dữ liệu phù hợpllvm-objdump -p - danh mục nhập khẩu, mục gợi ý/tên và sự phù hợp IAT
llvm-objdumpvàpefile, và IAT bằng bảng tìm kiếm trước khi kết nối - danh mục xuất khẩu và
DIR64mã giải mã chuyển đổi cơ sở, và chỉ số chuyển đổi đặt tên chuỗi "đối kết bằng tay" ff 15tại RVA0x100bgiải quyết cho khe IAT củaputs- Các lá cờ Mach-O, tên lệnh tải 19 và hai thư viện phù hợp.
otool - 7 đoạn mã hóa thành 8
__gotkhe cắm và các bảng biểu tượng gián tiếpotool -Iv - Các cái đầu của các bộ sợi dây đeo kết hợp.
llvm-objdump --macho --chained-fixups - chuỗi đi lại tái tạo tất cả mười hai từ thô của
dyld_info -fixup_chain_details - Các hàng cố định và bảng nhập khẩu phù hợp
dyld_info -fixupsvà-imports - xuất khẩu trie phù hợp
dyld_info -exportsĐối với cây đà và bản sao của nó bị cất nm_lines()=nm -mcho các bản sao có thể thực hiện và bản sao bị cởistripđược loại bỏ_main-_fns, và_names, giữ 9 tên và 1radr://5614542đâm, và chương trình bị tước đi in cùng đầu ra- thiết kế kế cũ đã có
LC_DYLD_INFO_ONLY, bảy điểm chỉ lười biếng, và thả xuống__stub_helper - arm64 đối tượng di chuyển phù hợp
llvm-objdump -r LC_FUNCTION_STARTSphục hồi 5 trong 5 ở cả hai cấp độ và bằng nhaunmvàdyld_info -function_startssub spScan tìm thấy 5 trong 5 ở-O0và một trong năm ở-O2, vàstpkhung scan không tìm thấyblmục tiêu cộng vớiLC_MAINnhập cảnh thu hồi năm trong năm- Các chữ ký đặt tên cho tất cả năm chức năng ở cả hai cấp độ và ghi lại các
gradeđụng độ chia bằng 44 byte - CRC16 của "123456789" là
0x6e90, và tám byte dưới cặp tải GOT củamainlà biến thể - mỗi byte đối tượng không biến đổi bằng với byte liên kết
- Các mô hình demo và dữ liệu số báo cáo các địa chỉ tương tự, và mô-đun trang web nhúng các dữ liệu tương tự trong ES5
Các chế độ sản xuất thất bại
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
A bl or call lands in a stub | "calls an unnamed function at 0x694" | the stub bytes, the slot it loads, and the import the slot binds |
| A GOT word read from the file | "corrupt pointer 0x8010000000000007" | bit 63 is bind, next is 2, ordinal 7 is _puts from library 2 |
PLT32 in an object | "every call goes through the PLT" | defined symbols are reduced to a direct distance, only undefined ones get an entry |
| Partial RELRO read as full | "GOT is read-only" | .got.plt slots after the three reserved ones sit outside PT_GNU_RELRO without -z now |
COFF REL32 compared with ELF PC32 | "the Windows build has different relocations" | same offsets, the -4 lives in the ELF addend and in the COFF field |
| IAT read before load | "imports point at strings" | the IAT equals the lookup table until the loader binds, and the strings are hint/name entries |
| A stripped dylib | "no symbols, nothing exported" | the exports trie and LC_FUNCTION_STARTS list every function |
nm on a stripped executable | "one function" | LC_FUNCTION_STARTS lists five starts, nine bytes long |
A prologue scan at -O2 | "one function" | four leaf functions have no frame, call targets and the starts table find them |
| Two functions share 36 bytes | "grade_label is grade" | the tail byte at 44 and the lengths 60 and 72 split the collision |
| A tool run on another machine's build | the numbers in this text | recapture every listing from your own fixtures |
Quy tắc hoạt động
Theo dõi mọi cuộc gọi bên ngoài từ trang web cuộc gọi đến khe cắm mà bộ tải chứa, và giải mã từ trong khe đó như định dạng xác định nó trước khi bạn gọi nó là địa chỉ. Chỉ cần thu thập những gì cần thiết cho bộ tải. Công việc phục hồi bắt đầu từ các bảng mà trình chỉnh sửa liên kết đã viết trước khi bạn quét các byte, đánh giá mỗi người tìm kiếm với một cặp đôi với các biểu tượng, và ghi lại mỗi sự va chạm chữ ký với các byte chia nó.
Đọc thêm
- System V gABI, chapter 5: Program Loading and Dynamic Linking
- System V Application Binary Interface, AMD64 Architecture Processor Supplement: section 5.2 and Table 4.9
- Drepper, "How To Write Shared Libraries", version 4.1.2
- Drepper, "ELF Symbol Versioning"
- Jelinek, "Little hardening DSOs/executables against exploits", binutils list, 2004-01-06
- Taylor, "Linker relro", 2008
- binutils commit bd7ab16b: PLT32 for calls, 2018-02-13và LLVM D43383
- GNU Binutils: stripvà llvm-objcopy
- Microsoft, PE Format
- CALL instruction reference, FF /2
- Apple, mach-o/loader.hvà mach-o/nlist.h
- Apple dyld, mach-o/fixup-chains.h
- Apple ld64, stub_arm64.hpp
- Xcode 13 release notes: chained fixupsvà WWDC22 session 110362, "Link fast: Improve build and launch times"
- strip(1)- ld(1), vàdyld(1)
- Hex-Rays, IDA F.L.I.R.T. Technology: In-Depthvà Lumina overview
- Ghidra Function ID helpvà AARCH64 function start patterns
- Vector 35, WARPvà Binary Ninja WARP guide
- BinDiff manualvà BinDiff source
- Diaphora
- rizin pat.c, the .pat parser- radare2 flirt.c, vàradare2 book: Zignatures
- Andriesse et al., "An In-Depth Analysis of Disassembly on Full-Scale x86/x64 Binaries" (USENIX Security 2016)và Andriesse, Slowinska, Bos, "Compiler-Agnostic Function Detection in Binaries" (EuroS&P 2017)
- Bao et al., ByteWeight (USENIX Security 2014)và Shin, Song, Moazzezi, "Recognizing Functions in Binaries with Neural Networks" (USENIX Security 2015)
- LIEF changelog- pyelftools, vàpefile
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.