Published
- 14 min read
Anti-Cheat ไม่ใช่แค่ตรวจ Cheat — บทเรียนจาก HerculesAC
บทนำ
เมื่อพูดถึง Anti-Cheat หลายคนมักนึกถึงโปรแกรมที่เปิดพร้อมเกม แล้วคอยค้นหาชื่ออย่าง Cheat Engine, debugger หรือโปรแกรมสำหรับแก้ไข memory
ภาพจำนี้ไม่ผิด แต่เป็นเพียงส่วนเล็กที่สุดของปัญหา
Anti-Cheat ที่ดีไม่ได้ตอบเพียงคำถามว่า
ตอนนี้มีโปรแกรมโกงเปิดอยู่หรือไม่?
แต่มันต้องตอบคำถามที่ยากกว่า เช่น
- Process ของเกมยังเป็น process ที่เราเริ่มต้นขึ้นมาจริงหรือไม่
- Code และ data ภายใน process ถูกแก้ไขไปแล้วหรือยัง
- Thread ใหม่เกิดจาก execution path ปกติหรือถูกสร้างขึ้นจากภายนอก
- Process อื่นกำลังขอ handle ที่มีสิทธิ์อ่านหรือเขียน memory ของเกมหรือไม่
- Sensor ที่ใช้ตรวจสอบระบบยังทำงานอยู่ หรือถูก hook และทำให้รายงานข้อมูลปลอมแล้ว
- Signal ที่ตรวจพบเป็นพฤติกรรมโกงจริง หรือเป็นเพียง debugger, overlay, accessibility tool หรือ software ที่ทำงานถูกต้อง
- เมื่อเครื่อง client ไม่สามารถเชื่อถือได้ทั้งหมด เราควรย้าย validation ส่วนใดไปไว้ที่ game server
ดังนั้น Anti-Cheat จึงไม่ใช่เพียง scanner แต่เป็นงานด้าน trust engineering
การสร้าง Anti-Cheat คือการออกแบบว่าเราจะเชื่อข้อมูลจากส่วนใดของระบบ เชื่อได้มากเพียงใด และต้องตรวจสอบข้อมูลนั้นซ้ำจากชั้นไหน
บทความนี้จะอธิบายแนวคิดดังกล่าวผ่าน HerculesAC ซึ่งเป็น Anti-Cheat research prototype ที่ผมเคยพัฒนา โดยจะถอด architecture จาก source code จริง ตั้งแต่ launcher, controller, process monitor, injected protection DLL, shared memory และ Windows kernel callbacks
บทความนี้เขียนในมุม defensive engineering และ software architecture ไม่ใช่คู่มือสำหรับหลบหรือโจมตีระบบ Anti-Cheat
1. Anti-Cheat กำลังปกป้องอะไร
ก่อนสร้างระบบตรวจจับ เราต้องระบุให้ได้ก่อนว่าสิ่งใดคือ asset ที่ต้องปกป้อง
สำหรับเกมบน Windows สิ่งที่มีค่ามักไม่ได้มีเพียง executable file แต่รวมถึง
- Game process — process ที่กำลัง execute game logic
- Process memory — state ของผู้เล่น, entity, coordinate, health และข้อมูล runtime
- Execution flow — thread, callback และ control flow ภายใน process
- Loaded modules — DLL และ executable code ที่ถูกโหลดเข้ามา
- Input path — เส้นทางจาก input device ไปถึง game logic
- Network protocol — packet และ state ที่แลกเปลี่ยนกับ server
- Game economy — ranking, item, currency และผลการแข่งขัน
- Trust relationship — ความสัมพันธ์ระหว่าง launcher, game, Anti-Cheat และ backend
การแก้ไขเกมจึงไม่ได้มีรูปแบบเดียว
File Modification
│
├── Patch executable on disk
├── Replace DLL
└── Modify configuration
Runtime Modification
│
├── Read process memory
├── Write process memory
├── Create or hijack thread
├── Load additional module
└── Change execution flow
External Automation
│
├── Input automation
├── Screen analysis
└── External hardware
Protocol and Logic Abuse
│
├── Invalid client state
├── Packet manipulation
└── Server-side logic abuse
ไม่มี sensor ตัวเดียวที่ตรวจจับทุกประเภทได้
Anti-Cheat จึงต้องเป็นระบบหลายชั้นที่รวม signal จากหลายแหล่งเข้าด้วยกัน
2. ทำไมการสร้าง Anti-Cheat จึงเป็นงานโชว์ความรู้ด้านระบบ
Anti-Cheat หนึ่งระบบแตะองค์ความรู้หลายแขนงพร้อมกัน
| ความสามารถ | ความรู้ที่เกี่ยวข้อง |
|---|---|
| เปิดเกมผ่าน launcher | Process creation, token, command line, environment |
| ตรวจ process รอบข้าง | Toolhelp, Native API, process security |
| ตรวจ window และ debugger | Window manager, desktop, session, user-mode telemetry |
| ตรวจ module และ resource | PE format, resource directory, version information |
| ตรวจ code hook | Assembly, instruction boundary, integrity checking |
| เฝ้าการสร้าง thread | Thread lifecycle, loader, execution provenance |
| ตรวจ memory access | Process handles, access mask, virtual memory |
| Inject protection component | DLL lifecycle, loader lock, architecture boundary |
| สื่อสารข้าม process | IPC, shared memory, synchronization, authentication |
| ป้องกัน process ด้วย driver | Object Manager, callbacks, kernel synchronization |
| รองรับ x86 และ x64 | WOW64, ABI, pointer size, hook compatibility |
| ลด false positive | Telemetry, statistics, risk scoring, product design |
| ทำงานโดยไม่ทำเกมพัง | Performance, race condition, deadlock, crash recovery |
ด้วยเหตุนี้ การสร้าง Anti-Cheat จึงไม่ได้แสดงเพียงว่าเรารู้จัก API สำหรับค้นหา Cheat Engine
มันแสดงว่าเราสามารถนำความรู้ด้าน Windows Internals, reverse engineering, kernel development, concurrency และ security architecture มาประกอบเป็นระบบเดียวได้หรือไม่
Anti-Cheat เป็นหนึ่งใน portfolio ที่โหดที่สุดของ Windows system programmer เพราะคู่ต่อสู้ของมันไม่ได้เป็นเพียง bug แต่เป็นโปรแกรมอีกชุดหนึ่งที่พยายามศึกษาการทำงานและเปลี่ยนผลลัพธ์ของมันโดยตั้งใจ
3. HerculesAC คืออะไร
HerculesAC เป็น Anti-Cheat prototype ที่ถูกออกแบบให้มี component หลายตัว แทนที่จะรวมทุกอย่างไว้ใน executable เดียว
จาก source code สามารถแบ่งระบบออกได้เป็นส่วนหลักดังนี้
| Component | หน้าที่หลัก |
|---|---|
GameLauncher | Entry point สำหรับเปิด HerculesAC |
HerculesAC | Controller, launcher orchestration และ detection หลัก |
GameMon | เฝ้าสถานะ game client และติดตั้ง message hook |
GameProtect | Protection DLL และ in-process memory sensor |
herculeskernel | Kernel callbacks สำหรับจำกัด process/thread handle rights |
Common | IPC, hashing, filesystem, logging และ hooking utilities |
Bypass | Adversarial test component สำหรับทดสอบสมมติฐานของระบบ |
ExampleDLL | DLL ตัวอย่างสำหรับการทดลอง |
Architecture โดยสรุปเป็นดังนี้
┌──────────────────────────────────────────────────────────┐
│ GameLauncher │
│ starts HerculesAC.aes │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ HerculesAC │
│ - validates configuration │
│ - launches game │
│ - starts GameMon x86/x64 │
│ - scans processes, windows and file metadata │
│ - checks selected code integrity │
└───────────────┬───────────────────────┬──────────────────┘
│ │
│ ▼
│ ┌──────────────────────┐
│ │ herculeskernel │
│ │ Object callbacks │
│ │ Handle mediation │
│ └──────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ GameMon │
│ - tracks game PID and path │
│ - loads matching GameProtect DLL │
│ - installs WH_GETMESSAGE hook │
│ - keeps monitor alive while game runs │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ GameProtect │
│ - receives target information through shared memory │
│ - observes ReadProcessMemory / WriteProcessMemory │
│ - supports hash-based whitelist │
└──────────────────────────────────────────────────────────┘
สิ่งที่น่าสนใจคือ HerculesAC ใช้ทั้ง orchestration, observation, integrity checking, behavioral detection และ kernel access mediation ภายในโปรเจกต์เดียว
4. GameLauncher — จุดเริ่มต้นของ Trust Chain
GameLauncher/WinMain.cpp มีหน้าที่ค่อนข้างเล็ก แต่มีความสำคัญทาง architecture
มันค้นหา directory ของตัวเอง แล้วเปิด HerculesAC.aes พร้อมส่ง path ไปให้
void InitHercules()
{
std::wstring wsProcPath = FileSystem::GetModuleDirectory(NULL);
if (!wsProcPath.empty())
{
StartProcess(
wsProcPath,
L"HerculesAC\\HerculesAC.aes",
L" " + wsProcPath
);
}
}
แนวคิดของ launcher คือทำให้เกมไม่ได้ถูกเปิดอย่างโดดเดี่ยว แต่เริ่มต้นผ่าน chain ที่ระบบ Anti-Cheat ควบคุม
User
│
▼
GameLauncher
│
▼
HerculesAC Controller
│
├── Validate files and configuration
├── Start protection threads
├── Start game
└── Start monitors
การเริ่มเกมจาก controller ทำให้ระบบทราบข้อมูลสำคัญตั้งแต่ต้น เช่น
- path ที่คาดหวังของเกม
- process ID ที่ถูกสร้างขึ้น
- process handle จาก
CreateProcess - เวลาที่ game process เริ่มทำงาน
- monitor ที่ควรเริ่มพร้อมกัน
นี่คือการสร้าง chain of custody สำหรับ process
อย่างไรก็ตาม process ที่ถูกเปิดโดย launcher ไม่ได้แปลว่าจะเชื่อถือได้ตลอดอายุของมัน เพราะหลังจากนั้น memory, thread และ module ภายใน process ยังสามารถเปลี่ยนแปลงได้
Launcher จึงเป็นเพียงจุดเริ่มต้นของ trust chain ไม่ใช่จุดจบ
5. HerculesAC Controller — Orchestrator และ User-Mode Detector
HerculesAC/WinMain.cpp คือ controller หลักของระบบ
เมื่อเริ่มทำงาน มันจะ
- โหลด configuration จาก
hac.dat - ตรวจว่าไฟล์ที่จำเป็นอยู่ครบ
- ติดตั้ง user-mode hooks
- สร้าง integrity baseline
- เปิด detection threads
- เริ่ม game process
- เปิด
GameMonทั้งรุ่น x86 และ x64 - ตรวจ process และ window ที่น่าสงสัยเป็นระยะ
ส่วนที่เปิด game และ monitor มีรูปแบบดังนี้
pi = StartProcess(Global::GamePath, config[L"starter"], L"");
root["GamePath"] = Common::wideStringToString(Global::GamePath);
root["pid"] = (UInt)pi.dwProcessId;
root["hProcess"] = (UInt)pi.hProcess;
StartProcess(filename, config[L"GameMon"], jsonCommandLine);
StartProcess(filename, config[L"GameMon64"], jsonCommandLine);
การเปิด monitor สอง architecture ไม่ใช่รายละเอียดเล็กน้อย
Windows แบ่งโลกของ user-mode hook และ DLL ตาม bitness ตัว DLL 32-bit ไม่สามารถทำหน้าที่แทน DLL 64-bit ได้ทุกกรณี และในทางกลับกัน เอกสารของ Microsoft สำหรับ SetWindowsHookEx ก็อธิบายข้อจำกัดระหว่าง 32-bit และ 64-bit hook ไว้โดยตรง
ดังนั้น HerculesAC จึงมี
GameMon x86 ──> GameProtect.dll
GameMon x64 ──> GameProtect64.dll
นี่เป็นตัวอย่างที่ดีว่า Anti-Cheat ไม่ได้ต้องเข้าใจเพียง game logic แต่ต้องเข้าใจ architecture boundary ของ Windows ด้วย
6. Configuration และ File Manifest
HerculesAC ใช้ไฟล์ hac.dat เป็น configuration และ manifest อย่างง่าย
Build script จะสร้าง section ประมาณนี้
[Game]
starter=cstrike.exe
Client=cstrike.exe
GameMon=GameMon.aes
GameMon64=GameMon64.aes
[MD5]
hash0=...
hash1=...
Count=...
Controller จะตรวจว่า executable ที่กำหนดไว้มีอยู่จริงใน directory ของเกมและ directory ของ Anti-Cheat
ขณะที่ GameProtect จะอ่านรายการ MD5 เพื่อสร้าง whitelist สำหรับ process ที่ควรข้ามการติดตั้ง hook บางส่วน
แนวคิดนี้ประกอบด้วยสองเรื่อง
- Expected layout — ระบบคาดหวังว่ามีไฟล์ใดอยู่ตรงไหน
- Expected identity — ระบบคาดหวังว่าไฟล์หนึ่งควรมี hash ใด
นี่เป็นรูปแบบพื้นฐานของ integrity manifest
อย่างไรก็ตาม MD5 ไม่เหมาะกับการใช้เป็น security boundary ในระบบใหม่ เนื่องจากมีปัญหา collision และ Microsoft แนะนำให้ใช้ SHA-256 หรือ SHA-512 สำหรับงานใหม่
สิ่งสำคัญยิ่งกว่าการเปลี่ยน hash คือ manifest ควรถูก authenticate ด้วย
Plain Hash List
└── ผู้ที่แก้ไฟล์ได้ อาจแก้รายการ hash ได้ด้วย
Signed Manifest
├── File hash
├── Version
├── Build identifier
├── Public-key signature
└── Verification policy
Hash บอกได้ว่าไฟล์สองชุดเหมือนกันหรือไม่ แต่ digital signature ช่วยตอบว่า manifest หรือ binary นั้นมาจากผู้สร้างที่เชื่อถือได้หรือไม่
7. Signature Detection — ชื่อ Process และ Window
HerculesAC มีรายการชื่อ process และข้อมูล window ที่ถือว่าน่าสงสัย เช่น debugger หรือเครื่องมือวิเคราะห์ memory บางชนิด
Controller ใช้สองมุมมองหลัก
Process Enumeration
└── IsProcessRunning(name)
Window Enumeration
├── Window class
└── Window title
วิธีนี้มีข้อดีคือ
- implementation ไม่ซับซ้อน
- ตอบสนองได้รวดเร็ว
- ตรวจเครื่องมือทั่วไปได้ดีในช่วงเริ่มต้น
- เหมาะกับ prototype และการทดสอบ flow
แต่ข้อจำกัดก็ชัดเจน
- filename เปลี่ยนได้
- window title และ class name เปลี่ยนได้
- software ถูกต้องบางตัวอาจมีชื่อใกล้เคียง
- ไม่สามารถพิสูจน์พฤติกรรมที่เกิดขึ้นจริงได้
- การพบ debugger ไม่ได้แปลว่ามีการโกงเสมอไป
ดังนั้น signature แบบชื่อควรถูกใช้เป็น signal ไม่ใช่ verdict สุดท้าย
Weak Design
One name match
└── Ban or kill immediately
Stronger Design
Name signal
+ Access behavior
+ Module evidence
+ Integrity anomaly
+ Server-side anomaly
└── Risk score and response policy
Anti-Cheat production system ต้องแยกคำว่า พบเครื่องมือที่มีความเสี่ยง ออกจากคำว่า พิสูจน์ว่าผู้เล่นโกง
8. Resource และ Version Metadata Fingerprinting
จุดหนึ่งที่น่าสนใจใน HerculesAC/Threads/ActiveThread.cpp คือระบบไม่ได้ตรวจเพียง filename
มันเปิด executable ของ process อื่นด้วย LoadLibraryEx(..., LOAD_LIBRARY_AS_DATAFILE) แล้วตรวจ resource เช่น
- icon
- bitmap
- version information
- company name
- file description
- internal name
- original filename
- product name
จากนั้นจึงคำนวณ hash แล้วเปรียบเทียบกับ signature ที่เก็บไว้
แนวคิดนี้พยายามแก้ปัญหาว่า
ถ้าเปลี่ยนชื่อ executable แล้ว เรายังระบุตัวตนจาก artifact ภายในไฟล์ได้หรือไม่?
Executable Identity
│
├── File name weak
├── Window title weak
├── Icon / bitmap resource stronger fingerprint
├── Version metadata stronger fingerprint
├── Code section hash build-specific
└── Authenticode signer publisher identity
Resource fingerprinting เป็นการเพิ่มมุมมองที่ดีสำหรับ research prototype เพราะทำให้เห็นว่า identity ของโปรแกรมไม่ได้มีเพียงชื่อไฟล์
แต่ signature แบบ static ยังมีข้อจำกัด
- เปลี่ยนเมื่อโปรแกรมอัปเดต
- เกิด false positive ได้ถ้าใช้ resource ร่วมกัน
- ถูกตัด resource ออกได้
- ต้องดูแลฐานข้อมูล signature ตลอดเวลา
ระบบสมัยใหม่จึงมักรวม static fingerprint กับ behavioral telemetry และ signer information แทนการพึ่งค่าใดค่าหนึ่ง
9. Self-Integrity — เมื่อ Sensor เองก็อาจถูกแก้ไข
Anti-Cheat ไม่สามารถตรวจสอบผู้อื่นได้อย่างน่าเชื่อถือ หาก code ที่ใช้ตรวจสอบถูกแก้ไขไปแล้ว
HerculesAC จึงสร้าง checksum baseline ให้ function สำคัญบางตัว ได้แก่
FindWindowWBaseThreadInitThunkCreateThread
ตัวอย่างแนวคิดจาก code คือ
CheckSum::FindWindowW_CheckSum = CRC32((BYTE*)FindWindowW, 5);
CheckSum::BaseThreadInitThunk_CheckSum =
CRC32((BYTE*)Original_BaseThreadInitThunk, 5);
CheckSum::CreateThread_CheckSum = CRC32((BYTE*)CreateThread, 5);
จากนั้น background thread จะคำนวณใหม่เป็นระยะ หากค่าเปลี่ยนก็ถือว่า selected function ถูกแก้ไข
นี่คือแนวคิด sensor integrity
Detector
│
├── observes the game
└── observes itself
อย่างไรก็ตาม การตรวจเพียงห้า byte แรกเป็น baseline แบบ prototype เพราะ
- hook ไม่จำเป็นต้องแก้ byte แรกเสมอไป
- code อาจถูกเปลี่ยนที่ call site หรือ pointer อื่น
- checksum function เองอาจถูกแทรกแซง
- baseline อาจถูกสร้างหลังจาก code ถูกแก้ไขไปแล้ว
- legit hotpatch หรือ compatibility layer อาจทำให้ค่าเปลี่ยน
บทเรียนที่สำคัญไม่ใช่ว่าต้องเพิ่มจาก 5 byte เป็นจำนวนใด แต่คือ integrity checking ต้องพิจารณาทั้ง สิ่งที่วัด, เวลาที่สร้าง baseline และ ผู้ที่มีสิทธิ์แก้ตัววัด
10. Thread Provenance — Thread นี้มาจากไหน
อีกแนวคิดหนึ่งใน HerculesAC คือการติดตามที่มาของ thread
Controller hook CreateThread เพื่อบันทึก start address ที่ถูกสร้างผ่านเส้นทางที่รู้จัก แล้วตรวจที่ BaseThreadInitThunk ว่า thread ที่กำลังเริ่มทำงานมี start address อยู่ในรายการหรือไม่
CreateThread called by expected code
│
▼
Record start address
│
▼
BaseThreadInitThunk
│
├── Known start address → allow
└── Unknown start address → suspicious
ใน source ยังมี logic ที่มอง start routine ซึ่งชี้ตรงไปยัง LoadLibraryA หรือ LoadLibraryW ว่าน่าสงสัย
นี่เป็นตัวอย่างของ execution provenance
แทนที่จะถามเพียงว่า “มี thread อยู่หรือไม่” ระบบถามว่า
- ใครเป็นผู้สร้าง thread
- start address อยู่ใน module ใด
- memory page นั้นเป็น image-backed หรือ private memory
- page protection เป็นแบบใด
- start address ตรงกับ known code path หรือไม่
- call stack และ creation event สอดคล้องกันหรือไม่
Prototype ของ HerculesAC เริ่มจาก allow-list ของ start address แต่แนวคิดสามารถพัฒนาไปสู่ thread telemetry ที่มีบริบทมากขึ้นได้
ข้อควรระวังคือ application จริงอาจสร้าง thread ผ่าน API หรือ runtime หลายชนิด เช่น thread pool, C runtime, COM, graphics driver หรือ middleware การตั้ง policy แบบ “ไม่รู้จักเท่ากับผิด” จึงอาจสร้าง false positive สูง
11. GameMon — แยก Monitoring ออกจาก Controller
GameMon รับข้อมูลจาก command line ในรูปแบบ JSON ซึ่งประกอบด้วย
- game path
- process ID
- process handle
จากนั้นมันจะ
- โหลด
GameProtect.dllหรือGameProtect64.dllตาม architecture - ขอ function pointer ที่ DLL export ไว้
- เขียนข้อมูล game client ลง shared memory
- ติดตั้ง message hook
- ตรวจว่า game client ยังทำงานอยู่หรือไม่
- ถอน hook เมื่อ game ปิด
การแยก GameMon ออกจาก controller มีข้อดีหลายด้าน
HerculesAC Controller
└── policy, orchestration, detection
GameMon
└── lifecycle and hook deployment
GameProtect
└── process-local observation
หากทุกอย่างอยู่ใน process เดียว failure หนึ่งจุดอาจทำให้ทั้งระบบหยุดพร้อมกัน การแยก component ทำให้สามารถกำหนด responsibility และ recovery policy ได้ชัดขึ้น
แต่จำนวน process ที่มากขึ้นก็เพิ่มสิ่งที่ต้องออกแบบ
- process authentication
- lifecycle dependency
- watchdog policy
- IPC security
- version compatibility
- shutdown order
- update consistency
Anti-Cheat แบบหลาย process จึงเป็น distributed system ขนาดเล็กที่ทำงานอยู่บนเครื่องเดียว
12. WH_GETMESSAGE Hook และการวาง GameProtect
GameMon โหลด protection DLL แล้วเรียก SetupMsgHook
ภายใน GameProtect/Hook/MsgHook/MsgHook.cpp ใช้
SetWindowsHookEx(
WH_GETMESSAGE,
GetMsgProc,
hinstDLL,
0
);
WH_GETMESSAGE hook ใช้เฝ้าข้อความที่กำลังถูกนำออกจาก message queue ผ่าน GetMessage หรือ PeekMessage
ใน architecture ของ HerculesAC กลไกนี้ถูกใช้เพื่อทำให้ GameProtect เข้าไปทำงานใน process ที่เข้ากันได้กับ hook
นี่อธิบายว่าทำไมโปรเจกต์ต้องมีทั้ง monitor และ protection DLL แบบ 32-bit และ 64-bit
อย่างไรก็ตาม global hook เป็นกลไกที่มี blast radius กว้าง
- DLL อาจถูกโหลดใน process มากกว่าที่ต้องการ
- compatibility แตกต่างกันในแต่ละ application
- bug ใน DLL สามารถทำให้ process อื่นมีปัญหา
- session และ desktop boundary มีผลต่อ behavior
- ต้องจัดการ architecture ให้ตรงกัน
สำหรับระบบ production การนำ sensor เข้า process จึงควรมี target policy ที่แคบที่สุด และต้องออกแบบ failure containment อย่างจริงจัง
13. Named Shared Memory — การส่ง Target Context
GameProtect จำเป็นต้องรู้ว่า process ใดคือ game client ที่ต้องเฝ้าระวัง
HerculesAC ใช้ named shared memory โดยสร้าง file mapping ชื่อ
hacSharedMemory
hacSharedMemory64
ข้อมูลหลักที่แลกเปลี่ยนมีลักษณะดังนี้
typedef struct _GAME_CLIENT {
DWORD dwPid;
TCHAR szClient[100];
TCHAR szGamePath[256];
} GAME_CLIENT;
Flow คือ
HerculesAC
│ starts game and knows PID
▼
GameMon
│ writes GAME_CLIENT data
▼
Named Shared Memory
│ read by injected component
▼
GameProtect
Microsoft อธิบาย named shared memory บน Windows ผ่าน CreateFileMapping และ MapViewOfFile ซึ่งทำให้หลาย process เปิด mapping object ชื่อเดียวกันแล้วแลกเปลี่ยนข้อมูลได้
HerculesAC ใช้แนวคิดนี้ได้ตรงไปตรงมาและเหมาะกับ payload ขนาดเล็ก
แต่ implementation ใน prototype มี comment ปิด synchronization ไว้ หมายความว่า production version ควรพิจารณาเพิ่ม
- synchronization primitive
- message version
- sequence number
- writer identity
- access control list
- integrity หรือ authentication tag
- validation ของ length และ string termination
- handling เมื่อ process หนึ่งตายระหว่างเขียนข้อมูล
IPC ไม่ได้เป็นเพียงท่อส่งข้อมูล แต่เป็น trust boundary ที่ attacker อาจพยายามส่งข้อมูลปลอมเข้ามาได้
14. GameProtect — Sensor ที่อยู่ใกล้ Behavior
GameProtect โหลด configuration และสร้าง shared memory จากนั้นติดตั้ง detour สำหรับ
ReadProcessMemoryWriteProcessMemory
เมื่อ process ที่มี DLL นี้เรียก API ดังกล่าว ระบบจะตรวจว่า target handle ชี้ไปยัง game process หรือไม่
14.1 WriteProcessMemory
หากพบความพยายามเขียน memory ของ game client ตัว prototype จะสั่งปิดเกม
Process calls WriteProcessMemory
│
▼
Is target PID the game?
│ │
No Yes
│ │
▼ ▼
Continue Record and respond
14.2 ReadProcessMemory
สำหรับการอ่าน memory ระบบใช้ counter ภายใน time window หากจำนวนการอ่านสูงเกิน threshold ก็ถือว่าน่าสงสัย
นี่เป็นตัวอย่างของความแตกต่างระหว่าง
- event detection — พบ write หนึ่งครั้งแล้วตอบสนอง
- rate-based detection — ประเมินจำนวน read ภายในช่วงเวลา
แนวคิด rate-based detection สำคัญ เพราะบาง API สามารถถูกเรียกอย่างถูกต้องโดยเครื่องมือระบบ ขณะที่การอ่านถี่ต่อเนื่องอาจสะท้อน behavior ที่ต่างออกไป
อย่างไรก็ตาม user-mode API hook มองเห็นเฉพาะ operation ที่ผ่าน function ที่ถูก hook ใน process ที่ DLL เข้าไปถึงเท่านั้น
มันไม่ควรถูกมองว่าเป็น complete mediation
User-mode hook เป็น sensor ที่มีประโยชน์ แต่ไม่ใช่แหล่งความจริงสูงสุด
นั่นคือเหตุผลที่ HerculesAC มี kernel component เพิ่มเข้ามา
15. Kernel Handle Mediation ด้วย ObRegisterCallbacks
herculeskernel ใช้ ObRegisterCallbacks เพื่อลงทะเบียน callback สำหรับ operation ที่เกี่ยวกับ process และ thread handles
Microsoft ระบุว่า API นี้ใช้ลงทะเบียน callback สำหรับ handle operation ของ process, thread และ desktop object
HerculesAC ลงทะเบียนทั้ง
PsProcessType
├── Handle create
└── Handle duplicate
PsThreadType
├── Handle create
└── Handle duplicate
ใน pre-operation callback ระบบตรวจ target object แล้วตัด access rights บางส่วนออก
สำหรับ process ได้แก่
DesiredAccess &= ~PROCESS_VM_READ;
DesiredAccess &= ~PROCESS_VM_WRITE;
สำหรับ thread ได้แก่
DesiredAccess &= ~THREAD_SUSPEND_RESUME;
Windows กำหนดให้ PROCESS_VM_READ เป็นสิทธิ์ที่ต้องใช้กับ ReadProcessMemory และ PROCESS_VM_WRITE เป็นสิทธิ์ที่ต้องใช้กับ WriteProcessMemory
ดังนั้นแทนที่จะรอให้ API ถูกเรียกแล้วค่อยตรวจ HerculesAC พยายามลดสิทธิ์ตั้งแต่ตอนที่ handle กำลังถูกสร้างหรือ duplicate
External Process
│
│ requests handle with VM_READ / VM_WRITE
▼
Windows Object Manager
│
▼
Hercules pre-operation callback
│
├── target is not protected → keep requested rights
└── target is protected → remove selected rights
นี่เป็นการย้าย control point จาก API layer ลงไปยัง Object Manager
Prototype detail ที่สำคัญ
ใน source ปัจจุบัน target PID ภายใน callback ถูก hard-code ไว้เป็นค่าหนึ่ง ซึ่งแสดงว่าส่วน kernel ยังเป็น experimental prototype มากกว่า production implementation
ระบบจริงควรมี protected-object registry ที่
- รับ PID จาก trusted controller
- ตรวจ identity ของ caller
- รองรับ process restart และ PID reuse
- ลบ entry เมื่อ process exit
- ป้องกัน race condition
- รองรับหลาย game process
- ไม่เปิด IOCTL ที่ให้ process ใดก็ได้เปลี่ยน policy
จุดนี้เป็นตัวอย่างที่ดีของความต่างระหว่าง พิสูจน์ว่าแนวคิดทำงานได้ กับ สร้าง security product ที่ deploy ได้จริง
16. Detection กับ Prevention ไม่ใช่สิ่งเดียวกัน
HerculesAC มีทั้งกลไก detection และ prevention
| กลไก | ประเภท |
|---|---|
| ตรวจชื่อ process | Detection |
| ตรวจ window class/title | Detection |
| Hash resource และ version metadata | Detection |
| ตรวจ checksum ของ function | Detection |
| ตรวจ unknown thread start | Detection/Prevention |
| Hook memory API | Observation/Response |
| ตัด process handle rights | Prevention |
| ตัด thread suspend right | Prevention |
| ปิดเกมเมื่อพบ signal | Response |
การแยกสามคำนี้ออกจากกันมีความสำคัญ
Detection
└── เราพบ signal อะไร
Prevention
└── เราหยุด operation ใดได้
Response
└── เราจะทำอะไรหลังพบหรือหยุด operation
Anti-Cheat ที่ตรวจพบทุกอย่างแต่ตอบสนองไม่ดี อาจทำให้ผู้เล่นถูกเตะผิด
Anti-Cheat ที่ block ทุกอย่างอาจทำให้ overlay, streaming, accessibility หรือ support tools ใช้งานไม่ได้
Anti-Cheat ที่รีบเปิดเผย detection ทันทีอาจทำให้คู่ต่อสู้เรียนรู้ rule ได้ง่ายขึ้น
ดังนั้น response policy ต้องคำนึงถึง
- confidence
- severity
- repeatability
- player impact
- evidence retention
- server authority
- appeal และ support process
17. User Mode ไม่พอ แต่ Kernel Mode ก็ไม่ใช่เวทมนตร์
การเพิ่ม kernel driver ทำให้ Anti-Cheat มีมุมมองและ control point ที่ user mode ไม่มี
แต่มันไม่ได้ทำให้ระบบ “ตรวจได้ทุกอย่าง” โดยอัตโนมัติ
User-mode strengths
- พัฒนาและ debug ง่ายกว่า
- update ได้เร็วกว่า
- เข้าถึง application context ได้ดี
- crash มักไม่ทำให้ทั้งระบบล่ม
- เหมาะกับ UI, orchestration และ high-level telemetry
User-mode limitations
- อยู่ใน privilege level เดียวกับโปรแกรมอื่นจำนวนมาก
- hook และ result สามารถถูกแก้ไขได้
- coverage ขึ้นกับ API และ process ที่ sensor เข้าถึง
- ไม่สามารถควบคุม kernel handle operation ได้โดยตรง
Kernel-mode strengths
- เห็น object และ handle operation ในระดับล่างกว่า
- บังคับ process/thread access policy ได้
- ยากกว่าสำหรับ user-mode process ที่จะเปลี่ยนผลลัพธ์โดยตรง
- รวม telemetry จากหลาย process ได้
Kernel-mode costs
- bug สามารถทำให้ระบบ BSOD
- compatibility กับ Windows build และ security feature ซับซ้อน
- driver signing และ deployment มีต้นทุน
- vulnerable driver อาจกลายเป็นเครื่องมือให้ attacker ใช้สิทธิ์ kernel
- privacy และ security impact สูงกว่า
Microsoft แนะนำให้ kernel driver จำกัด privileged behavior ให้อยู่ในขอบเขตที่จำเป็น ตรวจ input และไม่เปิด primitive อันตรายแบบ arbitrary ให้ user mode
ดังนั้นหลักคิดที่ถูกต้องไม่ใช่
ย้ายทุกอย่างลง kernel เพื่อให้ปลอดภัย
แต่คือ
ย้ายเฉพาะ enforcement ที่จำเป็นลง kernel และทำ interface ระหว่าง user mode กับ kernel ให้แคบ ตรวจสอบได้ และมี least privilege
18. Bypass Directory — เหตุผลที่ Defender ต้องสร้าง Attacker Model
ภายใน repository มี project ชื่อ Bypass
การมี component นี้ไม่ได้ลดคุณค่าของ Anti-Cheat แต่กลับสะท้อนวิธีคิดที่สำคัญมากในการทำ security engineering
ระบบป้องกันที่ไม่เคยถูกทดสอบจากมุมของผู้โจมตี เป็นเพียงระบบที่เราหวังว่าจะทำงาน
Bypass ทำหน้าที่เป็น adversarial test harness สำหรับตั้งคำถามกับสมมติฐาน เช่น
- ถ้า detection API ถูก hook ผลลัพธ์จะเป็นอย่างไร
- ถ้า integrity routine ถูกแทรกแซง ระบบยังเชื่อ checksum ได้หรือไม่
- ถ้า function address หรือ call path เปลี่ยน detection จะยังทำงานหรือไม่
- sensor รู้ได้อย่างไรว่าข้อมูลที่อ่านเป็นข้อมูลจริง
ในบทความนี้จะไม่ลงรายละเอียด implementation ของ bypass แต่สิ่งที่ควรเรียนรู้คือ workflow
Build Detection
│
▼
Build an Adversarial Test
│
▼
Break the Assumption
│
▼
Add Independent Signal
│
▼
Repeat
นี่คือวัฒนธรรมเดียวกับ secure code review, red teaming และ threat modeling
Anti-Cheat ไม่ควรมีเพียงทีมที่เขียน detector แต่ต้องมีคนที่พยายามพิสูจน์ว่า detector นั้นมองไม่เห็นอะไร
19. VMProtect และการปกป้องตัว Anti-Cheat
HerculesAC ใช้ VMProtect SDK ครอบบาง code path ผ่าน VMProtectBeginVirtualization, VMProtectEnd และ RAII wrapper
เป้าหมายของ software protector คือเพิ่มต้นทุนของการวิเคราะห์ static และ dynamic
แต่ protection layer ควรถูกมองว่าเป็น cost multiplier ไม่ใช่ root of trust
Without protection
Analysis cost = lower
With virtualization/obfuscation
Analysis cost = higher
But
Client code is still delivered to an untrusted machine
หาก Anti-Cheat ใช้ secret หรือ detection logic เดียวที่ฝังอยู่ใน client ในที่สุดข้อมูลนั้นอาจถูกศึกษาได้
การออกแบบที่แข็งแรงกว่าคือ
- ไม่พึ่ง client secret เพียงอย่างเดียว
- ใช้ server-side authority
- rotate rules และ manifests
- เก็บ evidence หลายประเภท
- ทำ protocol authentication
- ลดข้อมูลสำคัญที่ client จำเป็นต้องรู้
Obfuscation ช่วยซื้อเวลา แต่ architecture เป็นตัวกำหนดเพดานความปลอดภัย
20. บทเรียนจาก HerculesAC ในฐานะ Prototype
HerculesAC มีคุณค่าเพราะมันทำให้แนวคิดหลายอย่างกลายเป็น code ที่ทดลองได้จริง
ขณะเดียวกัน source ก็แสดงช่องว่างระหว่าง prototype กับ production ซึ่งเป็นบทเรียนที่สำคัญไม่แพ้ feature
20.1 Static name signature เปราะบาง
ชื่อ process และ window เหมาะกับ early detection แต่ควรรวมกับ behavior และ signer identity
20.2 MD5 ควรถูกแทนที่
ควรใช้ SHA-256 หรือ SHA-512 และใช้ signed manifest เพื่อป้องกันการแก้ทั้ง binary และรายการ hash
20.3 Polling มีต้นทุนและ race window
บาง thread ตรวจทุก 100 ms ขณะที่บาง loop ตรวจทุก 5 วินาที
Polling เขียนง่าย แต่ระบบขนาดใหญ่ควรประเมิน event-driven telemetry, adaptive interval และ CPU budget
20.4 Shared memory ต้องมี authentication และ synchronization
ชื่อ mapping ที่รู้ร่วมกันไม่เพียงพอสำหรับพิสูจน์ตัวตนของ writer
20.5 Kernel policy ต้อง dynamic
Hard-coded PID ควรถูกแทนที่ด้วย protected-process registry ที่จัดการ lifecycle และ PID reuse อย่างปลอดภัย
20.6 Kill-on-first-signal มี UX risk
การปิดเกมทันทีเหมาะกับ demo แต่ production ควรมี confidence model และ response tier
Low confidence
└── collect more telemetry
Medium confidence
└── restrict sensitive action / challenge / monitor
High confidence
└── end session and retain evidence
Confirmed server-side abuse
└── account enforcement according to policy
20.7 Global hook มี blast radius
ควรจำกัด target และออกแบบ cleanup เมื่อ monitor หรือ game crash
20.8 Kernel driver ต้องถูกออกแบบเหมือน security boundary
Driver ไม่ควรเปิด arbitrary read/write, process termination หรือ privileged primitive โดยไม่มี validation เพราะตัว driver อาจกลายเป็น vulnerability ใหม่ของระบบ
21. ถ้าจะพัฒนา HerculesAC เป็นรุ่นถัดไป
ถ้านำแนวคิดของ HerculesAC มาสร้างใหม่ในปัจจุบัน ผมจะแบ่งระบบออกเป็นชั้นดังนี้
┌──────────────────────────────────────────────────────┐
│ Game Server / Backend │
│ authoritative state, risk scoring, evidence policy │
└──────────────────────────┬───────────────────────────┘
│ authenticated telemetry
▼
┌──────────────────────────────────────────────────────┐
│ Local Anti-Cheat Service │
│ orchestration, update, policy, component health │
└──────────────┬──────────────────────┬────────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌─────────────────────────┐
│ In-Process Game Sensor │ │ Signed Kernel Driver │
│ code/module/thread │ │ handle mediation │
│ game-aware telemetry │ │ constrained telemetry │
└────────────────────────┘ └─────────────────────────┘
│ │
└──────────┬───────────┘
▼
Evidence Correlation
หลักการสำคัญคือ
- Server authoritative — สิ่งที่ server ตัดสินได้ไม่ควรเชื่อ client อย่างเดียว
- Multiple independent signals — detector หนึ่งตัวไม่ควรเป็น single point of truth
- Least privilege — แต่ละ component มีสิทธิ์เท่าที่จำเป็น
- Authenticated IPC — message ต้องระบุผู้ส่งและตรวจความถูกต้องได้
- Signed update chain — binary, config และ manifest ต้องตรวจที่มาได้
- Event-driven telemetry — ลด polling ที่ไม่จำเป็น
- Risk scoring — แยก anomaly ออกจาก confirmed cheating
- Evidence-first response — เก็บข้อมูลพอสำหรับ debugging และ appeal
- Privacy-aware collection — เก็บเฉพาะข้อมูลที่สัมพันธ์กับการป้องกันเกม
- Fail safely — Anti-Cheat ขัดข้องไม่ควรทำให้ระบบปฏิบัติการเสียหาย
22. Anti-Cheat คือระบบตรวจสอบความเชื่อใจ ไม่ใช่โปรแกรมล่ารายชื่อ
สิ่งที่ผมได้เรียนรู้จาก HerculesAC คือการตรวจชื่อโปรแกรมเป็นส่วนที่เขียนง่ายที่สุด
ส่วนที่ยากจริงคือ
- เชื่อม lifecycle ของ launcher, game และ monitor
- ส่ง context ให้ sensor หลาย architecture
- ตรวจว่า sensor ถูกแก้ไขหรือไม่
- แยก thread ปกติออกจาก execution ที่ไม่คาดคิด
- ควบคุม process handle ใน kernel
- ทำให้ policy ไม่รบกวน software ปกติ
- รักษา performance ของเกม
- ป้องกันไม่ให้ driver กลายเป็นช่องโหว่
- สร้าง evidence ที่อธิบายได้ว่าทำไมจึงตอบสนอง
Anti-Cheat จึงอยู่กึ่งกลางระหว่างหลายสาขา
Windows Internals
+
Reverse Engineering
+
Kernel Development
+
Application Security
+
Distributed Systems
+
Telemetry Engineering
+
Game Design
+
Adversarial Thinking
=
Anti-Cheat Engineering
23. HerculesAC ในฐานะ Portfolio
HerculesAC อาจไม่ใช่ commercial Anti-Cheat และ source บางส่วนยังมีลักษณะเป็น experiment
แต่นั่นไม่ได้ทำให้มันไม่มีคุณค่า
ตรงกันข้าม มันเป็นหลักฐานของกระบวนการเรียนรู้ที่ครอบคลุมหลายชั้น
| ส่วนของ HerculesAC | สิ่งที่แสดงออกมา |
|---|---|
| Launcher chain | Process orchestration และ application lifecycle |
| x86/x64 monitor | ความเข้าใจ Windows architecture boundary |
| Shared memory | IPC และ cross-process state sharing |
| DLL sensor | Loader, hooks และ process-local observation |
| Resource fingerprinting | PE resources และ executable identity |
| Code checksum | Self-integrity และ tamper detection |
| Thread tracking | Execution provenance และ runtime behavior |
| Kernel callbacks | Object Manager และ handle access control |
| Bypass test project | Adversarial validation และ threat modeling |
| Build/config scripts | Packaging, manifest และ deployment flow |
การสร้างระบบประเภทนี้คือการบอกว่า
“ผมไม่ได้เพียงรู้ว่า cheat ทำอะไร แต่ผมสามารถสร้างระบบหลาย component เพื่อสังเกต ควบคุม และทดสอบ trust boundary ของ Windows ได้”
นี่คือเหตุผลที่ Anti-Cheat สามารถเป็น portfolio ของ Windows system programmer ได้ในแบบเดียวกับที่ ARK Tool แสดงความรู้ด้าน kernel inspection
ความแตกต่างคือ ARK ถามว่า
ระบบกำลังซ่อนอะไรจากเรา?
ขณะที่ Anti-Cheat ถามว่า
เราจะรู้ได้อย่างไรว่าสถานะของเกมและ execution path ยังไม่ได้ถูกเปลี่ยนโดยบุคคลอื่น?
ทั้งสองงานเริ่มจากหลักคิดเดียวกัน
อย่าเชื่อข้อมูลจากมุมมองเดียว โดยเฉพาะเมื่อผู้ที่ถูกตรวจสอบสามารถแก้ไขมุมมองนั้นได้
24. สรุปท้ายบท
HerculesAC แสดงให้เห็นว่า Anti-Cheat ไม่ได้ประกอบด้วย detector เพียงหนึ่งตัว แต่เป็นระบบที่มีหลาย responsibility
GameLauncherสร้างจุดเริ่มต้นของ trust chainHerculesACทำหน้าที่ orchestrate game และตรวจ signal ใน user modeGameMonจัดการ lifecycle และ architecture-specific deploymentGameProtectวาง sensor ใกล้ memory-access behavior- Named shared memory ส่ง target context ระหว่าง component
- Resource และ metadata hashing เพิ่มมุมมองมากกว่าการตรวจชื่อไฟล์
- Code checksum ทำให้ detector เริ่มตรวจสอบตัวเอง
- Thread tracking พยายามอธิบาย provenance ของ execution
herculeskernelย้าย enforcement ลงไปยัง process/thread handle layerBypassสะท้อนว่าระบบป้องกันต้องถูกทดสอบจากมุมของฝ่ายตรงข้าม
สิ่งสำคัญที่สุดไม่ใช่ว่า HerculesAC ตรวจพบเครื่องมือได้กี่ตัว
แต่คือการที่โปรเจกต์นี้ทำให้ผมเห็นว่า Anti-Cheat เป็นปัญหาของ trust boundary, visibility และ evidence
Client machine ไม่เคยเป็นสภาพแวดล้อมที่เราเชื่อถือได้ทั้งหมด
User-mode sensor สามารถถูกแก้ไข
Kernel driver สามารถมีช่องโหว่
Static signature สามารถล้าสมัย
Behavioral rule สามารถสร้าง false positive
และ game server ก็ไม่สามารถมองเห็นทุกสิ่งที่เกิดขึ้นในเครื่องผู้เล่น
ดังนั้น Anti-Cheat ที่แข็งแรงไม่ได้เกิดจากการค้นหา “Ring ที่ลึกที่สุด” หรือ API ที่ตรวจได้มากที่สุด
มันเกิดจากการนำหลายมุมมองมาประกอบกัน แล้วออกแบบให้ความล้มเหลวของ sensor หนึ่งตัวไม่ทำให้ทั้งระบบสูญเสียความจริงทั้งหมด
Anti-Cheat ไม่ใช่การแข่งขันว่าใครซ่อนหรือค้นหาโปรแกรมได้เก่งกว่าเพียงอย่างเดียว แต่มันคือการแข่งขันว่าใครเข้าใจระบบ ความเชื่อใจ และข้อจำกัดของหลักฐานได้ลึกกว่ากัน
และสำหรับผม HerculesAC คือหนึ่งในโปรเจกต์ที่ทำให้เริ่มเข้าใจปัญหานั้นผ่าน code จริง
Happy Hacking :)
References
- HerculesAC Source Code
- Microsoft Learn — ObRegisterCallbacks
- Microsoft Learn — OB_PRE_OPERATION_INFORMATION
- Microsoft Learn — OB_PRE_CREATE_HANDLE_INFORMATION
- Microsoft Learn — Process Security and Access Rights
- Microsoft Learn — SetWindowsHookExW
- Microsoft Learn — Hooks Overview
- Microsoft Learn — Creating Named Shared Memory
- Microsoft Learn — Best Practices for Constraining High-Privileged Behavior in Kernel Drivers
- Microsoft Learn — MD5