Chương 1 Kiến thức nền tảng Chương đầu tiên sẽ khái quát kiến thức nền tảng xoay quanh đề tài nghiên cứu, các thuật ngữ, mô hình hỗ trợ. Đầu tiên nội dung chương tập trung vào việc cung cấp các kiến thức cơ bản liên quan đến phân tích mã nguồn, bao gồm các biểu diễn mã nguồn dưới dạng đồ thị như cây cú pháp trừu tượng, đồ thị luồng dữ liệu, đồ thị phụ thuộc hay đồ thị thuộc tính mã nguồn. Tiếp theo chương trình bày kiến thức cơ bản về lỗ hổng phần mềm và bản vá lỗ hổng phần mềm. Trong chương này, luận văn cũng trình bày tổng quan về các mô hình mạng nơ-ron đồ thị trong việc áp dụng với các đồ thị biểu diễn mã nguồn, mã hóa và phân tích các đồ thị.
Cuối cùng là điểm qua các nghiên cứu liên quan đến phát hiện bản vá khắc phục lỗ hổng hiện có, làm cơ sở để so sánh và phát triển phương pháp phát hiện bản vá khắc phục lỗ hổng được đề xuất trong luận văn.1 Phân tích mã nguồn Phân tích mã nguồn (Source code analysis) là quá trình đánh giá, kiểm tra và hiểu mã nguồn của một chương trình máy tính để tìm ra các lỗi, vấn đề bảo mật, sự cố tiềm năng và cải thiện chất lượng phần mềm. Nó bao gồm việc xem xét mã nguồn, tìm hiểu cách các thành phần của phần mềm tương tác với nhau và tìm ra cách nó hoạt động. Phân tích mã nguồn có thể được thực hiện thủ công bởi các nhà phát triển hoặc sử dụng các công cụ hỗ trợ tự động. 4 Một trong những công cụ phổ biến và mạnh mẽ nhất trong việc phân tích mã nguồn với việc tạo ra các đồ thị thuộc tính mã nguồn (Code Property Graph) là công cụ Joern.
CPG biểu diễn các thực thể và mối quan hệ giữa chúng trong mã nguồn, từ đó giúp dễ dàng khám phá và tìm kiếm thông tin cũng như phân tích sâu hơn về cấu trúc và tương tác giữa các thành phần của mã nguồn [18]. Trong việc phân tích chương trình, nhiều dạng biểu diễn khác nhau của mã nguồn đã được phát triển, trong đó CPG kết hợp ba biểu diễn chính là cây cú pháp trừu tượng (Abstract Syntax Tree), đồ thị luồng điều khiển (Control Flow Graph), và đồ thị phụ thuộc chương trình (Program Dependence Graph). AST là một biểu diễn trung gian quan trọng được tạo ra bởi các trình phân tích mã của trình biên dịch. Nó thể hiện tổ chức và sự kết hợp của các câu lệnh và biểu thức trong chương trình, trừu tượng hóa các cấu trúc quan trọng và ý nghĩa của mã nguồn.
CFG biểu diễn thứ tự thực thi của các câu lệnh và điều kiện trong chương trình. Nó mô tả cách các câu lệnh và điều kiện được thực thi theo một luồng thực thi cụ thể. Trong CFG, mỗi câu lệnh và điều kiện được biểu diễn bằng một nút, và các nút này được kết nối bởi các cạnh có hướng. Các cạnh này thể hiện sự chuyển giao của quyền điều khiển giữa các câu lệnh và điều kiện trong chương trình.
Với PDG, PDG là một đồ thị biểu diễn tất cả các câu lệnh và điều kiện trong chương trình mà ảnh hưởng đến giá trị của một biến tại một câu lệnh cụ thể. PDG giúp mô tả các phụ thuộc giữa các câu lệnh và điều kiện trong chương trình.1 Cây cú pháp trừu tượng Biểu diễn mã nguồn dựa trên cây cú pháp là một phương pháp phổ biến để phân tích và biểu diễn cú pháp của một ngôn ngữ lập trình. Trong biểu diễn này, cây cú pháp được sử dụng để mô phỏng cấu trúc cú pháp của mã nguồn dựa trên các quy tắc ngữ pháp của ngôn ngữ đó. Cây cú pháp cung cấp một cách trực quan để hiểu cấu trúc của mã nguồn.
Nó giúp phân tích mã nguồn, xác định lỗi cú pháp và hỗ trợ việc biên dịch hoặc dịch mã nguồn. Ngoài ra, cây cú pháp cũng là cơ sở cho nhiều công cụ phân tích mã nguồn, như biên dịch, kiểm tra lỗi, tạo mã tự động, đặc biệt cây cú pháp được áp dụng để biểu diễn sự thay đổi của mã nguồn. Giúp ích trong việc xây dựng dữ liệu về các phiên bản sửa đổi mã nguồn làm đầu vào để xác định, nhận diện lỗ hổng đã được sửa đổi.2 Đồ thị luồng điều khiển Đồ thị luồng điều khiển biểu diễn mã nguồn dưới dạng một đồ thị có hướng (di- rected graph), trong đó các nút của đồ thị đại diện cho các khối lệnh hoặc các điểm rẽ nhánh trong chương trình, và các cạnh thể hiện luồng điều khiển giữa các khối lệnh. Mỗi nút của đồ thị đại diện cho một khối lệnh trong chương trình, như một câu lệnh đơn, một khối if, một vòng lặp while, v.
Các cạnh của đồ thị biểu diễn các quan hệ điều khiển giữa các khối lệnh, chẳng hạn như điều kiện nhảy hoặc luồng thực thi của chương trình. Đồ thị luồng điều khiển được sử dụng trong nhiều ứng dụng khác nhau như phân tích chương trình, tối ưu hóa mã nguồn, kiểm thử và đảm bảo chất lượng phần mềm.3 Đồ thị phụ thuộc chương trình Đồ thị phụ thuộc chương trình biểu diễn mối quan hệ phụ thuộc giữa các phần của chương trình. Nó biểu diễn sự phụ thuộc của các biến, câu lệnh và điều kiện trong chương trình. Mỗi nút của đồ thị đại diện cho một thực thể trong chương trình, ví dụ như một biến, một câu lệnh hoặc một điều kiện.
Các cạnh của đồ thị biểu diễn các mối quan hệ phụ thuộc giữa các thực thể, chẳng hạn như một biến được sử dụng trong một lệnh hoặc một điều kiện phụ thuộc vào một biến. PDG được sử dụng trong nhiều lĩnh vực, bao gồm phân tích mã nguồn, kiểm thử phần mềm, tối ưu hóa mã nguồn và phát hiện lỗi. Nó giúp nhà phát triển và kiểm thử hiểu rõ cấu trúc của chương trình và tìm ra các vấn đề tiềm ẩn hoặc các điểm yếu trong mã nguồn.4 Đồ thị thuộc tính mã nguồn Đồ thị thuộc tính mã nguồn kết hợp ba biểu diễn chính là cây cú pháp trừu tượng, đồ thị luồng điều khiển và đồ thị phụ thuộc chương trình trong CPG, các nút của đồ thị đại diện cho các thực thể trong mã nguồn như biến, lớp, phương thức, và các câu lệnh. Các cạnh của đồ thị biểu diễn mối quan hệ giữa các thực thể.
Mỗi nút và cạnh trong đồ thị CPG có thể được gắn kèm với các thuộc tính, biểu diễn thông tin chi tiết về các thực thể và mối quan hệ giữa chúng. Các thuộc tính này có thể bao gồm tên, kiểu dữ liệu, phạm vi, và các thông tin khác. CPG được sử dụng rộng rãi trong nhiều lĩnh vực như phân tích mã nguồn, kiểm thử phần mềm, phát hiện mã độc, và tìm kiếm mã nguồn. Nó 6 giúp nhà phân tích hiểu rõ cấu trúc và hành vi của mã nguồn, từ đó tìm ra các vấn đề tiềm ẩn, cải thiện chất lượng mã nguồn, và phát triển các công cụ hỗ trợ phân tích và kiểm thử.2 Lỗ hổng phần mềm Lỗ hổng phần mềm là các điểm yếu hoặc sai sót trong mã nguồn của một ứng dụng, hệ điều hành, hoặc dịch vụ mạng, có thể bị khai thác bởi các kẻ tấn công để gây hại cho hệ thống hoặc tổ chức.
Những lỗ hổng này có thể phát sinh từ nhiều nguyên nhân khác nhau, bao gồm lập trình thiếu cẩn thận, thiết kế không an toàn, hoặc quản lý tài nguyên không hiệu quả. Khi bị khai thác, lỗ hổng phần mềm có thể dẫn đến nhiều hậu quả nghiêm trọng như mất mát dữ liệu, đánh cắp thông tin cá nhân, gián đoạn dịch vụ, hoặc thậm chí chiếm quyền kiểm soát toàn bộ hệ thống. Ngày nay, khi phần mềm ngày càng trở nên phức tạp và được tích hợp vào mọi lĩnh vực của cuộc sống, từ các ứng dụng tài chính đến các hệ thống điều khiển trong công nghiệp, bảo mật phần mềm trở thành một mối quan tâm hàng đầu. Đặc biệt, trong bối cảnh các cuộc tấn công mạng ngày càng tinh vi và phổ biến, việc nhận diện và khắc phục các lỗ hổng phần mềm đóng vai trò then chốt trong việc bảo vệ tài nguyên và duy trì sự an toàn của hệ thống.
Lỗ hổng phần mềm có thể được phân loại thành nhiều loại khác nhau, mỗi loại có các đặc điểm và phương pháp khai thác riêng. Việc hiểu rõ các loại lỗ hổng này không chỉ giúp các nhà phát triển và chuyên gia bảo mật nhanh chóng xác định và sửa chữa các vấn đề trong mã nguồn, mà còn giúp họ thiết kế các hệ thống phòng thủ hiệu quả hơn để ngăn chặn các cuộc tấn công trong tương lai. Một trong số loại lỗ hổng phổ biến và nguy hiểm nhất trong phát triển phần mềm có thể kể đến lỗ hổng Tràn bộ đệm (Buffer Overflow), loại lỗ hổng này gặp phải khi một chương trình cố gắng ghi nhiều dữ liệu hơn so với kích thước được cấp phát cho một vùng nhớ. 7 1 char * lccopy ( const char * str ) { 2 char buf [ BUFSIZE ]; 3 char * p ; 4 5 strcpy ( buf , str ) ; 6 for ( p = buf ; * p ; p ++) { 7 if ( isupper (* p ) ) { 8 * p = tolower (* p ) ; 9 } 10 } 11 return strdup ( buf ) ; 12 } Đoạn mã 1.1: Ví dụ mã nguồn C/C++ chứa lỗ hổng Tràn bộ nhớ đệm Đoạn mã 1.1 mô tả ví dụ của lỗ hổng Tràn bộ nhớ đệm trong mã nguồn ngôn ngữ C/C++ được báo cáo bởi OWASP [12], nơi mà mã nguồn phụ thuộc vào các thuộc tính của dữ liệu không được xác minh ngay tại chỗ.
Trong ví dụ này, một hàm tên là lccopy() nhận vào một chuỗi ký tự và trả về một bản sao được cấp phát trên heap của chuỗi đó, trong đó tất cả các chữ cái in hoa đã được chuyển đổi thành chữ thường. Hàm này không kiểm tra kích thước của đầu vào vì nó giả định rằng chuỗi str luôn nhỏ hơn BUFSIZE. Loại lỗ hổng về Tấn công từ chối dịch vụ (Denial of Service) cũng nằm trong danh sách những loại lỗ hổng gây nguy hiểm trong phát triển phần mềm, loại lỗ hổng này tấn công nhằm khiến cho một hệ thống hoặc dịch vụ không thể truy cập hoặc phục vụ người dùng hợp pháp bằng cách làm tràn ngập hệ thống với các yêu cầu hoặc khai thác các lỗi phần mềm để làm cho hệ thống ngừng hoạt động.