Tìm hiểu bài báo | ROSA - Phát hiện Backdoors với Fuzzing

Các thuật ngữ và công cụ

Trước khi đi vào tìm hiểu bài báo, có một vài thuật ngữ chúng ta cần phải nắm qua để tiếp thu được hướng tiếp cận và cơ chế hoạt động của ROSA.

Định nghĩa về các thuật ngữ dưới đây sẽ được dịch lại từ phần II. Background trong bài báo và bổ sung các nội dung khác.

Code-Level Backdoors

Trước hết về định nghĩa của backdoor, một backdoor - “cửa sau” trong một lâu đài kiên cố là một lối vào không được bảo vệ nhưng được che giấu một cách tinh vi. Nó cho phép những ai biết về sự tồn tại của nó có thể né tránh toàn bộ hàng rào bảo vệ và đi vào lâu đài mà không tốn bất kỳ công sức nào. Với nghĩa đó, backdoor trong hệ thống máy tính là một cơ chế ẩn được tạo ra bởi các nhà phát triển, có thể cấp các quyền hạn không chính đáng cho những người dùng biết về nó.

Đã có nhiều trường hợp về backdoor được phát hiện và báo cáo trong nhiều hệ thống máy tính khác nhau, như:

  • Backdoor phần cứng: Là một mạch điện tử độc hại được thiết kế có chủ đích và bí mật, được tích hợp vào một thiết bị phần cứng trong quá trình sản xuất (ví dụ, bộ vi xử lý - CPU). Cho phép kẻ tấn công có thể dễ dàng vượt qua các biện pháp bảo mật bằng phần mềm.
  • Backdoor toán học: Là một lỗ hổng được thiết kế có chủ ý ngay trong chính thuật toán mã hóa. Trong đó, những tham số toán học bí mật được sử dụng để tạo ra một lỗ hổng logic, phá vỡ tính an toàn của dữ liệu đã được mã hóa.
  • Backdoor bằng data poisoning: Không trực tiếp thay đổi kiến trúc của các mô hình học máy, mà thay đổi dữ liệu huấn luyện một cách có chủ đích. Khi được huấn luyện trên tập dữ liệu đã bị “đầu độc” này, mô hình sẽ học được một quy tắc ẩn nào đó. Sau khi đã được huấn luyện, backdoor sẽ tồn tại một cách “vô hình”.

Trong bài nghiên cứu này, đội ngũ nhắm đến các backdoor tại cấp độ mã trong các chương trình phần mềm cổ điển, ví dụ như các ứng dụng phía máy chủ hoặc công cụ dòng lệnh. Là những tính năng ẩn, được lập trình và che giấu trong mã nguồn của chương trình. Ví dụ cụ thể về code-level backdoor có thể là: một bộ thông tin đăng nhập được hard-coded trong chức năng xác thực của chương trình web, hay một lệnh FTP bí mật hoặc không chuẩn, cung cấp giao thức điều khiển từ xa đến hệ điều hành đang chạy với quyền root.

Metamorphic Oracle

Trong bối cảnh kiểm thử phần mềm, oracle (testing oracle) là một cơ chế hoặc nguồn thông tin được sử dụng để xác định xem kết quả đầu ra của một chương trình có đúng như mong đợi hay không. Nó trả lời câu hỏi: “Khi chương trình Y = F(X) nhận đầu vào X, kết quả đầu ra Y có đúng không?”. Các loại oracle phổ biến bao gồm:

  • Oracle dựa trên đặc tả: So sánh đầu ra của chương trình với các yêu cầu hoặc đặc tả thiết kế đã được định nghĩa từ trước.
  • Oracle dựa trên phiên bản cũ: So sánh đầu ra của phiên bản chương trình mới với đầu ra của phiên bản cũ, đã được kiểm chứng.
  • Oracle dựa trên sự tương đồng: So sánh đầu ra của chương trình đang kiểm thử với một chương trình tương tự khác.

Metamorphic oracle (oracle biến đổi) là một dạng oracle đặc biệt, được sử dụng khi xác định kết quả đầu ra chính xác là rất khó khăn hoặc bất khả thi. Thay vì so sánh trực tiếp đầu ra với một kết quả đúng đã biết, metamorphic oracle dựa trên các tính chất biến đổi (metamorphic properties) của chương trình, đó là mối quan hệ giữa các đầu vào của chương trình. Cụ thể, nếu bạn thay đổi đầu vào theo một cách nhất định, thì đầu ra cũng sẽ thay đổi theo một cách có thể dự đoán được. So với testing oracle, thì metamorphic oracle trả lời câu hỏi “Khi chương trình Y = F(X) nhận đầu vào X và ta biến đối nó thành X', liệu đầu ra YY' có tuân theo mối quan hệ đã biết giữa XX' hay không?”.

Giả sử ta cần kiểm thử một chương trình máy chủ web có chức năng đăng nhập login(X), đầu vào X là một cặp usernamepassword.

  • Chức năng mong đợi: Hàm này sẽ trả về một Y = session token (phiên đăng nhập) nếu thông tin đăng nhập là chính xác, và Y = null nếu ngược lại.
  • Backdoor: Một lập trình viên nào đó chèn một đoạn mã ẩn: nếu nhập passwordsecret, hàm sẽ luôn trả về một session token hợp lệ, bất kể username có tồn tại trong cơ sở dữ liệu hay không.

Việc kiểm tra từng cặp username, password là không khả thi. Thay vào đó, ta sẽ dựa vào tính chất biến đổi của chức năng đăng nhập: “Nếu một cặp X = (username, password) hợp lệ, thì việc thay đổi tên người dùng thành một chuỗi ký tự bất kỳ không tồn tại X' = (random, password) sẽ khiến hàm login() trả về null thay vì một session token”.

Ví dụ:

  • X = ("testuser", "testpass"), ta không cần biết hàm trả về Y là gì.
  • X' = ("kQpwVmZNcxls_testuser", "testpass"), hàm trả về Y' = null, điều này là đúng bởi ta biết chắc rằng username này không tồn tại.

Có hai trường hợp xảy ra:

  1. Y = Y' = null, không có gì bất thường.
  2. X hợp lệ, Y = session token, Y' = null, đây là tính chất biến đổi mong đợi.

Bây giờ, ta thử X của backdoor, X = ("admin","secret"), tất nhiên đầu ra Y phải là một session token. Với X' = ("KsAmwpVmzPqlwr_admin", "secret"), ta mong đợi Y' = null tuân theo tính chất biến đổi như đã xác định ở trên. Nhưng kết quả thực tế Y' là một session token hợp lệ bởi đoạn mã ẩn chỉ kiểm tra duy nhất password.

Theo quy tắc, nhiều nhất một trong hai test mới được phép trả về session token hợp lệ. Sự bất thường này chính là dấu hiệu của code-level backdoor.

Graybox và metamorphic fuzzing

Graybox Fuzzing

Theo Wikipedia, trong lập trình và phát triển phần mềm, fuzzing (hay còn được gọi là fuzz testing) là một kỹ thuật kiểm thử tự động, hoạt động bằng cách input một lượng lớn dữ liệu có thể là ngẫu nhiên, không hợp lệ, hoặc bất ngờ vào một chương trình máy tính. Sau đó, chương trình được giám sát để phát hiện ra những lỗi, thất bại hoặc lỗ hổng bảo mật, ví dụ như crash hay treo chương trình, lỗi bộ nhớ (có thể là buffer overflow, use-after-free), hoặc thực hiện hành vi không mong muốn.

Blackbox fuzzing không có bất kỳ kiến thức nào về mã nguồn bên trong của chương trình. Nó chỉ đơn giản là gửi đi các đầu vào ngẫu nhiên và quan sát xem chương trình có bị lỗi hay không. Phương pháp này rất nhanh và dễ thực hiện, nhưng hiệu quả thấp vì nó khó có thể khám phá được những đường đi phức tạp trong mã. Ngược lại, Whitebox fuzzing đòi hỏi phải có quyền truy cập vào mã nguồn. Nó sử dụng các kỹ thuật phân tích mã chuyên sâu để tạo ra các đầu vào tối ưu, nhằm đảm bảo bao phủ mã nguồn tối đa. Mặc dù hiệu quả cao trong việc tìm lỗi, phương pháp này thường rất chậm và tốn kém về mặt tài nguyên.

Graybox fuzzing là phương pháp nằm ở giữa, được xem là sweet spot giữa tốc độ của blackbox và hiệu quả của whitebox. Nó không cần truy cập đầy đủ mã nguồn nhưng vẫn tận dụng được một số thông tin nội bộ của chương trình, điển hình là độ bao phủ mã (code coverage) trong thời gian chạy. Phương pháp chạy fuzzer và theo dõi những phần mã nào đã được thực thi. Sau đó, nó sẽ sử dụng các phương pháp heuristic để tự động điều chỉnh các đầu vào tiếp theo, với mục tiêu khám phá những đường đi mã mới mà trước đó chưa được chạm tới.

AFL++

AFL++ là một fuzzer graybox rất nổi tiếng, hoạt động hiệu quả nhờ vào một cơ chế thông minh để tạo ra và lựa chọn đầu vào.

Cốt lõi của AFL++ là một vòng lặp biến đổi (mutation loop). Nó bắt đầu với một vài đầu vào ban đầu do người dùng cung cấp. Trong mỗi vòng lặp, AFL++ sẽ thực hiện các thay đổi nhỏ, ngẫu nhiên lên các đầu vào hiện có để tạo ra những đầu vào mới. Khi một đầu vào mới được đưa vào chương trình, AFL++ sẽ theo dõi những phần mã nào được thực thi. Nếu đầu vào đó giúp khám phá những phần mã chưa được chạm tới, nó sẽ được lưu lại làm đầu vào hạt giống (seed) cho các vòng lặp sau. Bằng cách này, AFL++ liên tục hướng quá trình fuzzing của nó đến những khu vực “bí ẩn” của chương trình, tối đa hóa độ bao phủ mã (code coverage) mà không cần phải phân tích quá sâu.

Để thu thập dữ liệu về độ bao phủ, AFL++ sử dụng kỹ thuật phân tích công cụ (instrumentation). Nó chèn thêm mã vào chương trình trong quá trình biên dịch để theo dõi các cạnh (edges) trong biểu đồ luồng điều khiển (Control-Flow Graph - CFG) của chương trình (CFG giống như một bản đồ biểu diễn tất cả các đường đi thực thi khả thi trong mã nguồn). Mỗi lần luồng thực thi đi qua một cạnh mới, nó sẽ được ghi lại, từ đó giúp AFL++ có một “bản đồ” chi tiết về những phần mã đã được kiểm tra.

Tuy nhiên, khi chỉ có tệp nhị phân, việc này trở nên vô cùng khó khăn. Để xử lý các chương trình không có mã nguồn, AFL++ sử dụng một chế độ đặc biệt gọi là binary fuzzing, chạy chương trình bên trong môi trường giả lập mã nguồn mở QEMU. QEMU thực thi từng lệnh của chương trình và đồng thời ghi lại các địa chỉ bộ nhớ mà luồng thực thi đi qua. Bằng cách theo dõi các lệnh nhảy (jumps) giữa các khối mã, trình giả lập có thể ước tính một cách hiệu quả các cạnh trong CFG của chương trình . Mặc dù phương pháp này chậm hơn so với việc chèn mã vào lúc biên dịch, nó vẫn cho phép AFL++ thu thập dữ liệu độ bao phủ cần thiết để hướng dẫn quá trình fuzzing.

Vấn đề

Nhìn chung, các phương pháp trước đây chỉ có thể xử lý được chương trình và phát hiện backdoor trong một phạm vi có giới hạn. Chúng thường đòi hỏi kỹ thuật dịch ngược thủ công (manual reverse-engineering) tệp nhị phân của chương trình cần kiểm tra. Đây là một quá trình tốn thời gian và công sức, khiến việc kiểm thử thường rất khó khăn hoặc không được thực hiện. Mặc dù đã có một số tiến bộ trong việc tự động hóa phát hiện backdoor nhưng không thực sự đáng kể trong hơn bảy năm qua về phạm vi và mức độ tự động hóa.

Bên cạnh đó là sự hạn chế về dữ liệu, không có bộ dữ liệu backdoor sẵn có nào để sử dụng trong việc đánh giá (benchmark) các công cụ phát hiện backdoor. Các bài báo về chủ đề này khoảng 7-11 năm trước đây sử dụng các bộ dữ liệu nhỏ khác nhau, nhiều mẫu đã bị mất hoặc vẫn có các binary firmware nhưng không được ghi chép lại và chỉ có thể chạy trên các thiết bị lỗi thời hoặc trên các thiết bị khó có thể tái tạo lại.

Hơn nữa, các graybox fuzzer truyền thống chỉ dựa vào các test oracles đơn giản như phát hiện crash chương trình (crash detector) và các công cụ làm sạch mã (code sanitizers - chèn thêm mã vào chương trình trong quá trình biên dịch giúp giám sát những hành vi không an toàn của chương trình) để tự động phát hiện lỗi trong thời gian chạy. Thông thường, các backdoor được lập trình tốt sẽ không gây ra lỗi khi được kích hoạt bởi các giá trị đầu vào của chúng. Vì vậy, tuy fuzzing cho phép việc tạo ra các đầu vào kiểm thử cho một số lượng lớn các chương trình khác nhau, nó không cung cấp bất kỳ phương tiện nào giúp phát hiện các backdoor được kích hoạt trong khi chương trình đang chạy.

Đóng góp của nhóm tác giả

ROSA là một phương pháp tiếp cận mới trong việc phát hiện backdoor, và các tác giả của bài báo đã thực hiện ba đóng góp chính để giúp giải quyết vấn đề này.

Công cụ kiểm tra biến hình mới

Đầu tiên, họ đã phát triển một công cụ kiểm tra biến hình (metamorphic oracle) mới, cho phép lần đầu tiên sử dụng fuzzing graybox để tìm kiếm các backdoor ở cấp độ mã. Công cụ này hoạt động dựa trên một mối quan hệ biến hình mới và một phương pháp heuristic để tự động tìm ra các cặp đầu vào có liên quan.

ROSA

Tiếp theo, họ giới thiệu ROSA, một phương pháp và công cụ phát hiện backdoor hiệu quả. Bằng cách kết hợp fuzzer hiện đại AFL++ với công cụ kiểm tra biến hình mới của họ, ROSA đã cải thiện đáng kể khả năng phát hiện nhiều loại backdoor hơn trong nhiều chương trình hơn. Quan trọng hơn, nó đã loại bỏ được yêu cầu đảo ngược mã nhị phân một cách thủ công mà các phương pháp hiện tại vẫn cần.

ROSARUM

Cuối cùng, các tác giả đã cung cấp một bộ dữ liệu có tên ROSARUM. Đây là bộ tiêu chuẩn công khai đầu tiên dùng để đánh giá các công cụ phát hiện backdoor và cũng là tập dữ liệu backdoor lớn nhất từng được sử dụng. Việc công khai bộ dữ liệu này không chỉ giúp đánh giá công bằng các công cụ mà còn thúc đẩy nghiên cứu trong lĩnh vực này.

Cách tiếp cận

ROSA là một phương pháp tiếp cận (và công cụ) mới, được thiết kế để giải quyết những thách thức lớn mà các phương pháp và công cụ fuzzing trước đó trong việc phát hiện backdoor. ROSA tiến hành bằng cách kết hợp một fuzzer hiện đại (state-of-the-art fuzzer) AFL++ với metamorphic test oracle, cung cấp khả năng phát hiện các điều kiện kích hoạt code-level backdoors trong thời gian chạy (runtime).

Ý tưởng

Ý tưởng cốt lõi của ROSA, đó là các giá trị đầu vào của một chương trình có thể được phân loại thành các “họ đầu vào” (Input Families). Mỗi họ là một tập hợp các giá trị đầu vào được coi là tương tự dựa vào đặc tả của chương trình, dẫn đến việc gần giống nhau trong luồng thực thi cũng như các tác động đến môi trường của chương trình. Nếu hai giá trị đầu vào cùng thuộc một họ được đưa vào chương trình mà dẫn đến hai đầu ra với những khác biệt đáng kể, thì mối quan hệ biến đổi bị vi phạm, và đây có thể được coi là dấu hiệu của backdoor.

Theo bài báo, tất cả các backdoor được thiết kế để thực hiện một hành vi độc hại nào đó, chẳng hạn như truy cập và chỉnh sửa trái phép tệp tin, thay đổi cài đặt hệ thống, đánh cắp dữ liệu, truy cập từ xa,… Tất cả những hành vi này đều không thể thực hiện trực tiếp mà suy đến cùng đều phải thông qua các lệnh gọi hệ thống (system calls) được cung cấp bởi hệ điều hành. Do đó, việc theo dõi các lệnh này là cách chính xác nhất để theo dõi dấu vết hoạt động của chương trình và phát hiện những hành vi bất thường ngay cả khi chúng được che giấu trong mã. ROSA không chỉ ghi lại toàn bộ chuỗi các lệnh gọi hệ thống trong runtime mà còn bao gồm cả các tham số được truyền vào, từ đó cung cấp ngữ cảnh tương tác của chương trình với mô trường bên ngoài. Vậy cụ thể hơn, mục tiêu chính của ROSA là so sánh các chuỗi lệnh gọi hệ thống được tạo ra bởi hai đầu vào thuộc cùng một họ, với kỳ vọng là các chuỗi lệnh gần tương tự nhau.

Ví dụ

Bài báo đưa một ví dụ cụ thể minh họa cách một kẻ tấn công có thể cấy một backdoor vào hàm verify_user công cụ dòng lệnh sudo của Unix:

int verify_user(const struct sudoers_context* ctx, const char* password){
	int ret = ctx->verify(password);
	// --- Beginning of backdoor ---
	if (strcmp(password, "let_me_in") == 0)
		{ ret = AUTH_SUCCESS; }
	// --- End of backdoor ---
	return ret;
}

Backdoor này sử dụng mật khẩu được hard-coded đó là “let_me_in”. Bất kể mật khẩu thực sự của người dùng là gì, nếu họ nhập “let_me_in”, backdoor sẽ lập tức trả về kết quả xác thực thành công AUTH_SUCCESS và cho phép quyền truy cập root vào hệ thống. Trong hai trường hợp sau: nếu mật khẩu không chính xác, sudo sẽ thực hiện các lệnh gọi hệ thống để in ra thông báo xác thực thất bại; ngược lại, sudo sẽ thực hiện các lệnh gọi hệ thống để tạo một tiến trình con và chạy các lệnh được kẻ tấn công cài đặt.

Cụ thể về hướng tiếp cận của ROSA tại ví dụ này:

  1. ROSA ghi lại các lệnh gọi hệ thống mà sudo thực hiện khi nhận một mật khẩu không chính xác bất kỳ.
  2. Fuzzer của ROSA sẽ tạo ra nhiều mật khẩu không chính xác khác nhau.
  3. Đối với mỗi mật khẩu được tạo ra, ROSA sẽ so sánh các lệnh gọi hệ thống của nó với chuỗi lệnh đã ghi lại ở bước 1.

Khi fuzzer tạo ra mật khẩu “let_me_in”, nó sẽ kích hoạt backdoor và thực thi một lệnh trong tiến trình con. Hành vi này tạo ra một chuỗi lệnh gọi hệ thống khác biệt đáng kể so với việc chỉ in ra thông báo lỗi. Khi phát hiện sự khác biệt này, ROSA báo cáo rằng mối quan hệ biến đổi đã bị vi phạm và đây có thể là một backdoor tiềm năng.

Xác định họ đầu vào

Với ví dụ về chương trình sudo như ở trên, đầu vào không chỉ là mật khẩu mà còn là bao gồm nhiều loại đầu vào khác nhau như người dùng gọi lệnh (impersonating users), người dùng thực sự có lệnh chạy dưới quyền (impersonated users), lệnh cần thực thi, các cờ và tùy chọn khác. Việc kiểm tra tất cả các tổ hợp của những đầu vào này một cách thủ công để xác định các họ đầu vào riêng biệt là bất khả thi bởi số lượng các trường hợp tăng theo cấp số nhân. Theo bài báo, đây được gọi là “bùng nổ tổ hợp” (combinatorial explosion).

Để giải quyết vấn đề này, ROSA không cần quan tâm bất kỳ kiến thức nào trước đó về các họ đầu vào của chương trình đang được kiểm tra, thay vào đó, nó dựa vào một phương pháp heuristic (các kỹ thuật dựa trên kinh nghiệm để giải quyết vấn đề, học hỏi, hay khám phá nhằm đưa ra giải pháp nhanh chóng, hiệu quả nhưng không đảm bảo tối ưu) để tự động tìm ra các cặp đầu vào có khả năng thuộc cùng một họ. Phương pháp này dựa trên biểu đồ luồng điều khiển CFG của chương trình.

Khái quát về phương pháp này như sau:

  • Khi fuzzer của ROSA tạo ra một đầu vào mới, nó sẽ theo dõi đường đi thực thi của chương trình.
  • ROSA sẽ so sánh đường đi này (đại diện bằng vectơ cạnh CFG) với các đường đi của các đầu vào đã được lưu trữ trước đó.
  • ROSA sẽ tìm ra đầu vào cũ có đường đi CFG gần giống nhất với đầu vào mới.

Cách tiếp cận này hiệu quả vì các đầu vào thuộc cùng một “họ” thường sẽ kích hoạt các đường đi thực thi gần giống nhau trong mã nguồn.

Triển khai cụ thể

dang viet

Hạn chế

Hạn chế lớn nhất của ROSA nằm ở việc phụ thuộc vào khả năng của fuzzer, vốn là một công cụ phân tích động mang tính “best-effort”. Khả năng phát hiện của ROSA bị giới hạn bởi các đầu vào mà fuzzer có thể tạo ra trong thời gian và tài nguyên cho phép. Những yếu tố sau có thể làm tăng khả năng bỏ sót backdoor, đó là không gian đầu vào lớn, định dạng đầu vào phức tạp, thời gian thực thi chương trình chậm, bị bảo vệ bởi các phép toán khó đảo ngược (chẳng hạn như băm - hashing, hoặc mã hóa - cryptography). Về bản chất ROSA đang thực hiện tấn công brute-force vào chương trình, nên các yếu tố trên có thể khiến cho phát hiện backdoor mất rất nhiều thời gian hoặc không khả thi.

Bên cạnh đó còn có những hạn chế về nền tảng và hiệu suất. Việc triển khai công cụ ROSA hiện tại chỉ giới hạn ở việc fuzzing có tệp nhị phân x86/x64 trên hệ điều hành Linux. Do đó, nó không cung cấp khả năng phân tích các chương trình trên các kiến trúc hoặc hệ điều hành khác. So sánh với công cụ phân tích tĩnh như STRINGER, ROSA chậm hơn đáng kể. Bài báo ghi nhận rằng STRINGER nhanh hơn ROSA trung bình 1,928 lần. Nguyên nhân là do ROSA dựa trên phân tích động (dynamic analysis) tốn nhiều thời gian, trong khi STRINGER chỉ thực hiện phân tích tĩnh (static analysis) đơn giản.

ROSA không phải là một giải pháp hoàn toàn tự động. Do việc xác định “họ đầu vào” chỉ dựa trên phương pháp heuristic, ROSA có thể tạo ra các báo cáo dương tính giả (false positive). Vì vậy mỗi báo cáo về backdoor tiềm năng phải được một chuyên gia thẩm định thủ công để xác nhận xem đó có thực sự là một hành vi độc hại hay không. Mặc dù quy trình này được bán tự động hóa, nó vẫn đòi hỏi nỗ lực thủ công, ước tính khoảng 2 phút cho mỗi đầu vào. Trung bình, một chuyên gia cần kiểm tra 7 đầu vào đáng ngờ cho mỗi lần chạy. Bài báo đã đưa một ví dụ thực tế: Với backdoor của ProFTPD, ROSA phát hiện ra đầu vào chứa lệnh HELP ACIDBITCHEZ. Chuyên gia chỉ cần chạy và thấy nó tạo ra một chuỗi các lời gọi hệ thống để mở một “root shell” (dấu hiệu chiếm quyền điều khiển cao nhất). Công việc này rất nhanh chóng và không yêu cầu phải đọc một dòng mã assembly nào của ProFTPD.

Cuối cùng, phương pháp này có thể không hiệu quả đối với các loại backdoor hoàn toàn khác biệt so với 17 backdoor đã có trong benchmark ROSARUM. Một kẻ tấn công có hiểu biết về cơ chế hoạt động của ROSA có thể thiết kế một backdoor tinh vi khác biệt so với bộ benchmark để lẩn tránh việc bị phát hiện bởi oracle dựa trên so sánh lời gọi hệ thống (system call).

Hướng nghiên cứu tiếp theo (draft)

  1. Cải tiến oracle: không chỉ so sánh chuỗi syscall, so sánh thêm tham số, kết quả trả về, vị trí tác động đến môi trường. Có thể áp dụng LLM.
  2. Mở rộng hỗ trợ các binaries thuộc các kiến trúc khác mà không thay đổi nhiều code.
  3. Kết hợp static analysis để tạo ra initla seed và hướng seed generation tốt hơn.
  4. Mở rộng benchmark.