Today we’ll talk about how BattlEye does integrity checks for loaded images, as well as implementing a work-around for these checks.
Image integrity checks
BattlEye does checks on images that get loaded by opening a handle to the file on disk with CreateFile, after this handle’s open, it retrieves certificate details for the file, and checks if it’s one of the blacklisted certificates. If it is, the file gets blocked from loading and BattlEye notifies you that a blacklisted file was attempting load.
In the section below, we’ll talk about how to implement a work around for this.
Implementation
Contrary to the what they do for other functions, they don’t do any integrity checks on CreateFile, and when you start any protected game, BEService (BattlEye’s user-mode component) stays unprotected until the game’s fully initialized, and no memory integrity checks are in place during this period either, so we can just hook it without any tricks.
Based on the information I talked about in the section above, we can fool BattlEye into believing that it’s opening a handle to an official Microsoft certified file, and we get straight through their certification checks.
HANDLE CreateFileW_hk( LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile )
{
if ( wcsstr( lpFileName, "our_file" ) )
return CreateFileW( "...\\Kernel32.dll", dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile );
return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile );
}
What this does is it checks if the lpFileName
parameter contains our file name, if it does then we just return CreateFileW but with the path to Kernel32 instead.
Proof of Concept
I’ve made a proof of concept injector, alongside a Rainbow Six: Siege example here.