比赛打完了,来继续搞了,因为那个主动防御正在写,所以想找找思路正好想到可以来逆向一下PT的驱动模块看看pt大大是怎么写的程序。
PT x64版本的驱动模块是这个kEvP64.sys。
0x0
先来看看DriverEntry
//IDA伪代码
__int64 __fastcall sub_3A010(struct _DRIVER_OBJECT *a1, __int64 a2)
{
char *v2; // rdi@1
signed __int64 i; // rcx@1
char *v4; // rdi@4
__int64 v5; // rsi@4
signed __int64 j; // rcx@4
_UNKNOWN *v7; // rdi@7
char *v8; // rsi@7
signed __int64 k; // rcx@7
__int64 result; // rax@11
unsigned int v11; // [sp+48h] [bp-A0h]@10
NTSTATUS v12; // [sp+48h] [bp-A0h]@12
NTSTATUS v13; // [sp+48h] [bp-A0h]@14
char v14; // [sp+ACh] [bp-3Ch]@1
char v15; // [sp+C0h] [bp-28h]@4
struct _DRIVER_OBJECT *DriverObject; // [sp+F0h] [bp+8h]@1 DriverObject = a1;
v2 = &v14;
for ( i = 4i64; i; --i )
*v2++ = ;
v4 = &v15;
v5 = a2;
for ( j = 16i64; j; --j )
*v4++ = *(_BYTE *)v5++;
v7 = &unk_37C60;
v8 = &v15;
for ( k = 16i64; k; --k )
{
*(_BYTE *)v7 = *v8++;
v7 = (char *)v7 + ;
}
RtlGetVersion(&unk_37AE0);
v11 = sub_19A30();
if ( (v11 & 0x80000000) == )
{
sub_39010();
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_29870;
RtlInitUnicodeString(&DeviceName, L"\\Device\\kEvP64");
v12 = IoCreateDevice(DriverObject, , &DeviceName, 0x22u, 0x100u, , &DeviceObject);
if ( v12 >= )
{
DriverObject->MajorFunction[] = (PDRIVER_DISPATCH)sub_298F0;
DriverObject->MajorFunction[] = (PDRIVER_DISPATCH)sub_298F0;
DriverObject->MajorFunction[] = (PDRIVER_DISPATCH)sub_29940;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\kEvP64");
v13 = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if ( v13 >= )
{
FltRegisterFilter(DriverObject, &unk_300C0, &qword_37AA8);
*((_DWORD *)DriverObject->DriverSection + ) |= 0x20u;
qword_37C28 = (__int64)DriverObject;
result = 0i64;
}
else
{
IoDeleteDevice(DeviceObject);
result = (unsigned int)v13;
}
}
else
{
result = (unsigned int)v12;
}
}
else
{
result = v11;
}
return result;
}
函数的26、27行把程序的注册表目录的字符串保存到了局部数组中,然后又存在了一个全局的缓冲区里,应该是一个全局数组。
然后是进行系统版本的判断,对于驱动模块来说判断系统很重要,否则很容易造成蓝屏。
使用RtlGetVersion获得一个有关系统信息的结构,WDK中对这个函数的描述如下
RtlGetVersion
The RtlGetVersion routine returns version information about the currently running operating system. NTSTATUS
RtlGetVersion(
IN OUT PRTL_OSVERSIONINFOW lpVersionInformation
);
来看看pt是怎么对版本进行的判断,
首先是对IRQL进行的判断,代码如下
if ( (signed int)(unsigned __int8)sub_11030() > )
{
v0 = sub_11030();
DbgPrint("EX: Pageable code called at IRQL %d\n", v0);
sub_11020();
}
其中sub_11030的反汇编如下
刚开始没明白是什么意思,后来查了一下原来X64的IRQL储存在CR8里,这个以前还真的不知道,学到了。
判断了一下IRQL是否合理,然后就是具体的判断了。
v2 = dword_37964;
v4 = dword_37968;
v3 = (unsigned __int16)word_37A74;
DbgPrint(
"[kEvP64]Windows %d.%d, SP%d.%d, build %d\n",
(unsigned int)dword_37964,
(unsigned int)dword_37968,
(unsigned __int16)word_37A74);
dword_37908 = ;
dword_378E0 = ;
if ( v2 == && v4 == )
{
dword_378FC = ;
if ( !v3 )
return 3221225659i64;
if ( v3 == )
return 3221225659i64;
if ( v3 != && v3 != )
return 3221225659i64;
return 0i64;
}
if ( v2 == && v4 == )
{
dword_378FC = ;
if ( v3 && v3 != && v3 != )
return 3221225659i64;
return 0i64;
}
if ( v2 == && !v4 )
{
dword_378FC = ;
if ( v3 )
{
if ( v3 == )
{
dword_37C40 = ;
dword_37C50 = ;
}
else
{
if ( v3 != )
return 3221225659i64;
dword_37C40 = ;
dword_37C50 = ;
}
}
else
{
dword_37C40 = ;
dword_37C50 = ;
}
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = ;
dword_378E4 = ;
dword_37A90 = ;
dword_378F8 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37908 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
return 0i64;
}
if ( v2 == && v4 == )
{
dword_378FC = ;
if ( v3 && v3 != )
return 3221225659i64;
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = ;
dword_378E4 = ;
dword_37A90 = ;
dword_378F8 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_37908 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 7 SP%d\n", v3);
return 0i64;
}
if ( v2 == && v4 == )
{
dword_378FC = ;
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37A90 = ;
dword_378F8 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 8 SP%d\n", v3);
return 0i64;
}
if ( v2 == && v4 == )
{
dword_378FC = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37C58 = ;
dword_378F8 = ;
dword_37C70 = ;
dword_37A90 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 8.1 SP%d\n", v3);
return 0i64;
}
if ( v2 == && !v4 )
{
dword_378FC = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37C58 = ;
dword_378F8 = ;
dword_37C70 = ;
dword_37A90 = ;
dword_378F0 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
return 0i64;
}
if ( v2 == && v4 || v2 > 0xA )
{
dword_378FC = -;
result = 3221225659i64;
}
else
{
result = 3221225659i64;
}
return result;
}
其中v2,v3,v4都是结构体中的成员,就是上面用RtlGetVersion获取到的结构体。
类似于return 3221225659i64;这种是NTSTATUS值,0就是STATUS_SUCESS,下面还可以看到
(v11 & 0x80000000) == 0
这种写法就是NT_SUCESS()宏
来具体看下这种判断过程是怎么个意思
dword_37960 = ;
v2 = RtlGetVersion(&dword_37960);
这个是指定使用了RTL_OSVERSIONINFOEXW结构,因为RtlGetVersion这个函数其实可以支持两种格式的输出。
根据反汇编的结果还原了一下C的源码,应该是根据不同的系统版本设置了全局变量不同的值,但是目前还不知道这些变量的作用,判断系统方法比较简单,根据dwMajorVersion判断主版本号,dwMinorVersion判断副版本号,再根据需要去判断wServicePackMajor的值就可以实现了。
RTL_OSVERSIONINFOEXW Struct={};
ULONG Version;
NTSTATUS CheckVersion(void)
{
ULONG MajorVersion;
ULONG MinorVersion;
ULONG ServicePackMajor;
ULONG IRQL;
ULONG result;
RtlGetVersion(&Struct);
if(KeGetCurrentirql()>PASSIVE_LEVEL)
{
IRQL=KeGetCurrentirql();
DbgPrint("EX: Pageable code called at IRQL %d\n", IRQL);
_asm{int 0x2c};
}
MajorVersion=Struct.dwMajorVersion;
MinorVersion=Struct.dwMinorVersion;
ServicePackMajor=Struct.wServicePackMajor;
DbgPrint(
"[kEvP64]Windows %d.%d, SP%d.%d, build %d\n",Struct.dwMajorVersion,Struct.dwMinorVersion,Struct.wServicePackMajor);
if(MajorVersion==&&MinorVersion==)
{
//WINDOWS_XP
Version=;
if(!ServicePackMajor)
return ;
if(ServicePackMajor==)
return ;
if(ServicePackMajor!=&&ServicePackMajor!=)
return ;
return STATUS_SUCCESS;
}
if(MajorVersion==&&MinorVersion==)
{
//WINDOWS_2003
Version=;
if(ServicePackMajor&&ServicePackMajor!=&&ServicePackMajor!=)
return ;
return STATUS_SUCCESS;
}
if(MajorVersion==&&!MinorVersion)
{
//WINDOWS_2003
Version=;
if(ServicePackMajor)
{
if(ServicePackMajor==)
{
dword_37C40 = ;
dword_37C50 = ;
}
else
{
if(ServicePackMajor!=)
return ;
dword_37C40 = ;
dword_37C50 = ;
}
}
else
{
dword_37C40 = ;
dword_37C50 = ;
}
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = ;
dword_378E4 = ;
dword_37A90 = ;
dword_378F8 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37908 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
return STATUS_SUCCESS;
}
if(MajorVersion==&&MinorVersion==)
{
//WINDOWS_7
Version=;
if(ServicePackMajor&&ServicePackMajor!=)
return ;
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = ;
dword_378E4 = ;
dword_37A90 = ;
dword_378F8 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_37908 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 7 SP%d\n", ServicePackMajor);
return STATUS_SUCCESS; }
if(MajorVersion==&&MinorVersion==)
{
//WINDOWS_8
dword_378FC = ;
dword_37C58 = ;
dword_37C70 = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37A90 = ;
dword_378F8 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 8 SP%d\n", ServicePackMajor);
return STATUS_SUCCESS;
}
if ( MajorVersion == && MinorVersion == )
{
//WINDOWS_8.1
dword_378FC = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37C58 = ;
dword_378F8 = ;
dword_37C70 = ;
dword_37A90 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_378F0 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
DbgPrint("[kEvP64]Initialized version-specific data for Windows 8.1 SP%d\n", ServicePackMajor);
return STATUS_SUCCESS;
}
if ( MajorVersion == && !MinorVersion )
{
//WINDOWS_10
dword_378FC = ;
dword_37C54 = -;
dword_378E4 = -;
dword_37C58 = ;
dword_378F8 = ;
dword_37C70 = ;
dword_37A90 = ;
dword_378F0 = ;
dword_37C40 = ;
dword_37C50 = ;
dword_37AC0 = ;
dword_37908 = ;
dword_37C10 = ;
dword_378E0 = ;
dword_37C44 = ;
dword_37C48 = ;
dword_37BF4 = ;
dword_37C30 = ;
dword_37C14 = ;
dword_37930 = ;
dword_378F4 = ;
dword_37BF8 = ;
dword_37888 = ;
dword_37AB0 = ;
dword_37C4C = ;
dword_37920 = ;
dword_37AC4 = ;
dword_377CC = ;
dword_37894 = ;
dword_377C4 = ;
dword_377B0 = ;
dword_377B4 = ;
dword_377B8 = ;
dword_377BC = ;
dword_377C0 = ;
dword_3788C = ;
dword_37890 = ;
dword_37898 = ;
dword_3789C = ;
dword_378A0 = ;
return STATUS_SUCCESS;
}
if ( MajorVersion == && MajorVersion || MinorVersion > 0xA )
{
dword_378FC = -;
result = ;
}
else
{
result = ;
}
return result;
}
因为代码比较长,我默认折叠了,想看的可以看下。
我们继续往下看了,接下来主要做了如下几件事:
- 创建一个设备
- 创建符号链接
- 设置设备分发函数
- 注册一个过滤驱动
首先作者实现了一个获取导出函数地址的函数,我想这是因为作者想使用一些已经导出但是没有在WDK文档中的函数吧。我这里取名为GetFuncAddress了。我用C重写了一下这个函数,如下
//根据反汇编写的
PVOID GetFuncAddress(WCHAR *Name)
{
ULONG IRQL;
UNICODE_STRING UnicodeFindName;
WCHAR *BufPointer=Name;
if(KeGetCurrentirql()>)
{
IRQL=KeGetCurrentirql();
DbgPrint("EX: Pageable code called at IRQL %d\n", IRQL);
_asm{int 0x2C};
}
RtlInitUnicodeString(&UnicodeFindName,BufPointer);
return MmGetSystemRoutineAddress(&UnicodeFindName);
}
然后作者用这个函数获取一些函数的地址(先判断了一下系统的版本),函数的列表如下
"ExfUnblockPushLock"
"ObGetObjectType"
"ObDereferenceObject"
"PsAcquireProcessExitSynchronization"
"PsIsProtectedProcess"
"PsReleaseProcessExitSynchronization"
"PsResumeProcess"
"PsSuspendProcess"
"KeSetAffinityThread"
这些部分都在第39行的sub_39010();函数中,程序判断完系统版本后马上就执行了这个函数。
这个函数的伪代码如下
__int64 sub_39010()
{
unsigned __int8 v0; // al@2
__int64 result; // rax@15 if ( (signed int)(unsigned __int8)sub_11030() > )
{
v0 = sub_11030();
DbgPrint("EX: Pageable code called at IRQL %d\n", v0);
sub_11020();
}
if ( (unsigned int)dword_378FC >= 0x3E )
qword_37AB8 = (__int64)sub_392B0(L"ExfUnblockPushLock");
qword_37928 = (__int64)sub_392B0(L"ObGetObjectType");
qword_378E8 = (__int64)sub_392B0(L"ObDereferenceObject");
qword_378D8 = (__int64)sub_392B0(L"PsAcquireProcessExitSynchronization");
qword_37A98 = (__int64)sub_392B0(L"PsIsProtectedProcess");
qword_37C38 = (__int64)sub_392B0(L"PsReleaseProcessExitSynchronization");
qword_37900 = (__int64)sub_392B0(L"PsResumeProcess");
qword_37C20 = (__int64)sub_392B0(L"PsSuspendProcess");
qword_378B8 = (__int64)sub_392B0(L"KeSetAffinityThread");
sub_26AD0((__int64)qword_39A00, (__int64)"KfdClassify2");
sub_26AD0((__int64)qword_39A00, (__int64)"GetCalloutEntry ");
sub_26AD0((__int64)qword_39A00, (__int64)"InitDefaultCallout ");
qword_377F8 = sub_26AD0((__int64)qword_39A70, (__int64)"KeInsertQueueApc");
if ( !qword_377F8 )
qword_377F8 = (__int64)sub_392B0(L"KeInsertQueueApc");
qword_37800 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetCreateProcessNotifyRoutine");
if ( !qword_37800 )
qword_37800 = (__int64)sub_392B0(L"PsSetCreateProcessNotifyRoutine");
qword_37808 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetCreateThreadNotifyRoutine");
if ( !qword_37808 )
qword_37808 = (__int64)sub_392B0(L"PsSetCreateThreadNotifyRoutine");
qword_37810 = sub_26AD0((__int64)qword_39A70, (__int64)"PsSetLoadImageNotifyRoutine");
if ( !qword_37810 )
qword_37810 = (__int64)sub_392B0(L"PsSetLoadImageNotifyRoutine");
qword_37818 = sub_26AD0((__int64)qword_39A70, (__int64)"CmUnRegisterCallback");
if ( !qword_37818 )
qword_37818 = (__int64)sub_392B0(L"CmUnRegisterCallback");
result = sub_26AD0((__int64)qword_39A70, (__int64)"IoRegisterShutdownNotification");
qword_37820 = result;
if ( !result )
{
result = (__int64)sub_392B0(L"IoRegisterShutdownNotification");
qword_37820 = result;
}
return result;
}
这个函数首先判断了一下IRQL这个在之前就已经分析过了,然后判断了一下系统版本,通过验证之后用自己实现的GetFuncAddress函数来获取这些函数的地址,并把地址储存在全局变量中sub_392B0就是GetFuncAddress(),然后又调用了sub_26AD0()这个函数,这个函数中又调用了这个函数
//IDA伪代码,被sub_39010()调用
PVOID __fastcall sub_260D0(unsigned int a1)
{
PVOID result; // rax@3
int v2; // [sp+20h] [bp-28h]@4
int v3; // [sp+24h] [bp-24h]@4
SIZE_T NumberOfBytes; // [sp+28h] [bp-20h]@1
PVOID P; // [sp+30h] [bp-18h]@2
unsigned int v6; // [sp+50h] [bp+8h]@1 v6 = a1;
for ( LODWORD(NumberOfBytes) = ; ; LODWORD(NumberOfBytes) = v3 + )
{
P = ExAllocatePool(, (unsigned int)NumberOfBytes);
if ( !P )
return 0i64;
v3 = ;
v2 = ZwQuerySystemInformation(v6, P, (unsigned int)NumberOfBytes, &v3);
if ( v2 == - )
{
ExFreePoolWithTag(P, );
P = 0i64;
if ( v3 )
continue;
}
break;
}
if ( v2 >= )
{
result = P;
}
else
{
sub_199F0(P);
result = 0i64;
}
return result;
}
这是调用ZwQuerySystemInformation()函数的11号功能也就是SystemModuleInformation功能,这个函数经常在安全类程序中调用,作为一个常规的枚举模块的方法。用循环是为了让ZwQuerySystemInformation返回需要的合适的大小。这个函数返回的结构如下
typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
Count为个数,有多少Count就有多少的SYSTEM_MODULE_INFORMATION_ENTRY,其中这个数组中的项的结构如下:
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {
HANDLE Section;
PVOID MappedBase;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[];
} SYSTEM_MODULE_INFORMATION_ENTRY,
*PSYSTEM_MODULE_INFORMATION_ENTRY;
注意这个调用返回的第一个模块一定是ntoskrnl.exe模块,很多人就是通过这个调用去获取内核模块的信息的。
__int64 __fastcall sub_21DB0(const char *a1)
{
__int64 result; // rax@2
UNICODE_STRING String1; // [sp+20h] [bp-108h]@3
UNICODE_STRING String2; // [sp+30h] [bp-F8h]@12
NTSTATUS v4; // [sp+40h] [bp-E8h]@1
PCWSTR v5; // [sp+48h] [bp-E0h]@1
__int64 v6; // [sp+50h] [bp-D8h]@1
__int64 v7; // [sp+58h] [bp-D0h]@1
__int64 v8; // [sp+60h] [bp-C8h]@1
__int64 v9; // [sp+68h] [bp-C0h]@1
__int64 v10; // [sp+70h] [bp-B8h]@1
unsigned int i; // [sp+78h] [bp-B0h]@1
UNICODE_STRING v12; // [sp+80h] [bp-A8h]@1
UNICODE_STRING v13; // [sp+90h] [bp-98h]@19
STRING v14; // [sp+A0h] [bp-88h]@3
STRING v15; // [sp+B0h] [bp-78h]@8
PCWSTR v16; // [sp+C0h] [bp-68h]@1
__int64 v17; // [sp+C8h] [bp-60h]@1
__int64 v18; // [sp+D0h] [bp-58h]@1
__int64 v19; // [sp+D8h] [bp-50h]@1
UNICODE_STRING UnicodeString; // [sp+E0h] [bp-48h]@8
int j; // [sp+F0h] [bp-38h]@1
PVOID v22; // [sp+F8h] [bp-30h]@1
UNICODE_STRING DestinationString; // [sp+100h] [bp-28h]@1
__int64 v24; // [sp+110h] [bp-18h]@1
PCSZ SourceString; // [sp+130h] [bp+8h]@1 SourceString = a1;
v24 = 0i64;
v4 = ;
i = ;
j = ;
v5 = L"hal.dll";
v6 = (__int64)L"halacpi.dll";
v7 = (__int64)L"halapic.dll";
v8 = (__int64)L"halmps.dll";
v9 = (__int64)L"halaacpi.dll";
v10 = (__int64)L"halmacpi.dll";
v16 = L"ntoskrnl.exe";
v17 = (__int64)L"ntkrnlpa.exe";
v18 = (__int64)L"ntkrnlmp.exe";
v19 = (__int64)L"ntkrpamp.exe";
v22 = 0i64;
RtlInitUnicodeString(&DestinationString, L"hal.dll");
RtlInitUnicodeString(&v12, L"ntoskrnl.exe");
v22 = sub_260D0(11u);
if ( v22 )
{
RtlInitAnsiString(&v14, SourceString);
v4 = RtlAnsiStringToUnicodeString(&String1, &v14, 1u);
if ( v4 >= )
{
for ( i = ; ; ++i )
{
if ( i < *(_DWORD *)v22 )
{
RtlInitAnsiString(&v15, (PCSZ)v22 + * i + *((_WORD *)v22 + * i + ) + );
v4 = RtlAnsiStringToUnicodeString(&UnicodeString, &v15, 1u);
if ( v4 < )
continue;
if ( RtlEqualUnicodeString(&String1, &DestinationString, ) )
{
for ( j = ; j < ; ++j )
{
RtlInitUnicodeString(&String2, (&v5)[ * j]);
if ( RtlEqualUnicodeString(&UnicodeString, &String2, ) )
{
v24 = *((_QWORD *)v22 + * i + );
break;
}
}
}
else if ( RtlEqualUnicodeString(&String1, &v12, ) )
{
for ( j = ; j < ; ++j )
{
RtlInitUnicodeString(&v13, (&v16)[ * j]);
if ( RtlEqualUnicodeString(&UnicodeString, &v13, ) )
{
v24 = *((_QWORD *)v22 + * i + );
break;
}
}
}
else if ( RtlEqualUnicodeString(&String1, &UnicodeString, ) )
{
v24 = *((_QWORD *)v22 + * i + );
}
RtlFreeUnicodeString(&UnicodeString);
if ( !v24 )
continue;
}
break;
}
RtlFreeUnicodeString(&String1);
sub_199F0(v22);
result = v24;
}
else
{
result = 0i64;
}
}
else
{
result = 0i64;
}
return result;
}
这个是遍历刚才得到的结果,就是刚才得到的SYSTEM_MODULE_INFORMATION结构。如果要得到的目标模块是nt内核模块或是hal.dll的话就特殊处理
0x1
接下来就是进入正题了,来看看PT的分发例程,在看分发例程之前先总结下,如下图
图中的真正的功能函数就是我们要分析的重点,PT的大部分功能都在这里完成。
0x2
我们来看switch语句,这个语句由最后的一个 DbgPrint("[kEvP64] Unknown IOCTL: 0x%X (%04X,%04X)\r\n", v37, (v37 & 0xFFFF0000) >> 16, (v37 >> 2) & 0xFFF);就可以看出是DeviceIoControl函数来发送的一些自定义的IOCTL。但是我们没有办法知道具体的含义,因为这些都是自定义的,在C中定义IOCTL也只是用一个宏来完成。
来看下这里
//IDA伪代码
Irp = a2;
v35 = -;
v27 = 0i64;
v28 = sub_11100((__int64)a2);
注意第5行的sub_11100,参数a2是Irp指针,它的反汇编如下图
只是一个简单的移位操作是吧。我猜这个应该就是IoGetCurrentIrpStackLocation()这个函数,可见IRP中是存有当前IRP栈的指针的。首先是对比Irp栈的总层数是不是大于当前层数,然后就是取出当前栈的指针(这个指针在Irp结构中)返回了。
而我们常用的操作就是
PIO_STACK_LOCATION irpSp;
IoControlCode=irpSp->Parameters.DeviceIoControl.IoControlCode;
switch(IoControlCode)
{
……
}