本文为看雪论坛精华文章
看雪论坛作者ID:鬼才zxy
一
BattlEye概述
BEService - 与BattlEye服务器通信的服务。
BEDaisy - 内核驱动,执行各种内核层的检测,并与BEClient通信。
BEClient - 一个DLL,运行在游戏进程中,负责执行各种应用层的BE shellcode,并与内核驱动进行通信。
BEServer - BattlEye服务器,收集上传的信息,并判定作弊行为。
二
BattlEye内核驱动检测模块深入分析
三
上传部分
struct AbnormalListItem {
// because nobody writes to report list 0, so some parts of the structure is unknown
BYTE Unknown[10];
BYTE Content[64];
};
struct UploadPatternBlackListItemType0 {
// -1 means no specified match offset, it will try every possible offset
// not -1 means a specified offset, it will just try the offset
BYTE MatchOffset;
// if the length <= 32, it will be copied to the g_PatternBlackList
BYTE PatternLength;
// length depends on PatternLength
BYTE Content[0];
};
struct PatternBlackListItemType0 {
// pattern in black list up to 32 bytes
BYTE Pattern[32];
// length up to 32
ULONG Length;
};
48 81 C4 80 01 00 00 5F C3
add rsp, 180h
pop rdi
ret
struct UploadPatternBlackListItemType1or2 {
// -1 means universal pattern, this check will be applied to each callback
// not -1 means this check only works on a specific callback
BYTE FunctionType;
// -1 means no specified match offset, it will try every possible offset
// not -1 means a specified offset, it will just try the offset
BYTE MatchOffset;
// length of the pattern
BYTE PatternLength;
// length depends on PatternLength
BYTE Content[0];
};
struct UploadSelfIntegrityCheck {
// if it is true, it means use stored driver memory range
// if it is false, it means use driver memory range read from driver object
BOOLEAN UseStoredDriverInfo;
// offset to the driver module
ULONG Offset;
// unknown, has an impact on the reporting policy
// if the flag is true, then normal means upload, abnormal means don't upload
// maybe use to detect some kind of attack?
BOOLEAN FlipReportPolicy;
// compare size, up to 64 bytes
ULONG CompareSize;
// content of normal data, length depends on CompareSize
BYTE Content[0];
};
struct UploadDxgkrnlInternalFunctionRangeCheck {
// length = upload packet length - 1
BYTE Pattern[0];
// how far is the function address from the pattern matching address
BYTE Offset;
}
四
检测部分
派遣函数完整性检测
系统线程启动地址检测
进程、线程回调功能性检测
游戏进程线程创建检测
PsLookupThreadByThreadId hook检测
[未知]
进程、线程、注册表回调hook检测
进程、线程、注册表回调地址模块范围检测
PhysicalMemory引用检测
系统调用完整性检测
[未知]
模块异常指令检测
DxgCoreInterface 地址范围检测
DxgCoreInterface hook检测
系统线程堆栈检测
隐藏驱动检测
[未知]
回调函数信息上报
BE驱动完整性检测
模块IAT hook检测
gDxgkInterface 地址范围检测
gDxgkInterface hook检测
Dxgkrnl某内部未导出函数范围检测(disabled)
infinity hook 检测
gDxgkWin32kEngInterface 地址范围检测
gDxgkWin32kEngInterface hook检测
PCI设备检测
HalDispatchTable 地址范围检测
HalDispatchTable hook检测
HalPrivateDispatchTable 地址范围检测
HalPrivateDispatchTable hook检测
FltMgrMsg对象callback模块范围检测
FltMgrMsg对象callback hook检测
ext_ms_win_core_win32k_full_export_l1 地址范围检测
...
struct PacketSelfIntegrityCheck {
// 18 is self integrity check
BYTE PacketType;
// if it is true, it means use stored driver memory range
// if it is false, it means use driver memory range read from driver object
BOOLEAN UseStoredDriverInfo;
// offset to the driver module
ULONG Offset;
// content of checked address, 64 bytes
BYTE Content[64];
};
函数指针修改
函数地址不在模块范围内(手动映射的驱动的hook)
追踪跳转后,函数地址不在模块范围内(类似上一个异常情况)
存在int 3断点,说明系统正在被调试
struct PacketSyscallIntegrityCheck {
// 9 is syscall integrity check
BYTE PacketType;
// each syscall function has an index
BYTE FuncIndex;
// -1: fine
// 0: function pointer modification
// 1: address out of module range
// 2: after jump, address out of range
// 3: int3 trap, may be under debugging
BYTE ErrorType;
// after useless jump instructions, the function body's address
PVOID Address;
// dump 64 bytes
BYTE Content[64];
};
struct PacketSystemThreadStartAddressCheck {
// 1 is system thread start address check
BYTE PacketType;
// start address read from SYSTEM_PROCESS_INFORMATION structure
PVOID StartAddress;
// dump 64 bytes from start address
BYTE Content[64];
// thread running time
// from thread creation to now
LARGE_INTEGER RunningTime;
// CountdonwId = SystemProcessInformation->NumberOfThreads - AbnormalThreadIndex - 1
// counting thread indexes from back to front
// making the ID generic
USHORT CountdownId;
// thread create time
// between process creation and thread creation
LARGE_INTEGER CreateTime;
};
struct PacketSystemThreadStartAddressCheck {
// 14 is system thread stack check
BYTE PacketType;
// bad caller index in the RtlWalkFrameChain result
BYTE CallerIndex;
// bad caller's return address
PVOID Address;
// 64 bytes of caller's content
BYTE Content[64];
// notice: only 32 bits
// which thread has the bad caller
ULONG ThreadId;
// image name length
BYTE ImageNameLength;
// image name buffer
// length depends on the ImageNameLength
BYTE ImageName[0];
// low 32 bits of StartAddress, always upload
ULONG LowStartAddress;
// may be null if the StartAddress is invalid
PVOID StartAddress;
// may be null if the StartAddress is invalid
HANDLE ProcessId;
// thread running time
// from thread creation to now
LARGE_INTEGER RunningTime;
// CountdonwId = SystemProcessInformation->NumberOfThreads - AbnormalThreadIndex - 1
// counting thread indexes from back to front
// making the ID generic
USHORT CountdownId;
// thread create time
// between process creation and thread creation
LARGE_INTEGER CreateTime;
// track the E9 jumps after the return address up to 60 bytes,
// record up to 10 addresses
BYTE FollowAddressCount;
// size depends on the FollowAddressCount
PVOID FollowAddressArr[0];
};
FF 25 XX XX XX XX: jmp [addr]
48 B8 XX XX XX XX XX XX XX XX: mov rax, imm
FF E0: jmp rax
struct PacketCallbackHookCheck {
// 6 is callback hook check
BYTE PacketType;
// function type:
// 0: process callback
// 1: thread callback
// 2: register callback
// 3: image notify callback
BYTE FunctionType;
// hooked offset to the callback function begin
BYTE HookOffset;
// absolute hooked address
PVOID HookAddress;
// dump 16 bytes of callback head
BTYE CallbackHeadContent[16];
// where to jump
PVOID JumpAddress;
// content of address after the jump
BYTE HookContent[64];
// up to 260 bytes, no terminator
CHAR ModulePath[0];
};
struct PacketCallbackRangeCheck {
// 7 is callback range check
BYTE PacketType;
// function type:
// 0: process callback
// 1: thread callback
// 2: register callback
// 3: image notify callback
BYTE FunctionType;
// address of the function
PVOID Address;
// 64 bytes content of the callback
BYTE Content[64];
};
struct PacketCallbackCheck {
// 17 is callback check
BYTE PacketType;
// function type:
// 0: process callback
// 1: thread callback
// 2: register callback
// 3: image notify callback
BYTE FunctionType;
// address of the callback
PVOID Address;
// 64 bytes content of the callback
BYTE Content[64];
// module path if exists, no terminator
CHAR ModulePath[0];
};
struct PacketPhysicalMemoryReferenceCheck {
// 8 is physical memory reference check
BYTE PacketType;
// fields in struct _CONTROL_AREA
ULONG64 NumberOfSectionReferences;
ULONG64 NumberOfPfnReferences;
ULONG64 NumberOfMappedViews;
ULONG64 NumberOfUserReferences;
};
struct PacketProcessThreadCallbackFunctionalityCheck {
// 2 is process thread callback functionality check
BYTE PacketType;
// probably always true
BOOLEAN Abnormal;
};
struct PacketDispatchFunctionIntegrityCheck {
// 0 is dispatch function integrity check
BYTE PacketType;
// driver name
// length = PacketLength - OtherFieldsLength
CHAR DriverName[0];
// major number
BYTE MajorNumber;
// hook function address
PVOID Address;
// 64 bytes of hook function
BYTE Content[64];
};
struct PacketPsLookupThreadByThreadIdHookCheck {
// 4 is PsLookupThreadByThreadId hook check
BYTE PacketType;
// PsLookupThreadByThreadId address
PVOID FunctionAddress;
// FF 25 (4 bytes offset)
ULONG JumpOffset1;
// address after the first jump
PVOID HookFunction1;
// whether there is another jump
BOOLEAN TwoJump;
union {
// no another jump
// dump 16 bytes of the first hook function
BYTE Content1[16];
// have another jump
struct {
// record the second hook function
PVOID HookFunction2;
// dump 16 bytes of the second hook function
BYTE Content2[16];
};
};
};
ConnectNotifyCallback
DisconnectNotifyCallback
MessageNotifyCallback
struct PacketFltMgrMsgCallbackRangeCheck {
// 31 is FltMgrMsg callback range check
BYTE PacketType;
// function type:
// 0: ConnectNotifyCallback
// 1: DisconnectNotifyCallback
// 2: MessageNotifyCallback
BYTE FunctionType;
// address of the function
PVOID Address;
// 64 bytes content of the callback
BYTE Content[64];
};
struct PacketFltMgrMsgCallbackHookCheck {
// 32 is FltMgrMsg callback hook check
BYTE PacketType;
// function type:
// 0: ConnectNotifyCallback
// 1: DisconnectNotifyCallback
// 2: MessageNotifyCallback
BYTE FunctionType;
// hooked offset to the callback function begin
BYTE HookOffset;
// absolute hooked address
PVOID HookAddress;
// dump 16 bytes of callback head
BTYE CallbackHeadContent[16];
// where to jump
PVOID JumpAddress;
// content of address after the jump
BYTE HookContent[64];
// up to 260 bytes, no terminator
CHAR ModulePath[0];
};
struct PacketDxgkrnlInternalFunctionRangeCheck {
// 22 is unknown function range check
BYTE PacketType;
// address of the function
PVOID Address;
// 64 bytes content of the function
BYTE Content[64];
};
struct PacketInfinityHookRangeCheck {
// 23 is infinity hook range check
BYTE PacketType;
// address of the function
PVOID Address;
// 64 bytes content of the function
BYTE Content[64];
};
hal.dll
clipsp.sys
CI.dll
tpm.sys
ks.sys
cdd.dll
TSDDD.dll
spsys.sys
atikmpag.sys
struct PacketModuleAbnormalInstructionCheck {
// 11 is module abnormal instruction check
BYTE PacketType;
// length of the module name, up to 64
BYTE ModuleNameLength;
// length depends on ModuleNameLength
CHAR ModuleName[0];
// offset in page
ULONG OffsetInPage;
// content of the page which contains the abnormal instruction, up to 0x1000 bytes
BYTE Content[0];
};
struct PacketModuleIATHookCheck {
// 19 is module IAT hook check
BYTE PacketType;
// module name
// no length is recorded yet !
CHAR ModuleName[0];
// function index in the IAT
ULONG FunctionIndex;
// offset of the function IAT entry to the module base
ULONG EntryOffset;
// function in the IAT entry
PVOID Function;
// content of the function
BYTE Content[64];
};
struct PacketHiddenDriverCheck {
// 15 is hidden driver check
BYTE PacketType;
// length of the device name
BYTE DeviceNameLength;
// name of the device whose driver is hidden
CHAR DeviceName[0];
// driver name of the hidden driver
// length = PacketLength - OtherFieldsLength
CHAR DriverName[0];
};
struct PacketHiddenDriverCheckType1 {
// 26 is pci device check
BYTE PacketType;
// PCI enumeration info
struct {
BYTE Bus;
BYTE Dev;
BYTE Func;
} Info;
// 4 bytes read from reg VENDOR_ID (0x0)
ULONG VendorId;
// 4 bytes read from reg PCI_CLASS (0x08)
ULONG PciClass;
// 1 byte read from reg HDRTYPE (0x0E)
BYTE HdrType;
// 4 bytes read from reg PCI_SUB_VENDOR_ID (0x2C)
ULONG SubVendorId;
};
struct PacketHiddenDriverCheckType2 {
// 26 is pci device check
BYTE PacketType;
// PCI enumeration info
struct {
BYTE Bus;
BYTE Dev;
BYTE Func;
} Info;
// 256 bytes read from reg VENDOR_ID (0x0)
BYTE VendorId[256];
};
struct PacketWin32kRangeCheckType1 {
// 20 is win32k gDxgkInterface range check
BYTE PacketType;
// function index in the gDxgkInterface table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
struct PacketWin32kRangeCheckType2 {
// 20 is win32k gDxgkWin32kEngInterface range check
BYTE PacketType;
// function index in the gDxgkWin32kEngInterface table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
mov rax, [addr1]
test rax, rax
je addr2
call qword ptr [addr3]
struct PacketWin32kRangeCheckType3 {
// 33 is win32k ext_ms_win_core_win32k_full_export_l1 range check
BYTE PacketType;
// function index in the ext_ms_win_core_win32k_full_export_l1 table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
struct PacketDxgkrnlRangeCheck {
// 12 is Dxgkrnl DxgCoreInterface range check
BYTE PacketType;
// function index in the DxgCoreInterface table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
struct PacketHalDispatchTableRangeCheck {
// 27 is HalDispatchTable range check
BYTE PacketType;
// function index in the HalDispatchTable table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
struct PacketHalPrivateDispatchTableRangeCheck {
// 29 is HalPrivateDispatchTable range check
BYTE PacketType;
// function index in the HalPrivateDispatchTable table
ULONG Index;
// function address
PVOID Function;
// 64 bytes of the function
BYTE Content[64];
};
jmp [addr]
mov rax, imm
jmp rax
struct PacketDispatchFunctionHookCheck {
// 5 is dispatch function hook check
BYTE PacketType;
// major number
BYTE MajorNumber;
// offset of the hook instructions to the function begin
BYTE HookOffset;
// address of the hook instructions
PVOID HookAddress;
// 16 bytes of the hook instructions
BYTE HookInstructions[16];
// hook function
PVOID HookFunction;
// 64 bytes of the hook function
BYTE Content[64];
// driver name read from the driver object (DriverObject->DriverName)
CHAR DriverName[0];
};
struct PacketOpenDriverObjectFailedCheck {
// 10 is open driver object failed check
BYTE PacketType;
// eg:\\Driver\\xxx or \\FileSystem\\xxx
CHAR DriverName[0];
// ObOpenObjectByName status
NTSTATUS Status;
};
struct PacketGameThreadCreateCheck {
// 3 is thread create check
BYTE PacketType;
// start address of the thread being created
PVOID StartAddress;
};
五
总结
六
相关工作
七
其他
看雪ID:鬼才zxy
https://bbs.pediy.com/user-home-749612.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!