内容简介:Cheat developers have specific interest in anti-cheat self-integrity checks. If you can circumvent them, you can effectively patch out or “hook” any anti-cheat code that could lead to a kick or even a ban. In EasyAntiCheat’s case, they use a kernel-mode dr
Cheat developers have specific interest in anti-cheat self-integrity checks. If you can circumvent them, you can effectively patch out or “hook” any anti-cheat code that could lead to a kick or even a ban. In EasyAntiCheat’s case, they use a kernel-mode driver which contains some interesting detection routines. We are going to examine how their integrity checks work and how to circumvent them, effectively allowing us to disable the anti-cheat.
[1] EPT stands for Extended Page Tables. It is a technology from Intel for MMU virtualization support. Check out Daax’s hypervisor development series if you want to learn more about virtualization.
The first thing to do is actually determine if there is any sort of integrity check. The easiest way is to patch any byte from .text
and see if the anti-cheat decides to kick or ban you after some time. About 10-40 seconds after I patched a random function, I was kicked, revealing that they are indeed doing integrity checks in their kernel module. With the assistance of my hypervisor-based debugger, which makes use of EPT facilities [1], I set a memory breakpoint on a function that was called by their LoadImage notify routine (see PsSetLoadImageNotifyRoutine
). After some time, I could find where they were accessing memory.
After examining xrefs in IDA Pro and setting some instruction breakpoints, I discovered where the integrity check function gets called from, one of them being inside the CreateProcess notify routine (see PsSetCreateProcessNotifyRoutine ). This routine takes care of some parts of the anti-cheat initialization, such as creating internal structures that will be used to represent the game process. EAC won’t initialize if it finds out that their kernel module has been tampered with.
The integrity check function itself is obfuscated, mainly containing junk instructions, which makes analyzing it very annoying. Here’s an example of obfuscated code:
mov [rsp+arg_8], rbx ror r9w, 2 lea r9, ds:588F66C5h[rdx*4] sar r9d, cl bts r9, 1Fh mov [rsp+arg_10], rbp lea r9, ds:0FFFFFFFFC17008A9h[rsi*2] sbb r9d, 2003FCE1h shrd r9w, cx, cl shl r9w, cl mov [rsp+arg_18], rsi cmc mov r9, cs:EasyAntiCheatBase
With the assist of Capstone , a public disassembly framework, I wrote a simple tool that disassembles every instruction from a block of code and keeps track of register modifications. After that, it finds out which instructions are useless based on register usage and remove them. Example of output:
mov [rsp+arg_8], rbx mov [rsp+arg_10], rbp mov [rsp+arg_18], rsi mov r9, cs:EasyAntiCheatBase
Time to reverse this guy!
The integrity check function
This is the C++ code for the integrity check function:
bool check_driver_integrity() { if ( !peac_base || !eac_size || !peac_driver_copy_base || !peac_copy_nt_headers ) return false; bool not_modified = true; const auto num_sections = peac_copy_nt_headers->FileHeader.NumberOfSections; const auto* psection_headers = IMAGE_FIRST_SECTION( peac_copy_nt_headers ); // Loop through all sections from EasyAntiCheat.sys for ( WORD i = 0; i < num_sections; ++i ) { const auto characteristics = psection_headers[ i ].Characteristics; // Ignore paged sections if ( psection_headers[ i ].SizeOfRawData != 0 && READABLE_NOT_PAGED_SECTION( characteristics ) ) { // Skip .rdata and writable sections if ( !WRITABLE_SECTION( characteristics ) && ( *reinterpret_cast< ULONG* >( psection_headers[ i ].Name ) != 'adr.' ) ) { auto psection = reinterpret_cast< const void* >( peac_base + psection_headers[ i ].VirtualAddress ); auto psection_copy = reinterpret_cast< const void* >( peac_driver_copy_base + psection_headers[ i ].VirtualAddress ); const auto virtual_size = psection_headers[ i ].VirtualSize & 0xFFFFFFF0; // Compare the original section with its copy if ( memcmp( psection, psection_copy, virtual_size ) != 0 ) { // Uh oh not_modified = false; break; } } } } return not_modified; }
As you can see, EAC allocates a pool and makes a copy of itself (you can check that by yourself) that will be used in their integrity check. It compares the bytes from EAC.sys with its copy and see if both match. It returns false if the module was patched.
Since the integrity check function is obfuscated, it would be pretty annoying to find it because it is subject to change between releases. Wanting the bypass to be simple, I began brainstorming some alternative solutions.
The .pdata
section contains an array of function table entries, which are required for exception handling purposes. As the semantics of the function itself is unlikely to change, we can take advantage of this information!
In order to make the solution cleaner, we need to patch EasyAntiCheat.sys
and its copy to disable the integrity checks. To find the pool containing the copy, we can use the undocumented API
ZwQuerySystemInformation
and pass SystemBigPoolInformation (0x42)
as the first argument. When the call is successful, it returns a
SYSTEM_BIGPOOL_INFORMATION
structure, which contains an array of
SYSTEM_BIGPOOL_ENTRY
structures and the number of elements returned in that array. The SYSTEM_BIGPOOL_ENTRY
structure contains information about the pool itself, like its pooltag, base and size. Using this information, we can find the pool that was allocated by EAC and modify its contents, granting us the unhindered ability to patch any EAC code without triggering integrity violations.
PoC code is released here
It contains the bypass for the integrity check and a patch to a function that’s called by their pre-operation callbacks, registered by ObRegisterCallbacks , letting you create handles to the target process. I’m aware that this is by no means an ideal solution because you’d need to take care of other things, like handle enumeration, but I’ll leave this as an exercise for the reader. You are free to improve this example to suit your needs.
The tests were made on four different games: Rust, Apex Legends, Ironsight and Cuisine Royale.
Have fun! See you in the next article!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法分析-有效的学习方法(影印版)
Jeffrey J.McConnell / 高等教育出版社 / 2003-03-01 / 28.0
本书主要目标是提高读者关于算法对程序效率的影响等问题的认知水平,并培养读者分析程序中的算法所必需的技巧。各章材料以激发读者有效的、协同的学习方法的形式讲述。通过全面的论述和完整的数学推导,本书帮助读者最大限度地理解基本概念。 本书内容包括促使学生参与其中的大量程序设计课题。书中所有算法以伪码形式给出,使得具备条件表达式、循环与递归方面知识的读者均易于理解。本书以简洁的写作风格向读者介绍了兼具......一起来看看 《算法分析-有效的学习方法(影印版)》 这本书的介绍吧!