[Tetcon 2015] Xây dựng thư viện dịch ngược “hoàn hảo” cho kiến trúc X86 – Nguyễn Anh Quỳnh
Tetcon 2015 vừa kết thúc, là một người tham gia hội thảo cá nhân tôi thấy tetcon 2015 là kỳ Tetcon thành công nhất từ trước tới nay. Hi vọng anh Dương Ngọc Thái và ban tổ chức sẽ tiếp tục tổ chức các hội thảo tương tự nhằm đẩy mạnh phong trào nghiên cứu về an toàn thông tin, cơ hội để các chuyên ...
Tetcon 2015 vừa kết thúc, là một người tham gia hội thảo cá nhân tôi thấy tetcon 2015 là kỳ Tetcon thành công nhất từ trước tới nay. Hi vọng anh Dương Ngọc Thái và ban tổ chức sẽ tiếp tục tổ chức các hội thảo tương tự nhằm đẩy mạnh phong trào nghiên cứu về an toàn thông tin, cơ hội để các chuyên gia chia sẻ những kết quả nghiên cứu, kinh nghiệm về an toàn thông tin. Nghe toàn bộ các bài trình bày tại Tetcon, tôi bị ấn tượng trước bài trình bày của anh Nguyễn Anh Quỳnh, một người có rất nhiều kinh nghiệm nghiên cứu về an toàn thông tin, lập trình hệ thống, một người mà tôi rất ngương mộ. Dưới đây tôi sẽ trình bày lại những gì mà tôi nghe trong hội thảo kết hợp với vốn hiểu biết của tôi, hi vọng sẽ chia sẻ được những kiến thức đến cho tất cả các bạn.
Anh Nguyễn Anh Quỳnh tại Tetcon 2015
Intel X86 là bộ vi xử lý có kiến trúc phức tạp về cả về mặt cấu trúc lẫn số lượng các lệnh, các tài liệu về tất cả các lệnh lại không thực sự hoàn hảo, có nhiều lệnh không có hướng dẫn chi tiết, thậm chí không có tài liệu mô tả. Đây là một số lý do chính tại sao mà tác giả Nguyễn Anh Quỳnh chia sẻ rằng X86 thực sự là “cơn ác mộng” khi anh phát triển nền tảng hỗ trợ dịch ngược trên X86.
Tác giả mong muốn xây dựng một công cụ hỗ trợ dịch ngược một các tốt nhất có thể “hoàn hảo” cho bộ xử lý X86 của Intel mà theo tác giả định nghĩa là một công cụ dịch đúng (dựa trên hoạt động thực tế của CPU) và dịch đủ (CPU hỗ trợ lệnh nào thì công cụ phải có khả năng dịch ngược những lệnh đó).
Trong bài trình bày, tác giả tập trung vào một số nội dung chính:
- Những khó khăn khi phát triển trên bộ xử lý X86 và các biện pháp giải quyết.
- Phương pháp xây dựng các mã hợp ngữ dựa trên hoạt động vật lý của CPU.
- Một số ví dụ chứng minh nền tảng tác giả xây dựng thực sự khác biệt với các công cụ và nền tảng hiện tại.
Vậy vấn đề khó khăn với bộ xử lý X86 của Intel là gì?
Trước tiên, Intel X86 là bộ vi xử lý có cấu trúc lệnh rất phức tạp.
Như trên hình vẽ chúng ta thấy, sự phức tạp của câu lệnh ở chỗ các thành phần được quy định một cách rất lỏng lẻo, ngoài một số thành phần bắt buộc, các thành phần khác đều có thể có hoặc không. Ngoài ra, với mỗi giá trị của từng cụm bit khác nhau thì các thành phần đằng sau lại có sự thay đổi khác nhau. Điều này gây ra những trở ngại rất lớn khi tiến hành dịch ngược.
Điểm thứ hai tạo ra sự khó khăn ở đây chính là vấn đề một số mã lệnh không có trong mô tả của các tài liệu của Intel.
Hình bên trên chúng ta có thể thấy mã D6 (Hàng D, cột số 6) hoàn toàn để trắng, điều này cũng đồng nghĩa với việc Intel hoàn toàn không hỗ trợ tài liệu cho mã lệnh này. Tuy nhiên, khi gặp các mã máy này, CPU vẫn có thể hoạt động bình thường, như vậy rõ ràng virus có thể lợi dụng những kẽ hở như vậy để có thể vượt qua các chương trình diệt virus.
Chính hai khó khăn này ảnh hưởng trực tiếp tới mong muốn của tác giả: dịch đúng và dịch đủ. Như vậy, làm sao để giải quyết hai vấn đề trên.
- Trước tiên, ARM là nền tảng dựa trên Intel và cũng cung cấp nhiều tài liệu về cấu trúc các tập lệnh. Do đó, việc sử dụng các tài liệu để đối sánh chéo giúp việc phân tích các câu lệnh chính xác hơn.
- Thứ hai, xây dựng phương pháp tự động giúp phát hiện và tìm các lệnh không có trong mô tả. Tác giả trình bày phương pháp phát hiện dựa trên hoạt động thực tế của CPU vật lý, thông qua việc quan sát ngữ cảnh của CPU lúc trước và sau khi thực hiện câu lệnh có thể kết luận về tác dụng của câu lệnh qua đó đưa ra hành vi và định nghĩa tương ứng của câu lệnh.
Tác giả chỉ ra sự nhầm lẫn của các Disasembler hiện tại là do họ không dựa vào trạng thái hoạt động vật lý của CPU để quyết định về hành vi của câu lệnh. Do đó dễ dàng đưa ra các câu lệnh dịch sai với các chuỗi dữ liệu không mong muốn. Ví dụ: Lệnh lặp (REPNE – F2, REPE – F3) là các lệnh chỉ được thực hiện khi xử lý với chuỗi nhưng với đoạn mã: F240 (40-47 là các lệnh tăng giá trị thanh ghi), rõ ràng sự kết hợp này là vô nghĩa nhưng CPU vẫn có thể chạy đúng.
Thông qua một số kiểm nghiệm thực tế, tác giả nhận định công cụ tác giả xây dựng đã hoàn toàn vượt trội so với các công cụ hiện tại trên bộ mã X86, bao gồm cả việc nâng cấp, cập nhật, độ chính xác và đầy đủ của tập lệnh. Công cụ tác giả xây dựng cho việc dịch ngược Intel X86 thực chất là một phần của Framework Engine hỗ trợ cho việc dịch ngược mà tác giả là người khởi xướng với tên Capstone.
SecurityDaily đánh giá bài nói chuyện của tác giả Nguyễn Anh Quỳnh thực sự là một trong những bài mang tính học thuật và tính nghiên cứu sâu nhất, mang lại nhiều thông tin kỹ thuật, hơn nữa cũng có rất nhiều ứng dụng trong thực tiễn, đặc biệt là trong việc phân tích mã độc.
Một số thông tin về Framework Capstone do tác giả Nguyễn Anh Quỳnh phát triển
Hai năm về trước, năm 2013, tác giả hiện nay của Capstone (http://capstone-engine.org/) khi đó mong muốn tìm được một nền tảng hỗ trợ dịch ngược với những yêu cầu rất đơn giản:
- Hỗ trợ hai nền tảng ARM và Intel
- Dễ dàng lập trình (Cần xem lại)
Tuy nhiên, tại thời điểm đó, tác giả không tìm thấy được một nền tảng nào thỏa mãn nhu cầu của mình. Đây chính là lý do ra đời Capstone. Capstone engine là một framework dịch ngược hỗ trợ đa ngôn ngữ, đa nền tảng, hội tụ đủ các tính năng giúp thỏa mãn nhu cầu của tác giả:
- Đa dạng kiến trúc: Capstone hiện đang hỗ trợ 8 loại kiến trúc: Arm, Arm64 (Armv8), Mips, PowerPC, Sparc, SystemZ, XCore & X86 (include X86_64)
- API hỗ trợ lập trình rất trong sáng, đơn giản và dễ sử dụng
- Cung cấp chi tiết về cấu trúc lệnh disasembly. Với các engine hỗ trợ disasembly thông thường, với một chuỗi nhị phân đầu vào, đầu ra mong muốn là các mã lệnh dạng hợp ngữ. Tuy nhiên, tác giả yêu cầu Capstone phải cung cấp hơn thế, cũng với chuỗi đầu vào tương tự nhưng Capstone cần cho ra những thông tin chi tiết, liên quan tới các toán tử, toán hạng, cách thức sử dụng, các tài nguyên mà nó tác động.
- Dễ dàng lập trình. Capstone được viết bằng ngôn ngữ C nhưng có thể hỗ trợ lập trình trên 9 loại ngôn ngữ (Hầu hết các ngôn ngữ lập trình phổ biến hiện nay: Python, Java, C++, C#, Ruby, NodeJS, OCalm, Vala và GO).
- Hỗ trợ trên Windows, Linux, MacOSX, IOS, Android. Thường xuyên cập nhật.
- Thiết kế mềm dẻo, tối ưu về hiệu năng, dung lượng, tài nguyên sử dụng và được phát hành dưới dạng cho phép phát triển các phiên bản thương mại mà không phải công bố mã nguồn (BSD).
Capstone được xây dựng dựa trên LLVM, trong đó, tác giả đã sử dụng lại Phần hỗ trợ dịch ngược và phần xuất kết quả. Những phần còn lại liên quan tới nhân, tác giả đã xây dựng lại cho tương thích với ngôn ngữ Native C. Ngoài ra, tác giả xây dựng một số phương thức Hook cho phép tương tác vào phần xuất các thông tin kết quả để đưa ra nhiều thông tin hơn.
Trong khoảng hơn một năm từ ngày bắt đầu hình thành dự án (Tháng 8 năm 2013), Capstone đã đưa ra phiên bản 3.0 và có một cộng đồng phát triển nhất định. Tác giả hy vọng sẽ tiến hành các nâng cấp với nhiều nền tảng hơn nữa và sẽ ra phiên bản 4.0 trong một vài năm tới.
Sắp tới có lẽ tôi sẽ cùng tham gia với anh Quỳnh, mục đích chính là để tăng khả năng cho bản thân, học hỏi các kinh nghiệm từ anh Quỳnh. Hi vọng các bạn sẽ cùng tham gia.