Cách hoạt động của phần mềm chống gian lận kernel
How kernel anti-cheats work
Không ngoa khi nói rằng các hệ thống chống gian lận kernel hiện đại nằm trong số những phần mềm tinh vi nhất chạy trên các máy Windows tiêu dùng. Chúng hoạt động ở cấp đặc quyền cao nhất dành cho phần mềm, chúng chặn các lệnh gọi lại kernel được thiết kế cho các sản phẩm bảo mật hợp pháp, chúng quét các cấu trúc bộ nhớ mà hầu hết các lập trình viên không bao giờ chạm tới trong toàn bộ sự nghiệp của mình và chúng thực hiện tất cả những điều này một cách minh bạch trong khi trò chơi đang chạy. Nếu bạn đã từng thắc mắc làm thế nào BattlEye thực sự bắt được một trò gian lận hoặc tại sao Vanguard nhất quyết tải trước khi Windows khởi động hoặc việc thiết bị PCIe DMA vượt qua từng biện pháp bảo vệ này có ý nghĩa gì, thì bài đăng này là dành cho bạn.
Các hệ thống chống gian lận hạt nhân hiện đại, không hề cường điệu, là một trong những phần mềm tinh vi nhất chạy trên các máy Windows dành cho người tiêu dùng. Chúng hoạt động ở cấp đặc quyền cao nhất dành cho phần mềm, chúng chặn các lệnh gọi lại kernel được thiết kế cho các sản phẩm bảo mật hợp pháp, chúng quét các cấu trúc bộ nhớ mà hầu hết các lập trình viên không bao giờ chạm tới trong toàn bộ sự nghiệp của mình và chúng thực hiện tất cả những điều này một cách minh bạch trong khi trò chơi đang chạy. Nếu bạn đã từng thắc mắc làm thế nào BattlEye thực sự phát hiện được một trò gian lận hoặc tại sao Vanguard nhất quyết tải trước khi Windows khởi động hoặc việc thiết bị PCIe DMA vượt qua từng biện pháp bảo vệ này có ý nghĩa gì, thì bài đăng này là dành cho bạn.
Đây không phải là tài liệu tham khảo toàn diện hoặc đáng tin cậy. Chỉ là tôi ghi lại những gì tôi tìm thấy và cố gắng giải thích nó một cách rõ ràng. Một số trong số đó đến từ nghiên cứu công cộng và các bài báo mà tôi đã liên kết ở phía dưới, một số từ việc đọc nguồn kernel và tự đảo ngược trình điều khiển. Nếu có điều gì không ổn, vui lòng liên hệ. Bài đăng này giả định bạn đã hiểu biết phần nào về nội bộ Windows và lập trình cấp thấp, nhưng tôi đã cố gắng giải thích từng khái niệm trước khi sử dụng.
1. Giới thiệu
Tại sao biện pháp bảo vệ bằng chế độ người dùng là không đủ
Vấn đề cơ bản với tính năng chống gian lận chỉ dành cho chế độ người dùng là mô hình tin cậy. Quá trình mã người dùng chạy ở vòng 3, phụ thuộc vào toàn quyền của kernel. Bất kỳ biện pháp bảo vệ nào được triển khai hoàn toàn trong chế độ người dùng đều có thể bị bỏ qua bởi bất kỳ thứ gì chạy ở cấp đặc quyền cao hơn và trong Windows, điều đó có nghĩa là đổ chuông 0 (trình điều khiển hạt nhân) trở xuống (trình ảo hóa, chương trình cơ sở). Trình chống gian lận ở chế độ người dùng gọi ReadProcessMemory để kiểm tra tính toàn vẹn của bộ nhớ trò chơi có thể bị đánh bại bởi trình điều khiển hạt nhân móc nối NtReadVirtualMemory và trả về dữ liệu giả mạo. Trình chống gian lận ở chế độ người dùng liệt kê các mô-đun đã tải thông qua EnumProcessModules có thể bị đánh bại bởi trình điều khiển vá danh sách mô-đun PEB. Quá trình mã hóa người dùng hoàn toàn không biết những gì xảy ra phía trên nó.
Các nhà phát triển gian lận đã hiểu rõ điều này từ nhiều năm trước khi hầu hết các kỹ sư chống gian lận sẵn sàng hành động theo nó. Kernel, trong một thời gian dài, là miền độc quyền của các trò gian lận. Các trò gian lận ở chế độ hạt nhân có thể thao túng trực tiếp bộ nhớ trò chơi mà không cần thông qua bất kỳ API nào mà tính năng chống gian lận ở chế độ người dùng có thể chặn. Họ có thể che giấu sự hiện diện của mình khỏi các API liệt kê chế độ người dùng một cách tầm thường. Họ có thể chặn và giả mạo kết quả của bất kỳ hoạt động kiểm tra nào mà tính năng chống gian lận ở chế độ người dùng có thể thực hiện.
Câu trả lời là không thể tránh khỏi: chuyển tính năng chống gian lận vào kernel.
Cuộc chạy đua vũ trang
Sự leo thang không ngừng nghỉ. Các mánh gian lận ở chế độ người dùng đã nhường chỗ cho các mánh gian lận trong kernel. Kernel chống gian lận đã xuất hiện để đáp lại. Các nhà phát triển gian lận bắt đầu khai thác các trình điều khiển hợp pháp, có chữ ký và có lỗ hổng bảo mật để thực thi kernel mà không cần tải trình điều khiển không dấu (cuộc tấn công BYOVD). Những kẻ chống gian lận đã phản ứng bằng danh sách chặn và liệt kê trình điều khiển chặt chẽ hơn. Các nhà phát triển gian lận đã chuyển sang phần mềm ảo hóa, chạy bên dưới kernel và ảo hóa toàn bộ hệ điều hành. Tính năng chống gian lận được thêm vào tính năng phát hiện hypervisor. Các nhà phát triển gian lận bắt đầu sử dụng thiết bị PCIe DMA để đọc bộ nhớ trò chơi trực tiếp thông qua phần cứng mà không cần chạm vào hệ điều hành. Biện pháp ứng phó với vấn đề đó vẫn đang được phát triển.
Mỗi lần leo thang đòi hỏi bên tấn công phải đầu tư thêm vốn và kiến thức chuyên môn. Điều này có tác dụng quan trọng: nó lọc ra những kẻ gian lận thông thường. Nhiều người có thể đăng ký gian lận kernel trị giá $30. Một thiết lập FPGA DMA tùy chỉnh có giá hàng trăm đô la và đòi hỏi kiến thức kỹ thuật quan trọng để định cấu hình. Cuộc chạy đua vũ trang, mặc dù gây khó chịu cho các kỹ sư chống gian lận, nhưng lại phục vụ mục tiêu thực tế là khiến việc gian lận trở nên tốn kém và khó khăn đến mức hầu hết những kẻ gian lận không bận tâm.
Các hệ thống chống gian lận chính
Bốn hệ thống thống trị bối cảnh trò chơi cạnh tranh:
BattlEye được sử dụng bởi PUBG, Rainbow Six Siege, DayZ, Arma và hàng chục tựa game khác. Thành phần hạt nhân của nó là BEDaisy.sys và là chủ đề của công việc kỹ thuật đảo ngược chi tiết được công khai, đáng chú ý nhất là bởi các nhà nghiên cứu của secret.club và blog back.engineering.
EasyAntiCheat (EAC) hiện thuộc sở hữu của Epic Games và được sử dụng trong Fortnite, Apex Legends, Rust và nhiều sản phẩm khác. Kiến trúc của nó nhìn chung giống với BattlEye ở thiết kế ba thành phần nhưng khác biệt đáng kể về chi tiết triển khai.
Vanguard là tính năng chống gian lận độc quyền của Riot Games được sử dụng trong Valorant và Liên minh huyền thoại. Đáng chú ý là nó tải thành phần hạt nhân (vgk.sys) khi khởi động hệ thống thay vì khi khởi chạy trò chơi và vì quan điểm tích cực của nó đối với danh sách cho phép trình điều khiển.
FACEIT AC được sử dụng cho nền tảng cạnh tranh FACEIT cho Counter-Strike. Đây là một hệ thống cấp hạt nhân có danh tiếng được đánh giá cao trong cộng đồng cạnh tranh về khả năng phát hiện gian lận hiệu quả và là chủ đề phân tích học thuật nhằm kiểm tra các đặc tính kiến trúc của phần mềm chống gian lận hạt nhân ở phạm vi rộng hơn.
Bài viết ARES 2024 về Kiến trúc chống gian lận hạt nhân
Bài báo năm 2024 “Nếu nó trông giống như một Rootkit và lừa dối như một Rootkit” (được trình bày tại ARES 2024) đã phân tích FACEIT AC và Vanguard qua lăng kính phân loại rootkit, lưu ý rằng cả hai hệ thống đều có chung các đặc điểm kỹ thuật với loại phần mềm đó: hoạt động cấp hạt nhân, đăng ký gọi lại trên toàn hệ thống và khả năng hiển thị rộng rãi về hoạt động của hệ điều hành. Các tác giả cẩn thận phân biệt giữa phân loại kỹ thuật và mục đích, thừa nhận rõ ràng rằng các hệ thống này là phần mềm hợp pháp phục vụ mục đích phòng thủ. Đóng góp của bài viết chủ yếu mang tính phân loại chứ không phải buộc tội.
Quan sát cơ bản chỉ đơn giản là việc chống gian lận hạt nhân hiệu quả cần có cùng các phiên bản hệ điều hành nguyên thủy mà phần mềm hạt nhân độc hại sử dụng, bởi vì những phiên bản nguyên thủy đó là thứ cung cấp khả năng hiển thị cần thiết để phát hiện các mã gian lận. Bất kỳ tính năng chống gian lận hạt nhân nào có đủ khả năng sẽ trông giống như một rootkit trong phân tích hành vi tĩnh, bởi vì khả năng và mục đích là trực giao ở cấp độ API hạt nhân. Đây là hạn chế do kiến trúc Windows áp đặt, không phải là lựa chọn thiết kế dành riêng cho bất kỳ nhà cung cấp dịch vụ chống gian lận cụ thể nào.
2. Kiến trúc của Kernel Anti-Cheat
Mô hình ba thành phần
Các chương trình chống gian lận hạt nhân hiện đại thường tuân theo kiến trúc ba lớp:
Trình điều khiển hạt nhân: Chạy ở vòng 0. Đăng ký lệnh gọi lại, chặn lệnh gọi hệ thống, quét bộ nhớ, thực thi các biện pháp bảo vệ. Đây là thành phần thực sự có khả năng thực hiện bất kỳ điều gì có ý nghĩa.
Dịch vụ chế độ người dùng: Chạy như một dịch vụ Windows, thường có các đặc quyền
SYSTEM. Giao tiếp với trình điều khiển kernel thông qua IOCTL. Xử lý giao tiếp mạng với các máy chủ phụ trợ, quản lý việc thực thi lệnh cấm, thu thập và truyền dữ liệu đo từ xa.DLL được đưa vào trò chơi: Được đưa vào (hoặc được tải bởi) quá trình trò chơi. Thực hiện kiểm tra phía chế độ người dùng, liên lạc với dịch vụ và đóng vai trò là điểm cuối cho các biện pháp bảo vệ được áp dụng cụ thể cho quy trình trò chơi.
Việc tách biệt các mối quan tâm ở đây vừa mang tính kiến trúc vừa thúc đẩy bảo mật. Trình điều khiển hạt nhân có thể thực hiện những việc mà không thành phần mã người dùng nào có thể làm được, nhưng nó không thể dễ dàng tạo kết nối mạng hoặc triển khai logic ứng dụng phức tạp. Dịch vụ có thể thực hiện những việc đó nhưng không thể chặn trực tiếp các cuộc gọi hệ thống. DLL trong trò chơi có quyền truy cập trực tiếp vào trạng thái trò chơi nhưng chạy trong ngữ cảnh vòng 3 không đáng tin cậy.
Kênh liên lạc
IOCTLs (Mã điều khiển I/O) là cơ chế giao tiếp chính giữa mã người dùng và trình điều khiển hạt nhân. Quy trình usermode sẽ mở một bộ điều khiển cho đối tượng thiết bị của trình điều khiển và gọi DeviceIoControl bằng mã điều khiển. Trình điều khiển xử lý việc này trong quy trình gửi IRP_MJ_DEVICE_Control của nó. Toàn bộ hoạt động giao tiếp được trung gian bởi hạt nhân, nghĩa là thành phần mã người dùng bị xâm phạm không thể giả mạo các hoạt động hạt nhân tùy ý - nó chỉ có thể đưa ra các yêu cầu mà trình điều khiển đã được lập trình để phục vụ.
Các đường dẫn có tên được sử dụng cho IPC giữa dịch vụ và DLL được đưa vào trò chơi. Đường dẫn được đặt tên nhanh hơn và đơn giản hơn việc định tuyến mọi thứ thông qua nhân và cho phép dịch vụ đẩy thông báo đến thành phần trò chơi mà không cần thăm dò ý kiến.
Các phần bộ nhớ dùng chung được tạo bằng NtCreateSection và được ánh xạ vào cả quy trình dịch vụ và quy trình trò chơi thông qua NtMapViewOfSection cho phép chia sẻ dữ liệu có băng thông cao, độ trễ thấp. Dữ liệu đo từ xa (sự kiện đầu vào, dữ liệu thời gian) có thể được DLL trò chơi ghi vào vùng đệm chung và được dịch vụ đọc mà không cần sử dụng IPC cho mỗi sự kiện.
Đang tải trình điều khiển thời gian khởi động và thời gian chạy
Sự khác biệt giữa việc tải trình điều khiển trong thời gian khởi động và thời gian chạy có ý nghĩa quan trọng hơn những gì bạn tưởng.
BattlEye và EAC tải trình điều khiển hạt nhân của họ khi trò chơi được khởi chạy. BEDaisy.sys và EAC tương đương của nó được đăng ký làm trình điều khiển khởi động theo yêu cầu và được tải qua ZwLoadDriver từ dịch vụ khi trò chơi bắt đầu. Chúng sẽ được tải xuống khi trò chơi thoát.
Vanguard tải vgk.sys khi khởi động hệ thống. Trình điều khiển được định cấu hình làm trình điều khiển khởi động khởi động (SERVICE_BOOT_START trong sổ đăng ký), nghĩa là nhân Windows tải nó trước khi hầu hết hệ thống khởi chạy. Điều này mang lại cho Vanguard một lợi thế quan trọng: nó có thể quan sát mọi trình điều khiển tải sau nó. Bất kỳ trình điều khiển nào tải sau vgk.sys đều có thể được kiểm tra trước khi mã của nó chạy một cách có ý nghĩa. Một trình điều khiển gian lận tải ở giai đoạn khởi tạo trình điều khiển thông thường đang tải vào một hệ thống mà Vanguard đã để mắt tới.
Ý nghĩa thực tế của việc tải trong thời gian khởi động cũng là lý do tại sao Vanguard yêu cầu khởi động lại hệ thống để kích hoạt: trình điều khiển phải có sẵn trước khi phần còn lại của hệ thống khởi chạy, điều đó có nghĩa là nó không thể được tải sau khi thực tế là không khởi động lại.
Yêu cầu ký trình điều khiển
Windows thực thi Thực thi chữ ký trình điều khiển (DSE) trên hệ thống 64 bit, yêu cầu trình điều khiển hạt nhân phải được ký bằng chứng chỉ liên kết với một thư mục gốc đáng tin cậy và tính toàn vẹn mã của trình điều khiển phải được xác minh vào thời điểm tải. Điều này được triển khai thông qua CiValidateImageHeader và các hàm liên quan trong ci.dll. Hạt nhân cũng buộc các chứng chỉ trình điều khiển phải đáp ứng một số yêu cầu nhất định về Sử dụng khóa mở rộng (EKU).
Các phần mềm chống gian lận xử lý việc đăng nhập theo cách rõ ràng: chúng trả tiền cho các chứng chỉ ký mã xác thực mở rộng (EV), trải qua quy trình WHQL của Microsoft đối với một số thành phần hoặc sử dụng ký chéo. Các yêu cầu về chứng chỉ đã được thắt chặt đáng kể trong những năm qua; Microsoft hiện yêu cầu chứng chỉ EV cho trình điều khiển hạt nhân mới và cổng ký trình điều khiển hạt nhân yêu cầu gửi WHQL đối với các trình điều khiển nhắm mục tiêu Windows 10 trở lên trong nhiều trường hợp.
Lý do khiến điều này trở nên quan trọng đối với các hành vi gian lận là vì DSE là một rào cản đáng kể. Nếu không có trình điều khiển đã được ký hoặc cách vượt qua DSE, kẻ giả mạo gian lận không thể tải mã hạt nhân tùy ý. Các cuộc tấn công BYOVD (được đề cập trong phần 7) là cơ chế chính để vượt qua hạn chế này.
Sự cố thành phần BattlEye
BattlEye's kiến trúc được ghi chép đầy đủ thông qua kỹ thuật đảo ngược:
BEDaisy.sys là trình điều khiển hạt nhân. Nó đăng ký các cuộc gọi lại để tạo quy trình, tạo luồng, tải hình ảnh và xử lý đối tượng. Nó triển khai logic quét và bảo vệ thực tế.
BEService.exe (hoặc BEService_x64.exe) là dịch vụ usermode. Nó giao tiếp với BEDaisy.sys thông qua một đối tượng thiết bị mà trình điều khiển hiển thị. Nó xử lý giao tiếp mạng với các máy chủ phụ trợ của BattlEye, nhận kết quả phát hiện từ trình điều khiển và chịu trách nhiệm thực thi lệnh cấm (đá người chơi khỏi máy chủ trò chơi).
BEClient_x64.dll được đưa vào quá trình trò chơi. BattlEye không đưa nội dung này qua CreateRemoteThread theo nghĩa truyền thống - nó được tải như một phần của quá trình khởi tạo trò chơi với sự hợp tác của trò chơi. DLL này chịu trách nhiệm thực hiện kiểm tra phía chế độ người dùng trong bối cảnh quy trình trò chơi: nó xác minh tính toàn vẹn của chính nó, thực hiện nhiều kiểm tra môi trường khác nhau và đóng vai trò là mục tiêu cho các biện pháp bảo vệ mà trình điều khiển hạt nhân áp dụng cụ thể cho quy trình trò chơi.
Luồng giao tiếp diễn ra: BEDaisy.sys phát hiện điều gì đó đáng ngờ, báo hiệu BEService.exe thông qua việc hoàn thành IOCTL hoặc thông báo bộ nhớ dùng chung, BEService.exe báo cáo cho máy chủ của BattlEye, máy chủ quyết định một hành động (kick/ban) và BEService.exe hướng dẫn trò chơi chấm dứt kết nối.
Cấu trúc ba thành phần của BattlEye: BEDaisy.sys tại vòng 0 giao tiếp lên trên thông qua IOCTL tới BEService.exe chạy dưới dạng dịch vụ HỆ THỐNG, quản lý BEClient_x64.dll được đưa vào quá trình trò chơi.
Kiến trúc của Vanguard
vgk.sys mạnh mẽ hơn đáng kể so với trình điều khiển BattlEye trong phạm vi của nó. Vì nó tải khi khởi động nên nó có thể chặn chính quá trình tải trình điều khiển. Vanguard duy trì danh sách nội bộ cho phép các trình điều khiển được phép cùng tồn tại với trò chơi được bảo vệ. Bất kỳ trình điều khiển nào không có trong danh sách này hoặc bất kỳ trình điều khiển nào không kiểm tra tính toàn vẹn đều có thể dẫn đến việc Vanguard từ chối cho phép trò chơi khởi chạy. Đây là mô hình danh sách cho phép chứ không phải mô hình danh sách chặn, có kiến trúc mạnh hơn nhiều.
vgauth.exe là dịch vụ Vanguard xử lý hoạt động liên lạc giữa vgk.sys và cơ sở hạ tầng phụ trợ của Riot.
3. Giám sát và gọi lại hạt nhân
Đây là nền tảng của mọi thứ mà tính năng chống gian lận hạt nhân thực hiện. Nhân Windows cung cấp một tập hợp API đăng ký gọi lại phong phú dành cho các sản phẩm bảo mật và các chương trình chống gian lận sử dụng từng API đó.
ObRegisterCallbacks
ObRegisterCallbacks có lẽ là API quan trọng nhất để bảo vệ quy trình. Nó cho phép trình điều khiển đăng ký một cuộc gọi lại được gọi bất cứ khi nào một điều khiển cho một loại đối tượng được chỉ định được mở hoặc sao chép. Vì mục đích chống gian lận, các loại đối tượng được quan tâm là PsProcessType và PsThreadType.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
OB_CALLBACK_REGISTration callbackReg = {0};
OB_OPERATION_REGISTATION opReg[ 2] = {0};
// Chuỗi độ cao là bắt buộc - phải là duy nhất cho mỗi người lái xe
UNICODE_STRING độ cao = RTL_CONSTANT_STRING(L"31001");
// Tay cầm màn hình mở để xử lý đối tượng
opReg[0].Loại đối tượng = PsProcessType;
opReg[0].Hoạt động = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg[0].PreOperation = ObPreOperationCallback;
opReg[ 0].PostOperation = ObPostOperationCallback;
// Tay cầm màn hình mở ra các đối tượng luồng
opReg[1].ObjectType = PsThreadType;
opReg[1].Hoạt động = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg[1].PreOperation = ObPreOperationCallback;
opReg[1].PostOperation = NULL;
callbackReg.Phiên bản = OB_FLT_REGISTATION_VERSION;
callbackReg.OperationRegistrationCount = 2;
callbackReg.Độ cao = độ cao;
callbackReg.RegistrationContext = NULL;
callbackReg .Đăng ký hoạt động = opReg;
NTSTATUS trạng thái = ObRegisterCallbacks(&callbackReg, &gCallbackHandle);
Lệnh gọi lại trước hoạt động nhận được cấu trúc POB_PRE_OPERATION_INFORMATION. Trường quan trọng là Parameters->CreateHandleInformation.DesiredAccess. Lệnh gọi lại có thể loại bỏ quyền truy cập khỏi quyền truy cập mong muốn bằng cách sửa đổi Parameters->CreateHandleInformation.DesiredAccess trước khi tạo mã điều khiển. Đây là cách tính năng chống gian lận ngăn chặn các quy trình bên ngoài mở tay cầm cho quy trình trò chơi bằng quyền truy cập PROCESS_VM_READ hoặc PROCESS_VM_WRITE.
Khi kẻ gian lận gọi OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, gameProcessId), của phần mềm chống gian lận ObRegisterCallbacks lệnh gọi lại trước hoạt động kích hoạt. Lệnh gọi lại kiểm tra xem quy trình đích có phải là quy trình trò chơi được bảo vệ hay không. Nếu đúng như vậy, nó sẽ loại bỏ PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_VM_OPERATION và PROCESS_DUP_HANDLE khỏi quyền truy cập mong muốn. Kẻ gian lận nhận được một tay cầm, nhưng tay cầm này vô dụng để đọc hoặc ghi bộ nhớ trò chơi. Lệnh gọi ReadProcessMemory của kẻ lừa đảo sẽ không thành công với ERROR_ACCESS_DENIED.
IRQL dành cho ObRegisterCallbacks trước hoạt động lệnh gọi lại là PASSIVE_LEVEL, có nghĩa là lệnh gọi lại có thể gọi mã có thể phân trang và thực hiện các thao tác chặn (trong phạm vi lý do).
ObCallbackDemo.sys đang hoạt động. DebugView hiển thị trình điều khiển tước quyền truy cập, Target.exe đang chạy với dữ liệu bí mật trong bộ nhớ và Verifier.exe không đọc được dữ liệu đó bằng Access Denied.
PsSetCreateProcessNotifyRoutineEx
PsSetCreateProcessNotifyRoutineEx cho phép trình điều khiển đăng ký lệnh gọi lại kích hoạt mọi sự kiện tạo và chấm dứt quy trình trên toàn hệ thống. Lệnh gọi lại nhận được PEPROCESS cho quy trình, PID và cấu trúc PPS_CREATE_NOTIFY_INFO chứa thông tin chi tiết về quy trình đang được tạo (tên hình ảnh, dòng lệnh, cấp độ gốc PID).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
VOID ProcessNotifyCallback(
QUY TRÌNH Quy trình,
XỬ LÝ ProcessId,
PPS_CREATE_NOTIFY_INFO Tạothông tin
)
{
if (CreateInfo == NULL) {
// Quá trình đang kết thúc
HandleProcessTermination(Quy trình, ProcessId);
trở lại;
// Quá trình đang được tạo
if (CreateInfo->Tên tệp hình ảnh != NULL) {
// Kiểm tra xem đây có phải là một quá trình gian lận hay không
if (IsKnownCheatProcess(CreateInfo->ImageFileName)) {
// Đặt trạng thái lỗi để ngăn quá trình khởi chạy
CreatInfo-> CreationStatus = STATUS_ACCESS_DENIED;
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, FALSE);
Đáng chú ý là Ex biến thể (được giới thiệu trong Windows Vista SP1) cung cấp tên tệp hình ảnh và dòng lệnh, trong khi PsSetCreateProcessNotifyRoutine ban đầu không có. Lệnh gọi lại được gọi ở PASSIVE_LEVEL từ ngữ cảnh chuỗi hệ thống.
Những kẻ chống gian lận sử dụng lệnh gọi lại này để phát hiện các quy trình của công cụ gian lận xuất hiện trên hệ thống. Nếu một trình khởi chạy hoặc trình tiêm gian lận đã biết được tạo ra trong khi trò chơi đang chạy, hệ thống chống gian lận có thể ngay lập tức gắn cờ điều này. Một số cách triển khai cũng đặt CreateInfo->CreationStatus thành mã lỗi để ngăn chặn hoàn toàn quá trình khởi chạy.
PsSetCreateThreadNotifyRoutine
PsSetCreateThreadNotifyRoutine kích hoạt mọi hoạt động tạo và chấm dứt chuỗi trên toàn hệ thống. Tính năng chống gian lận sử dụng nó đặc biệt để phát hiện việc tạo chuỗi trong quy trình trò chơi được bảo vệ. Khi một chuỗi mới được tạo trong quá trình trò chơi, lệnh gọi lại sẽ kích hoạt và tính năng chống gian lận có thể kiểm tra địa chỉ bắt đầu của chuỗi đó.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
VOID ThreadNotifyCallback(XỬ LÝ ProcessId, HANDLE ThreadId, BOOLEAN Tạo)
{
if (!Tạo) return;
if (IsProtectedProcess(ProcessId)) {
PETHREAD Chủ đề;
PsLookupThreadByThreadId(ThreadId, &Chủ đề);
// Lấy địa chỉ bắt đầu luồng - địa chỉ này được lưu trữ trong ETHREAD
PVOID StartAddress = PsGetThreadWin32StartAddress(Chủ đề);
// Kiểm tra xem địa chỉ bắt đầu có nằm trong mô-đun đã biết hay không
if (!IsAddressInKnownModule (Địa chỉ bắt đầu)) {
// Chuỗi bắt đầu tại một địa chỉ không có mô-đun hỗ trợ - đáng ngờ
FlagSuspiciousThread(Chủ đề, StartAddress);
ObDereferenceObject(Chủ đề);
Cuộc gọi tới PsLookupThreadByThreadId truy xuất con trỏ ETHREAD cho chuỗi mới. PsGetThreadWin32StartAddress trả về địa chỉ bắt đầu Win32 mà quy trình nhìn thấy, địa chỉ này khác với địa chỉ bắt đầu bên trong kernel. Sau khi hoàn tất với đối tượng luồng, ObDereferenceObject sẽ giải phóng tham chiếu mà PsLookupThreadByThreadId thu được.
Một luồng được tạo trong quá trình trò chơi có địa chỉ bắt đầu không nằm trong phạm vi địa chỉ của bất kỳ mô-đun đã tải nào là một chỉ báo mạnh mẽ về mã được tiêm. Các chủ đề hợp pháp bắt đầu bên trong mã mô-đun. Một luồng được chèn thường bắt đầu bằng shellcode hoặc mã PE được ánh xạ thủ công và không có mô-đun sao lưu.
PsSetLoadImageNotifyRoutine
PsSetLoadImageNotifyRoutine kích hoạt bất cứ khi nào một hình ảnh (DLL hoặc EXE) được ánh xạ vào bất kỳ quy trình nào. Nó cung cấp tên tệp hình ảnh và cấu trúc PIMAGE_INFO chứa địa chỉ và kích thước cơ sở.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
VOID LoadImageCallback(
PUNICODE_STRING FullImageName,
XỬ LÝ ProcessId,
PIMAGE_INFO Thông tin hình ảnh
)
{
if (IsProtectedProcess(ProcessId)) {
// Một DLL đã được tải vào tiến trình trò chơi được bảo vệ
Đây là IRQL PASSIVE_LEVEL. Lệnh gọi lại kích hoạt sau khi hình ảnh được ánh xạ nhưng trước khi điểm vào của nó thực thi, điều này mang lại cơ hội chống gian lận để quét hình ảnh trước khi bất kỳ mã nào của nó chạy.
CmRegisterCallbackEx
CmRegisterCallbackEx đăng ký lệnh gọi lại cho các hoạt động đăng ký. Hệ thống chống gian lận sử dụng tính năng này để theo dõi các sửa đổi sổ đăng ký có thể cho thấy các gian lận đang tự cấu hình hoặc cố gắng sửa đổi cài đặt chống gian lận.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NTSTATUS Đăng kýGọi lại(
PVOID CallbackContext,
PVOID Đối số 1 , // REG_NOTIFY_CLASS
PVOID Đối số2 // Dữ liệu dành riêng cho hoạt động
)
{
REG_NOTIFY_CLASS notifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Đối số1;
if (notifyClass == RegNtPreSetValueKey) {
PREG_SET_VALUE_KEY_INFORMATION thông tin =
(PREG_SET_VALUE_KEY_INFORMATION)Đối số 2;
// Kiểm tra xem có ai đang sửa đổi khóa đăng ký chống gian lận hay không
if (IsProtectedRegistryKey(thông tin ->Đối tượng)) {
trở lại STATUS_ACCESS_DENIED;
trở lại STATUS_SUCCESS;
Trình điều khiển MiniFilter để giám sát hệ thống tệp
Trình điều khiển bộ lọc mini nằm trong ngăn xếp bộ lọc hệ thống tệp và chặn các yêu cầu IRP đến và đi từ trình điều khiển hệ thống tệp. Hệ thống chống gian lận sử dụng các bộ lọc nhỏ để theo dõi việc đánh rơi tệp gian lận (ghi các tệp thực thi gian lận hoặc DLL đã biết vào đĩa), để phát hiện việc đọc các tệp trình điều khiển của chính chúng (có thể cho biết các nỗ lực vá tệp nhị phân của trình điều khiển trên đĩa trước khi nó được xác minh) và để thực thi các hạn chế truy cập tệp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FLT_PREOP_CALLBACK_STATUS PreOperationCallback(
PFLT_CALLBACK_DATA Dữ liệu,
PCFLT_RELATED_OBJECTS FltObject,
PVOID *CompletionContext
)
{
if (Dữ liệu->Iopb->Chức năng chính == IRP_MJ_WRITE) {
// Kiểm tra xem tệp mục tiêu có phải là tên tệp gian lận đã biết hay không
PFLT_FILE_NAME_INFORMATION nameInfo;
Thông tin FltGetFileName(Dữ liệu, FLT_FILE_NAME_NORMALIZED, &nameInfo);
nếu ( IsKnownCheatFileName(&nameInfo->Tên)) {
Dữ liệu->IoStatus.Trạng thái = STATUS_ACCESS_DENIED;
Thông tin FltReleaseFileName(nameInfo);
FltGetFileNameInformation truy xuất tên tệp đã chuẩn hóa cho mục tiêu của thao tác. FltReleaseFileNameInformation phải được gọi để giải phóng tham chiếu khi hoàn tất. Lệnh gọi lại Minifilter thường chạy ở APC_LEVEL hoặc PASSIVE_LEVEL, tùy thuộc vào hoạt động và hệ thống tệp. Điều này rất quan trọng vì nhiều thao tác (như phân bổ nhóm phân trang hoặc gọi các hàm có thể phân trang) không an toàn ở DISPATCH_LEVEL trở lên.
4. Quét và bảo vệ bộ nhớ
Trình điều khiển hạt nhân có thể làm được nhiều việc hơn là chỉ đăng ký lệnh gọi lại. Nó có thể chủ động quét bộ nhớ của quá trình trò chơi và nhóm bộ nhớ trên toàn hệ thống để tìm các tạo phẩm gian lận.
Chặn quyền truy cập của tay cầm
Như được đề cập trong ObRegisterCallback phần này, cơ chế chính để bảo vệ bộ nhớ trò chơi khỏi hoạt động đọc và ghi bên ngoài là loại bỏ PROCESS_VM_READ và PROCESS_VM_WRITE khỏi các thẻ điều khiển được mở trong quá trình trò chơi. Điều này có hiệu quả chống lại mọi hành vi gian lận sử dụng API Win32 tiêu chuẩn (ReadProcessMemory, WriteProcessMemory) vì những thứ này cuối cùng gọi NtReadVirtualMemory và NtWriteVirtualMemory, yêu cầu quyền truy cập xử lý thích hợp.
Tuy nhiên, gian lận ở chế độ kernel có thể vượt qua hoàn toàn điều này. Nó có thể gọi trực tiếp MmCopyVirtualMemory (một hàm kernel chưa xuất nhưng có thể định vị được) hoặc thao tác trực tiếp các mục trong bảng trang để truy cập bộ nhớ trò chơi mà không cần thông qua hệ thống kiểm soát truy cập dựa trên tay cầm. Đây là lý do tại sao chỉ bảo vệ tay cầm là không đủ và tại sao các hành vi gian lận cấp hạt nhân lại yêu cầu phản hồi chống gian lận cấp hạt nhân.
Băm toàn vẹn bộ nhớ định kỳ
Các phần mềm chống gian lận băm định kỳ các phần mã (.text phần) của trò chơi thực thi và các DLL cốt lõi của nó. Giá trị băm cơ sở được tính khi bắt đầu trò chơi và số lần băm lại định kỳ được so sánh với giá trị cơ sở. Nếu giá trị băm thay đổi thì có nghĩa là ai đó đã ghi vào mã trò chơi. Đây là dấu hiệu rõ ràng về việc vá mã (thường được sử dụng để kích hoạt chức năng không giật, tốc độ hoặc aimbot bằng cách vá logic trò chơi).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Mã giả để kiểm tra tính toàn vẹn của phần mã
BOOLEAN VerifyCodeSectionIntegrity(PEPROCESS Quy trình, PVOID ModuleBase)
{
// Đính kèm vào ngữ cảnh của tiến trình để đọc bộ nhớ của nó
KAPC_STATE apcState;
KeStackAttachProcess(Quy trình, &apcState); // Phân tích tiêu đề PE để tìm phần .text
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(ModuleBase);
PIMAGE_SECTION_HEADER phần = IMAGE_FIRST_SECTION(ntHeaders);
cho (USHORT i = 0; i < ntHeaders->FileHeader.Số phần; i++, phần) {
if (memcmp(phần->Tên, ".text", 5) == 0) {
PVOID sectionBase = (PVOID)((ULONG_PTR)ModuleBase + phần->Địa chỉ ảo);
ULONG Kích thước phần = phần->Khác.Kích thước ảo;
// Tính hàm băm của nội dung phần mã hiện tại
UCHAR currentHash[32];
ComputeSHA256(sectionBase, kích thước phần, currentHash );
// So sánh với hàm băm cơ sở được lưu trữ
if (memcmp(currentHash, gBaselineHash, 32) != 0) {
KeUnstackDetachProcess(&apcState); return FALSE; // Đã phát hiện thấy sửa đổi mã
KeUnstackDetachProcess(&apcState);
trở lại ĐÚNG;
KeStackAttachProcess / KeUnstackDetachProcess mẫu được sử dụng để tạm thời gắn luồng gọi vào không gian địa chỉ của quy trình đích, cho phép trình điều khiển đọc bộ nhớ được ánh xạ vào quy trình trò chơi mà không cần thông qua các điều khiển truy cập dựa trên tay cầm. RtlImageNtHeader phân tích cú pháp các tiêu đề PE từ cơ sở hình ảnh trong bộ nhớ.
Quét theo hành vi: Phát hiện mã được ánh xạ thủ công
Việc quét bộ nhớ thú vị nhất là phát hiện heuristic của mã được ánh xạ thủ công. Khi một DLL hợp lệ tải, nó sẽ xuất hiện trong danh sách mô-đun PEB của quy trình, trong InLoadOrderModuleList và có mục nhập VAD_NODE tương ứng với MemoryAreaType cho biết ánh xạ đến từ một tệp. Ánh xạ thủ công bỏ qua trình tải thông thường, do đó, mã được ánh xạ xuất hiện trong bộ nhớ dưới dạng ánh xạ riêng tư ẩn danh hoặc dưới dạng ánh xạ dựa trên tệp có đặc điểm đáng ngờ.
Phương pháp phỏng đoán chính là: tìm tất cả các vùng bộ nhớ thực thi trong quy trình, sau đó tham chiếu chéo từng vùng dựa trên danh sách các mô-đun đã tải. Bộ nhớ thực thi không tương ứng với bất kỳ mô-đun được tải nào là đáng ngờ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Đi theo cây VAD để tìm các ánh xạ ẩn danh có thể thực thi được
VOID ScanForManuallyMappedCode(PEPROCESS Quy trình)
ZwQueryVirtualMemory lặp qua các vùng bộ nhớ đã cam kết, trả về cấu trúc MEMORY_BASIC_INFORMATION cho từng vùng. Trường Loại phân biệt phân bổ riêng tư (MEM_PRIVATE) với ánh xạ dựa trên tệp (MEM_IMAGE, MEM_MAPPED). Phương pháp quét của BattlEye, như được ghi lại trong các phân tích của secret.club và back.engineering, bao gồm việc quét tất cả các vùng bộ nhớ của quy trình được bảo vệ và gắn cờ cụ thể cho các vùng thực thi mà không cần sao lưu tệp. Nó cũng quét các trang bộ nhớ của quy trình bên ngoài để tìm kiếm các điểm bất thường về bit thực thi, đặc biệt nhắm mục tiêu các trường hợp trong đó cờ bảo vệ trang đã được thay đổi theo chương trình để làm cho bộ nhớ không thể thực thi được (một kỹ thuật phổ biến khi shellcode được tổ chức).
VAD Tree Walking
Cây VAD (Bộ mô tả địa chỉ ảo) là cấu trúc bên trong kernel mà trình quản lý bộ nhớ sử dụng để theo dõi tất cả các vùng bộ nhớ được phân bổ trong một quy trình. Mỗi VAD_NODE (thực tế là cấu trúc MMVAD trong thuật ngữ kernel) chứa thông tin về khu vực: địa chỉ và kích thước cơ sở, khả năng bảo vệ của nó, liệu nó có được hỗ trợ bởi tệp hay không (và nếu có thì là tệp nào) và các cờ khác nhau.
Những kẻ chống gian lận đi trực tiếp vào cây VAD thay vì dựa vào ZwQueryVirtualMemory, vì cây VAD không thể bị ẩn khỏi chế độ kernel giống như cách mà danh sách mô-đun có thể được thao tác. Đi bộ trên VAD:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Trình hướng dẫn VAD được đơn giản hóa - mức bù trừ thực tế tùy theo từng phiên bản
VOID WalkVAD(PEPROCESS Quy trình)
{
// VadRoot ở mức chênh lệch dành riêng cho phiên bản trong EPROCESS
Chúng ta có thể quan sát phát hiện này trong thực tế bằng cách sử dụng của WinDbg !vad lệnh trên một quy trình có mã được chèn.
Mục đầu tiên là vùng EXECUTE_READWRITE Riêng tư không có tệp sao lưu, được đưa vào bởi công cụ kiểm tra của chúng tôi. Mọi mô-đun hợp pháp đều hiển thị dưới dạng Mapped Exe với đường dẫn tệp đầy đủ.
Sức mạnh của việc đi bộ VAD là nó bắt được mã được ánh xạ thủ công ngay cả khi kẻ gian lận đã thao túng danh sách mô-đun PEB hoặc chuỗi LDR_DATA_TABLE_ENTRY để ẩn chính nó. VAD là cấu trúc hạt nhân mà mã mã người dùng không thể sửa đổi trực tiếp.
5. Phát hiện chống tiêm
CreateRemoteThread Tiêm
Kỹ thuật chèn cổ điển: gọi CreateRemoteThread trong quy trình đích với LoadLibraryA làm địa chỉ bắt đầu luồng và đường dẫn DLL làm đối số. Điều này có thể được phát hiện một cách dễ dàng thông qua PsSetCreateThreadNotifyRoutine: địa chỉ bắt đầu của luồng mới sẽ là LoadLibraryA (hay đúng hơn là địa chỉ của nó trong kernel32.dll) và quá trình gọi không phải là chính trò chơi.
Một cách kiểm tra tinh tế hơn là CLIENT_ID của luồng tạo. Khi CreateRemoteThread được gọi, kernel sẽ ghi lại tiến trình đã tạo luồng. Tính năng chống gian lận có thể kiểm tra xem một luồng trong quy trình trò chơi có được tạo bởi một quy trình bên ngoài hay không. Đây là dấu hiệu đáng tin cậy của việc tiêm chích.
APC Tiêm
QueueUserAPC và NtQueueApcThread cơ bản cho phép xếp hàng Cuộc gọi thủ tục không đồng bộ tới một chuỗi trong bất kỳ quy trình nào mà người gọi có quyền truy cập THREAD_SET_CONTEXT. Khi luồng đích bước vào trạng thái chờ có thể cảnh báo, APC sẽ kích hoạt và thực thi mã tùy ý trong ngữ cảnh của luồng đích.
Tính năng phát hiện ở cấp hạt nhân tận dụng cấu trúc KAPC. Mỗi luồng có hàng đợi APC kernel và hàng đợi APC của người dùng. Tính năng chống gian lận có thể kiểm tra hàng đợi APC gồm các luồng xử lý trò chơi đang chờ xử lý để phát hiện các mục tiêu APC đáng ngờ:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Kiểm tra các APC đáng ngờ đang chờ xử lý trên một chuỗi
VOID InspectThreadAPCQueue(PETHREAD Chủ đề)
{
Tiêm dựa trên NtMapViewOfSection
Một kỹ thuật chèn tinh vi sẽ ánh xạ một đối tượng phần dùng chung (được hỗ trợ bởi một tệp hoặc được tạo bằng NtCreateSection) vào quy trình đích bằng cách sử dụng NtMapViewOfSection. Điều này bỏ qua các phương pháp chẩn đoán phát hiện dựa trên CreateRemoteThread vì ban đầu không có luồng từ xa nào được tạo. Sau đó, mã được chèn thường được kích hoạt thông qua APC hoặc bằng cách sửa đổi ngữ cảnh của chuỗi hiện có.
Việc phát hiện được thực hiện thông qua VAD: ánh xạ phần xuất hiện trong quy trình trò chơi nhưng được tạo bởi quy trình bên ngoài sẽ có mẫu riêng biệt trong VAD. Cụ thể, MMVAD::u.VadFlags.NoChange và các cờ liên quan, kết hợp với đối tượng tệp sao lưu phần đó (hoặc thiếu phần đó), có thể tiết lộ kỹ thuật này.
Tiêm DLL phản chiếu và ánh xạ thủ công
Nội dung DLL phản chiếu nhúng một trình tải phản chiếu bên trong DLL mà khi được thực thi sẽ ánh xạ DLL vào bộ nhớ mà không cần sử dụng LoadLibrary. DLL phân tích cú pháp các tiêu đề PE của chính nó, giải quyết việc nhập, áp dụng chuyển vị trí và gọi DllMain. Kết quả là một DLL đầy đủ chức năng trong bộ nhớ không bao giờ xuất hiện trong InLoadOrderModuleList.
Phát hiện: bộ nhớ thực thi có tiêu đề PE hợp lệ (kiểm tra các byte ma thuật MZ và chữ ký PE\0\0 ở phần bù được chỉ định bởi e_lfanew) nhưng không có mục nhập danh sách mô-đun tương ứng. Đây là một chỉ báo đáng tin cậy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Kiểm tra tiêu đề PE trong bộ nhớ riêng thực thi
BOOLEAN CóValidPEHeader(PVOID cơ sở, SIZE_T kích thước)
{
if (kích thước < sizeof(IMAGE_DOS_HEADER)) return FALSE;
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)cơ sở;
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) trở lại FALSE;
if (dosHeader->e_lfanew >= size - sizeof(IMAGE_NT_HEADERS)) return FALSE;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS )
((ULONG_PTR)base + dosHeader->e_lfanew);
if (ntHeaders->Chữ ký != IMAGE_NT_SIGNATURE) trở lại FALSE;
trở lại ĐÚNG;
Chúng ta có thể quan sát điều này trong thực tế bằng cách sử dụng một công cụ kiểm tra đơn giản phân bổ vùng RWX và ghi tiêu đề PE tối thiểu vào một quy trình đang chạy:
Dạo cây VAD bằng !vad sẽ hiển thị vùng được chèn ngay lập tức. Mục nhập đầu tiên tại 0x8A0 là vùng EXECUTE_READWRITE riêng tư không có tệp sao lưu. So sánh hình ảnh này với hình ảnh Target.exe hợp pháp ở dưới cùng, Đã ánh xạ Exe EXECUTE_WRITECOPY với đường dẫn tệp đầy đủ. Việc kết xuất cơ sở của mô-đun hợp pháp bằng db xác nhận tiêu đề PE hoàn chỉnh với sơ khai DOS:
Kết xuất vùng được chèn tại 0x008A0000 cũng hiển thị chữ ký MZ hợp lệ, nhưng phần còn lại của tiêu đề hầu hết là số 0 và không có phần gốc DOS. Đây là đặc điểm của mã được ánh xạ thủ công:
Cuối cùng, !peb xác nhận rằng vùng được chèn không xuất hiện trong bất kỳ danh sách mô-đun nào. PEB chỉ chứa Target.exe, ntdll.dll, kernel32.dll và KernelBase.dll. Vùng tại 0x008A0000 hoàn toàn ẩn đối với bất kỳ API chế độ người dùng nào liệt kê các mô-đun đã tải:
Stack Walking với RtlWalkFrameChain
Khi BEDaisy muốn kiểm tra ngăn xếp cuộc gọi của một chuỗi, nó sử dụng cơ chế APC để ghi lại các khung ngăn xếp trong khi chuỗi ở chế độ người dùng. APC kích hoạt trong ngữ cảnh của chuỗi trò chơi và gọi RtlWalkFrameChain hoặc RtlCaptureStackBackTrace để nắm bắt chuỗi địa chỉ trả lại.
Phân tích back.engineering của BEDaisy (và nghiên cứu Aki2k/BEDaisy GitHub) ghi lại điều này một cách cụ thể: BEDaisy xếp hàng các APC hạt nhân vào các luồng trong quy trình được bảo vệ. Quy trình nhân APC chạy ở APC_LEVEL, ghi lại ngăn xếp của luồng rồi phân tích từng địa chỉ trả về theo danh sách các mô-đun đã tải. Địa chỉ trả về trỏ ra bên ngoài bất kỳ mô-đun nào được tải là một chỉ báo rõ ràng về mã được chèn vào ngăn xếp, điều này cho thấy luồng hiện đang thực thi mã được chèn vào hoặc được trả về từ mã đó.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Mã giả để kiểm tra ngăn xếp dựa trên APC
VOID KernelApcRoutine(
PKAPC Apc,
PKNORMAL_ROUTINE *Quy trình thông thường,
PVOID *NormalContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
{
6. Phát hiện móc câu
Móc câu là cơ chế chính mà mã người dùng sử dụng để gian lận và thao túng sự tương tác của trò chơi với hệ điều hành. Phát hiện chúng là chức năng chống gian lận cốt lõi.
Phát hiện móc nối IAT
Bảng địa chỉ nhập (IAT) của tệp PE chứa địa chỉ của các hàm đã nhập. Khi một quá trình tải, trình tải sẽ giải quyết các địa chỉ này bằng cách tra cứu từng hàm đã nhập trong DLL xuất và ghi địa chỉ của hàm vào IAT. Móc IAT ghi đè một trong các mục nhập này bằng một con trỏ tới mã do kẻ tấn công kiểm soát.
Việc phát hiện rất đơn giản: đối với mỗi mục nhập IAT, hãy so sánh địa chỉ đã giải quyết với nội dung xuất trên đĩa của tệp DLL chính xác cho biết địa chỉ đó phải như thế nào.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
VÔ HIỆU DetectIATHooks(PVOID moduleBase)
{
PIMAGE_IMPORT_DESCRIPTOR importDesc =
RtlImageDirectoryEntryToData(moduleBase, TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT, NULL); while (importDesc->Tên != 0) {
PCHAR dllName = (PCHAR)((ULONG_PTR)moduleBase + importDesc->Tên);
PVOID importDllBase = GetLoadedModuleBase(dllName);
PULONG_PTR iat = (PULONG_PTR)((ULONG_PTR)moduleBase +
importDesc->FirstThunk);
RtlImageDirectoryEntryToData định vị thư mục nhập từ tiêu đề PE. Tham số TRUE chỉ định rằng hình ảnh được ánh xạ (trái ngược với tệp thô trên đĩa), điều này đúng khi làm việc với các mô-đun trong bộ nhớ. Vòng lặp bên ngoài đi qua mảng IMAGE_IMPORT_DESCRIPTOR, kết thúc ở trường Tên bằng 0. Vòng lặp bên trong so sánh từng mục nhập IAT đã giải quyết với địa chỉ xuất dự kiến.
Phát hiện móc nội tuyến
Móc nội tuyến vá một vài byte đầu tiên của hàm bằng JMP (opcode 0xE9 để nhảy gần tương đối hoặc 0xFF 0x25 để nhảy gián tiếp qua con trỏ bộ nhớ) để chuyển hướng thực thi sang mã kẻ tấn công. Mã này thường thực hiện các sửa đổi và sau đó quay trở lại mã ban đầu (mẫu "tấm bạt lò xo").
Việc phát hiện bao gồm việc đọc 16-32 byte đầu tiên của mỗi mã được giám sát chức năng và kiểm tra:
0xE9(JMP rel32)0xFF 0x25(JMP [rip+disp32]) - phổ biến cho móc 64-bit0x48 0xB8 ... 0xFF 0xE0(MOV RAX, imm64; JMP RAX) - một chuỗi nhảy 64-bit tuyệt đối0xCC(INT 3) - một điểm ngắt phần mềm, cũng có thể là một điểm móc nối
Phần mềm chống gian lận đọc tệp PE trên đĩa và so sánh các byte trên đĩa của phần mở đầu hàm với những gì hiện có trong bộ nhớ. Bất kỳ sự khác biệt nào đều cho thấy việc vá lỗi.
Để chứng minh khả năng phát hiện hook nội tuyến, chúng tôi sử dụng một công cụ kiểm tra để vá NtReadVirtualMemory trong một quy trình đang chạy bằng MOV RAX; JMP RAX hook:
Trước khi vá lỗi, phần mở đầu của hàm sẽ hiển thị sơ khai hệ thống rõ ràng. mov r10, rcx lưu đối số đầu tiên, mov eax, 3Fh tải số tòa nhà và syscall chuyển sang chế độ kernel:
Sau khi cài đặt hook, 12 byte đầu tiên được ghi đè bằng mov rax, 0xDEADBEEFCAFEBABE; jmp rax, chuyển hướng thực thi đến địa chỉ do kẻ tấn công kiểm soát. Một chương trình chống gian lận so sánh các byte này với bản sao trên đĩa của ntdll sẽ ngay lập tức gắn cờ sự không khớp:
Kiểm tra tính toàn vẹn của SSDT
Bảng mô tả dịch vụ hệ thống (SSDT) là bảng điều phối của kernel dành cho các tòa nhà cao tầng. Khi một quy trình mã người dùng thực thi một lệnh gọi hệ thống, hạt nhân sẽ sử dụng số hiệu hệ thống (được đặt trong EAX) để lập chỉ mục vào SSDT và gọi hàm hạt nhân tương ứng. Việc vá lỗi SSDT sẽ chuyển hướng các tòa nhà cao tầng tới mã do kẻ tấn công kiểm soát.
Việc nối SSDT là một kỹ thuật cổ điển đã trở nên khó khăn hơn đáng kể sau khi PatchGuard (Kernel Patch Protection, KPP) ra đời trong Windows 64-bit. PatchGuard giám sát SSDT (trong số nhiều cấu trúc khác) và kích hoạt kiểm tra lỗi CRITICAL_STRUCTURE_CORRUPTION (0x109) nếu phát hiện thấy sửa đổi. Kết quả là, việc kết nối SSDT về cơ bản đã không còn hoạt động trong Windows 64-bit. Tuy nhiên, hệ thống chống gian lận vẫn xác minh tính toàn vẹn của SSDT như một biện pháp bảo vệ chuyên sâu.
Giám sát IDT và GDT
Bộ mô tả ngắt Bảng (IDT) ánh xạ các vectơ ngắt tới các quy trình xử lý của chúng. Bảng mô tả toàn cầu (GDT) xác định các phân đoạn bộ nhớ. Cả hai đều là cấu trúc cấp bộ xử lý và không thể dễ dàng bảo vệ chỉ bằng PatchGuard trên tất cả các cấu hình.
Một mánh gian lận hoạt động ở cấp hạt nhân có thể cố gắng thay thế các mục nhập IDT để chặn các ngắt cụ thể, có thể được sử dụng để chặn luồng điều khiển hoặc làm kênh bí mật. Tính năng chống gian lận xác minh rằng các mục IDT trỏ đến vị trí kernel dự kiến:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
VOID Xác minhIDTIntegrity(void)
{
IDTR idtr;
__sidt(&idtr); // Đọc thanh ghi IDTR
PIDT_ENTRY64 idt = (PIDT_ENTRY64)idtr.Cơ sở;
for (int i = 0; i < 256; i++) { ULONG_PTR trình xử lý = GetIDTHandlerAddress(&idt[i]);
// Xác minh trình xử lý nằm trong ntoskrnl hoặc dải địa chỉ trình điều khiển đã biết
if (!IsKernelCodeAddress(trình xử lý)) {
Phát hiện việc sử dụng hệ thống trực tiếp bằng cách gian lận
Một kỹ thuật trốn tránh phổ biến là gian lận thực hiện trực tiếp các cuộc gọi tòa nhà (sử dụng lệnh syscall với số cuộc gọi tòa nhà thích hợp) thay vì thông qua các hàm ntdll.dll. Điều này bỏ qua các hook usermode được đặt trong ntdll. Hệ thống chống gian lận phát hiện điều này bằng cách giám sát các luồng trong quy trình trò chơi để thực thi lệnh syscall từ các vị trí mã không mong muốn và bằng cách kiểm tra xem các hàm ntdll đáng được gọi có thực sự được gọi với tần suất và mẫu dự kiến hay không.
7. Bảo vệ cấp trình điều khiển
Phát hiện trình điều khiển chưa được ký và đã được ký kiểm tra
Trên hệ thống Windows được cấu hình đúng cách đã bật Khởi động an toàn, tất cả các trình điều khiển hạt nhân phải được ký bằng chứng chỉ được Microsoft tin cậy. Chế độ ký kiểm tra (được bật bằng bcdedit /set testsigning on) cho phép tải trình điều khiển tự ký và là một kỹ thuật phát triển và triển khai gian lận phổ biến.
Các chương trình chống gian lận phát hiện chế độ ký kiểm tra bằng cách đọc cấu hình khởi động Windows và bằng cách kiểm tra biến kernel phản ánh xem DSE hiện có được thực thi hay không. Một số phần mềm chống gian lận từ chối khởi chạy nếu tính năng ký thử được bật.
Các chức năng SeValidateImageHeader và SeValidateImageData trong kernel xác thực chữ ký trình điều khiển. Tính năng chống gian lận có thể kiểm tra các đối tượng trình điều khiển đã tải và xác minh rằng các trường IMAGE_INFO_EX ImageSignatureType và ImageSignatureLevel của chúng phản ánh việc ký hợp lệ.
Tấn công BYOVD
Mang theo trình điều khiển dễ bị tổn thương của riêng bạn là kỹ thuật chiếm ưu thế để tải mã hạt nhân không dấu trong năm 2024-2026. Cuộc tấn công này hoạt động như sau:
- Kẻ tấn công tìm thấy một trình điều khiển hợp pháp, có chữ ký và có lỗ hổng (thường là trình xử lý IOCTL nguy hiểm cho phép đọc/ghi bộ nhớ hạt nhân tùy ý hoặc gọi
MmMapIoSpacedo kẻ tấn công kiểm soát thông số). - Kẻ tấn công tải trình điều khiển hợp pháp này (chuyển DSE vì nó có chữ ký hợp lệ).
- Kẻ tấn công khai thác lỗ hổng trong trình điều khiển hợp pháp để thực thi mã hạt nhân tùy ý.
- Bằng cách thực thi hạt nhân đó, kẻ tấn công sẽ vô hiệu hóa DSE hoặc trực tiếp ánh xạ trình điều khiển gian lận không dấu của chúng.
Các mục tiêu BYOVD phổ biến bao gồm các trình điều khiển từ MSI, Gigabyte, ASUS và nhiều nhà cung cấp phần cứng khác nhau. Những trình điều khiển này thường có trình xử lý IOCTL để lộ khả năng đọc/ghi trực tiếp vào bộ nhớ vật lý, đây là tất cả những gì kẻ tấn công cần.
Danh sách chặn trình điều khiển chống gian lận
The biện pháp bảo vệ chính chống lại BYOVD là danh sách chặn các trình điều khiển dễ bị tổn thương đã biết. Danh sách chặn trình điều khiển dễ bị tổn thương của Microsoft (được duy trì trong DriverSiPolicy.p7b) được tích hợp vào Windows và được phân phối qua Windows Update. Những kẻ chống gian lận duy trì danh sách chặn tích cực hơn của riêng họ.
Vanguard đặc biệt nổi tiếng với việc chủ động so sánh tập hợp trình điều khiển đã tải với danh sách chặn của nó và từ chối cho phép trò chơi được bảo vệ khởi chạy nếu có trình điều khiển trong danh sách chặn. Điều này được thực thi vì một số cuộc tấn công BYOVD liên quan đến việc tải trình điều khiển dễ bị tấn công và sử dụng ngay trước khi dỡ tải. Vì vậy, chỉ kiểm tra khi khởi chạy trò chơi bằng tính năng quét trước trong hầu hết các trường hợp.
PiDDBCache và PiDDBLock
Đây là một trong những phần nội bộ thú vị hơn mà các nhà phát triển gian lận kernel và kỹ sư chống gian lận đều quan tâm sâu sắc.
PiDDBCacheTable là một cây AVL nội bộ trong kernel giúp lưu trữ thông tin về các trình điều khiển đã tải trước đó. Khi trình điều khiển được tải, hạt nhân sẽ lưu trữ một mục nhập được khóa bởi TimeDateStamp của trình điều khiển (từ tiêu đề PE) và SizeOfImage. Bộ đệm này được sử dụng để tra cứu nhanh xem trình điều khiển đã được nhìn thấy trước đó hay chưa. Cấu trúc là một RTL_AVL_TABLE được bảo vệ bởi PiDDBLock (một khóa ERESOURCE).
Những kẻ phát triển gian lận ánh xạ trình điều khiển theo cách thủ công mà không đi qua đường dẫn tải thông thường sẽ cố gắng xóa hoặc sửa đổi mục nhập PiDDBCacheTable tương ứng để che giấu rằng trình điều khiển của họ đã từng được tải. Các chương trình chống gian lận phát hiện điều này bằng cách:
- Xác minh tính nhất quán của
PiDDBCacheTable- nếu một trình điều khiển nằm trong bộ nhớ (được tìm thấy thông qua quét thẻ nhóm hoặc các phương tiện khác) nhưng không có mục nhậpPiDDBCacheTablethì mục nhập đó có thể đã bị xóa. - Giám sát
PiDDBLockđể phát hiện các hoạt động mua lại không mong muốn từ các chuỗi không phải nhân. - So sánh các kết hợp dấu thời gian/kích thước của tất cả các trình điều khiển được tải đã biết dựa vào
Các mục nhập PiDDBCacheTable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Định vị PiDDBCacheTable (phải định vị thông qua quét chữ ký vì chưa xuất)
// Đây là phiên bản cụ thể và dễ hỏng; chống gian lận duy trì nhiều chữ ký
BOOLEAN FindPiDDBCacheTable(PVOID * Địa chỉ bảng)
{
// Mẫu định vị PiDDBCacheTable trong ntoskrnl
// Đây là minh họa đơn giản - việc triển khai thực tế sử dụng tính năng khớp mẫu mạnh mẽ
PVOID ntoskrnl = GetKernelModuleBase("ntoskrnl.exe");
PUCHAR mẫu = " \x48\x8D\x0D"; // LEA RCX, [RIP+...]
PVOID match = FindPattern(ntoskrnl, mẫu, 3, NTOSKRNL_TEXT_RANGE);
if (khớp) {
Đảo ngược PiCompareDDBCacheEntries
PiDDBCacheTable không được xuất và PiDDBCacheEntry không ở dạng ký hiệu công khai. Để tương tác với bộ đệm, chúng ta cần đảo ngược cấu trúc mục nhập. Quy trình so sánh là điểm khởi đầu tốt nhất vì nó truy cập trực tiếp vào các trường được sử dụng để đặt hàng.
Kết quả dịch ngược hiển thị bố cục cấu trúc. Hàm nhận hai con trỏ PiDDBCacheEntry và trước tiên so sánh các trường DriverName của chúng (một UNICODE_STRING ở độ lệch 0x10) bằng cách sử dụng RtlCompareUnicodeString. Nếu tên bằng nhau và TableContext khác 0 thì các mục được coi là bằng nhau. Nếu không, sẽ phải so sánh trường TimeDateStamp (một ULONG ở độ lệch 0x20). Điều này mang lại cho chúng ta cấu trúc được phục hồi:
1
2
3
4
5
6
cấu trúc PiDDBCacheEntry
{
RTL_BALANCED_LINKS Liên kết; // 0x00 - Con trỏ nút cây AVL (0x20 byte)
UNICODE_STRING Tên trình điều khiển; // 0x10 - tên tệp trình điều khiển (từ phần bù so sánh thông thường) ULONG TimeDateStamp; // 0x20 - Dấu thời gian của tiêu đề PE (khóa sắp xếp phụ)
};
Đi dạo trên cây AVL
PiDDBCacheTable là một RTL_AVL_TABLE , cây tìm kiếm nhị phân tự cân bằng. Mỗi nút trong cây bắt đầu bằng tiêu đề _RTL_BALANCED_LINKS chứa các con trỏ Parent, LeftChild và RightChild. Dữ liệu PiDDBCacheEntry thực tế nằm ngay sau tiêu đề này.
Để liệt kê các mục nhập, chúng tôi bắt đầu bằng cách phân giải địa chỉ bảng. PiDDBCacheTable không được xuất, vì vậy các chương trình chống gian lận sẽ xác định vị trí của nó thông qua chức năng quét chữ ký trong ntoskrnl.exe. Trong WinDbg với các ký hiệu được tải, chúng tôi có thể giải quyết trực tiếp:
Bảng này chứa 151 mục trình điều khiển được lưu trong bộ nhớ đệm với độ sâu cây là 9. CompareRoutine trỏ đến PiCompareDDBCacheEntries, xác nhận đây là bảng bên phải. BalancedRoot là điểm vào trong cây. RightChild của nó cung cấp cho chúng ta nút thực đầu tiên:
Từ mỗi nút, dữ liệu nhập bắt đầu ở độ lệch 0x20 (qua tiêu đề _RTL_BALANCED_LINKS). Thêm các giá trị bù đắp đã khôi phục của chúng tôi: DriverName tại nút+0x30, TimeDateStamp tại nút+0x40. Đi theo con trỏ LeftChild và RightChild cho phép chúng ta duyệt toàn bộ cây:
Kẻ phát triển gian lận ánh xạ trình điều khiển hạt nhân theo cách thủ công sẽ cố gắng tìm và xóa mục nhập của họ khỏi cây này để tránh bị phát hiện. Một tính năng chống gian lận phát hiện trình điều khiển trong bộ nhớ (thông qua quét thẻ nhóm hoặc các phương tiện khác) nhưng không tìm thấy mục nhập PiDDBCacheTable tương ứng nào cho biết mục nhập đó đã bị xóa.
MmUnloadedDrivers
MmUnloadedDrivers là một mảng nhân (cũng không được xuất) duy trì bộ đệm vòng của 50 trình điều khiển chưa được tải gần đây nhất, lưu trữ tên, địa chỉ bắt đầu, địa chỉ kết thúc và dấu thời gian dỡ tải. Cấu trúc này cho phép gỡ lỗi và điều tra hoạt động của trình điều khiển.
Các nhà phát triển gian lận tải thành công rồi dỡ bỏ trình điều khiển hạt nhân thường cố gắng loại bỏ hoặc làm hỏng mục nhập của họ trong MmUnloadedDrivers để ẩn dấu vết. Hệ thống chống gian lận phát hiện điều này bằng cách:
- Duy trì bản sao ẩn của các mục nhập
MmUnloadedDriversdự kiến. - Phát hiện các mục nhập bất thường không chứa đầy dữ liệu ở giữa vùng đệm tròn (dấu hiệu của việc cố ý xóa).
- Tham chiếu chéo
MmUnloadedDriversdựa trên các dấu thời gian và nhật ký hạt nhân khác.
Phân bổ BigPool
Khi phân bổ hạt nhân vượt quá khoảng 4KB (chính xác hơn là khi nó vượt quá ngưỡng do người cấp phát nhóm quản lý), nó được quản lý dưới dạng "phân bổ nhóm lớn" được theo dõi trong PoolBigPageTable. Tính năng chống gian lận quét bảng này để tìm các phân bổ bộ nhớ được thực hiện bởi các trình điều khiển được ánh xạ thủ công. Trình điều khiển được ánh xạ thủ công thường thực hiện phân bổ lớn cho các phần mã và dữ liệu của nó; những thứ này hiển thị trong bảng nhóm lớn với địa chỉ phân bổ nhưng không có trình điều khiển được tải tương ứng.
Kỹ thuật này là liệt kê tất cả các mục nhập nhóm lớn, sau đó tham chiếu chéo từng địa chỉ phân bổ với danh sách các dải địa chỉ trình điều khiển đã tải. Việc phân bổ không có phạm vi của trình điều khiển có kích thước phù hợp làm phần mã trình điều khiển là đáng ngờ.
8. Các biện pháp bảo vệ chống gỡ lỗi
Bản thân mã chống gian lận là mục tiêu có giá trị cao cho kỹ thuật đảo ngược. Các kỹ sư đảo ngược phân tích trình điều khiển chống gian lận cần sử dụng trình gỡ lỗi kernel, giúp các trình chống gian lận phát hiện mạnh mẽ.
NtQueryInformationProcess Checks
Ở cấp độ chế độ người dùng (trong DLL được đưa vào trò chơi), tính năng chống gian lận sử dụng NtQueryInformationProcess với nhiều lớp thông tin:
ProcessDebugPort(7): Trả về giá trị khác 0 nếu trình gỡ lỗi được đính kèm quaDebugActiveProcess. Trình điều khiển hạt nhân có thể giả mạo điều này bằng cách móc nốiNtQueryInformationProcess, nhưng việc kiểm tra cũng được thực hiện trong chính trình điều khiển hạt nhân.ProcessDebugObjectHandle(30): Trả về một mã điều khiển cho đối tượng gỡ lỗi nếu có.ProcessDebugFlags(31): CờNoDebugInherit; việc kiểm tra nghịch đảo của nó sẽ tiết lộ sự hiện diện của trình gỡ lỗi.
Phát hiện trình gỡ lỗi hạt nhân
Trình điều khiển hạt nhân kiểm tra các biến được xuất hạt nhân KdDebuggerEnabled và KdDebuggerNotPresent. Trên hệ thống có đính kèm WinDbg (hoặc bất kỳ trình gỡ lỗi kernel nào), KdDebuggerEnabled là TRUE và KdDebuggerNotPresent là FALSE.
1
2
3
4
5
6
7
8
9
10
11
12
BOOLEAN IsKernelDebuggerPresent(void)
{
// KD_DEBUGGER_ENABLED là xuất hạt nhân
nếu ( *KdDebuggerEnabled && !*KdDebuggerNotPresent) {
trở lại ĐÚNG;
// Kiểm tra bổ sung: thử tạm dừng gỡ lỗi và xem liệu nó có được xử lý hay không
// Phức tạp hơn: kiểm tra cấu trúc kernel cụ thể
trở lại FALSE;
Một số phần mềm chống gian lận còn tiến xa hơn và trực tiếp kiểm tra cấu trúc KDDEBUGGER_DATA64 và trang dữ liệu hạt nhân dùng chung (KUSER_SHARED_DATA) cho các cờ liên quan đến trình gỡ lỗi.
Phát hiện ẩn luồng
NtSetInformationThread với ThreadHideFromDebugger (17) đặt cờ trong cấu trúc ETHREAD của luồng (CrossThreadFlags.HideFromDebugger). Sau khi được thiết lập, kernel sẽ không gửi các sự kiện gỡ lỗi cho luồng đó tới bất kỳ trình gỡ lỗi đính kèm nào. Về cơ bản, luồng trở nên vô hình đối với WinDbg: các điểm ngắt trong luồng không kích hoạt thông báo trình gỡ lỗi, các ngoại lệ không được chuyển tiếp.
Những kẻ chống gian lận sử dụng điều này để bảo vệ các luồng của chính chúng. Tuy nhiên, họ cũng phát hiện xem kẻ gian lận có đang sử dụng nó để ẩn các luồng được chèn của chính họ hay không. Phương pháp phát hiện là liệt kê tất cả các luồng trong hệ thống thông qua bảng liệt kê kernel (không phải thông qua API usermode có thể được nối) và kiểm tra bit HideFromDebugger trong CrossThreadFlags cho mỗi luồng. Một sợi dây ẩn trong quá trình trò chơi mà bản thân hệ thống chống gian lận không che giấu được đó là cờ đỏ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Kiểm tra CrossThreadFlags để tìm HideFromDebugger
#define PS_CROSS_THREAD_FLAGS_HIDEFROMDEBUGGER 0x4
VOID CheckThreadDebugVisibility(PETHREAD Chủ đề) {
// CrossThreadFlags ở mức chênh lệch dành riêng cho phiên bản trong ETHREAD
ULONG crossFlags = *(ULONG*)((ULONG_PTR)Chủ đề + ETHREAD_CROSS_THREAD_FLAGS_OFFSET);
if (crossFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDEBUGGER) {
// Chủ đề bị ẩn khỏi trình gỡ lỗi
// Nếu chúng tôi không ẩn nó, hãy gắn cờ nó
if (!IsAntiCheatOwnedThread(Chủ đề)) {
ReportHiddenThread(Chủ đề);
Để chứng minh khả năng phát hiện này, chúng tôi sử dụng một công cụ kiểm tra để tạo một luồng từ xa trong Target.exe rồi đặt ThreadHideFromDebugger trên đó:
Trong WinDbg, chúng tôi chuyển đổi TID thập phân thành hex, định vị chuỗi trong quy trình và kiểm tra CrossThreadFlags của nó. Trước khi đặt cờ, giá trị là 0x5402 với bit 2 (HideFromDebugger) rõ ràng:
Sau khi gọi NtSetInformationThread bằng ThreadHideFromDebugger, giá trị thay đổi thành 0x5406. Bit 2 hiện đã được đặt, làm cho luồng này ẩn với bất kỳ trình gỡ lỗi đính kèm nào:
Một chuỗi liệt kê chống gian lận trong quá trình chơi game sẽ kiểm tra bit này trên mỗi chuỗi. Một chuỗi ẩn mà phần mềm chống gian lận không tự tạo ra là dấu hiệu rõ ràng cho thấy mã gian lận đã được tiêm vào.
Chống gỡ lỗi dựa trên thời gian
Gỡ lỗi một bước (thông qua cờ TF trong EFLAGS) và các điểm dừng phần cứng làm tăng đáng kể thời gian giữa các lần thực hiện lệnh. Hệ thống chống gian lận sử dụng RDTSC thời gian dựa trên hướng dẫn để phát hiện điều này:
1
2
3
4
5
6
7
8
9
10
11
UINT64 trước = __rdtsc();
// Thực hiện một số thao tác cố định
dễ bay hơi ULONG giả = 0;
cho ( int i = 0; i < 1000; i++) giả += i;
UINT64 sau = __rdtsc();
UINT64 đã trôi qua = sau - trước ;
if (đã trôi qua > EXPECTED_MAXIMUM_CYCLES) {
// Quá trình thực thi bị chậm lại - có thể là một bước hoặc một điểm ngắt
Báo cáoDebuggerDetected();
Ngưỡng EXPECTED_MAXIMUM_CYCLES được hiệu chỉnh dựa trên hoạt động đã biết của CPU. Một bước có thể thêm hàng nghìn chu kỳ cho mỗi lệnh (do xử lý ngoại lệ gỡ lỗi), làm cho sự khác biệt về thời gian trở nên rõ ràng.
Phát hiện điểm dừng phần cứng
Các thanh ghi gỡ lỗi x86-64 (DR0-DR3 cho địa chỉ điểm dừng, DR6 cho trạng thái, DR7 cho điều khiển) có thể truy cập được ở chế độ kernel. Việc đọc chúng cho phép phát hiện các điểm dừng phần cứng do trình gỡ lỗi đặt ra:
1
2
3
4
5
6
7
8
9
10
11
BOOLEAN Có điểm ngắt phần cứng(void)
{
ULONG_PTR dr7 = __readdr(7); // Đọc DR7 (thanh ghi kiểm soát gỡ lỗi)
// Kiểm tra các bit kích hoạt cục bộ (L0, L1, L2, L3) cho từng điểm dừng
// Các bit 0, 2, 4, 6 của DR7 là các bit kích hoạt cục bộ cho BP 0-3
if (dr7 & 0x55) { // 0x55 = 01010101b - tất cả bốn bit kích hoạt cục bộ
trở lại ĐÚNG;
trở lại FALSE;
Chống gian lận quét trạng thái đăng ký gỡ lỗi đã lưu của tất cả các luồng (có thể truy cập qua CONTEXT cấu trúc thu được bằng KeGetContextThread hoặc trực tiếp từ KTHREAD::TrapFrame) cho các điểm dừng phần cứng đang hoạt động không do chính chương trình chống gian lận đặt ra.
Phát hiện trình gỡ lỗi dựa trên Hypervisor
Trình gỡ lỗi dựa trên bộ ảo hóa loại 1 (như trình ảo hóa tùy chỉnh chạy máy ảo Windows để gỡ lỗi riêng biệt) khó phát hiện hơn đáng kể. Các vectơ phát hiện chính là:
Kiểm tra CPUID: Bit hiện tại của bộ điều khiển ảo hóa (bit 31 của ECX khi lá CPUID 1 được thực thi) cho biết có bộ điều khiển ảo hóa hiện diện. Nhà cung cấp bộ ảo hóa có thể được truy vấn bằng lá CPUID 0x40000000. VMware trả về “VMwareVMware”, VirtualBox trả về “VBoxVBoxVBox”. Chuỗi nhà cung cấp không xác định là đáng ngờ.
Thời gian MSR: Việc thực thi RDMSR trong VM gây ra chi phí bổ sung so với thực thi gốc. Thời gian chống gian lận MSR đọc và gắn cờ các điểm bất thường.
Thời gian hướng dẫn CPUID: Bản thân lệnh CPUID là một lệnh đặc quyền trong môi trường ảo hóa và phải được trình ảo hóa xử lý, gây ra độ trễ có thể đo lường được.
9. Phát hiện và gian lận DMA
Các gian lận DMA đại diện cho giới hạn hiện tại của cuộc chạy đua vũ trang chống gian lận và chúng thực sự khó giải quyết chỉ bằng phần mềm.
Cái gì Trò gian lận DMA là
Một mánh gian lận PCIe DMA (Truy cập bộ nhớ trực tiếp) sử dụng thiết bị kết nối PCIe - điển hình là bo mạch FPGA phát triển - có thể đọc trực tiếp bộ nhớ vật lý của hệ thống máy chủ thông qua bus PCIe mà không cần CPU tham gia. Khung pcileech và thư viện LeechCore của nó cung cấp kho phần mềm cho các thiết bị này. Thiết bị xuất hiện trên bus PCIe, giành quyền truy cập vào bộ nhớ vật lý của máy chủ thông qua giao thức PCIe TLP (Gói lớp giao dịch) và đọc bộ nhớ tiến trình trò chơi bằng cách dịch địa chỉ ảo sang địa chỉ vật lý (sử dụng bảng trang cũng nằm trong bộ nhớ vật lý và thiết bị có thể đọc được).
Máy tấn công (chạy phần mềm gian lận) tách biệt về mặt vật lý với máy nạn nhân (chạy trò chơi). Tất cả logic gian lận đều chạy trên máy của kẻ tấn công. Máy trò chơi không có quy trình, không có trình điều khiển, không có sự phân bổ bộ nhớ từ trò gian lận. Từ góc độ phần mềm thuần túy, máy chơi game hoàn toàn sạch.
FPGA nằm trên bus PCIe của PC chơi game và đọc bộ nhớ vật lý thông qua TLP mà không cần sự tham gia của CPU. Phần mềm gian lận chạy hoàn toàn trên một máy riêng biệt, giúp PC chơi game sạch sẽ từ góc độ phần mềm.
PCIe Internals
Giao tiếp PCIe được cấu trúc xung quanh TLP. TLP đọc bộ nhớ từ thiết bị DMA chứa địa chỉ vật lý cần đọc và số byte được yêu cầu. Tổ hợp gốc PCIe phục vụ yêu cầu này bằng cách đọc bộ nhớ vật lý được chỉ định và trả về dữ liệu trong TLP hoàn chỉnh. Đây hoàn toàn là ở cấp độ phần cứng và CPU không liên quan đến việc đáp ứng yêu cầu.
Thiết bị cần được định cấu hình bằng BAR (Thanh ghi địa chỉ cơ sở) hợp lệ mà BIOS chỉ định trong quá trình liệt kê PCIe. Thiết bị cũng cần vô hiệu hóa IOMMU của hệ thống mục tiêu (nếu có) hoặc cho phép các giao dịch DMA của thiết bị thông qua nó.
IOMMU với vai trò Phòng thủ
IOMMU (Intel VT-d, AMD-Vi) là đơn vị phần cứng dịch địa chỉ DMA từ các thiết bị PCIe bằng bảng trang dành riêng cho thiết bị (tương tự như bảng trang của CPU để dịch địa chỉ mã người dùng). Nếu IOMMU được bật và định cấu hình đúng cách, thiết bị PCIe chỉ có thể truy cập bộ nhớ vật lý mà HĐH đã cấp rõ ràng cho nó thông qua các bảng trang IOMMU.
Khi IOMMU được bật, thiết bị DMA không được liên kết với bất kỳ trình điều khiển HĐH nào sẽ không có ánh xạ IOMMU và không thể truy cập bộ nhớ vật lý tùy ý. Về mặt lý thuyết, đây là biện pháp phòng thủ cấp phần cứng chống lại các cuộc tấn công DMA.
Trên thực tế, biện pháp phòng thủ của IOMMU có những lỗ hổng đáng kể. Nhiều bo mạch chủ chơi game được cài đặt mặc định tắt IOMMU. Ngay cả khi được bật, cấu hình IOMMU vẫn phức tạp và nhiều hệ thống đã định cấu hình sai các chính sách IOMMU khiến cho phạm vi bộ nhớ vật lý lớn có thể truy cập được. Và điều quan trọng là chương trình cơ sở DMA giả mạo thành công thiết bị PCIe hợp pháp (ví dụ: bộ điều khiển USB hoặc card mạng mà HĐH đã cấp quyền truy cập cho IOMMU) có khả năng truy cập vào bộ nhớ thông qua IOMMU bằng cách sử dụng các quyền được cấp của thiết bị hợp pháp.
Bắt chước chương trình cơ sở DMA
Phần mềm gian lận DMA tinh vi được thiết kế để bắt chước ID thiết bị PCIe, ID nhà cung cấp, ID hệ thống con và cấu hình BAR0 của một thiết bị hợp pháp. Một Xilinx FPGA chạy chương trình cơ sở tùy chỉnh có thể tự hiển thị trên BIOS và HĐH, chẳng hạn như bộ điều khiển máy chủ USB. Hệ điều hành tải trình điều khiển hợp pháp cho thiết bị đó (cung cấp vùng phủ sóng IOMMU) và chương trình cơ sở FPGA sử dụng vùng phủ sóng đó để thực hiện đọc DMA.
Các chương trình chống gian lận cố gắng phát hiện điều này bằng cách liệt kê tất cả các thiết bị PCIe và xác thực rằng các đặc điểm được báo cáo của mỗi thiết bị phù hợp với phần cứng dự kiến. Nhưng nếu không có chứng thực cấp chương trình cơ sở cụ thể thì rất khó để phân biệt phần cứng hợp pháp với FPGA bắt chước đúng cách.
Khởi động an toàn và TPM là biện pháp giảm thiểu một phần
Yêu cầu của Epic Games đối với Khởi động an toàn và TPM 2.0 cho Fortnite có liên quan trực tiếp đến mối đe dọa DMA. Khởi động an toàn đảm bảo rằng chỉ các bộ tải khởi động đã ký mới chạy, điều này ngăn chặn các cuộc tấn công trong thời gian khởi động có thể vô hiệu hóa IOMMU hoặc cài đặt các mánh gian lận cấp chương trình cơ sở. TPM 2.0 cho phép khởi động đo lường (trong đó hàm băm của mỗi giai đoạn khởi động được ghi lại trong sổ đăng ký PCR của TPM), cung cấp chuỗi chứng thực chứng minh hệ thống đã khởi động ở trạng thái tốt đã biết. Chứng thực từ xa bằng TPM có thể cho phép máy chủ xác minh rằng hệ thống máy khách không bị giả mạo ở cấp độ chương trình cơ sở.
Điều này không giải quyết trực tiếp vấn đề DMA (một thiết bị tấn công DMA được gắn vật lý vào khe cắm PCIe sẽ bỏ qua tất cả những điều này), nhưng nó đóng một số đường dẫn tấn công DMA được phần mềm hỗ trợ.
10. Phát hiện hành vi và đo từ xa
Không có sơ đồ bảo vệ tĩnh nào là đủ. Tính năng phát hiện hành vi hoạt động trên dữ liệu đo từ xa của trò chơi là lớp bổ sung nhằm giải quyết những vấn đề mà biện pháp bảo vệ hạt nhân không thể thực hiện được.
Phân tích đầu vào và chuột
Trình điều khiển chống gian lận hạt nhân hoạt động ở tốc độ cao nhất mức mà nó có thể chặn đầu vào thô trước khi đến trò chơi. Trình điều khiển cho đầu vào HID (Thiết bị giao diện con người), đặc biệt dành cho chuột và bàn phím, nằm trong ngăn xếp trình điều khiển đầu vào. Bằng cách cài đặt trình điều khiển bộ lọc phía trên mouclass.sys hoặc kbdclass.sys, chương trình chống gian lận có thể quan sát tất cả các sự kiện đầu vào có dấu thời gian chính xác theo đồng hồ hệ thống (độ phân giải micro giây).
Tính năng phát hiện Aimbot nhắm vào các thuộc tính thống kê về chuyển động của chuột. Việc nhắm mục tiêu của con người thể hiện các đặc tính cụ thể: Định luật Fitts chi phối quỹ đạo tiếp cận, có sự giảm tốc đặc trưng khi con trỏ tiếp cận mục tiêu, cấu hình vận tốc có các đường cong tăng tốc và giảm tốc cụ thể và có nhiễu đo lường. Một aimbot thực hiện phép nội suy tuyến tính hoàn hảo tới mục tiêu sẽ tạo ra chuyển động vi phạm các đặc tính này. Một triggerbot (tự động kích hoạt khi tâm ngắm nhắm vào mục tiêu nhưng không điều khiển chuyển động của chuột) được phát hiện thông qua phân tích thời gian phản ứng: thời gian phản ứng của con người đối với mục tiêu đi qua tâm ngắm có sàn sinh lý tối thiểu (khoảng 150-200 mili giây) với sự phân bố đặc trưng. Thời gian phản ứng dưới mức sàn này với độ nhất quán cao cho thấy quá trình tự động hóa.
Phát hiện máy học
The Collins et al. (CheckMATE 2024) tài liệu về ứng dụng CNN để phát hiện triggerbot, đạt độ chính xác khoảng 99,2% trên các tập dữ liệu được gắn nhãn. Các tính năng được cung cấp cho mạng bao gồm chuỗi thời gian vị trí chuột, thời gian nhấp chuột tương ứng với vị trí mục tiêu và cấu hình vận tốc.
Bài báo AntiCheatPT (2025) áp dụng kiến trúc máy biến áp để phát hiện aimbot. Sử dụng cửa sổ 256 dấu tích với 44 điểm dữ liệu trên mỗi dấu tích (bao gồm vị trí, vận tốc, gia tốc, tốc độ góc xem và sự kiện nhấp chuột), mô hình đạt được độ chính xác 89,17% trong việc phân biệt người chơi hợp pháp với người dùng aimbot. Kiến trúc máy biến áp rất phù hợp với vấn đề này vì aimbot thường đưa ra các mối tương quan về thời gian trong dữ liệu đầu vào (theo dõi trơn tru, hiệu chỉnh định kỳ) mà các cơ chế chú ý có thể khai thác.
Mạng nơ-ron đồ thị được sử dụng để phát hiện hành vi thông đồng (hack tường và gian lận dựa trên giao tiếp trong các trò chơi đồng đội) bằng cách lập mô hình biểu đồ tương tác của người chơi và phát hiện các mô hình bất thường, chẳng hạn như người chơi liên tục nhắm mục tiêu vào kẻ thù xuyên tường hoặc thể hiện nhận thức hoàn hảo về vị trí của kẻ thù mà không cần tầm nhìn.
Trái: Đường di chuột của người chơi hợp pháp tuân theo Định luật Fitts với đường cong chữ S tự nhiên, độ vọt lố và các hiệu chỉnh vi mô. Đúng: aimbot tạo ra một giai đoạn không hoạt động, sau đó là một cú chụp tuyến tính ngay lập tức tới mục tiêu mà không bị giảm tốc tự nhiên.
Đường ống đo từ xa
Luồng từ dữ liệu thô đến quyết định cấm thường hoạt động như sau:
- Trình điều khiển hạt nhân ghi lại các sự kiện đầu vào và đánh dấu thời gian của chúng ở mức gián đoạn phần cứng.
- Những sự kiện này được ghi vào bộ đệm vòng bộ nhớ dùng chung.
- Dịch vụ chế độ người dùng đọc từ bộ đệm vòng, phân nhóm các sự kiện, mã hóa chúng và truyền đến máy chủ phụ trợ.
- Suy luận ML phụ trợ chạy trên luồng sự kiện và tạo ra điểm số bất thường.
- Hàng xem xét thủ công sẽ nhận các phiên được gắn cờ có độ tin cậy cao.
- Các quyết định cấm được gửi lại cho ứng dụng khách trò chơi thông qua dịch vụ.
Việc mã hóa dữ liệu đo từ xa rất quan trọng đối với cả quyền riêng tư (dữ liệu bao gồm tất cả các chuyển động của chuột) và chống giả mạo (ngăn chặn hành vi gian lận xác định và làm sai lệch phép đo từ xa).
11. Kiểm tra chống VM và môi trường
Phát hiện VM dựa trên CPU
Tính năng phát hiện VM đáng tin cậy nhất là dựa trên CPUID. Khi CPUID được thực thi với EAX=1, bit 31 của ECX được đặt nếu có bộ điều khiển ảo hóa (đây là bit "Hiện diện của bộ điều khiển ảo hóa"). Với EAX=0x40000000, chuỗi nhà cung cấp bộ điều khiển ảo hóa được trả về trong EBX, ECX, EDX:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
BOOLEAN IsRunningInVM(void)
{
int cpuInfo[4];
__cpuid (cpuInfo, 1);
// Kiểm tra bit hiện tại của bộ ảo hóa ảo hóa (bit ECX 31)
if (cpuInfo[2] & (1 << 31)) {
// Nhận nhà cung cấp phần mềm ảo hóa
__cpuid( cpuInfo, 0x40000000);
char nhà cung cấp[13];
memcpy(nhà cung cấp, &cpuInfo[1], 4);
memcpy(nhà cung cấp + 4, &cpuInfo[2], 4);
memcpy(nhà cung cấp + 8, &cpuInfo[3], 4);
nhà cung cấp[12] = '\0'; // Nhà cung cấp VM đã biết
if (strcmp(nhà cung cấp, "VMwareVMware") == 0 ||
strcmp(nhà cung cấp, "VBoxVBoxVBox") == 0 ||
strcmp( nhà cung cấp, "Microsoft Hv") == 0 || // Hyper-V
strcmp(nhà cung cấp, "KVMKVMKVM") == 0) {
trở lại ĐÚNG;
trở lại TRUE; // Trình ảo hóa ảo hóa không xác định cũng đáng ngờ
trở lại FALSE;
Phát hiện máy ảo dựa trên giả tạo
Mỗi nền tảng VM để lại các thành phần đặc trưng trong sổ đăng ký và liệt kê thiết bị:
- VMware: Khóa sổ đăng ký
HKLM\SOFTWARE\VMware, Inc.\VMware Tools; Thiết bị PCI\Device\VMwareHGFS; các thiết bị ảo xuất hiện trongWin32_PnPEntityvới tên “VMware”. - VirtualBox:
HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions; Trình điều khiểnVBoxMiniRdDN; khóa đăng kýHKLM\HARDWARE\ACPI\DSDT\VBOX__. - Hyper-V:
HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters; sự hiện diện của các đối tượng trình điều khiểnvmbusvàstorvsc.
Các phần mềm chống gian lận truy vấn các tạo phẩm này từ chế độ kernel nơi chúng không thể bị chặn bởi móc nối chế độ người dùng. Một hệ thống có bất kỳ cấu phần phần mềm nào trong số này có khả năng đang chạy trong máy ảo và hệ thống chống gian lận có thể từ chối vận hành hoặc gắn cờ phiên.
Phát hiện các phần mềm ảo hóa lồng nhau
Các nhà phát triển gian lận đôi khi sử dụng các trình ảo hóa lồng nhau để tạo môi trường phân tích minh bạch: họ chạy trò chơi trong máy ảo, với mã gian lận chạy trên máy chủ của máy ảo. Việc phát hiện các trình ảo hóa lồng nhau phụ thuộc vào sự bất thường về thời gian: CPUID được thực thi bên trong một VM lồng nhau được xử lý bởi hai trình ảo hóa theo trình tự, gây ra chi phí gấp đôi. Các lệnh RDMSR và WRMSR tương tự có độ trễ tăng lên. Phân tích thống kê của hàng trăm phép đo thời gian có thể phân biệt một cách đáng tin cậy việc thực thi gốc, ảo hóa cấp đơn và ảo hóa lồng nhau.
12. Lấy dấu vân tay phần cứng và thực thi lệnh cấm
Số nhận dạng được thu thập
Hệ thống chống gian lận thu thập nhiều mã nhận dạng phần cứng để tạo một dấu vân tay duy nhất tồn tại sau lệnh cấm tài khoản:
- Dữ liệu SMBIOS: Nhà sản xuất, tên sản phẩm, số sê-ri, UUID. Được truy cập qua
NtQuerySystemInformation(SystemFirmwareTableInformation, ...)hoặc trực tiếp qua bảng chương trình cơ sở. - Số sê-ri đĩa : Số sê-ri của đĩa vật lý qua IOCTL
IOCTL_STORAGE_QUERY_PROPERTY. Đây là những giá trị nhận dạng ổn định vẫn tồn tại sau khi cài đặt lại hệ điều hành. - Số nhận dạng GPU: ID phiên bản thiết bị, LUID bộ điều hợp.
- Địa chỉ MAC: Địa chỉ MAC NIC thông qua NDIS hoặc sổ đăng ký. Những điều này có thể bị giả mạo ở cấp độ phần mềm nhưng thường không được thay đổi bởi người dùng không có hiểu biết về kỹ thuật.
- GUID khởi động:
MachineGuidtrongHKLM\SOFTWARE\Microsoft\Cryptographyhoặc lâu dài hơn là UUID nền tảng của chương trình cơ sở UEFI có thể truy cập thông qua SMBIOS.
Phân tích Vanguard từ ý chính GitHub của rhaym-tech tài liệu vgk.sys thu thập thông tin BIOS, bao gồm UUID hệ thống và các trường SMBIOS khác nhau, được kết hợp thành dấu vân tay phần cứng để thực thi lệnh cấm.
Phát hiện và giả mạo HWID
Việc giả mạo HWID liên quan đến việc sửa đổi các mã nhận dạng mà phần mềm chống gian lận đọc để trốn tránh lệnh cấm phần cứng. Các phương pháp giả mạo bao gồm:
- Giả mạo dựa trên sổ đăng ký: Vá các mục đăng ký báo cáo số sê-ri đĩa, địa chỉ MAC và dữ liệu SMBIOS. Điều này hoạt động chống lại các phần mềm chống gian lận truy vấn chúng thông qua đường dẫn đăng ký thay vì trực tiếp.
- Giả mạo cấp trình điều khiển: Trình điều khiển hạt nhân chặn các yêu cầu IOCTL về số nhận dạng phần cứng và trả về các giá trị giả mạo. Tính năng này hoạt động chống lại các chương trình chống gian lận sử dụng đường dẫn IOCTL tiêu chuẩn nhưng không chống được các chương trình chống gian lận truy vấn trực tiếp phần cứng.
- Giả mạo vật lý: Lập trình một địa chỉ MAC khác vào chương trình cơ sở NIC, nhấp nháy số sê-ri đĩa mới (được một số ổ đĩa hỗ trợ). Điều này rất hiếm và đôi khi là vĩnh viễn.
Các phần mềm chống gian lận phát hiện hành vi giả mạo bằng cách tham chiếu chéo nhiều nguồn nhận dạng. Nếu UUID SMBIOS là FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF (một giá trị giả mạo phổ biến), thì đó là cờ ngay lập tức. Nếu mẫu đĩa được báo cáo là “Samsung 970 EVO” nhưng định dạng số sê-ri của đĩa không khớp với định dạng của Samsung thì đó là dấu hiệu giả mạo. Nếu các bảng chương trình cơ sở UEFI báo cáo một UUID và cơ quan đăng ký báo cáo một UUID khác thì giá trị đăng ký đã bị giả mạo.
13. Cuộc chạy đua vũ trang: Xu hướng hiện tại và định hướng tương lai
Hệ thống phân cấp leo thang
Sự leo thang mà chúng ta đã thấy trong thập kỷ qua tuân theo một mô hình rõ ràng:
- Các hành vi gian lận trong chế độ người dùng đã bị chống lại bởi tính năng chống gian lận ở chế độ người dùng.
- Các hành vi gian lận hạt nhân đã bị chống lại bởi tính năng chống gian lận của kernel.
- Các hành vi gian lận hạt nhân với BYOVD đã bị các danh sách chặn trình điều khiển và thực thi DSE nghiêm ngặt hơn chống lại.
- Các hành vi gian lận dựa trên Hypervisor đã bị ngăn chặn bằng tính năng phát hiện của bộ giám sát ảo.
- Gian lận DMA là giới hạn hiện tại, bị chống lại một phần bởi chứng thực IOMMU, Secure Boot và TPM.
- Cấp độ tiếp theo là các cuộc tấn công dựa trên chương trình cơ sở, trong đó gian lận được nhúng trong chương trình cơ sở SSD, chương trình cơ sở GPU hoặc chương trình cơ sở NIC.
Các cuộc tấn công phần mềm đặc biệt đáng lo ngại vì chúng vẫn tồn tại sau khi cài đặt lại hệ điều hành, vô hình đối với tất cả các hoạt động kiểm tra cấp độ hạt nhân và cực kỳ khó phát hiện nếu không có quyền truy cập vật lý vào thiết bị để xác minh phần mềm cơ sở. Ngày nay, không có biện pháp chống gian lận phổ biến nào chống lại các hành vi gian lận trong chương trình cơ sở.
Các trò lừa đảo được hỗ trợ bởi AI
Mối đe dọa thế hệ tiếp theo là aimbots được hỗ trợ bởi các mô hình thị giác máy tính chạy trên GPU hoặc thiết bị phụ máy tính. Các hệ thống này sử dụng camera hoặc chụp ảnh màn hình để phân tích khung hình trò chơi, xác định mục tiêu và di chuyển chuột thông qua phần cứng (thiết bị USB HID, bỏ qua hoàn toàn việc kiểm tra đầu vào phần mềm). Chuyển động của chuột mà chúng tạo ra có thể được định cấu hình để bắt chước kiểu chuyển động của con người, khiến việc phát hiện thống kê trở nên khó khăn hơn nhiều.
Một aimbot AI hoạt động thông qua HID phần cứng, theo quan điểm của máy trò chơi, hoàn toàn không thể phân biệt được với con người sử dụng chuột. Tất cả đầu vào đều thông qua các kênh phần cứng hợp pháp. Không có mã chạy trong quá trình trò chơi. Hạt nhân hoàn toàn sạch sẽ. Bề mặt phát hiện duy nhất là hồ sơ hành vi: độ chính xác, thời gian phản ứng và kiểu chuyển động mà AI tạo ra.
Đây là lý do tại sao các phương pháp ML hành vi được thảo luận trong phần 10 không phải là tùy chọn mà ngày càng đóng vai trò quan trọng trong việc chống gian lận hiệu quả.
Cuộc tranh luận về quyền riêng tư
Tính năng chống gian lận cấp hạt nhân không được những người ủng hộ quyền riêng tư ưa chuộng. Những lời chỉ trích rất có cơ sở:
Trình điều khiển chạy ở vòng 0 với quá trình tải trong thời gian khởi động có quyền truy cập vào mọi thứ trên hệ thống. Mặc dù BattlEye, EAC và Vanguard không được ghi nhận là lạm dụng quyền truy cập này để giám sát nhưng khả năng kỹ thuật vẫn tồn tại. Phân tích của bài báo ARES 2024 nhấn mạnh rằng mô hình tin cậy giống hệt với mô hình chúng tôi sử dụng cho phần mềm quan trọng về bảo mật, nghĩa là bất kỳ lỗ hổng nào trong các thành phần này đều là sự leo thang đặc quyền cục bộ lên vòng 0.
Thực tế là các trò chơi yêu cầu cài đặt trình điều khiển hạt nhân trong thời gian khởi động làm điều kiện chơi cũng là một mối lo ngại đáng kể về bề mặt tấn công. Lỗ hổng trong vgk.sys là sự leo thang đặc quyền cục bộ tới đổ chuông 0. Bản thân phần mềm chống gian lận trở thành mục tiêu tấn công.
Các phương pháp tiếp cận dựa trên chứng thực
Hướng đi hứa hẹn nhất về mặt kỹ thuật cho việc chống gian lận là từ xa chứng thực. Thay vì chạy trình điều khiển ring-0 để tích cực chống gian lận, hệ thống này chứng minh cho máy chủ trò chơi thấy rằng nó đang chạy ở trạng thái tốt. Khởi động đo lường dựa trên TPM, kết hợp với Khởi động an toàn UEFI, có thể tạo ra chứng thực được ký bằng mật mã rằng các bộ tải khởi động, hạt nhân và trình điều khiển cụ thể đã được tải. Máy chủ từ chối kết nối từ các hệ thống không thể cung cấp chứng thực hợp lệ.
Đây không phải là giải pháp hoàn chỉnh (kẻ tấn công đủ tinh vi có thể thao túng chứng thực), nhưng nó nâng cao đáng kể tiêu chuẩn. Chứng thực có thể cùng tồn tại với tính năng quét truyền thống để cung cấp khả năng phòng thủ theo chiều sâu.
Trò chơi trên đám mây có chức năng chống gian lận
Chơi game trên đám mây (GeForce Now, Xbox Cloud Gaming) về mặt kiến trúc là giải pháp chống gian lận tối ưu cho một số danh mục trò chơi nhất định. Nếu trò chơi chạy trong trung tâm dữ liệu và chỉ có video được truyền tới máy khách thì sẽ không có mã máy khách trò chơi nào để khai thác, không có bộ nhớ trò chơi để đọc và không có môi trường cục bộ để thao tác. Bề mặt tấn công gian lận giảm thiểu thao tác đầu vào và phân tích video, cả hai đều có phương pháp phát hiện tương đối đơn giản.
Hạn chế là độ trễ: trò chơi trên đám mây không phù hợp với các tựa game cạnh tranh trong đó thời gian phản ứng một phần nghìn giây là vấn đề quan trọng. Đối với lối chơi thông thường và bán mang tính cạnh tranh, phân phối trên nền tảng đám mây có thể ngày càng trở thành câu trả lời.
14. Kết luận
Các hệ thống chống gian lận hạt nhân hiện đại thể hiện kiến trúc phòng thủ nhiều lớp hoạt động trên mọi cấp độ có sẵn của mô hình đặc quyền Windows:
- Lệnh gọi lại hạt nhân (
ObRegisterCallbacks,PsSetCreateProcessNotifyRoutineEx,PsSetLoadImageNotifyRoutine) cung cấp khả năng hiển thị theo thời gian thực về các sự kiện hệ thống với khả năng chủ động chặn các hoạt động độc hại. - Quét bộ nhớ (VAD đi bộ, liệt kê nhóm lớn, băm phần mã) cung cấp xác minh định kỳ rằng bộ nhớ trò chơi không bị giả mạo và không có mã được chèn vào.
- Đo lường hành vi từ xa (phân tích đầu vào, lập hồ sơ thống kê, suy luận ML) phát hiện các lỗi gian lận mà về mặt kiến trúc không thể quét được.
- Lấy dấu vân tay phần cứng thực thi các quyết định cấm đối với các lần đặt lại tài khoản.
- Các biện pháp bảo vệ chống gỡ lỗi và chống VM làm cho kỹ thuật đảo ngược và phát triển trở nên khó khăn hơn đáng kể.
Không có một kỹ thuật đơn lẻ nào là đủ. Cuộc gọi lại hạt nhân có thể bị bỏ qua bởi các cuộc tấn công DMA. Việc quét bộ nhớ có thể bị tránh bằng các mánh gian lận dựa trên bộ ảo hóa nhằm chặn việc đọc bộ nhớ. Khả năng phát hiện hành vi có thể bị đánh lừa bởi AI bắt chước con người. Dấu vân tay phần cứng có thể bị đánh bại bởi những kẻ giả mạo phần cứng. Chính sự kết hợp của tất cả các lớp này, được cập nhật liên tục để đáp ứng với các kỹ thuật trốn tránh mới, mang lại khả năng bảo vệ có ý nghĩa.
Quỹ đạo của cuộc chạy đua vũ trang này hướng tới chứng thực phần cứng và xác minh phía máy chủ là nền tảng cuối cùng của bảo mật trò chơi đáng tin cậy. Bảo vệ phía máy khách chỉ dành cho phần mềm sẽ luôn không đối xứng: người bảo vệ phải kiểm tra mọi thứ, kẻ tấn công chỉ cần tìm một lỗ hổng. Chứng thực phần cứng làm thay đổi sự bất đối xứng này bằng cách khiến việc chứng minh trạng thái đáng tin cậy khi vận hành một hệ thống đã sửa đổi trở nên cực kỳ khó khăn.
Cho đến khi nền tảng đó được phổ biến và thực thi trên toàn cầu, tính năng chống gian lận trong kernel vẫn là biện pháp bảo vệ thực tế tốt nhất hiện có, với tất cả độ phức tạp liên quan, hàm ý về quyền riêng tư và bề mặt tấn công đòi hỏi.
Tài liệu tham khảo
Collins, R. và cộng sự. “Chống gian lận: Các cuộc tấn công và tính hiệu quả của biện pháp phòng vệ phía khách hàng.” CheckMATE 2024 (Hội thảo đặt cùng địa điểm với CCS 2024). https://tomchothia.gitlab.io/Papers/AntiCheat2024.pdf
Vella, R. et al. “Nếu nó trông giống như một Rootkit và lừa dối như một Rootkit: Một phân tích quan trọng về các hệ thống chống gian lận cấp hạt nhân.” ARES 2024. https://arxiv.org/pdf/2408.00500
Sousa, J. và cộng sự. “AntiCheatPT: Phương pháp tiếp cận dựa trên máy biến áp để phát hiện gian lận trong trò chơi bắn súng góc nhìn thứ nhất.” 2025. https://arxiv.org/html/2508.06348v1
secret.club. “Đảo ngược trình điều khiển hạt nhân chống gian lận của BattlEye.” 2019. https://secret.club/2019/02/10/battleye-anticheat.html
secret.club. “Bỏ qua kiểm tra tính toàn vẹn của Anti-Cheat dễ dàng.” 2020. https://secret.club/2020/04/08/eac_integrity_check_bypass.html
back.engineering. “Đảo ngược BEDaisy.sys.” 2020. https://back.engineering/blog/2020/08/22/
Aki2k. “Kỹ thuật đảo ngược BEDaisy.” GitHub. https://github.com/Aki2k/BEDaisy
archie-osu. “Phân tích móc bảng Vanguard Dispatch.” 2025. https://archie-osu.github.io/2025/04/11/vanguard-research.html
rhaym-tech. “Gist phân tích Vanguard vgk.sys.” Ý chính của GitHub. https://Gist.github.com/rhaym-tech/f636b76deeca15528e70304b5ee95980
donnaskiez. “ac: Chống gian lận hạt nhân mã nguồn mở.” GitHub. https://github.com/donnaskiez/ac
Tác giả: davikr













