一种注册表沙箱的思路、实现——Hook Nt函数
Nt函数是在Ring3层最底层的函数了,选择此类函数进行Hook,是为了提高绕过门槛。我的Hook方案使用的是微软的Detours。(转载请指明出处)
Detours的Hook和反Hook的写入如下:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(lpOriFuncAddr, lpNewFuncAddr);
DetourTransactionCommit();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(lpOriFuncAddr, lpNewFuncAddr);
DetourTransactionCommit();
从以上可以见得,我们需要保存lpOriFuncAddr(原始函数入口地址)和lpNewFuncAddr(修改后函数入口地址),我们使用一个结构体来保存这两个信息。
// 记录(函数原始函数地址,修改后函数地址)的结构
typedef struct _FuncPointer_{
VOID* lpOri;
VOID* lpNew;
}FuncPointer, *pFuncPointer;
因为我们要Hook很多函数,我们定义一个Map来保存信息,以方便寻找到相关的函数入口,我们的Map是以函数名为Key,以保存原始函数入口和修改后函数入口结构体为值。
// 以函数名为Key,其(函数原始函数地址,修改后函数地址)为值的map
typedef std::map<std::string,FuncPointer> MapFuncPointer;
typedef MapFuncPointer::iterator MapFuncPointerIter;
typedef MapFuncPointer::const_iterator MapFuncPointerCIter;
寻找原始函数的入口地址可以通过Detours提供的DetourFindFunction,而修改后的函数入口地址则需要我们自己定义。在我工程的NewNtRegFunc.h和NewNtRegFunc.cpp文件中,我分别定义了所有修改后函数声明和实现。新函数名的规则是“New_原函数名”,如
NTSTATUS __stdcall New_NtOpenKey(
__out PHANDLE KeyHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes );
我们第一步就是要生成这样Map
extern MapFuncPointer g_MapRegFuncPointer;
VOID GetHookNtRegFuncInfo()
{
HOOKNTDLLFUNC( CreateKey );
HOOKNTDLLFUNC( OpenKey );
HOOKNTDLLFUNC( DeleteKey );
HOOKNTDLLFUNC( DeleteValueKey );
HOOKNTDLLFUNC( QueryKey );
HOOKNTDLLFUNC( SetValueKey );
HOOKNTDLLFUNC( QueryValueKey );
HOOKNTDLLFUNC( EnumerateKey );
HOOKNTDLLFUNC( EnumerateValueKey );
HOOKNTDLLFUNC( FlushKey );
HOOKNTDLLFUNC( NotifyChangeKey );
HOOKNTDLLFUNC( Close );
HOOKNTDLLFUNC( SetSecurityObject );
HOOKNTDLLFUNC( QuerySecurityObject );
// vista及以上系统
HOOKNTDLLFUNC( OpenKeyEx );
HOOKNTDLLFUNC( CreateKeyTransacted );
HOOKNTDLLFUNC( OpenKeyTransacted );
HOOKNTDLLFUNC( OpenKeyTransactedEx );
}
其中HOOKTNDLLFUNC是一个宏,其定义如下
#define HOOKNTDLLFUNC(x)
HOOKNTITEM("NtDll.dll", x )
HOOKNTITEM也是个宏
// Hook函数,同时将{函数名,(函数原始函数地址,修改后函数地址)}保存到map中
#define HOOKNTITEM(x,y)
FuncPointer FP_##y; FP_##y.lpOri = DetourFindFunction( x , "Nt"#y );FP_##y.lpNew = static_cast<VOID*>(New_Nt##y); g_MapRegFuncPointer["Nt"#y]= FP_##y;
现在来说说这个宏,这个也是这篇文章中的一个重点。宏其实可以认为是一个模板,对于使用宏的地方,编译器会将其原封不动的替换到使用该宏处,这个特点和内联函数(inline)很像(但是VC的内联却不是人能控制的,编译器会根据它的判断来决定是否将你定义为inline的函数内容替换到调用处)。首先,我们要定义一个FuncPointer结构体对象,如果你在宏定义这个对象如下:FuncPointer FP;则在编译时报很多错,错误的原因就是一个变量名被定义多次,想想为什么?就是因为我们多次调用HOOKNTITEM,编译器也多次将FuncPointer FP插入到调用处。结果展开后如下
{
FuncPointer FP;
……
FuncPointer FP;
……
FuncPointer FP;
……
}
为了让每次定义的变量名不一样,我们使用了结合传入参数的方法,故定义为:
FuncPointer FP_##y
该宏之后的内容很浅显易懂了。
现在开始我们的HOOK操作
BOOL HookRegApi()
{
BOOL bSuc = FALSE;
do {
GetHookNtRegFuncInfo();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
for ( MapFuncPointerIter it = g_MapRegFuncPointer.begin(); it != g_MapRegFuncPointer.end(); it++ ) {
// 必须要加以判断,否则之后不应该是失败的也失败了
if ( NULL != it->second.lpOri && NULL != it->second.lpNew ) {
LONG lRes = DetourAttach(&(it->second.lpOri), it->second.lpNew );
}
}
DetourTransactionCommit();
bSuc = TRUE;
} while (0);
return bSuc;
}
该函数也很简单,但是其中有个细节一定要注意,那就是要判断原函数地址和修改后函数地址是否为空。使用Detours进行Hook特别要注意这点,否则会出现你也想不到的问题。当时我写这块时,伪代码可以如下表示
HOOK(CreateKey)
HOOK(OpenKeyEx)
HOOK(OpenKey)
结果我发现NtCreateKey函数被Hook了,而OpenKey却没有被Hook,出错的原因是:NtOpenKeyEx这个函数在vista及其以上系统才有,而我的开发环境是Xp,于是DetourFindFunction寻找到NtOpenKeyEx函数的入口地址为NULL,调用DetourAttach(NULL,lpNewFuncAddr)失败后,所有理论上应该成功的Hook(如Hook(OpenKey),NtOpenKey在xp上是存在的)也都会失败。当然最好还可以判断之前一步DetourAttach的返回值是否成功来决定是否继续Hook。
反Hook的代码如下
BOOL UnHookRegApi()
{
BOOL bSuc = FALSE;
do
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
for ( MapFuncPointerIter it = g_MapRegFuncPointer.begin(); it != g_MapRegFuncPointer.end(); it++ ) {
// 必须要加以判断,否则之后不应该是失败的也失败了
if ( NULL != it->second.lpOri && NULL != it->second.lpNew ) {
LONG lRes = DetourDetach(&(PVOID&)it->second.lpOri, it->second.lpNew );
}
}
DetourTransactionCommit();
bSuc = TRUE;
} while (0);
return bSuc;
}
为了之后方便调用真实函数,我定义了一个宏
// 获取原始函数入口
#define GETORIFUNC(x)
( (pfn_Nt##x)g_MapRegFuncPointer["Nt"#x].lpOri )
- 我的职业是前端工程师【十】客户端存储艺术:数据存储与模型
- 【开源】2md:将复制的内容、网页转成 markdown
- React Native 持续部署实践— push 代码构建出新版的 Growth
- 技巧 - 如何好一个 Git 提交信息及几种不同的规范
- React、Vue、Ember 及其他前端开发者,请暂缓更新到 Chrome 59 浏览器
- 微软开源全新的文档生成工具DocFX
- 使用 MimeKit 和 MailKit 发送邮件
- 使用 React Native 重写大型 Ionic 应用后,我们想分享一下这八个经验
- 基于OWin的Web服务器Katana发布版本3
- 【工具推荐】图像界的魔术师 ImageMagick
- 使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标
- 如何设计完善的构建系统,为日常开发提速一倍
- 两年 100 期技术周报后,我收获了这四点
- 如何为技术博客设计一个推荐系统(中):基于 Google 搜索的半自动推荐
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法