在 NT5 平台下,要监控进线程句柄的操作。
通常要挂钩三个API:NtOpenProcess、NtOpenThread、NtDuplicateObject。但是在 VISTA SP1 以及之后的系统中,我们可以完全抛弃 HOOK 方案了,转而使用一个标准的 API:ObRegisterCallbacks。下面做一个监视进线程句柄操作的程序,并实现保护名为 CALC.EXE 的进程不被结束。
首先介绍一下 ObRegisterCallbacks 这个函数。此函数的前缀是Ob,看得出它是属于对象管理器的函数,Register 是注册,Callbacks 是回调(复数)。
因此从字面意思上看,它是注册一个对象回调的意思。现在它只能监控进程对象和线程对象。但微软承诺会给此函数增加功能,实现对其它内核对象的监控。这个函数在不能合法进行内核挂钩的 WIN64 上特别有用,但是微软做了一个很扯淡的限制: 驱动程序必须有数字签名才能使用 此函数。不过国外的黑客对此限制很不爽,他们通过逆向 ObRegisterCallbacks,找到了破解这个限制的方法。经研究,内核通过 MmVerifyCallbackFunction 验证此回调是否合法,但此函数只是简单的验证了一下 DriverObject->DriverSection->Flags 的值是不是为 0x20:所以可以简单破解掉这个限制:
X32
typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderLinks;
LIST_ENTRY32 InInitializationOrderLinks;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY32 HashLinks;
struct {
ULONG SectionPointer;
ULONG CheckSum;
};
};
union {
struct {
ULONG TimeDateStamp;
};
struct {
ULONG LoadedImports;
};
};
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
PLDR_DATA_TABLE_ENTRY32 ldr;
ldr = (PLDR_DATA_TABLE_ENTRY32)(pDriverObj->DriverSection);
ldr->Flags |= 0x20;
X64
typedef struct _LDR_DATA_TABLE_ENTRY64
{
LIST_ENTRY64 InLoadOrderLinks;
LIST_ENTRY64 InMemoryOrderLinks;
LIST_ENTRY64 InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY64 ForwarderLinks;
LIST_ENTRY64 ServiceTagLinks;
LIST_ENTRY64 StaticLinks;
PVOID ContextInformation;
ULONG64 OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
PLDR_DATA_TABLE_ENTRY64 ldr;
ldr = (PLDR_DATA_TABLE_ENTRY64)(pDriverObj->DriverSection);
ldr->Flags |= 0x20;
上面代码如果是用于商业或者其他正当场合,注意要好好测试下,我是在网上找了到了那个结构体定义,然后自己在win7 32和win764位机器上测试了一下,没问题。小伙伴记得好好测试其他系统再用。然后就是来两个回调函数,一个是进程回调,一个是线程回调:
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);
BOOLEAN IsProtectedProcessName(PEPROCESS eprocess)
{
char *Name=PsGetProcessImageFileName(eprocess);
if(!_stricmp("calc.exe",Name))
return TRUE;
else
return FALSE;
}
PVOID obHandle=NULL,obHandle2=NULL;
OB_PREOP_CALLBACK_STATUS preCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
#define PROCESS_TERMINATE 0x1
HANDLE pid;
if(pOperationInformation->ObjectType!=*PsProcessType)
goto exit_sub;
pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
DbgPrint("[OBCALLBACK][Process]PID=%ld\n",pid);
UNREFERENCED_PARAMETER(RegistrationContext);
if( IsProtectedProcessName((PEPROCESS)pOperationInformation->Object) )
{
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
//pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
}
}
if(pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
{
//pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
{
pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
}
}
}
exit_sub:
return OB_PREOP_SUCCESS;
}
OB_PREOP_CALLBACK_STATUS preCall2(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
#define THREAD_TERMINATE2 0x1
PEPROCESS ep;
PETHREAD et;
HANDLE pid;
if(pOperationInformation->ObjectType!=*PsThreadType)
goto exit_sub;
et=(PETHREAD)pOperationInformation->Object;
ep=IoThreadToProcess(et);
pid = PsGetProcessId(ep);
DbgPrint("[OBCALLBACK][Thread]PID=%ld; TID=%ld\n",pid,PsGetThreadId(et));
UNREFERENCED_PARAMETER(RegistrationContext);
if( IsProtectedProcessName(ep) )
{
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
//pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
}
}
if(pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
{
//pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
{
pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
}
}
}
exit_sub:
return OB_PREOP_SUCCESS;
}
然后就是在驱动里注册/卸载这两个回调函数:
NTSTATUS ObProtectProcess(BOOLEAN Enable)
{
if(Enable==TRUE)
{
NTSTATUS obst1=0,obst2=0;
OB_CALLBACK_REGISTRATION obReg,obReg2;
OB_OPERATION_REGISTRATION opReg,opReg2;
//reg ob callback 1
memset(&obReg, 0, sizeof(obReg));
obReg.Version = ObGetFilterVersion();
obReg.OperationRegistrationCount = 1;
obReg.RegistrationContext = NULL;
RtlInitUnicodeString(&obReg.Altitude, L"321124");
obReg.OperationRegistration = &opReg;
memset(&opReg, 0, sizeof(opReg));
opReg.ObjectType = PsProcessType;
opReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)&preCall;
obst1=ObRegisterCallbacks(&obReg, &obHandle);
//reg ob callback 2
memset(&obReg2, 0, sizeof(obReg2));
obReg2.Version = ObGetFilterVersion();
obReg2.OperationRegistrationCount = 1;
obReg2.RegistrationContext = NULL;
RtlInitUnicodeString(&obReg2.Altitude, L"321125");
obReg2.OperationRegistration = &opReg2;
memset(&opReg2, 0, sizeof(opReg2));
opReg2.ObjectType = PsThreadType;
opReg2.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg2.PreOperation = (POB_PRE_OPERATION_CALLBACK)&preCall2;
obst1=ObRegisterCallbacks(&obReg2, &obHandle2);
return NT_SUCCESS(obst1) & NT_SUCCESS(obst2);
}
else
{
if(obHandle!=NULL)
ObUnRegisterCallbacks(obHandle);
if(obHandle2!=NULL)
ObUnRegisterCallbacks(obHandle2);
return TRUE;
}
}
执行结果: