Win64 驱动内核编程-11.回调监控进线程句柄操作
无HOOK监控进线程句柄操作
在 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;
}
}
执行结果:
Win64 驱动内核编程-11.回调监控进线程句柄操作的更多相关文章
- Win64 驱动内核编程-12.回调监控进线程创建和退出
回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine 进程回调PsSetCreateThreadNotifyRoutine 线程回调分 ...
- Win64 驱动内核编程-14.回调监控文件
回调监控文件 使用 ObRegisterCallbacks 实现保护进程,其实稍微 PATCH 下内核,这个函数还能实现文件操作监视.但可惜只能在 WIN7X64 上用.因为在 WIN7X64 上 P ...
- Win64 驱动内核编程-15.回调监控注册表
回调监控注册表 在 WIN32 平台上,监控注册表的手段通常是 SSDT HOOK.不过用 SSDT HOOK 的方式监控注册表实在是太麻烦了,要 HOOK 一大堆函数,还要处理一些 NT6 系统有而 ...
- Win64 驱动内核编程-30.枚举与删除线程回调
枚举与删除线程回调 进程回调可以监视进程的创建和退出,这个在前面的章节已经总结过了.某些游戏保护的驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退出.而线程回调则通常用 ...
- Win64 驱动内核编程-13.回调监控模块加载
回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...
- Win64 驱动内核编程-31.枚举与删除映像回调
枚举与删除映像回调 映像回调可以拦截 RING3 和 RING0 的映像加载.某些游戏保护会用此来拦截黑名单中的驱动加载,比如 XUETR.WIN64AST 的驱动.同理,在反游戏保护的过程中,也可以 ...
- Win64 驱动内核编程-8.内核里的其他常用
内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...
- Win64 驱动内核编程-3.内核里使用内存
内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...
- Win64 驱动内核编程-18.SSDT
SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...
随机推荐
- PCA——主成分分析
PCA(Principal Components Analysis)主成分分析是一个简单的机器学习算法,利用正交变换把由线性相关变量表示的观测数据转换为由少量线性无关比变量表示的数据,实现降维的同时尽 ...
- Linux速通08 网络原理及基础设置、软件包管理
使用 ifconfig命令来维护网络 # ifconfig 命令:显示所有正在启动的网卡的详细信息或设定系统中网卡的 IP地址 # 应用 ifconfig命令设定网卡的 IP地址: * 例:修改 et ...
- Java 集合框架 04
集合框架·Map 和 Collections集合工具类 Map集合的概述和特点 * A:Map接口概述 * 查看API可知: * 将键映射到值的对象 * 一个映射不能包含重复的键 * 每个键最多只能映 ...
- sanic-jwt 的使用
Sanic 是基于 Python 的一个支持高并发的异步 web 框架,sanic-jwt 则是针对Sanic 开发的一个基于 PyJWT 封装的 JWT 授权认证模块. sanic-jwt 项目主页 ...
- phpMyAdmin Transformation 任意文件包含/远程代码执行漏洞
漏洞参考 https://yq.aliyun.com/articles/679633 国外提供了一个在线测试的靶场 默认密码 root toor https://www.vsplate.c ...
- 话说CAS
一.前言 cas 一般认为是compare and swap 也可以认为是compare and set cas涉及三个值 1)P 变量内存地址 2)E 期望值 ,CPU做计算之前拿出来的旧值 3) ...
- Java volatile关键字详解
Java volatile关键字详解 volatile是java中的一个关键字,用于修饰变量.被此关键修饰的变量可以禁止对此变量操作的指令进行重排,还有保持内存的可见性. 简言之它的作用就是: 禁止指 ...
- react+lib-flexible适配浏览器宽度配置
p.p1 { margin: 0; font: 12px "Helvetica Neue" } p.p2 { margin: 0; font: 12px "Helveti ...
- 常用开发库 - MapStruct工具库详解
常用开发库 - MapStruct工具库详解 MapStruct是一款非常实用Java工具,主要用于解决对象之间的拷贝问题,比如PO/DTO/VO/QueryParam之间的转换问题.区别于BeanU ...
- 2018ICPC南京I. Magic Potion
题目: 题意:n个士兵打m个怪兽,每个士兵只能打一个,但是如果有魔法药水就可多打一个问最多能打几个. 题解:如果没有魔法药就是一道裸二分图,因为现在有魔法要我们可以这样建图: 多建一个i+n的节点存放 ...