-
-
[原创]记录简单逆向某反作弊的过程
-
发表于: 2026-8-2 03:55 887
-
初步去虚拟化后 xxxGAME.sys已经基本可读,直接ida。
DriverEntry
先找到DriverEntry伪代码如下 发现前函数单纯当跳板直接跟进去:
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
return DriverEntry_0(DriverObject, RegistryPath);
}
NTSTATUS __stdcall DriverEntry_0(_DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
_security_init_cookie();
return sub_1400130D0(DriverObject);
}
__int64 __fastcall sub_1400130D0(PDRIVER_OBJECT DriverObject)
{
return sub_1402CB140(DriverObject);
}
_int64 __fastcall sub_1402CB140(PDRIVER_OBJECT DriverObject)
{
struct _UNICODE_STRING DeviceName; // [rsp+40h] [rbp-48h] BYREF
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_140013038;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_140013038;
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_140013058;
DriverObject->MajorFunction[16] = (PDRIVER_DISPATCH)sub_1400130AC;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_140013074;
if ( (unsigned int)sub_1400127A4(DriverObject) )
return 0;
RtlInitUnicodeString((PUNICODE_STRING)&DeviceName.Buffer, L"\\Device\\XXXGAME");
if ( IoCreateDevice(DriverObject, 0, &DeviceName, 0x22u, 0, 0, &DeviceObject) < 0 )
return 0;
if ( IoRegisterShutdownNotification(DeviceObject) >= 0 )
byte_140030AB8 = 1;
return 0;
}先看 sub_1400127A4 进去后发现依旧是跳板,实际进入 sub_1402CAAE0。
__int64 __fastcall sub_1402CAAE0(__int64 a1, __int64 a2)
{
sub_14000C1A4();
if ( byte_140030608 )
return 0;
sub_140012764(a1);
sub_14000E660();
sub_14001EE80();
sub_1400126F0(); //尝试删除一个匿名后的辅助驱动文件
if ( (unsigned __int8)sub_140012848(a2) ) //先将路径转为小写,再检查是否包含主驱动文件名
{
byte_140030609 = 1;
sub_14001296C(a2);//构造辅助驱动路径 xxxgamebase-0.sys
sub_1400128E4(a2);
}
else
{
sub_14001ED08();
sub_14001E80C();
sub_14001191C(a2);
sub_1400104F0();
sub_14000C37C();
sub_140013318(); //注册进程和注册表回调
sub_140018B1C(a2, &qword_140030620, 256);
}
byte_140030608 = 1;//初始化
return 0;
}bool __fastcall sub_140012848(__int64 a1)
{
char v2; // di
__int64 String[64]; // [rsp+20h] [rbp-218h] BYREF
v2 = 0;
sub_140027980(String, 0, 0x200u);
if ( (int)sub_140018B1C(a1, String, 256) >= 0 )
{
wcslwr((wchar_t *)String);
return sub_140023180(String, L"ace-game.sys") != 0;
}
return v2;
}
__int64 sub_1400126F0()
{
struct _UNICODE_STRING DestinationString; // [rsp+20h] [rbp-238h] BYREF
__int64 Dst[66]; // [rsp+30h] [rbp-228h] BYREF
sub_140027980(Dst, 0, 0x208u);
wcscat_s((wchar_t *)Dst, 0x104u, L"\\SystemRoot\\System32\\drivers\\ace-game-0.sys");
RtlInitUnicodeString(&DestinationString, (PCWSTR)Dst);
return sub_14001A894(&DestinationString);
}PCI设备枚举
发现导入表调用 两个函数调用 IoGetDeviceProperty
__int64 __fastcall sub_140007C04(PDEVICE_OBJECT DeviceObject)
{
unsigned int n2; // ebx
wchar_t *Pool; // rax
wchar_t *Pool_1; // rdi
ULONG ResultLength; // [rsp+48h] [rbp+10h] BYREF
n2 = 0;
Pool = (wchar_t *)ExAllocatePool(NonPagedPool, 0x100u);
ResultLength = 0;
Pool_1 = Pool;
if ( Pool )
{
sub_140027980(Pool, 0, 256);
if ( !IoGetDeviceProperty(DeviceObject, DevicePropertyClassName, 0x100u, Pool_1, &ResultLength) )
{
if ( !wcscmp(Pool_1, L"Net") || !wcscmp(Pool_1, L"net") )
{
n2 = 1;
}
else if ( !wcscmp(Pool_1, L"Display") || !wcscmp(Pool_1, L"display") )
{
n2 = 2;
}
else if ( !wcscmp(Pool_1, L"System") || !wcscmp(Pool_1, L"system") )
{
n2 = 3;
}
else if ( !wcscmp(Pool_1, L"MEDIA") || !wcscmp(Pool_1, L"media") )
{
n2 = 6;
}
else if ( !wcscmp(Pool_1, L"USB") || !wcscmp(Pool_1, L"usb") )
{
n2 = 4;
}
else if ( !wcscmp(Pool_1, L"SCSIAdapter") )
{
n2 = 5;
}
else if ( !wcscmp(Pool_1, L"DiskDrive") || !wcscmp(Pool_1, L"diskdrive") )
{
n2 = 8;
}
else if ( !wcscmp(Pool_1, L"SoftwareComponent") || !wcscmp(Pool_1, L"softwarecomponent") )
{
n2 = 9;
}
}
ExFreePoolWithTag(Pool_1, 0);
}
return n2;
}bool __fastcall sub_140008958(PDEVICE_OBJECT DeviceObject, __int64 a2, __int64 a3)
{
bool v3; // bl
struct _DEVICE_OBJECT *AttachedDevice; // rax
struct _DEVICE_OBJECT *AttachedDevice_1; // rcx
PVOID DeviceExtension; // rdi
WCHAR *Pool; // rdi
ULONG ResultLength; // [rsp+30h] [rbp-B8h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+38h] [rbp-B0h] BYREF
struct _UNICODE_STRING p_DestinationString; // [rsp+48h] [rbp-A0h] BYREF
WCHAR SourceString[40]; // [rsp+60h] [rbp-88h] BYREF
v3 = 0;
if ( a2 )
{
if ( a3 )
{
if ( (unsigned int)sub_140007C04(DeviceObject) == 1 ) //只获取 Net 类设备
{
AttachedDevice = DeviceObject->AttachedDevice;
if ( AttachedDevice )
{
AttachedDevice_1 = AttachedDevice->AttachedDevice;
if ( AttachedDevice_1 )
{
//获取第二层附加设备的私有设备扩展
DeviceExtension = AttachedDevice_1->DeviceExtension;
if ( DeviceExtension )
{
sub_140027980(SourceString, 0, 80);//清空缓冲区
RtlInitUnicodeString(&DestinationString, SourceString);//提取字符串
if ( (unsigned __int8)sub_1400082E4(DeviceExtension, SourceString) )
{
return (unsigned int)sub_1400140A0(&DestinationString, a2, a3) == 0;
}
else
{
Pool = (WCHAR *)ExAllocatePool(NonPagedPool, 0x200u);
if ( Pool )
{
ResultLength = 512;
if ( IoGetDeviceProperty(DeviceObject, DevicePropertyHardwareID, 0x200u, Pool, &ResultLentth) >= 0 )
{
RtlInitUnicodeString(&p_DestinationString, Pool);
v3 = (unsigned int)sub_1400140A0(&p_DestinationString, a2, a3) == 0;
}
ExFreePoolWithTag(Pool, 0);
}
}
}
}
}
}
}
}
return v3;
}看看到底提取了什么字符串 于是我们直接看sub_1400082E4 和 sub_1400140A0 两个函数
总结下:
sub_1400082E4
直接读 MatchingDeviceId
sub_1400140A0
从 MatchingDeviceId 中解析:
VEN_xxxx -> *(ULONG *)a2
DEV_xxxx -> *(ULONG *)a3
char __fastcall sub_1400082E4(PVOID DeviceExtension, WCHAR *SourceString)
{
char v3; // bl
USHORT Length; // cx
size_t n39; // r8
struct _UNICODE_STRING DestinationString; // [rsp+30h] [rbp-30h] BYREF
struct _NDIS_CONFIGURATION_OBJECT ConfigObject; // [rsp+40h] [rbp-20h] BYREF
int Status; // [rsp+80h] [rbp+20h] BYREF
PVOID ConfigurationHandle; // [rsp+90h] [rbp+30h] BYREF
PNDIS_CONFIGURATION_PARAMETER ParameterValue; // [rsp+98h] [rbp+38h] BYREF
ConfigurationHandle = 0;
ConfigObject.NdisHandle = DeviceExtension;
*((_DWORD *)&ConfigObject.Header + 1) = 0;
*(_QWORD *)&ConfigObject.Flags = 0;
v3 = 0;
ConfigObject.Header = (NDIS_OBJECT_HEADER)1311145;
Status = NdisOpenConfigurationEx(&ConfigObject, &ConfigurationHandle);
if ( !Status )
{
ParameterValue = 0;
RtlInitUnicodeString(&DestinationString, L"MatchingDeviceId");
NdisReadConfiguration(&Status, &ParameterValue, ConfigurationHandle, &DestinationString, NdisParameterString);
if ( !Status )
{
Length = ParameterValue->ParameterData.StringData.Length;//解析 PCI 厂商 ID(VEN)和设备 ID(DEV)
if ( (Length & 0xFFFEu) >= 0x4E )
n39 = 39;
else
n39 = (unsigned __int64)Length >> 1;
wcsncpy(SourceString, ParameterValue->ParameterData.StringData.Buffer, n39);
v3 = 1;
}
}
if ( ConfigurationHandle )
NdisCloseConfiguration(ConfigurationHandle);
return v3;
}__int64 __fastcall sub_1400140A0(struct _UNICODE_STRING *p_DestinationString, ULONG *a2, ULONG *a3)
{
unsigned int v6; // edi
PWSTR Buffer; // rcx
__int64 v8; // rax
__int64 v9; // rsi
__int64 v10; // rax
size_t n9; // r8
PWSTR Buffer_1; // rcx
__int64 v13; // rax
__int64 v14; // rbx
__int64 v15; // rax
size_t n9_1; // r8
ULONG Value__1; // ecx
struct _UNICODE_STRING DestinationString; // [rsp+20h] [rbp-30h] BYREF
WCHAR SourceString; // [rsp+30h] [rbp-20h] BYREF
__int64 v21; // [rsp+32h] [rbp-1Eh]
__int64 v22; // [rsp+3Ah] [rbp-16h]
__int16 v23; // [rsp+42h] [rbp-Eh]
ULONG Value; // [rsp+90h] [rbp+40h] BYREF
ULONG Value_; // [rsp+A8h] [rbp+58h] BYREF
v6 = -1073741823;
if ( p_DestinationString )
{
Buffer = p_DestinationString->Buffer;
if ( Buffer )
{
if ( a2 )
{
Value = 0;
v8 = sub_140023180(Buffer, L"VEN_");
v9 = v8;
if ( v8 )
{
v10 = sub_140023180(v8, L"&DEV_");
if ( v10 )
{
SourceString = 0;
v21 = 0;
v22 = 0;
v23 = 0;
n9 = ((v10 - v9) >> 1) - 4;
if ( n9 > 9 )
n9 = 9;
wcsncpy(&SourceString, (const wchar_t *)(v9 + 8), n9);
RtlInitUnicodeString(&DestinationString, &SourceString);
RtlUnicodeStringToInteger(&DestinationString, 0x10u, &Value);
}
}
Buffer_1 = p_DestinationString->Buffer;
Value_ = 0;
v13 = sub_140023180(Buffer_1, L"DEV_");
v14 = v13;
if ( v13 )
{
v15 = sub_140023180(v13, L"&SUB");
if ( v15 )
{
SourceString = 0;
v21 = 0;
v22 = 0;
v23 = 0;
n9_1 = ((v15 - v14) >> 1) - 4;
if ( n9_1 > 9 )
n9_1 = 9;
wcsncpy(&SourceString, (const wchar_t *)(v14 + 8), n9_1);
RtlInitUnicodeString(&DestinationString, &SourceString);
RtlUnicodeStringToInteger(&DestinationString, 0x10u, &Value_);
}
}
if ( Value )
{
Value__1 = Value_;
if ( Value_ )
{
*a2 = Value;
v6 = 0;
*a3 = Value__1;
}
}
}
}
}
return v6;
}读取 DevicePropertyLocationInformation 获取设备和功能号。
char __fastcall sub_140007EBC(PDEVICE_OBJECT DeviceObject, ULONG *a2, ULONG *a3, ULONG *a4)
{
char v4; // bl
ULONG *v5; // r15
PVOID Pool; // rdi
__int64 v10; // rax
__int64 v11; // r14
__int64 v12; // rax
size_t n9; // r8
__int64 v14; // rax
__int64 v15; // r14
__int64 v16; // rax
size_t n9_1; // r8
__int64 v18; // rdx
__int64 v19; // r8
size_t n9_2; // r8
__int64 v21; // rax
__int64 v22; // rax
__int64 v23; // r14
__int64 v24; // r15
size_t n9_3; // r8
__int64 v26; // rax
__int64 v27; // r15
__int64 v28; // r14
size_t n9_4; // r8
__int64 v30; // rdx
__int64 v31; // r8
size_t n9_5; // r8
ULONG ResultLength; // [rsp+30h] [rbp-99h] BYREF
ULONG Value; // [rsp+34h] [rbp-95h] BYREF
ULONG Value_; // [rsp+38h] [rbp-91h] BYREF
ULONG Value__1; // [rsp+3Ch] [rbp-8Dh] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+40h] [rbp-89h] BYREF
UNICODE_STRING DestinationString_; // [rsp+50h] [rbp-79h] BYREF
UNICODE_STRING DestinationString__1; // [rsp+60h] [rbp-69h] BYREF
WCHAR Dest; // [rsp+70h] [rbp-59h] BYREF
__int64 v42; // [rsp+72h] [rbp-57h]
__int64 v43; // [rsp+7Ah] [rbp-4Fh]
__int16 v44; // [rsp+82h] [rbp-47h]
WCHAR Dest_; // [rsp+88h] [rbp-41h] BYREF
__int64 v46; // [rsp+8Ah] [rbp-3Fh]
__int64 v47; // [rsp+92h] [rbp-37h]
__int16 v48; // [rsp+9Ah] [rbp-2Fh]
WCHAR SourceString; // [rsp+A0h] [rbp-29h] BYREF
__int64 v50; // [rsp+A2h] [rbp-27h]
__int64 v51; // [rsp+AAh] [rbp-1Fh]
__int16 v52; // [rsp+B2h] [rbp-17h]
WCHAR Dest__1; // [rsp+B8h] [rbp-11h] BYREF
__int64 v54; // [rsp+BAh] [rbp-Fh]
__int64 v55; // [rsp+C2h] [rbp-7h]
__int16 v56; // [rsp+CAh] [rbp+1h]
v4 = 0;
*(_QWORD *)&DestinationString.Length = a2;
v5 = a2;
Value = 0;
Value_ = 0;
Value__1 = 0;
Pool = ExAllocatePool(NonPagedPool, 0x200u);
if ( Pool )
{
ResultLength = 512;
if ( IoGetDeviceProperty(DeviceObject, DevicePropertyLocationInformation, 0x200u, Pool, &ResultLength) >= 0 )
{
v10 = sub_140023180(Pool, L"bus ");
v11 = v10;
if ( v10 )
{
v12 = sub_140023180(v10, L", device");
if ( !v12 )
goto LABEL_35;
SourceString = 0;
v50 = 0;
v51 = 0;
v52 = 0;
n9 = ((v12 - v11) >> 1) - 4;
if ( n9 > 9 )
n9 = 9;
wcsncpy(&SourceString, (const wchar_t *)(v11 + 8), n9);
RtlInitUnicodeString(&DestinationString, &SourceString);
RtlUnicodeStringToInteger(&DestinationString, 0xAu, &Value);
v14 = sub_140023180(Pool, L"device ");
v15 = v14;
if ( !v14 )
goto LABEL_35;
v16 = sub_140023180(v14, L", function");
if ( !v16 )
goto LABEL_35;
Dest = 0;
v42 = 0;
v43 = 0;
v44 = 0;
n9_1 = ((v16 - v15) >> 1) - 7;
if ( n9_1 > 9 )
n9_1 = 9;
wcsncpy(&Dest, (const wchar_t *)(v15 + 14), n9_1);
RtlInitUnicodeString(&DestinationString_, &Dest);
RtlUnicodeStringToInteger(&DestinationString_, 0xAu, &Value_);
v18 = sub_140023180(Pool, L"function ");
if ( !v18 )
goto LABEL_35;
Dest_ = 0;
v46 = 0;
v47 = 0;
v48 = 0;
v19 = (ResultLength >> 1) - ((v18 - (__int64)Pool) >> 1);
ResultLength >>= 1;
n9_2 = v19 - 9;
if ( n9_2 > 9 )
n9_2 = 9;
wcsncpy(&Dest_, (const wchar_t *)(v18 + 18), n9_2);
RtlInitUnicodeString(&DestinationString__1, &Dest_);
RtlUnicodeStringToInteger(&DestinationString__1, 0xAu, &Value__1);
}
else
{
v21 = sub_140023180(Pool, L"location PCI ");
if ( !v21 )
goto LABEL_35;
v22 = sub_140023180(v21 + 26, qword_140027D20);
v23 = v22;
if ( !v22 )
goto LABEL_35;
v24 = sub_140023180(v22, qword_140027D30);
if ( !v24 )
goto LABEL_35;
Dest_ = 0;
v46 = 0;
v47 = 0;
n9_3 = ((v24 - v23) >> 1) - 1;
v48 = 0;
if ( n9_3 > 9 )
n9_3 = 9;
wcsncpy(&Dest_, (const wchar_t *)(v23 + 2), n9_3);
RtlInitUnicodeString(&DestinationString__1, &Dest_);
RtlUnicodeStringToInteger(&DestinationString__1, 0xAu, &Value);
v26 = sub_140023180(v24, qword_140027D20);
v27 = v26;
if ( !v26 )
goto LABEL_35;
v28 = sub_140023180(v26, qword_140027D30);
if ( !v28 )
goto LABEL_35;
Dest = 0;
v42 = 0;
v43 = 0;
n9_4 = ((v28 - v27) >> 1) - 1;
v44 = 0;
if ( n9_4 > 9 )
n9_4 = 9;
wcsncpy(&Dest, (const wchar_t *)(v27 + 2), n9_4);
RtlInitUnicodeString(&DestinationString_, &Dest);
RtlUnicodeStringToInteger(&DestinationString_, 0xAu, &Value_);
v30 = sub_140023180(v28, qword_140027D20);
if ( !v30 )
goto LABEL_35;
Dest__1 = 0;
v54 = 0;
v55 = 0;
v56 = 0;
v31 = (ResultLength >> 1) - ((v30 - (__int64)Pool) >> 1);
ResultLength >>= 1;
n9_5 = v31 - 1;
if ( n9_5 > 9 )
n9_5 = 9;
wcsncpy(&Dest__1, (const wchar_t *)(v30 + 2), n9_5);
RtlInitUnicodeString((PUNICODE_STRING)&SourceString, &Dest__1);
RtlUnicodeStringToInteger((PCUNICODE_STRING)&SourceString, 0xAu, &Value__1);
v5 = *(ULONG **)&DestinationString.Length;
}
if ( v5 )
*v5 = Value;
if ( a3 )
*a3 = Value_;
if ( a4 )
*a4 = Value__1;
v4 = 1;
}
LABEL_35:
ExFreePoolWithTag(Pool, 0);
}
return v4;
}IOMMU
直接查看导入表,四个函数交叉引用随便一看MmMapIoSpace ,MmUnmapIoSpace,MmGetPhysicalAddress,MmIsAddressValid。
sub_140010C98函数中存在1380011332 , 1397904969两个十进制常量。
转换为十六进制后 分别为 0x52414D44,0x53525649, 小端为: 44 4D 41 52 ->DMAR ,49 56 52 53 -> "IVRS"
void __fastcall sub_140010C98(__int64 a1)
{
unsigned int *BaseAddress; // rax
unsigned int *BaseAddress_2; // rsi
PHYSICAL_ADDRESS PhysicalAddress; // rbx
unsigned int NumberOfBytes; // edi
PHYSICAL_ADDRESS *VirtualAddress; // rbx
PHYSICAL_ADDRESS *VirtualAddress_1; // rbp
__int64 QuadPart; // r14
_DWORD *BaseAddress_1; // rbx
unsigned int v9; // esi
int v10; // r13d
int v11; // r12d
int v12; // r15d
PHYSICAL_ADDRESS *BaseAddress_3; // rax
PHYSICAL_ADDRESS *BaseAddress_4; // rsi
PHYSICAL_ADDRESS PhysicalAddress_1; // rbx
unsigned int HighPart; // edi
__int64 v17; // r14
unsigned int v18; // esi
int v19; // r13d
int v20; // r12d
int v21; // r15d
int v22; // [rsp+E0h] [rbp+8h]
int v23; // [rsp+E0h] [rbp+8h]
int v24; // [rsp+E8h] [rbp+10h]
int v25; // [rsp+E8h] [rbp+10h]
if ( !(unsigned __int8)sub_14001A400(a1) )
{
if ( (unsigned __int8)sub_140013ED4() )
{
BaseAddress = (unsigned int *)sub_140008E8C(1380011332); //DMAR
BaseAddress_2 = BaseAddress;
if ( BaseAddress )
{
PhysicalAddress = MmGetPhysicalAddress(BaseAddress);
if ( !PhysicalAddress.QuadPart
|| (NumberOfBytes = BaseAddress_2[1],
MmUnmapIoSpace(BaseAddress_2, 8u),
(BaseAddress_2 = (unsigned int *)MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached)) != 0) )
{
VirtualAddress = (PHYSICAL_ADDRESS *)(BaseAddress_2 + 12);
VirtualAddress_1 = (PHYSICAL_ADDRESS *)((char *)BaseAddress_2 + BaseAddress_2[1]);
if ( BaseAddress_2 != (unsigned int *)-48LL )
{
while ( VirtualAddress < VirtualAddress_1 && MmIsAddressValid(VirtualAddress) )
{
if ( !LOWORD(VirtualAddress->LowPart) && (VirtualAddress->QuadPart & 0x100000000LL) != 0 )
{
QuadPart = VirtualAddress[1].QuadPart;
if ( QuadPart )
{
BaseAddress_1 = MmMapIoSpace(VirtualAddress[1], 0x100u, MmNonCached);
if ( BaseAddress_1 )
{
if ( *BaseAddress_1 == -1 )
goto LABEL_16;
v9 = 0;
v10 = BaseAddress_1[20];
v11 = BaseAddress_1[21];
v12 = BaseAddress_1[22];
v22 = BaseAddress_1[18];
v24 = BaseAddress_1[19];
BaseAddress_1[18] = -1;
BaseAddress_1[19] = -1;
BaseAddress_1[20] = -1;
BaseAddress_1[21] = -1;
BaseAddress_1[22] = -1;
sub_140014074(1000);
if ( v22 != BaseAddress_1[18] )
{
v9 = 1;
BaseAddress_1[18] = v22;
}
if ( v24 != BaseAddress_1[19] )
{
++v9;
BaseAddress_1[19] = v24;
}
if ( v10 != BaseAddress_1[20] )
{
++v9;
BaseAddress_1[20] = v10;
}
if ( v11 != BaseAddress_1[21] )
{
++v9;
BaseAddress_1[21] = v11;
}
if ( v12 != BaseAddress_1[22] )
{
++v9;
BaseAddress_1[22] = v12;
}
MmUnmapIoSpace(BaseAddress_1, 0x100u);
if ( v9 )
sub_140012048(0, 1078, v9, QuadPart, 0, byte_14002F350, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
return;
}
VirtualAddress = (PHYSICAL_ADDRESS *)((char *)VirtualAddress + HIWORD(VirtualAddress->u.LowPart));
if ( !VirtualAddress )
return;
}
}
}
}
}
else if ( (unsigned __int8)sub_140013D9C() )
{
BaseAddress_3 = (PHYSICAL_ADDRESS *)sub_140008E8C(1397904969);
BaseAddress_4 = BaseAddress_3;
if ( BaseAddress_3 )
{
PhysicalAddress_1 = MmGetPhysicalAddress(BaseAddress_3);
if ( !PhysicalAddress_1.QuadPart
|| (HighPart = BaseAddress_4->HighPart,
MmUnmapIoSpace(BaseAddress_4, 8u),
(BaseAddress_4 = (PHYSICAL_ADDRESS *)MmMapIoSpace(PhysicalAddress_1, HighPart, MmNonCached)) != 0) )
{
v17 = BaseAddress_4[7].QuadPart;
if ( v17 )
{
BaseAddress_1 = MmMapIoSpace(BaseAddress_4[7], 0x100u, MmNonCached);
if ( BaseAddress_1 )
{
if ( *BaseAddress_1 == -1 )
{
LABEL_16:
MmUnmapIoSpace(BaseAddress_1, 0x100u);
}
else
{
v18 = 0;
v19 = BaseAddress_1[47];
v20 = BaseAddress_1[51];
v21 = BaseAddress_1[55];
v23 = BaseAddress_1[39];
v25 = BaseAddress_1[43];
BaseAddress_1[39] = -1;
BaseAddress_1[43] = -1;
BaseAddress_1[47] = -1;
BaseAddress_1[51] = -1;
BaseAddress_1[55] = -1;
sub_140014074(1000);
if ( v23 != BaseAddress_1[39] )
{
v18 = 1;
BaseAddress_1[39] = v23;
}
if ( v25 != BaseAddress_1[43] )
{
++v18;
BaseAddress_1[43] = v25;
}
if ( v19 != BaseAddress_1[47] )
{
++v18;
BaseAddress_1[47] = v19;
}
if ( v20 != BaseAddress_1[51] )
{
++v18;
BaseAddress_1[51] = v20;
}
if ( v21 != BaseAddress_1[55] )
{
++v18;
BaseAddress_1[55] = v21;
}
MmUnmapIoSpace(BaseAddress_1, 0x100u);
if ( v18 )
sub_140012048(0, 1078, v18, v17, 0, byte_14002F350, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
}
}
}
}
}
}继续分析 sub_140008E8C 可以发现它通过签名取得固件表;相邻逻辑还处理:FACP DSDT FACS
基本可以确定这个函数在处理Intel ACPI DMAR 和 AMD ACPI IVRS
__int64 __fastcall sub_140008E94(__int64 n1380011332, _DWORD *a2)
{
unsigned int n1380011332_1; // ebx
__int64 result; // rax
char *v5; // rdi
char v6; // [rsp+38h] [rbp+10h] BYREF
n1380011332_1 = n1380011332;
if ( a2 )
{
if ( *a2 != (_DWORD)n1380011332
|| (_DWORD)n1380011332 == 1346584902
|| (_DWORD)n1380011332 == 1413763908
|| (_DWORD)n1380011332 == 1396916550 ) //前文相同处理
{
return 0;
}
v5 = &v6;
v6 = 0;
}
else
{
v5 = 0;
}
result = sub_140008F24(&unk_14002D010, (unsigned int)n1380011332, a2, v5);
if ( result )
return result;
if ( v5 && *v5 )
return 0;
return sub_140008F24(&unk_14002D000, n1380011332_1, a2, v5);
}跟进 sub_140013ED4 一眼顶针
1970169159 -> 0x756E6547
1818588270 -> 0x6C65746E
1231384169 -> 0x49656E69
EBX = 0x756E6547 -> 47 65 6E 75 -> "Genu"
EDX = 0x49656E69 -> 69 6E 65 49 -> "ineI"
ECX = 0x6C65746E -> 6E 74 65 6C -> "ntel"
AMD的CPU:
bool sub_140013D9C()
{
bool result; // al
_RAX = 0;
__asm { cpuid }
result = 0;
if ( (_DWORD)_RBX == 1752462657 && (_DWORD)_RCX == 1145913699 )
return (_DWORD)_RDX == 1769238117;
return result;
}所以最终结构大概是
if (IsIntel()) {
dmar = GetAcpiTableBySignature("DMAR");
ProbeIntelVtd(dmar);
}
else if (IsAMD()) {
ivrs = GetAcpiTableBySignature("IVRS");
ProbeAmdVi(ivrs);
}Intel 分支从 DMAR 的 DRHD 类结构中取出寄存器物理基址,映射 `0x100` 字节后保存五个 DWORD:
v10 = BaseAddress_1[20]; // +0x48 v11 = BaseAddress_1[21];// +0x4C v12 = BaseAddress_1[22];// +0x50 v22 = BaseAddress_1[18];// +0x54 v24 = BaseAddress_1[19]; // +0x58 BaseAddress_1[18] = -1; BaseAddress_1[19] = -1; BaseAddress_1[20] = -1; BaseAddress_1[21] = -1; BaseAddress_1[22] = -1; sub_140014074(1000); //KeDelayExecutionThread 延时等待约一秒
延时结束后,函数逐项比较当前值和保存值;如果不同,计数加一并写回原值。
两条分支都在变化计数非零时上报事件 1078
sub_140012048( 0, 1078, changed_count, iommu_mmio_base, ..., is_intel_or_amd, ...);
系统版本检测
函数调用了RlGetVersion ,但是除了版本号检查以外,还做了一些其他检查,比较杂。
__int64 __fastcall sub_1402C3F80()
{
__int64 v0; // rbx
__int64 v1; // rbp
__int64 v2; // rdi
__int16 n119; // r14
__int64 v4; // rsi
NTSTATUS Version; // eax
unsigned __int64 v6; // r8
unsigned __int64 v12; // rax
__int64 v13; // rax
__int64 v16[36]; // [rsp+80h] [rbp-1C8h] BYREF
__int64 v17; // [rsp+1A0h] [rbp-A8h]
__int64 v18; // [rsp+1B0h] [rbp-98h]
__int64 v19; // [rsp+1C0h] [rbp-88h]
__int64 v20; // [rsp+1C8h] [rbp-80h]
int _RAX_1; // [rsp+1D8h] [rbp-70h] BYREF
int _RBX_1; // [rsp+1DCh] [rbp-6Ch]
int n1835012; // [rsp+1E0h] [rbp-68h]
int _RDX_1; // [rsp+1E4h] [rbp-64h]
int n5832731; // [rsp+1E8h] [rbp-60h]
int n983058; // [rsp+1ECh] [rbp-5Ch]
__int16 n18; // [rsp+1F0h] [rbp-58h]
int v28; // [rsp+1F2h] [rbp-56h]
int v29; // [rsp+1F8h] [rbp-50h]
char lpVersionInformation_; // [rsp+208h] [rbp-40h] BYREF
int v31; // [rsp+20Ch] [rbp-3Ch]
n119 = 119;
v4 = 0;
v19 = v0;
v20 = v2;
v18 = v1;
sub_140027980(v16, 0, 0x114u);
Version = RtlGetVersion((PRTL_OSVERSIONINFOW)&lpVersionInformation_);
v17 = 0;
if ( Version )
{
if ( Version >= 0 )
goto LABEL_3;
return 30;
}
if ( (unsigned int)(v31 - 10240) > 0x4D2F ) // 10240 <= BuildNumber <= 29999
return 30;
LABEL_3:
if ( sub_1400020D0() )
return 43;
v6 = __readcr4(); //检查CET
_RAX = 7;
__asm { cpuid }
_RAX_1 = _RAX;
_RBX_1 = _RBX;
_RDX_1 = _RDX;
if ( (_RCX & 0x80) != 0 )
{
v12 = __readmsr(0x6A2u);
if ( (v6 & 0x800000) != 0 && (v12 & 1) != 0 )
return 44;
}
v29 = 0;
_RAX_1 = 1638519;
_RBX_1 = 1572867;
n1835012 = 1835012;
_RDX_1 = 1638405;
n18 = 18;
n5832731 = 5832731;
n983058 = 983058;
v28 = 0;
while ( 1 )
{
HIWORD(_RAX_1) ^= n119;
if ( (unsigned __int64)++v4 >= 0xC )
break;
n119 = _RAX_1;
}
LOWORD(v28) = 0;
v13 = sub_140005C38((char *)&_RAX_1 + 2);
if ( v13 )
sub_1400013E8(v13);
return 0;
}其中sub_1400020D0先用 CPUID 0 匹配 "AuthenticAMD" ,随后调用sub_140022E54。
char sub_1400020D0()
{
char v0; // di
__int128 v16; // xmm2
__int128 v22; // xmm1
__int128 v28; // [rsp+20h] [rbp-49h]
__int64 v29; // [rsp+24h] [rbp-45h]
__int64 v30[6]; // [rsp+30h] [rbp-39h] BYREF
__int128 v31; // [rsp+60h] [rbp-9h]
_OWORD v32[3]; // [rsp+70h] [rbp+7h] BYREF
__int128 v33; // [rsp+A0h] [rbp+37h]
v29 = 0;
v0 = 0;
__asm { cpuid }
if ( (_DWORD)_RBX == 1752462657 && (_DWORD)_RCX == 1145913699 && (_DWORD)_RDX == 1769238117 )//FAN
{
sub_140027980(v30, 0, 0x40u);
_RAX = 0x80000000LL;
__asm { cpuid }
if ( (unsigned int)_RAX >= 0x80000004 )// CPUID 0x80000002 CPUID 0x80000003 CPUID 0x80000004
{
_RAX = 2147483650LL;
__asm { cpuid }
*((_QWORD *)&v28 + 1) = __PAIR64__(_RDX, _RCX);
*(_QWORD *)&v28 = __PAIR64__(_RBX, _RAX);
_RAX = 2147483651LL;
v16 = v28;
__asm { cpuid }
*((_QWORD *)&v28 + 1) = __PAIR64__(_RDX, _RCX);
*(_QWORD *)&v28 = __PAIR64__(_RBX, _RAX);
_RAX = 2147483652LL;
v22 = v28;
__asm { cpuid }
*((_QWORD *)&v28 + 1) = __PAIR64__(_RDX, _RCX);
*(_QWORD *)&v28 = __PAIR64__(_RBX, _RAX);
v32[0] = v16;
v32[2] = v28;
v33 = v31;
HIBYTE(v33) = 0;
v32[1] = v22;
if ( sub_140022E54(v32, qword_1400295E0)
|| sub_140022E54(v32, qword_1400295F0)
|| sub_140022E54(v32, qword_140029600)
|| sub_140022E54(v32, qword_140029610)
|| sub_140022E54(v32, qword_140029620)
|| sub_140022E54(v32, qword_140029630)
|| sub_140022E54(v32, qword_140029640)
|| sub_140022E54(v32, qword_140029650)
|| sub_140022E54(v32, qword_140029660) )
{
return 1;
}
}
}
return v0;
}读取这三个CPUID 正好返回 Processor Brand String。
NotifyRoutine
NotifyRoutine函数本身作为跳板 实际进入 sub_1402CB230
void sub_1402CB230(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo)
{
if (CreateInfo == NULL //进程退出
&& sub_140011DEC(ProcessId)
&& qword_140030AC8)
{
if (byte_14002D064)
{
KeClearEvent(&Event__0);
byte_14002D064 = 0;
KeWaitForSingleObject(
&Event__0,
Executive,
KernelMode,
FALSE,
Timeout);
}
}
}枚举内核模块
定位ZwQuerySystemInformation的交叉引用 ,几番查找找到sub_140005A74,ida无法正常输出伪代码。
还原调用处伪代码大致如下:
ZwQuerySystemInformation( SystemModuleInformation, NULL, 0, &required_length); buffer = allocate(required_length); ZwQuerySystemInformation( SystemModuleInformation, buffer, required_length, &required_length);
根据后续偏移和步长得到:
buffer + 0x00 -> NumberOfModules
buffer + 0x08 -> first RTL_PROCESS_MODULE_INFORMATION
record size -> 0x128
record + 0x10 -> ImageBase
record + 0x18 -> ImageSize
record + 0x28 -> FullPathName
让AI还原大致如此
bool FindLoadedKernelModuleBySubstring(
const wchar_t *target,
MODULE_RESULT *result)
{
modules = QuerySystemModuleInformation();
for (ULONG i = 0; i < modules->NumberOfModules; ++i) {
module = &modules->Entries[i];
if ((ULONG_PTR)module->ImageBase < 0x8000000000000000ULL ||
module->ImageSize == 0)
continue;
path = ansi_to_unicode(module->FullPathName);
uppercase(path);
if (sub_140023180(path, target)) {
result->ImageBase = module->ImageBase; // 输出结构 +0x208
result->ImageSize = module->ImageSize; // 输出结构 +0x210
return true;
}
}
return false;
}
__int64 __fastcall sub_140005C38(_WORD *Src, _DWORD *a2)
{
__int64 v3; // rbx
unsigned __int64 n0x104; // rax
__int64 v6; // rdx
__int64 v7; // rcx
wchar_t String[260]; // [rsp+20h] [rbp-248h] BYREF
__int64 v10; // [rsp+228h] [rbp-40h]
int v11; // [rsp+230h] [rbp-38h]
v3 = -1;
n0x104 = -1;
v6 = 0;
do
++n0x104;
while ( Src[n0x104] );
if ( n0x104 < 0x104 ) //检查目标文本长度小于 转成大写,并返回映像基址与可选大小。
{
sub_140027980(String, 0, 544);
do
++v3;
while ( Src[v3] );
memmove(String, Src, 2 * v3);
wcsupr(String);
sub_140005A74(v7, String);
v6 = v10;
if ( a2 )
*a2 = v11;
}
return v6;
}字符串搜索中打开一看 ntoskrnl.exe
v1 = sub_1400188D8(L"ntoskrnl.exe", &v6);
sub_14001964C("PAGE", v1, &v9, &v7, 0);
v2 = sub_1400172F0(v9, v7, v4, 17, 0);_int64 sub_140015878()
{
__int64 v0; // rbx
__int64 v1; // rax
__int64 v2; // rax
_DWORD v4[4]; // [rsp+30h] [rbp-28h] BYREF
char v5; // [rsp+40h] [rbp-18h]
int v6; // [rsp+70h] [rbp+18h] BYREF
int v7; // [rsp+78h] [rbp+20h] BYREF
_BYTE PAGE[8]; // [rsp+80h] [rbp+28h] BYREF
__int64 v9; // [rsp+88h] [rbp+30h] BYREF
v0 = 0;
v6 = 0;
v1 = sub_1400188D8(L"ntoskrnl.exe", &v6);
if ( v1 )
{
if ( v6 )
{
strcpy(PAGE, "PAGE");
v9 = 0;
v7 = 0;
sub_14001964C(PAGE, v1, &v9, &v7, 0);
if ( v9 )
{
if ( v7 )
{
v4[0] = 1057852744;
v4[1] = -398508225;
v4[2] = 1061109567;
v4[3] = 65597;
v5 = 0;
v2 = sub_1400172F0(v9, v7, (unsigned int)v4, 17, 0);
if ( v2 )
return *(int *)(v2 + 3) + v2 + 7;
}
}
}
}
return v0;
}四个DWORD按最后一个字节小端展开得到:
48 8D 0D 3F 3F 3F 3F E8 3F 3F 3F 3F 3D 00 01 00 00 也就是: lea rcx, [rip+xxxxxxxx] call xxxxxxxx cmp eax, 10000h
所以 sub_1400172F0应该是FindPattern 函数
匹配位置开头是:
48 8D 0D xx xx xx xx
也就是:
lea rcx, [rip + disp32]
该指令长度为 7 字节,位移位于 +3,所以 RIP 相对地址计算为:
target = match_address + 7 + *(INT32 *)(match_address + 3);
NMI回调
导入表中 直接找KeRegisterNmiCallback,KeDeregisterNmiCallback。
这几个函数都没能正确还原
sub_14000B1DC
-> sub_14000AE70
-> sub_14000B678
-> sub_14000B4B4
粗略查看了下 其调用KeIpiGenericCall 并且读取
MSR 0x1A0
MSR 0x1FC
MSR 0x34
MSR 0xE8 / IA32_APERF
CR4.VMXE 或 AMD EFER.SVME
sub_14000AE70 计算时间差应该是检测虚拟机
WIN32K 获取 gTimerHashTable
依旧字符串大法
__int64 sub_140014354()
{
__int64 n16_1; // rdi
__int64 v1; // rsi
unsigned int n16; // ebx
__int64 v3; // rax
ULONG RequiredAccess; // edx
char csrss.exe[16]; // [rsp+20h] [rbp-40h] BYREF
_BYTE v7[48]; // [rsp+30h] [rbp-30h] BYREF
PIRP Irp; // [rsp+80h] [rbp+20h] BYREF
n16_1 = 0;
v1 = sub_1400188D8((__int64)L"win32k.sys", 0);
if ( v1 )
{
n16 = 16;
while ( 1 )
{
Irp = 0;
if ( (int)sub_140017710(n16, &Irp) < 0 || !Irp )
goto LABEL_11;
if ( !(unsigned __int8)sub_14001E440(Irp) )
break;
LABEL_12:
n16 += 4;
if ( n16 >= 0x4000 )
return n16_1;
}
v3 = sub_1400176C8(Irp);
strcpy(csrss.exe, "csrss.exe");
if ( !(unsigned int)j___ascii_strnicmp(v3, csrss.exe, 9) )
{
sub_14001755C(Irp, v7);
if ( (unsigned __int8)sub_1400175AC(v1 + 4096) )
n16_1 = n16;
sub_140017580(v7);
}
WdmlibIoValidateDeviceIoControlAccess(Irp, RequiredAccess);
LABEL_11:
if ( n16_1 )
return n16_1;
goto LABEL_12;
}
return n16_1;
}也就是找到映射 win32k.sys 的 CSRSS PID 并且保存到qword_1400311D0
其中对应表为:
sub_140017710 -> PsLookupProcessByProcessId
sub_1400176C8 -> PsGetProcessImageFileName
sub_14001755C -> KeStackAttachProcess
sub_140017580 -> KeUnstackDetachProcess
sub_1400175AC -> MmIsAddressValid
继续向下追,寻找qword_1400311D0引用
nsigned __int64 sub_140015FA0()
{
unsigned __int64 v0; // rbx
IRP *Irp; // rax
IRP *Irp_1; // rdi
unsigned __int64 v3; // rax
ULONG RequiredAccess; // edx
_QWORD v6[7]; // [rsp+20h] [rbp-38h] BYREF
v0 = 0;
sub_140027980(v6, 0, 48);
if ( qword_1400311D0 )
{
Irp = (IRP *)sub_14001E000(qword_1400311D0); 根据 qword_1400311D0 查找 CSRSS 的 EPROCESS
Irp_1 = Irp;
if ( Irp )
{
sub_14001755C(Irp, (__int64)v6);
if ( (unsigned __int8)sub_140019448() )
{
v3 = sub_1400160C4(); ResolveTimerHashTableWin7
}
else
{
if ( (unsigned __int8)sub_1400194C8() || !(unsigned __int8)sub_140019418() ) 判断版本
goto LABEL_9;
v3 = sub_14001602C(); ResolveTimerHashTableWin10
}
v0 = v3;
LABEL_9:
sub_140017580((__int64)v6);
WdmlibIoValidateDeviceIoControlAccess(Irp_1, RequiredAccess);
}
}
return v0;
}
char sub_140019448()
{
__int64 v0; // rax
char v1; // cl
if ( *(_DWORD *)(sub_140016FE8() + 4) != 6 )
return 0;
v0 = sub_140016FE8();
v1 = 1;
if ( *(_DWORD *)(v0 + 8) != 1 )
return 0;
return v1;
}
_BOOL8 sub_1400194C8()
{
return *(_DWORD *)(sub_140016FE8() + 4) == 6 && *(_DWORD *)(sub_140016FE8() + 8) == 2;
}
bool sub_140019418()
{
return *(_DWORD *)(sub_140016FE8() + 4) == 10;
}
继续大调查
__int64 sub_14001602C()
{
__int64 v0; // rbx
__int64 v1; // rax
__int64 v2; // rdi
__m128i si128; // [rsp+20h] [rbp-30h] BYREF
_DWORD v5[7]; // [rsp+30h] [rbp-20h] BYREF
__int16 v6; // [rsp+4Ch] [rbp-4h]
v0 = 0;
v5[0] = 6881399; //win32kbase.sys
v6 = 0;
v5[1] = 3342446;
v5[2] = 7012402;
v5[3] = 6357090;
v5[4] = 6619251;
v5[5] = 7536686;
v5[6] = 7536761;
v1 = sub_1400188D8((__int64)v5, 0); //FindLoadedKernelModule
v2 = v1;
if ( v1 && sub_1400175AC(v1) )
{
si128 = _mm_load_si128((const __m128i *)&xmmword_14002A510);
return sub_14001B43C(v2, &si128);
}
return v0;
}xmmword_14002A510 的原始字节为:
67 54 69 6D 65 72 48 61 73 68 54 61 62 6C 65 00
转换为 ASCII:
gTimerHashTable
可得:
qword_1400311D0 -> g_Win32kSessionCsrssPid
qword_1400311E0 -> g_TimerHashTableAddress
接下来看看他是怎么获取的
Windows7:
unsigned __int64 sub_1400160C4()
{
__int64 v0; // rdi
__int64 v1; // rax
__int64 v2; // rsi
unsigned int v3; // eax
char *VirtualAddress; // rbx
char *VirtualAddress_1; // rsi
v0 = 0;
v1 = sub_1400188D8((__int64)L"win32k.sys", 0);
v2 = v1;
if ( v1 )
{
if ( (unsigned __int8)sub_1400175AC(v1) )
{
v3 = sub_14001B3FC(v2);
if ( v3 )
{
VirtualAddress = (char *)(v2 + v3);
if ( MmIsAddressValid(VirtualAddress) )
{
VirtualAddress_1 = VirtualAddress + 4096;
if ( MmIsAddressValid(VirtualAddress + 4096) )
{
while ( VirtualAddress < VirtualAddress_1 )
{
if ( *VirtualAddress == 72
&& VirtualAddress[1] == -119
&& VirtualAddress[2] == 5
&& VirtualAddress[7] == 72
&& VirtualAddress[8] == -119
&& VirtualAddress[9] == 5
&& VirtualAddress[14] == 72
&& VirtualAddress[15] == -115
&& VirtualAddress[16] == 5 )
{
return ((unsigned __int64)(VirtualAddress + 14) & 0xFFFFFFFF00000000uLL)
+ (unsigned int)((_DWORD)VirtualAddress + 14 + *(_DWORD *)(VirtualAddress + 17) + 7);
}
++VirtualAddress;
}
}
}
}
}
}
return v0;
}
win32k_base = FindLoadedKernelModule(L"win32k.sys"); sub_14001B3FC(win32k_base) 返回的值结合其调用方式,接近 PE 的 AddressOfEntryPoint RVA: entry = win32k_base + entry_rva;
随后只扫描入口附近 0x1000 字节,寻找:
48 89 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? mov [rip+disp32_1], rax mov [rip+disp32_2], rax lea rax, [rip+disp32_3]
Windows10和11则都使用 FindPeExportOrNamedSymbol获取。