- Sản phẩm & Dịch vụSản phẩm & Dịch vụ
- Giải phápGiải pháp
- Bảng giáBảng giá
- Công tyCông ty
- Tài liệuTài liệu
vi
vi
Trung Nguyen

Static binary injection là một kỹ thuật dùng để chèn những đoạn code từ ngoài vào trong một file thực thi để theo dõi hoặc thay đổi hành vi của chương trình trong quá trình chạy. Nếu là một kẻ tấn công, hắn có thể sử dụng kỹ thuật này để thực hiện việc lây nhiễm lâu dài, còn đối với người nghiên cứu bảo mật, đây sẽ là sự hỗ trợ tuyệt vời cho việc instrument các file thực thi.
Hiện tại các tool thực hiện việc này chủ yếu đều là inject những đoạn mã assembly vào file thực thi và cũng chỉ trên một số hệ điều hành hay kiến trúc máy tính nhất định. Việc inject mã assembly vào file sẽ mất rất nhiều thời gian nếu đoạn mã của ta phức tạp.
Bài viết này chỉ ra một kỹ thuật mới giúp ta có thể inject được một đoạn mã được viết bằng C/C++ vào 1 file thực thi trên 2 nền tảng MacOS và Linux.
File thực thi trên hệ điều hành MacOS, hay còn gọi là MachO, thường sẽ có các phần như sau:

Load-command là một đoạn dữ liệu trên file dùng để cung cấp thông tin cho hệ điều hành khi nó load file thực thi lên. Từng load-commands sẽ có từng vai trò, nhiệm vụ khác nhau. Ví dụ:

LC_SEGMENT là một kiểu load-command mà nhiệm vụ của nó là giúp OS load trực tiếp các nội dung từ file lên RAM, dựa vào các thông tin như là:

Bài toán được đặt ra là:
input:
- target file: ta sẽ inject code vào file này
- inject file: ta tạo ra file này bằng cách code một đoạn mã C/C++ sau đó compile ra thư viện động
output: file chứa cả code cũ và code mới
Với target file, ta sẽ code một đoạn code đơn giản in ra màn hình, còn với inject file, ta sẽ code một đoạn code phức tạp vào gọi nhiều hàm từ hệ thống mà target file không gọi

Như các bạn đã biết thì code trong thư viện động đã là code có thể thực thi được, tức là tất cả các vị trí đều được đặt đúng chỗ, chỉ cần link với file thực thi và load lên RAM là xong, đây là công việc của một static linker. Vậy ta sẽ làm việc đó như sau:

Tiếp theo, ta sẽ cần xử lý với những con trỏ toàn cục và những hàm được gọi từ hệ thống, tức địa chỉ của nó không có trong file thực thi, mà sẽ được tìm và lấy khi chương trình chạy. Một số lưu ý khi làm việc này đó là đôi khi thứ tự của libSystem.dylib ( thư viện chính mà mọi file thực thi đều load lên ) trong thư viện mà ta biên dịch khác với trong file cần lây nhiễm. Ngoài ra một số opcode cũng như các đoạn jump code cần phải sửa đề phù hợp với context mới.
Như vậy điều ta cần làm là tận dụng dynamic linker xử lý những vấn đề đó.
dyld xử lý chỉ khi nó được gọi tớidyld xử lý trước khi file chạyTa sẽ đi vào một ví dụ để thấy rõ cách hoạt động của dyld
#include <stdio.h>
int main()
{
puts("Hello world!");
return 0;
}
Ban đầu, địa chỉ hàm puts không tồn tại trong file thực thi, và nó sẽ chỉ có tại lúc lần đầu tiên được gọi tới. Và thay vì địa chỉ thực đấy, puts sẽ lưu một con trỏ hàm trỏ vào một vùng jump code. Vùng jump code này sẽ setup môi trường và gọi dynamic linker sau đó:

Một số load-command khác sẽ liên quan đến quá trình này, đó là:
Vậy opcode là gì?
Opcode là một byte có chức năng điều khiển hoạt động của dynamic linker. Một opcode được cấu thành từ 8 bit với 4 bit đầu là định danh chức năng của hoạt động và 4 bit sau chứa giá trị truyền vào. Một số hoạt động cần có gía trị truyền vào lớn hơn 4 bit, khi đó chuỗi opcode sẽ được mã hoá bằng phương thức Uleb128.
Có 2 loại opcode:
Với ví dụ bên trên thì có 2 quá trình sẽ xảy ra:
Đầu tiên, khi file thực thi được chạy với ASLR, hệ điều hành sẽ trích xuất thông tin có được từ __LINKEDIT load-command, sau đó sẽ lấy được ra chuỗi opcode mà nó cần truyền cho dyld, ta sẽ có được chuỗi 11 22 10 51 00 00 00 00. Bạn đọc có thể tìm trên mạng bảng quy đổi opcode sang công việc mà nó cần làm. Ở đây, với byte đầu tiên (11) dyld sẽ đặt kiểu dữ liệu là 1, tức là kiểu con trỏ. Sau đó (22), dyld đi đến segment thứ 2, chính là phần segment của __DATA, rồi cộng thêm 10 là trỏ đúng vào entry của puts trên vùng __nl_symbol_pointer. Cuối cùng (55) sẽ thực hiện việc chỉnh lại địa chỉ.

Tương tự với rebase, hệ điều hành trích xuất thông tin có được cùng với gía trị mà ta đẩy vào stack trước khi gọi dyld, sau đó lấy được chuỗi opcode tương ứng là 72 10 11 40 5F 70 75 74 73 00 90 00 00 00 00 00. Đầu tiên (72 10), dyld đi tới ô địa chỉ của puts trên __nl_symbol_pointer. Sau đấy (11) nó sẽ lấy ra thư viện cần để tìm hàm đó ( libSystem.dylib sẽ có thứ tự là 1 trong file thực thi ). Tiếp theo (40 5F 70 75 74 73) là đọc tên hàm mà cần tìm , chính là puts. Cuối cùng là lấy địa chỉ thực vừa tìm được điền vào vị trí của puts trên __nl_symbol_pointer.

Quay trở lại bài toán của chúng ta:
Con trỏ toàn cục: ta sẽ sử dụng chính những thông tin và rebase opcode lấy được từ thư viện, sau đó chạy những công việc đó bằng tay với địa chỉ cơ sở bằng đúng địa chỉ của segment __RB__DATA. Di chuyển rebase opcode của file cần inject vào cuối vùng __LINKEDIT và nối vào đó rebase opcode lấy được từ thư viện. Như vậy các opcode cũ của file sẽ ko bị dịch chuyển, và khi file chạy với ASLR thì nó sẽ chạy tiếp với các con trỏ mới. Nhớ rằng phải sửa các opcode liên quan đến segment ( vì __RB__DATA ko còn là segment đầu tiên như trong thư viện nữa mà sẽ là segment cuối cùng ở file ouput ) cũng như các thông tin ở DYLD_INFO load-command
Hàm đươc gọi từ bên ngoài: tương tự với việc rebase, tuy nhiên ở đây ta cần phải sửa lại chỉ số tương ứng với libSystem.dylib khi chạy opcode, cũng như fix lại các thông tin về segment. vị trí của opcode khi đẩy vào stack và các thông tin trên __DYLD_INFO
Bạn đọc có thể nhìn mô tả dưới đây:

Sau khi hoàn thành xong các bước trên, ta sẽ có được file ta cần:

Tool sẽ được tác giả bài viết công bố trong thời gian sớm nhất.
Ở phần tiếp theo, chúng ta sẽ thực hiện việc inject trên file ELF của hệ điều hành Linux, mời các bạn đón đọc.

Reading Time: 17 minutesRead the English version Tóm tắt Bối cảnh Vào giữa tháng 01/2026, đội ngũ bảo mật của CyStack phát hiện […]

Reading Time: 7 minutesMở đầu Flash Loan Attack là một hình thức tấn công DeFi đã xuất hiện từ lâu, gây ra rất […]

Reading Time: 7 minutesRead the English version here Log4Shell hiện đang là một cơn ác mộng (có lẽ là tồi tệ nhất cho […]