HN chia sẻ: tiện ích mở rộng Ghidra xuất một phần chương trình thành object file
(github.com/boricj)- Tiện ích mở rộng Ghidra này xuất một phần chương trình thành object file, và tệp được xuất ra chứa metadata hợp lệ như symbol và bảng relocation để có thể được xử lý lại trong toolchain
- Các mục đích sử dụng chính gồm vá nhị phân nâng cao, port phần mềm, chuyển đổi định dạng tệp, tạo thư viện, và trong các dự án decompile là chia chương trình thành nhiều object file để tái hiện lại
- Các tổ hợp được hỗ trợ là COFF x86/x86_64, ELF x86/x86_64/MIPS, và OMF x86; COFF MIPS, OMF x86_64, và OMF MIPS không được hỗ trợ
- Người dùng chọn phạm vi địa chỉ cần trích xuất trong Ghidra Listing, chạy analyzer Relocation table synthesizer, rồi gọi exporter object file có thể relocation từ
File > Export Program… - Việc tổng hợp bảng relocation phụ thuộc vào độ chính xác của cơ sở dữ liệu Ghidra, nên nếu có thông tin sai hoặc thiếu thì relocation có thể bị hỏng hoặc bị bỏ sót; an toàn nhất là chạy lại analyzer ngay trước khi export
Tiện ích mở rộng này làm gì
- Object file exporter extension for Ghidra là tiện ích mở rộng Ghidra cho phép xuất một phần chương trình thành object file
- Object file được tạo ra chứa metadata hợp lệ như symbol và bảng relocation
- Nhờ metadata này, object file đã xuất có thể được tái sử dụng trực tiếp trong toolchain để xử lý thêm
Các trường hợp sử dụng
- Advanced binary patching
- Thay vì phải căn chỉnh thủ công phần gốc và phần đã sửa, có thể dùng linker để ghép chúng lại với nhau
- Software ports
- Có thể tách mã độc lập với hệ thống ra khỏi chương trình và thay thế phần còn lại
- Có thể chuyển đổi chương trình hoặc object file từ một định dạng tệp sang định dạng khác
- Trích xuất một phần chương trình và tạo thư viện
- Có thể trích xuất một phần chương trình để tái sử dụng trong ngữ cảnh khác
- Trong các dự án decompile, có thể chia chương trình thành nhiều object file và tái hiện lại theo cách Ship of Theseus
Kiến trúc và định dạng object file được hỗ trợ
- Ma trận hỗ trợ như sau
- COFF: hỗ trợ x86, x86_64 / không hỗ trợ MIPS
- ELF: hỗ trợ x86, x86_64, MIPS
- OMF: hỗ trợ x86 / không hỗ trợ x86_64, MIPS
Build và cài đặt
- Quy trình build bằng CLI
- clone repository
- đặt biến môi trường
GHIDRA_INSTALL_DIRtới thư mục cài đặt Ghidra - chạy
gradle buildExtension - archive tiện ích mở rộng Ghidra được tạo trong thư mục
dist/
- Cần xác thực để tải package từ GitHub Maven repository
- tạo GitHub classic token có quyền
read:packagesrồi thêmgithubToken=ghp_xxxvào${GRADLE_USER_HOME}/gradle.properties - hoặc chạy
gradle installStandaloneDepsđể build và cài đặt vendored dependency của submodule
- tạo GitHub classic token có quyền
- Quy trình cài đặt
- tải tiện ích mở rộng từ releases page hoặc build cục bộ
- trong Ghidra, cài tiện ích từ File > Install Extensions…
- trong cửa sổ CodeBrowser, bật plugin RelocationTableSynthesizedPlugin tại File > Configure > Experimental
Luồng sử dụng và lưu ý
- Quy trình sử dụng cơ bản
- chọn tập địa chỉ cần trích xuất trong Listing view
- chạy analyzer Relocation table synthesizer ở chế độ one-shot
- gọi exporter object file có thể relocation tại File > Export Program…
- Có thể xem relocation đã được tái tạo tại Window > Relocation table (synthesized)
- Có thể bật báo cáo đánh giá chi tiết bằng cách đặt tùy chọn Evaluation report policy của analyzer Relocation table synthesizer trong hộp thoại Analysis > Auto Analyze...
- Không cần phải reverse engineering toàn bộ chương trình trước khi dùng tiện ích mở rộng này
- Việc delinking thành công thường phụ thuộc rất lớn vào metadata trong phần sẽ export và trong các tham chiếu bên ngoài
- các hàm và con trỏ được dùng làm vị trí relocation
- symbol footprint được dùng làm đích relocation
- các tham chiếu giữa chúng
- Analyzer Relocation table synthesizer phụ thuộc vào độ chính xác của cơ sở dữ liệu Ghidra
- thông tin không chính xác hoặc bị thiếu có thể dẫn tới relocation bị hỏng hoặc bị bỏ sót trong quá trình phân tích
- Object file exporter phụ thuộc vào kết quả phân tích của Relocation table synthesizer
- nếu có nghi ngờ, nên chạy analyzer ngay trước khi export object file để bảo đảm bảng relocation là mới nhất
Cơ chế hoạt động
- Object file gồm ba phần
- byte của section có thể relocation
-
bảng symbol
- bảng relocation
- các công việc mà linker thực hiện khi tạo executable từ nhiều object file
- đặt các section vào bộ nhớ
- tính địa chỉ symbol trong không gian địa chỉ ảo
- áp dụng relocation lên byte của section dựa trên địa chỉ symbol cuối cùng
- thông thường sau khi quá trình này kết thúc, bảng relocation sẽ bị loại bỏ
- nếu không giữ lại debugging symbol, bảng symbol cũng bị loại bỏ và chỉ còn lại byte của section không thể relocation
- tiện ích mở rộng này có thể dựng lại dữ liệu đó bằng phân tích cẩn thận, từ đó cho phép delink chương trình trở lại thành object file
1 bình luận
Các ý kiến trên Hacker News
Rất vui khi thấy nó ở đây. Tôi nghĩ đây là một dự án thật sự tuyệt vời, và tôi đã giúp thêm hỗ trợ MS COFF
Tuy vậy PR ban đầu của tôi còn khá thiếu sót so với phần hỗ trợ ELF đã có sẵn, nên nếu có vấn đề thì có lẽ là lỗi của tôi. Dù sao cũng thấy nó đang được cải thiện
Tôi chưa dùng nó cho việc lớn nào, nhưng điều thú vị nhất tôi từng làm là delink một file thực thi Hello World được biên dịch bằng Visual Studio 2003, rồi liên kết lại với GCC+glibc trên Linux x86, sau đó liên kết lại lần nữa với MinGW+msvcrt
Những thứ lớn hơn Hello World thì hiện vẫn khó, đặc biệt là tôi cũng không rành Ghidra lắm nên vẫn chưa tìm được cách hay để chọn phạm vi cần delink trong các binary lớn
Tình cờ là hôm nay gói phái sinh Nixpkgs của công cụ này đã được merge, nên trên NixOS unstable có thể cài bằng
ghidra.withExtensions. Vị trí làghidra-extensions.ghidra-delinker-extensionTuy nhiên vài ngày trước đã có phiên bản mới mà tôi chưa rebase PR, nên hiện nó vẫn là bản cũ; tôi sẽ cố cập nhật sớm
Ví dụ, nếu bạn có một chương trình Ghidra đã xác định được tên và phạm vi của nhiều file object cấu thành file thực thi gốc, bạn có thể nhấp phải vào folder hoặc fragment đó > Select Addresses để chọn toàn bộ
Trình phân tích tổng hợp relocation và phần export cũng có thể được script hóa riêng hoặc tự động hóa thông qua trình quản lý cây của chương trình, giúp giảm việc phải tự tay chọn phạm vi mong muốn rồi chạy thủ công analyzer và export
Trông khá thú vị, làm tôi muốn xem lại một dự án reverse engineering game mà tôi đã bỏ dở vài năm trước
Giá mà có một ví dụ hoàn chỉnh cho thấy từ đầu đến cuối cách dùng cái này và cách tận dụng đầu ra của nó
https://github.com/boricj/ghidra-delinker-extension/blob/mas...
Tôi tò mò cần bao nhiêu công sức để biết nên export những section nào của file thực thi
Có thực tế không nếu export một game Win32 tương đối hiện đại, khoảng giai đoạn 2008–2015, thành file object rồi biên dịch/liên kết lại thành một file thực thi hoàn chỉnh trong vòng vài giờ?
Export cái gì lại là vấn đề riêng, cần có hiểu biết về chương trình. Nếu có debug symbol thì dễ hơn nhiều; còn nếu không, sau khi bạn đã làm cho cơ sở dữ liệu Ghidra đủ chính xác để có thể export, thường bạn cũng sẽ có cảm giác cái gì nằm ở đâu
Trong case người dùng của bài gửi, họ mở issue đầu tiên vào đầu tháng 7, và đến khoảng giữa tháng 8 thì có được file thực thi liên kết lại hoạt động tương đương về mặt chức năng
Tuy nhiên khi đó phần export COFF còn nhiều bug cần sửa và analyzer i386 cũng có chỗ cần chỉnh, nên giờ tôi hy vọng người khác sẽ ít gặp lại các vấn đề tương tự hơn
Tôi không biết sẽ mất bao lâu, nhưng trừ khi có debug symbol và thật sự rất may mắn, khả năng cao là sẽ lâu hơn vài giờ. Một reverse engineer có kinh nghiệm có thể trong khoảng thời gian đó tạo được thứ gì đó chạy lên, nhưng cũng có thể nó chết giữa màn hình loading đầu tiên; đây gần như là loại việc mà trước khi xong thì không biết khi nào mới xong
Trông hay đấy. Có thể một ngày nào đó nó sẽ giúp phân rã các chương trình sẵn có dễ hơn để dùng theo cách mình muốn
Có những nghiên cứu như LLM Compiler của Meta hay decompile bằng LLM; việc tách chương trình ra và thay thế một phần có thể là một lĩnh vực thú vị, giàu dữ liệu để LLM khám phá và tự cải thiện
Từ góc độ học tập cũng có nhiều token thú vị ẩn bên trong, và có vẻ có rất nhiều việc có thể làm với chúng
Thành thật mà nói nghe như phép thuật. Tôi phải tìm hiểu xem chuyện này có thể làm được như thế nào
Khi linker tạo file thực thi từ nhiều file đối tượng, nó đặt các section vào bộ nhớ, tính địa chỉ symbol trong không gian địa chỉ ảo, rồi áp dụng tái định vị lên các byte section dựa trên địa chỉ symbol cuối cùng
Mẹo của delink là tìm ra các tái định vị đó đã được áp dụng ở đâu rồi đảo ngược chúng, để thu lại các byte có thể tái định vị. Sau đó tạo bảng tái định vị và bảng symbol dựa trên nội dung đã đảo ngược, đóng gói lại thì sẽ ra file đối tượng
Phần thật sự khó là phân tích để tìm ra các điểm tái định vị. Phần lớn việc này giao cho Ghidra, nhưng vẫn cần chuyển các tham chiếu thành điểm tái định vị. Trên x86 thì tương đối dễ, còn trên MIPS thì khó như ác mộng. Extension này được tạo ra để tự động hóa cả việc thu thập dữ liệu cần thiết cho việc đó lẫn serialize file đối tượng
Ngoại trừ nền tảng Microsoft, nhiều khi định dạng file thực tế còn giống nhau, ví dụ như ELF
Khác biệt lớn là tái định vị. File đối tượng có thông tin tái định vị chi tiết, còn file thực thi thì thường không có. Ảnh thực thi của Windows chỉ có lượng tái định vị tối thiểu, trỏ tới các địa chỉ code và data cần sửa khi file thực thi bị tái định vị, và bản thân image chỉ có thể tái định vị theo toàn bộ địa chỉ cơ sở của image
Ngược lại, file đối tượng chứa tái định vị theo đơn vị symbol. Để tái dựng chính xác thông tin này, cần gắn thông tin khá chính xác về symbol vào phần disassembly
Một khác biệt lớn nữa là file đối tượng vẫn chưa được link. Không symbol nào đã được phân giải. Điều này thực ra lại dễ sửa hơn: trong quá trình delink, nếu là symbol nằm ngoài phạm vi hiện tại thì thường chỉ cần biến nó thành symbol chưa phân giải. Khi link lại sau đó, file đối tượng hoặc thư viện khác phải cung cấp symbol đó thì mới nối lại được
Cũng có những khác biệt nhỏ như dường như không có entry point, nhưng không quá quan trọng
Vì vậy, ranh giới để cắt file đối tượng ra từ image thực thi hoặc shared object thực chất gần như tùy ý. Khi được biên dịch ban đầu, nó hẳn được chia theo translation unit, nhưng ở bước link thì các ranh giới đó không được quan tâm nhiều. Tất nhiên, nếu muốn decompile khớp, ranh giới file đối tượng sai có thể khiến việc đó cực kỳ khó, nên nếu có thể thì nên tìm ra
Tôi từng xử lý vấn đề này trong thực tế, nhưng có thể hơi sai ở vài chi tiết, nên chỉ nên xem như tham khảo. Tôi đã định viết một bài blog về file đối tượng, và cũng đã có vài bài khá hay rồi
Tôi tò mò quy trình này có hoàn toàn an toàn không. Muốn biết liệu nó luôn được đảm bảo thành công, hay phần phân tích hoạt động theo hướng bảo thủ
Ví dụ nếu trong ELF thiếu mảnh, dữ liệu hoặc chức năng nào đó thì delink sẽ thất bại chẳng hạn?
Trình phân tích của tôi phụ thuộc vào cơ sở dữ liệu Ghidra chính xác, ít nhất là đối với phần muốn xuất ra. Tôi đã khá cố gắng ghi log nhiều vấn đề cần sửa, nhưng không thể thấy thứ không tồn tại
Đặc biệt, các tham chiếu bị thiếu và biến bị cắt sẽ không được phát hiện, và có thể dẫn tới hành vi chưa xác định kỳ quặc
Có vài cách để truy vết một phần các vấn đề này. Cách tốt nhất tôi tìm được đến nay là link lại file thực thi ở một địa chỉ cơ sở khác và tránh map dải địa chỉ của chương trình gốc. Khi đó, các điểm tái định vị tuyệt đối bị bỏ sót sẽ gây lỗi segmentation fault và có thể debug được. Tuy nhiên cách này chỉ khả thi khi mục tiêu có MMU
Biến bị cắt đặc biệt khó truy vết nếu bạn không nghi ngờ trước, vì phần bị hỏng là bộ nhớ nằm sau biến bị cắt. Trường hợp nhầm số nguyên thành con trỏ cũng khó truy vết. Lý do là giá trị số nguyên sẽ thay đổi theo địa chỉ nơi symbol đích được đặt, khiến hành vi chương trình thất thường; vấn đề đặc biệt lớn với các chương trình được load ở vùng rất thấp của không gian địa chỉ
Dù vậy, nếu cơ sở dữ liệu Ghidra đủ chính xác, và bạn xuất lại đúng định dạng file đối tượng như ban đầu rồi dùng cùng nền tảng và cùng toolchain, thì có thể delink thành công các chương trình gồm hàng megabyte code và data. Tôi cho rằng nếu linker đã làm được thì cũng nên có thể đảo ngược được
Ngược lại, nếu bắt đầu delink chéo không khớp nền tảng và toolchain của chương trình gốc, chẳng hạn delink từ file thực thi Linux i386 ELF sang file đối tượng COFF để dùng với toolchain i386 Windows, thì câu chuyện khác hẳn. Nếu phần xuất có thể biểu diễn các tái định vị, bạn có thể thu được file đối tượng có thể tái định vị hoạt động được, nhưng còn phải vật lộn với bất đồng ABI. Có thể làm, nhưng tôi không khuyến nghị làm dự án đầu tay
Tóm lại, tùy bạn đang làm gì và độ chính xác của cơ sở dữ liệu Ghidra, mức độ có thể dao động từ “cứ thế là chạy” đến “cầu xin Cthulhu rủ lòng thương”
Ví dụ hãy nghĩ tới một hàm như
int getSpecialArrayElement(char *array, uint64 key) { i = computeOffset(key); return array[i]; }computeOffsetcó thể phức tạp tùy ý, và nếu muốn làm rối thì còn có thể cố tình khiến nó khó phân tích. Không có gì ngăn hàm này truy cập tới vị trí tùy ý trong bộ nhớNếu không thử toàn bộ mọi đầu vào có thể, bạn sẽ không biết nó có truy cập vào symbol mà linker đã định nghĩa sẵn trong bộ nhớ hay không, và trong
computeOffsetcũng có thể chứa bẫy Turing để cản trở những phép thử như vậySẽ khá thú vị nếu liên hệ với một ý tưởng trước đây tôi chỉ từng tưởng tượng chứ chưa thực sự làm: tạo file header từ thông tin debug, rồi nếu cần thì để LLM dọn dẹp lại.
https://github.com/wbenny/pdbex
Còn phần dọn dẹp bằng LLM thì hiện chưa thấy nhiều trường hợp áp dụng mô hình LLM vào reverse engineering mà đạt thành công lớn. Có lẽ đây cũng có thể là một lĩnh vực làm lộ ra giới hạn của kiến trúc LLM
Tôi không phải chuyên gia, nhưng nếu phải đặt cược thì trong nhiều trường hợp sử dụng reverse engineering, phía mô hình khuếch tán có vẻ thú vị hơn
Không hoàn toàn giống, nhưng Binary Ninja có một tính năng tên là Sidekick, cố gắng dùng LLM để dọn dẹp phần disassembly. Cá nhân tôi không thấy ấn tượng lắm, nhưng có thể hữu ích với ai đó
paholetạo file header C có thể biên dịch được từ thông tin ELF DWARFỞ đây LLM có vẻ không liên quan mấy. File header hoặc là chứa đúng mọi kiểu mà file thực thi xuất ra kèm giá trị gốc và có thể dùng được, hoặc là sai hay chưa đầy đủ. Để LLM bịa thêm gì đó cũng không giúp ích
Ghidra cũng có sẵn chức năng xuất cấu trúc dữ liệu, và có thể tạo từ cấu trúc DWARF. Nhấp chuột phải -> Export to C header
Việc tạo header và sau đó tạo mutator có thể chỉnh sửa theo ràng buộc kiểu cũng là một phần trong đó
Nếu thêm phần LLM vào, có vẻ có thể làm những việc như đặt tên cho các struct ẩn danh, nhưng tôi không chắc đó có phải ý hay không. Thú vị hơn có lẽ là thử để LLM diễn giải và tóm tắt bằng lời các ràng buộc kiểu đã biết cho mục đích tài liệu hóa
Lý do tôi chưa triển khai là vì đến giờ vẫn có thể xoay xở mà không cần. Hơn nữa nghe như một cái hố thỏ khá sâu, mà cái hố thỏ tôi đang ở trong hiện cũng đã đủ lớn rồi
Trông thật sự rất hay, và cũng liên quan đến một ý tưởng mod game mà tôi từng nghĩ tới. Loạt bài blog decompile Tenchu cũng rất hay
Công việc hiện tại của tôi chưa có chỗ dùng ngay, nhưng có vẻ là một công cụ mà nếu có trước đây thì hẳn đã cực kỳ hữu ích
Hy vọng sớm có thời gian hoặc cơ hội để thử nghiệm