QQ电脑管家逆向系列之PsSetCreateProcessNotifyRoutine(1)
程序员文章站
2022-03-16 17:27:04
作 者: 星逝
下午一时兴起,打算研究下QQ电脑管家的一系列功能,于是先从PsSetCreateProcessNofityRoutine下手了,打算逆成一个系...
作 者: 星逝
下午一时兴起,打算研究下QQ电脑管家的一系列功能,于是先从PsSetCreateProcessNofityRoutine下手了,打算逆成一个系列,希望大家多多支持。不喜勿喷。
由于下午时间仓促,NofityRoutine仅逆了一部分,当作(1)先分享给大家,总体流程还是比较清楚了的。
用XT查看内核--->系统回调,发现有两个CreateProcess回调,分别在TCSafeBox.sys和TCKsp.sys中,这里分析的是TCSafeBox.sys。
DriverEntry里调用PsSetCreateProcessNotifyRoutine注册NotifyRoutine,下面是NotifyRoutine的总体流程。
代码:
void __stdcall NotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
if(KeGetCurrentIrql() > 0)
{
return;
}
if(FALSE == Create)
{
sub_144DE(ProcessId, ParentId);
}
else
{
if(0 == dword_15300)
{
return;
}
else
{
sub_143C2(ProcessId, ParentId);
}
}
}
这部分代码比较简单,Create标识这次回调是因为进程创建还是销毁,这里先关注创建的过程,毕竟防御为主。在创建过程的处理中QQ电脑管家设置了一个功能开关,也就是这里的dword_15300。
sub_143C2的流程如下
代码:
void sub_143C2(HANDLE ProcessId, HANDLE ParentId)
{
char buf[0x314];
memset(buf, 0, 0x314);
int a, b;
a = b = 0;
*(HANDLE *)buf = ProcessId;
if(!sub_12326(ProcessId, buf + 8,0x103))
{
return;
}
}
这里就是NotifyRoutine的主要功能了,防御需要两个步骤,一是获得创建进程的相关信息,二是根据这些信息对其进行处理。由于时间关系,这里还只是分析了第一个步骤,也就是这里的sub_12326。第二个步骤放在下一篇讲叙吧。
代码:
BOOL __stdcall sub_12326(HANDLE ProcessId, PVOID Buffer, int arg2)
{
PVOID MbString = Buffer;
if(NULL == Buffer)
{
return 0;
}
wchar_t SourceString[0x208] = {0};
wchar_t UnicodeString[0x208] = {0};
if(!sub_11B6E(ProcessId, SourceString, 0x100))
{
return FALSE;
}
if(!sub_11D0A(SourceString, UnicodeString, 0x100))
{
return FALSE;
}
DWORD ReturnSize = 0;
RtlUnicodeToMultiByteN();
return TRUE;
}
sub_12326就是获取进程相关信息了,其主要功能就是将得到的进程信息拷贝至sub_143C2所给的缓冲区,让sub_143C2做进一步防御的处理。sub_12326里调用了两个子过程sub_11B6E和sub11D0A。它们一个是获取ProcessImageFileName,另一个根据获得的进程镜像路径将其转换为FileDosDeviceName。
代码:
BOOL __stdcall sub_11B6E(HANDLE ProcessId, wchar_t *pStr, DWORD dwLength)
{
if(NULL == ProcessId)
{
return FALSE;
}
if(NULL == puniStr)
{
return FALSE;
}
BOOL ret = FALSE;
PEPROCESS peprocess = NULL;
HANDLE handle = NULL;
if(NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &peprocess)))
{
if(NT_SUCCESS(ObOpenObjectByPointer(peprocess, 0x200, 0, 0, 0, 0, &handle))
{
PVOID pv = ExAllocatePoolWithTag(NonPagedPool, 0x800, 0x5A5A4D4D);
if(pv != NULL)
{
memset(pv, 0, 0x800);
if(NT_SUCCESS(ZwQueryInformationProcess(handle, ProcessImageFileName, pv, 0x800, &ProcessId))
{
PUNICODE_STRING puniImageFileName = pv;
if(puniImageFileName->Buffer[0] != 0)
{
if(puniImageFileName->Length > 0)
{
wcsncpy(pStr, puniImageFileName->Buffer, dwLength);
ret = TRUE;
}
}
}
ExFreePoolWithTag(pv, NonPagedPool);
}
}
}
if(handle != NULL)
{
ZwClose(handle);
}
if(peprocess != NULL)
{
ObfDereferenceObject(peprocess)
}
return ret;
}
sub_11B6E获取ProcessImageFileName。
代码:
BOOL __stdcall sub_11D0A(wchar_t *pStr1, wchar_t *pStr2, DWORD dwLength)
{
PFILE_OBJECT Object = NULL;
PVOID pv = NULL;
BOOL ret = FALSE;
HANDLE hFile = sub_11C56(pStr1, FALSE);
if(hFile != NULL)
{
if(NT_SUCCESS(ObReferenceObjectByHandle(hFile, 0, *IoFileObjectType, 0, &Object, 0))
{
if(NT_SUCCESS(IoQueryFileDosDeviceName(Object, &pv))
{
POBJECT_NAME_INFORMATION pInfo = pv;
if(pv->Name.Length < dwLength * 2)
{
memcpy(pStr2, pv->Name.Buffer, pv->Name.Length);
ret = TRUE;
}
}
}
}
if(Object != NULL)
{
ObfDereferenceObject(Object);
}
if(hFile != NULL)
{
ZwClose(hFile);
}
if(pv != NULL)
{
ExFreePoolWithTag(pv, 0);
}
return ret;
}
HANDLE __stdcall sub_11C56(wchar_t *pStr, BOOL flag)
{
if(NULL == pStr)
{
return NULL;
}
ObjectAttributes ObjAttr = {0};
UNICODE_STRING DestString = {0};
IoStatusBlock block = {0};
HANDLE handle = NULL;
RtlInitUnicodeString(&DestString, pStr1);
ULONG CreateOptions = 0;
if(!flag)
{
CreateOptions = 0x21;
}
else
{
CreateOptions = 0x60;
}
ObjAttr.ObjectName = &DestString;
ObjAttr.Length = sizeof(ObjectAttributes);
ObjAttr.RootDirectory = NULL;
ObjAttr.Attributes = 0x240;
ObjAttr.SecurityDescriptor = NULL;
ObjAttr.SecurityQualityOfService = NULL;
if(NT_SUCCESS(IoCreateFile(&handle, 0x80000000, &ObjAttr, &block, 0, 0x80, 1, 1, CreateOptions, 0, 0, 0, 0, 0x100))
{
return handle;
}
return NULL;
}
sub_11D0A转换FileDosDeviceName,其子过程sub_11C56主要是IoCreateFile获取进程镜像文件的句柄。
逆出来的代码都很简单,所以只是简单介绍了下它的流程,相信大家也能看得懂
下午一时兴起,打算研究下QQ电脑管家的一系列功能,于是先从PsSetCreateProcessNofityRoutine下手了,打算逆成一个系列,希望大家多多支持。不喜勿喷。
由于下午时间仓促,NofityRoutine仅逆了一部分,当作(1)先分享给大家,总体流程还是比较清楚了的。
用XT查看内核--->系统回调,发现有两个CreateProcess回调,分别在TCSafeBox.sys和TCKsp.sys中,这里分析的是TCSafeBox.sys。
DriverEntry里调用PsSetCreateProcessNotifyRoutine注册NotifyRoutine,下面是NotifyRoutine的总体流程。
代码:
void __stdcall NotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
if(KeGetCurrentIrql() > 0)
{
return;
}
if(FALSE == Create)
{
sub_144DE(ProcessId, ParentId);
}
else
{
if(0 == dword_15300)
{
return;
}
else
{
sub_143C2(ProcessId, ParentId);
}
}
}
这部分代码比较简单,Create标识这次回调是因为进程创建还是销毁,这里先关注创建的过程,毕竟防御为主。在创建过程的处理中QQ电脑管家设置了一个功能开关,也就是这里的dword_15300。
sub_143C2的流程如下
代码:
void sub_143C2(HANDLE ProcessId, HANDLE ParentId)
{
char buf[0x314];
memset(buf, 0, 0x314);
int a, b;
a = b = 0;
*(HANDLE *)buf = ProcessId;
if(!sub_12326(ProcessId, buf + 8,0x103))
{
return;
}
}
这里就是NotifyRoutine的主要功能了,防御需要两个步骤,一是获得创建进程的相关信息,二是根据这些信息对其进行处理。由于时间关系,这里还只是分析了第一个步骤,也就是这里的sub_12326。第二个步骤放在下一篇讲叙吧。
代码:
BOOL __stdcall sub_12326(HANDLE ProcessId, PVOID Buffer, int arg2)
{
PVOID MbString = Buffer;
if(NULL == Buffer)
{
return 0;
}
wchar_t SourceString[0x208] = {0};
wchar_t UnicodeString[0x208] = {0};
if(!sub_11B6E(ProcessId, SourceString, 0x100))
{
return FALSE;
}
if(!sub_11D0A(SourceString, UnicodeString, 0x100))
{
return FALSE;
}
DWORD ReturnSize = 0;
RtlUnicodeToMultiByteN();
return TRUE;
}
sub_12326就是获取进程相关信息了,其主要功能就是将得到的进程信息拷贝至sub_143C2所给的缓冲区,让sub_143C2做进一步防御的处理。sub_12326里调用了两个子过程sub_11B6E和sub11D0A。它们一个是获取ProcessImageFileName,另一个根据获得的进程镜像路径将其转换为FileDosDeviceName。
代码:
BOOL __stdcall sub_11B6E(HANDLE ProcessId, wchar_t *pStr, DWORD dwLength)
{
if(NULL == ProcessId)
{
return FALSE;
}
if(NULL == puniStr)
{
return FALSE;
}
BOOL ret = FALSE;
PEPROCESS peprocess = NULL;
HANDLE handle = NULL;
if(NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &peprocess)))
{
if(NT_SUCCESS(ObOpenObjectByPointer(peprocess, 0x200, 0, 0, 0, 0, &handle))
{
PVOID pv = ExAllocatePoolWithTag(NonPagedPool, 0x800, 0x5A5A4D4D);
if(pv != NULL)
{
memset(pv, 0, 0x800);
if(NT_SUCCESS(ZwQueryInformationProcess(handle, ProcessImageFileName, pv, 0x800, &ProcessId))
{
PUNICODE_STRING puniImageFileName = pv;
if(puniImageFileName->Buffer[0] != 0)
{
if(puniImageFileName->Length > 0)
{
wcsncpy(pStr, puniImageFileName->Buffer, dwLength);
ret = TRUE;
}
}
}
ExFreePoolWithTag(pv, NonPagedPool);
}
}
}
if(handle != NULL)
{
ZwClose(handle);
}
if(peprocess != NULL)
{
ObfDereferenceObject(peprocess)
}
return ret;
}
sub_11B6E获取ProcessImageFileName。
代码:
BOOL __stdcall sub_11D0A(wchar_t *pStr1, wchar_t *pStr2, DWORD dwLength)
{
PFILE_OBJECT Object = NULL;
PVOID pv = NULL;
BOOL ret = FALSE;
HANDLE hFile = sub_11C56(pStr1, FALSE);
if(hFile != NULL)
{
if(NT_SUCCESS(ObReferenceObjectByHandle(hFile, 0, *IoFileObjectType, 0, &Object, 0))
{
if(NT_SUCCESS(IoQueryFileDosDeviceName(Object, &pv))
{
POBJECT_NAME_INFORMATION pInfo = pv;
if(pv->Name.Length < dwLength * 2)
{
memcpy(pStr2, pv->Name.Buffer, pv->Name.Length);
ret = TRUE;
}
}
}
}
if(Object != NULL)
{
ObfDereferenceObject(Object);
}
if(hFile != NULL)
{
ZwClose(hFile);
}
if(pv != NULL)
{
ExFreePoolWithTag(pv, 0);
}
return ret;
}
HANDLE __stdcall sub_11C56(wchar_t *pStr, BOOL flag)
{
if(NULL == pStr)
{
return NULL;
}
ObjectAttributes ObjAttr = {0};
UNICODE_STRING DestString = {0};
IoStatusBlock block = {0};
HANDLE handle = NULL;
RtlInitUnicodeString(&DestString, pStr1);
ULONG CreateOptions = 0;
if(!flag)
{
CreateOptions = 0x21;
}
else
{
CreateOptions = 0x60;
}
ObjAttr.ObjectName = &DestString;
ObjAttr.Length = sizeof(ObjectAttributes);
ObjAttr.RootDirectory = NULL;
ObjAttr.Attributes = 0x240;
ObjAttr.SecurityDescriptor = NULL;
ObjAttr.SecurityQualityOfService = NULL;
if(NT_SUCCESS(IoCreateFile(&handle, 0x80000000, &ObjAttr, &block, 0, 0x80, 1, 1, CreateOptions, 0, 0, 0, 0, 0x100))
{
return handle;
}
return NULL;
}
sub_11D0A转换FileDosDeviceName,其子过程sub_11C56主要是IoCreateFile获取进程镜像文件的句柄。
逆出来的代码都很简单,所以只是简单介绍了下它的流程,相信大家也能看得懂
上一篇: 运行exe!微软密研骁龙845新品:Surface手机?
下一篇: redis 是如何做持久化的