基本保护分析
注入功能测试
R3分析
反虚拟机
虚拟设备检测
CreateFileW
和CreateFileA
这两个 API,可以看出在尝试打开如下的设备和文件。\.vmmemctl
C:Windowssystem32DRIVERSvm3dmp.sys
C:Windowssystem32driversvm3dmp_loader.sys
...
HANDLE gh_CreateFileW(...) {
for (auto it : DeviceFileBlacklist) {
if (CaseInsensitiveContains(lpFileName, it)) {
DBG_PRINT("black device "%ws" not allowed to openn", lpFileName);
return INVALID_HANDLE_VALUE;
}
}
HANDLE hFile = CreateFileW(...);
bool flag = true;
for(auto it:FileBlacklist){
if (CaseInsensitiveContains(lpFileName,it)) {
flag = false;
break;
}
}
DBG_PRINT("CreateFileW called with %ws return value %pn", lpFileName, hFile);
return hFile;
}
yxxxshen.exe
和mxxxbase.dll
两个模块做 IAT hook 即可。下面是拦截成功的一些日志,实际上还有更多的设备,这里不一一展示:[Debug Info]black device "\.vmmemctl" not allowed to open
[Debug Info]black device "C:Windowssystem32DRIVERSvm3dmp.sys" not allowed to open
[Debug Info]black device "C:Windowssystem32driversvm3dmp_loader.sys" not allowed to open
...
进程检测
Process32NextW
,应该是检测虚拟机的相关进程,这里直接匹配当前虚拟机存在的一些虚拟机特有的进程不让它返回即可。BOOL gh_ProcessNextW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe) {
BOOL ret = Process32NextW(hSnapshot, lppe);
WCHAR *szExeFile = lppe->szExeFile;
while (CaseInsensitiveContains(szExeFile, L"vm")||CaseInsensitiveContains(szExeFile,L"VGAuthService") && ret) {
DBG_PRINT("Found Vm in Process name %ws,try to execute againn", szExeFile);
ret = Process32NextW(hSnapshot, lppe);
szExeFile = lppe->szExeFile;
DBG_PRINT("new Process Name %ws pid=%d ret=%dn", lppe->szExeFile, lppe->th32ProcessID, ret);
}
DBG_PRINT("ProcessNextW called with %ws pid=%d ret=%dn", lppe->szExeFile,lppe->th32ProcessID ,ret);
return ret;
}
VGAuthService
即可。下面是一些拦截成功的日志:[Debug Info]Found Vm in Process name vm3dservice.exe,try to execute again
[Debug Info]new Process Name vmtoolsd.exe pid=3916 ret=1
[Debug Info]Found Vm in Process name vmtoolsd.exe,try to execute again
[Debug Info]new Process Name svchost.exe pid=3928 ret=1
[Debug Info]ProcessNextW called with svchost.exe pid=3928 ret=1
驱动目录检测
NtOpenDirectoryObject
和NtQueryDirectoryObject
两个 API,经过测试发现它打开了Device
路径,也就是开始遍历了驱动对象。NTSTATUS gh_NtQueryDirectoryObject(...) {
auto ret = NtQueryDirectoryObject(...);
auto info = (POBJECT_DIRECTORY_INFORMATION)Buffer;
for(auto it:DeviceBlackList){
if(CaseInsensitiveEqual(info->Name.Buffer,it)){
DBG_PRINT("NtQueryDirectoryObject name="%wZ" return %d Deny to open!n",
info->Name, info->TypeName, ret);
info->Name = DeniedDevice;
return 0;
}
}
DBG_PRINT("NtQueryDirectoryObject name="%wZ",Type="%wZ" return %dn",
info->Name, info->TypeName, ret);
return ret;
}
[Debug Info]NtQueryDirectoryObject name="gpuenergydrv",Type="Device" return 0
[Debug Info]NtQueryDirectoryObject name="VMCIHostDev" return 697297488 Deny to open!
[Debug Info]NtQueryDirectoryObject name="00000068",Type="Device" return 0
计时器检测
ULONGLONG st=40000;
ULONGLONG gh_GetTickCount64() {
auto ret = GetTickCount64();
if (st == 0) {
DBG_PRINT("GetTickCount64 called %lldn", ret);
st = ret;
}
else {
DBG_PRINT("GetTickCount64 called change %lld to %lldn", ret, st);
ret = st;
st = 0;
}
return ret;
}
[Debug Info]GetTickCount64 called 4117687
[Debug Info]GetTickCount64 called change 4117718 to 4117687
[Debug Info]GetTickCount64 called 4117734
[Debug Info]GetTickCount64 called change 4117812 to 4117734
MAC地址检测
ULONG gh_GetAdaptersInfo(...) {
auto ret = GetAdaptersInfo(AdapterInfo, SizePointer);
DBG_PRINT("GetAdaptersInfo called with %p %p return %dn",...);
//换成intel的MAC地址60:45:2E
AdapterInfo->Address[0] = 0x60;
AdapterInfo->Address[1] = 0x45;
AdapterInfo->Address[2] = 0x2E;
return ret;
}
注册表检测
[Debug Info]RegOpenKeyExA called with FFFFFFFF80000002 "SYSTEMCurrentControlSetservicesvm3dmp_loader" 0 131353 000000702B0FF5B0 return 0
[Debug Info]CreateFileW called with C:Program Files (x86)mihoyogamesGenshin Impact Gameyuanshen_DataPersistentbase_res_version_hash return value 0000000000000CDC
[Debug Info]black device "C:Windowssystem32driversvm3dmp_loader.sys" not allowed to open
最终效果
反调试
IsDebuggerPresent
之外,早期的版本似乎hook
了DbgBreakPoint
和DbgUiRemoteBreakin
两个 API 来防止调试器附加,现在仍有hook
,不过只hook
了DbgBreak
,并且同样也有ThreadHideFromDebugger
检测。IsDebuggerPresent
:hook 返回 0 即可。ThreadHideFromDebugger
:需要根据参数和调用的时机合理地选择返回,稍有不慎就会crash,具体看下文分析。API hook
:目前无须绕过。ThreadHideFromDebugger
NtSetInformationThread
这个 API 本意是设置线程优先级的,其中有一个参数ThreadInformationClass
,这是一个THREADINFOCLASS
的枚举类型。typedef enum _THREADINFOCLASS {
ThreadBasicInformation = 0,
//...
ThreadPriorityBoost = 14,
ThreadSetTlsArrayAddress = 15, // Obsolete
ThreadIsIoPending = 16,
ThreadHideFromDebugger = 17,
//...
MaxThreadInfoClass = 51,
} THREADINFOCLASS;
ThreadHideFromDebugger
,字面意思也不难理解,就是从调试器中隐藏该线程,据看雪一篇文章的分析,该函数关于ThreadHideFromDebugger
的实现如下:case ThreadHideFromDebugger:
if (ThreadInformationLength != 0) {
return STATUS_INFO_LENGTH_MISMATCH;
}
st = ObReferenceObjectByHandle (...);
if (!NT_SUCCESS (st)) {
return st;
}
PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HIDEFROMDBG);
ObDereferenceObject (Thread);
return st;
break;
class
为ThreadHideFromDebugger
时,若ThreadInformationLength
不为 0 则返回一个错误。因此过这个反调试不能无脑拦截class
为ThreadHideFromDebugger
的调用,而应注意这里的 Length 是否为 0。根据拦截 yxxxshen.exe 的调用可以看出。[Debug Info]NtSetInformationThread called with handle fffffffe 17 at ... length 1 return c0000004
[Debug Info]NtSetInformationThread called with handle fffffffe 17 at ... length 0 return 0
Length
为 1,看是否调用失败,第二次才是真正的反调试,因此需要辨别出这一点。UINT64 gh_NtSetInformationThread(...) {
if(ThreadInformationClass==0x11 && ThreadInformationLength==0){
DBG_PRINT("Try to set ThreadHideFromDebugger,Stop itn");
return 0;
}
auto ret = NtSetInformationThread(...);
DBG_PRINT("lasterror=%dn", GetLastError());
DBG_PRINT("NtSetInformationThread called with handle %x %d at %p length %d return %xn",...);
return ret;
}
NtQueryInformationThread
。[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 4 return c0000004
[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 1 return 0
[Debug Info]NtSetInformationThread called with handle fffffffe 17 at ... length 1 return c0000004
[Debug Info]NtSetInformationThread called with handle fffffffe 17 at ... length 0 return 0
[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 4 return c0000004
[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 1 return 0
NtQueryInformationThread
,并且将 class 设为了ThreadHideFromDebugger
。[Debug Info]past information=34
[Debug Info]after information=00
[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 1 return 0
[Debug Info]NtSetInformationThread called with handle fffffffe 17 at ... length 0 return 0
[Debug Info]past information=95
[Debug Info]after information=01
[Debug Info]NtQueryInformationThread called with handle fffffffe 17 at ... length 1 return 0
NtQueryInformationThread
和NtSetInformationThread
,严格判断参数,并合理过滤掉一些检测反调试和反-反-反调试的东西。IsDebuggerPresent
最终效果
R0分析
反调试
0x2f0c0
,重复顺序执行以下逻辑:KdDebuggerEnabled
标志位,如果置1
则清零。kdcom.dll
,使用 MDL 的方式将kdcom.dll
的data
段清零。KdDebuggerNotPresent
标志位。KdDebuggerEnabled
标志位。KdDebuggerNotPresent
标志位。动态调试
version:5.2_rel CNRELWin5.2.0_28336591_29063028_28887986_28772242_28351161
deviceName:DESKTOP-DLBRLIS
time:2024-12-28 15.08.15.9001
deviceModel:VMware20,1 (VMware, Inc.)
operatingSystem:Windows 10 (10.0.19045) 64bit Microsoft Windows NT 10.0.19045.0
uid:14xxxxxx3
memoryInfo:695
cpuInfo:Intel(R) Core(TM) i9-14900HX
gpuInfo:VMware SVGA 3D
clientIp:fe80::374b:96c4:2526:ec61
isRelease:1
type:Windows Crash Release
GetSystemFirmwareTable
读出来的,这里为了防止被上传,最好把注册表处理干净,所有跟 Vmware 相关的全部替换掉。VOID Routine() {
while (TRUE) {
DBG_PRINT("%d %dn", *KdDebuggerEnabled, *KdDebuggerNotPresent);
LARGE_INTEGER interval;
interval.QuadPart = -10ll * 1000 * 1000;
KeDelayExecutionThread(KernelMode, FALSE, &interval);
}
}
1 0
。0 1
,而KdDebuggerEnabled
标志位一旦被复位,windbg 会直接被剥离,因此需要阻止。.upx0:000000014034794F mov rsi, [rcx]
.upx0:0000000140347952 mov [r11], rsi
.upx0:00000001402BD371 lock xchg r11d, [rax]
.upx0:00000001402BD375 bt r10w, r10w
kdcom.dll
,蓝屏代码为IRQL_NOT_LESS_OR_EQUAL
。模拟执行
monitor
或者sc
这种简单的工具加载,很不幸这个二次元开放世界冒险游戏并不能。分析其 R3 的行为,发现游戏运行的时候会往注册表写下一个ConfigData
,加载驱动之后会立刻删除,由于中间延时还挺高,所以可以捕获这一过程。综合分析
DriverEntry
的执行步骤。ConfigData
,判断游戏是否正确启动。DeviceHXXXProtect
PsLookupProcessByProcessId
获取 system 进程的 EProcessMmGetSystemRoutineAddress
获取了一遍PsLookupProcessByProcessId
地址MSR_LSTAR
获得 syscall 的入口点&0x00F0FFFF
的操作,最后和0x00108D4C
判断是否相等,转小端序来看,它需要找到类似这样的特征码4C 8D 1? ??
,这里不管直接在条件满足的分支下断点,在jne
下方下断点再过来看看它找到了什么位置。KiSystemServiceRepeat
的地址,通过搜索找到了一篇文章,里面提到了通过 MSR 寻找得到未导出的KeServiceDescriptorTable
和KeServiceDescriptorTableShadow
,文章里面使用了特征偏移的方法寻找这两个表,当然不同的系统版本这个值必然也不同,因此该驱动使用了兼容性更好的特征KiSystemServiceRepeat
函数头的方式去寻找这个偏移,拿到对应的两个表的地址。[TID:0000625c] Emulating read from ntoskrnl.exe:+00e018d0
[TID:0000625c] Executing ntoskrnl.exe!MmIsAddressValid
[TID:0000625c] Checking if address is valid : 7ff716feca40
[TID:0000625c] Getting data @ ntoskrnl.exe!KeServiceDescriptorTable
[TID:0000625c] Getting data @ ntoskrnl.exe!KeServiceDescriptorTableShadow
[TID:0000625c] Emulating read from ntoskrnl.exe:+00e018d0
[TID:0000625c] Executing ntoskrnl.exe!MmIsAddressValid
[TID:0000625c] Checking if address is valid : 0
0x1d8
(表中系统服务的数量)。MmIsAddressValid
,在模拟器中可以看到,模拟调用了一个0,因为模拟器的局限性,不太可能还原真实的内核情况,那来看看实际上它取的 0 来自哪里。FFFFF8052FCC79F0
,也就是 SSDT 存的KiServiceTable
,也许很大概率是发现 SSDT 存的KiServiceTable
为 0 了,所以模拟器加载驱动才会失败。[TID:00002b88] Getting data @ ntoskrnl.exe!KeServiceDescriptorTable
[TID:00002b88] Getting data @ ntoskrnl.exe!KeServiceDescriptorTableShadow
[TID:00002b88] Emulating read from ntoskrnl.exe:+00e018d0
[TID:00002b88] Executing ntoskrnl.exe!MmIsAddressValid
[TID:00002b88] Checking if address is valid : 7ff7a2c402c0
[TID:00002b88] Executing ntoskrnl.exe!MmIsAddressValid
[TID:00002b88] Checking if address is valid : 7ff7170f18c0
[TID:00002b88] Executing ntoskrnl.exe!KeInitializeEvent
[TID:00002b88] Event object : 7ff6375c7710
[TID:00002b88] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00002b88] Executing ntoskrnl.exe!KeInitializeEvent
[TID:00002b88] Event object : 22326561050
[TID:00002b88] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00002b88] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00002b88] Executing ntoskrnl.exe!ExInitializeResourceLite
[TID:00002b88] INSIDE STUB, RETURNING 0
[TID:00002b88] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00002b88] Executing ntoskrnl.exe!ExInitializeResourceLite
[TID:00002b88] INSIDE STUB, RETURNING 0
[TID:00002b88] Reading CR8
[TID:00002b88] Reading CR8
[TID:00002b88] Executing ntoskrnl.exe!RtlInitUnicodeString
[TID:00002b88] Executing ntoskrnl.exe!ZwCreateFile
[TID:00002b88] Creating file : SystemRootSystem32csrss.exe
[TID:00002b88] Return : 00000000
[TID:00002b88] Getting data @ ntoskrnl.exe!IoFileObjectType
[TID:00002b88] Exported Data ntoskrnl.exe!IoFileObjectType is not implemented
[TID:00002b88] Emulating read from ntoskrnl.exe:+00cfc448
[TID:00002b88] Executing ntoskrnl.exe!ObReferenceObjectByHandle
[TID:00002b88] Executing ntoskrnl.exe!ZwClose
[TID:00002b88] Closing Kernel Handle : 1c4
[TID:00002b88] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00002b88] Reading CR8
void __fastcall sub_140028B70(struct _OBJECT_NAME_INFORMATION **a1)
{
_DWORD v2[15]; // [rsp+20h] [rbp-E0h] BYREF
//...
//some definition
v2[0] = 0x53005C;
//...
//Imm assgin
v11 = 0;
sub_140029100((__int64)a1, (const WCHAR *)v2, a1 + 16);
sub_140029100((__int64)a1, (const WCHAR *)v4, a1 + 17);
sub_140029100((__int64)a1, (const WCHAR *)v12, a1 + 18);
sub_140029100((__int64)a1, (const WCHAR *)v6, a1 + 19);
sub_140029100((__int64)a1, (const WCHAR *)v8, a1 + 20);
sub_140029100((__int64)a1, (const WCHAR *)v13, a1 + 21);
sub_140029100((__int64)a1, (const WCHAR *)v15, a1 + 22);
sub_140029100((__int64)a1, (const WCHAR *)v10, a1 + 23);
}
sub_140029100
是它封装的打开文件的函数。这里的调用链路也很符合模拟器跑的结果,既然没 v,那就直接拦截,断点,调试一气呵成。SystemRootSystem32csrss.exe
SystemRootSystem32lsass.exe
SystemRootSystem32svchost.exe
SystemRootSysWow64svchost.exe
SystemRootSystem32audiodg.exe
SystemRootSystem32services.exe
SystemRootSystem32wbemwmiprvse.exe
SystemRootSysWow64wbemwmiprvse.exe
hash
的操作,感觉可能是检查这些进程的签名,如果是白签名那么不限制获取游戏的句柄。[TID:00005e28] Executing ntoskrnl.exe!ObGetFilterVersion
[TID:00005e28] Executing ntoskrnl.exe!RtlInitUnicodeString
[TID:00005e28] Executing ntoskrnl.exe!ObRegisterCallbacks
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!KeWaitForMutexObject
[TID:00005e28] Executing ntoskrnl.exe!KeReleaseMutex
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!KeWaitForMutexObject
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!KeReleaseMutex
[TID:00005e28] Executing ntoskrnl.exe!KeWaitForMutexObject
[TID:00005e28] Executing ntoskrnl.exe!KeReleaseMutex
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!KeWaitForMutexObject
[TID:00005e28] Executing ntoskrnl.exe!KeReleaseMutex
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!ExInitializePushLock
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!PsSetCreateProcessNotifyRoutineEx
[TID:00005e28] Executing ntoskrnl.exe!ExInitializePushLock
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!PsSetLoadImageNotifyRoutine
[TID:00005e28] Executing ntoskrnl.exe!ExInitializePushLock
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!PsSetCreateThreadNotifyRoutine
[TID:00005e28] Executing ntoskrnl.exe!ExInitializeResourceLite
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!ExInitializePushLock
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!ExCreateCallback
[TID:00005e28] Callback object : 299ce937f90
[TID:00005e28] *Callback object : 0
[TID:00005e28] Callback name : CallbackPowerState
[TID:00005e28] Executing ntoskrnl.exe!ExRegisterCallback
[TID:00005e28] Executing ntoskrnl.exe!KeAreApcsDisabled
[TID:00005e28] Executing ntoskrnl.exe!ExEnterCriticalRegionAndAcquireResourceExclusive
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!ExAllocatePoolWithTag
[TID:00005e28] Executing ntoskrnl.exe!ExReleaseResourceAndLeaveCriticalRegion
[TID:00005e28] INSIDE STUB, RETURNING 0
[TID:00005e28] Executing ntoskrnl.exe!PsCreateSystemThread
[TID:00009cb8] Thread Initialized, starting...
[TID:00005e28] Thread created at 299ce91f0c0
[TID:00009cb8] Executing ntoskrnl.exe!PsGetCurrentThreadId
[TID:00005e28] Executing ntoskrnl.exe!ZwClose
[TID:00005e28] Closing Kernel Handle : 1c4
[TID:00009cb8] Executing ntoskrnl.exe!KeSetPriorityThread
[TID:00005e28] Executing ntoskrnl.exe!KeQueryTimeIncrement
+0x2f0c0
的地址上,这个地址虽然是.text
段,但是 v 了,不太好静态分析,选择模拟器分析。针对标志位的反调试
[TID:0000a3dc] Driver Base at 7ff7bb200000
[TID:0000b058] [Warning] Read with execute at 00007FF7BB571B7A
[TID:0000b058] Getting data @ ntoskrnl.exe!KdDebuggerEnabled
[TID:0000b058] [Warning] Read with execute at 00007FF7BB553B1C
[TID:0000b058] Getting data @ ntoskrnl.exe!KdDebuggerNotPresent
[TID:0000b058] [Warning] Read with execute at 00007FF7BB53E5C9
[TID:0000b058] Getting data @ ntoskrnl.exe!KdDebuggerEnabled
[TID:0000b058] [Warning] Read with execute at 00007FF7BB56B94C
[TID:0000b058] Getting data @ ntoskrnl.exe!KdDebuggerNotPresent
0x371b7a
0x353b1c
0x33e5c9
0x36b94c
[TID:00005aac] Driver Base at 7ff7bb200000
[TID:0000a8c8] [Warning] Read with execute at 00007FF7BB571B7A
[TID:0000a8c8] Getting data @ ntoskrnl.exe!KdDebuggerEnabled
[TID:0000a8c8] [Warning] change KdDebuggerEnabled flags 00007FF7BB571B7A
[TID:0000a8c8] [Info] Write Violation at 00007FF7BB481D20
[TID:0000a8c8] Getting data @ ntoskrnl.exe!KdDebuggerEnabled
[TID:0000a8c8] Unhandled Mnemonic.
0x281d20
.upx0:0000000140281D20 F0 45 87 19 lock xchg r11d, [r9]
mov r11d, [r9]
指令,再来看看会不会被剥离调试器,经测试发现 kdcom 还是蓝屏,那么尝试在读取的指令入手,经测试,0x371b7a
若读到了 1 则会写,因此尝试把这里的。.upx0:0000000140371B7A 66 41 0F B6 12 movzx dx, byte ptr [r10]
xor dx,dx
,同理改掉四个读取的位置,让它们读的值分别为未调试状态读取的值,但是会导致虚拟机被卡死,同时调试器也是未响应的状态,似乎没什么思路了,于是再次考虑动态调试。针对kdcom的反调试
data
段被清零了。0xC3
,观察是否蓝屏。ZwQuerySystemInformation
获取kdcom.dll
的基址和模块大小。.data
段的基址和大小。kdcom.dll
的magic
清零之后kdcom
工作将不正常,其中fffff8033e455000
为模块本身的虚拟内存,RCX
指向了MDL
分配的虚拟内存,此时汇编代码通过RCX
写入0
,在现在这种情况下,单步调试会导致调试器直接断开。PsSetLoadImageNotifyRoutine
注册了加载模块回调,然后识别HxxxYOKProtect.sys
再用MDL
把这个内存给改了吧。回调分析
进程、线程句柄回调
hook
,只要比较一下打开PROCESS_ALL_ACCESS
的游戏句柄,看看最终得到的权限就行了,这里选择hook注册回调的函数,在注册回调的时候拦截,注册上自己的回调,自己的回调再调用真正的回调函数即可。PreOperationCallback = NULL;
PostOperationCallback = NULL;
POB_PRE_OPERATION_CALLBACK gh_ObPreOperationCallback(...)
{
if (PreOperationCallback &&!OperationInformation->KernelHandle) {
DBG_PRINT("handle access(pre)=%xn",...);
PreOperationCallback(RegistrationContext, OperationInformation);
DBG_PRINT("handle access(after)=%xn",...);
}
return 0;
}
NTSTATUS gh_ObPostOperationCallback(...)
{
if (PostOperationCallback && !OperationInformation->KernelHandle) {
DBG_PRINT("handle access(pre)=%xn",...);
PostOperationCallback(RegistrationContext, OperationInformation);
DBG_PRINT("handle access(after)=%xn",...);
}
return 0;
}
NTSTATUS gh_ObRegisterCallbacks(...)
{
DBG_PRINT("ObRegisterCallbacks operations=%d,preOperation=%p,PostOperation=%pn",...);
auto ret = ObRegisterCallbacks(CallbackRegistration, RegistrationHandle);
ObUnRegisterCallbacks(*RegistrationHandle);
*RegistrationHandle = 0;
PVOID gs_HandleCallback;
OB_CALLBACK_REGISTRATION obl_callback_reg = { 0 };
OB_OPERATION_REGISTRATION ob2_operation = *CallbackRegistration->OperationRegistration;
//...
auto ret2=ObRegisterCallbacks(&obl_callback_reg, &gs_HandleCallback);//自己注册一个回调
DBG_PRINT("status:%pn", ret2);
return 0;
}
0xC0000022
。LOG
#define PROCESS_TERMINATE (0x0001)
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_SET_SESSIONID (0x0004)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_DUP_HANDLE (0x0040)
#define PROCESS_CREATE_PROCESS (0x0080)
#define PROCESS_SET_QUOTA (0x0100)
#define PROCESS_SET_INFORMATION (0x0200)
#define PROCESS_QUERY_INFORMATION (0x0400)
#define PROCESS_SUSPEND_RESUME (0x0800)
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#define PROCESS_SET_LIMITED_INFORMATION (0x2000)
PROCESS_CREATE_THREAD
:创建线程PROCESS_VM_OPERATION
:虚拟内存操作PROCESS_VM_READ
:读内存PROCESS_VM_WRITE
:写内存PROCESS_DUP_HANDLE
:复制句柄PROCESS_SET_INFORMATION
:设置线程优先级PROCESS_SUSPEND_RESUME
:挂起和恢复进程创建回调
xdbg
之类的,没想到没拦,为了保证安全把回调直接取消了也可以。线程,模块回调
最后
参考文献
看雪ID:xi@0ji233
https://bbs.kanxue.com/user-home-919002.htm
#
原文始发于微信公众号(看雪学苑):二次元开放世界冒险游戏反作弊分析报告
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论