搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
在线问答5
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
Metabase学习教程:入门-1_metabase 中文教程
2
mac 配置 gradle 、gradle-wrapper.properties文件 distributionUrl 指定本地gradle.zip_gradle-wrapper.properties配置本地
3
AI在线免费视频工具2:视频配声音FoleyCrafter;图片说话hedra、hallo;图生视频_类似hedra的工具
4
Hadoop、Hive、Spark的简单认识和总结_hive和hadoop先学哪个
5
微信小程序源码-基于Java后端的校园综合服务系统毕业设计(附源码+演示录像+LW)_java后端实现微信小程序登录
6
Ubuntu 下安装和部署redis,并配置为开机启动(转)_ubuntu 部署redis 二进制
7
android PorterDuffXfermode ,PorterDuff.Mode 使用 以及Porter-Duff规则详解_porter-duff模式详解
8
linux dev sda1扩容,使用gparted 调整Linux分区扩容
9
windows系统下git pull时部分文件因文件名过长导致拉取失败_git拉取代码 windows文件夹目录过长
10
Xcode Playgrounds:探索Swift编程的交互式乐园
当前位置:
article
> 正文
对DirectX/COM接口的挂钩_hook com 类
作者:在线问答5 | 2024-07-26 05:55:39
赞
踩
hook com 类
对DirectX/COM
接口的挂钩
一般的挂钩(Hook)都是针对Windows API或消息的,而本文要讲的是如何挂钩一个DirectX/COM接口,有意思吧,请往下看,文中以DirectInput作为范例进行讲解。
目标
相比挂钩一个API调用,拦截一个COM接口的方法需要做更多的工作,如果我们要拦截的DLL已经被作者仔细检查过,只导出了类似create这样的接口函数,那么怎样才能达到我们挂钩的目的呢?
从本质上来说,一个COM接口就是一张与其链接在一起的虚函数指针列表,所以,我们只需跟踪链接,并查看每一个节点,直到找到想要替换的函数指针即可。
第一步
从上图也可以看到,只有类似create的接口COM函数是可见的,由于DirectInputCreate函数会返回一个COM接口,所以就从它开始跟踪,在此,可以把DLL注入到目标程序的输入地址表(IAT)中。
第二步
如果目标程序调用了DirectInputCreate,我们的函数也会被调用,而且,会得到一个指针,其指向了虚函数表的指针,而这个虚函数表就是DirectInput的接口。
DECLARE_INTERFACE_(IDirectInputW, IUnknown)
{
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid,
LPVOID * ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirectInputW methods ***/
STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,
LPUNKNOWN) PURE;
STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,
LPVOID,DWORD) PURE;
STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE;
STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE;
STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE;
};
第三步
现在,就可用CreateDevice创建自己的设备了,在此会再次得到一个不同的虚函数指针表地址,它代表了设备。
选择需要替换的方法,并在适当位置修改虚函数指针表以注入我们自己的函数。
实现
下面是实现步骤
第一步
要对一个API函数进行挂钩,可以使用SetWindowsHookEx这个Windows API,在此,我们创建了一个系统钩子,以监视启动的进程,检查其中是否有我们的目标程序。在确定之后,必须把它的输入模块名与想要进行替换的DLL进行比较,因为我们是对DirectInput进行挂钩,所以此项为DINPUT8.DLL。要找到此DLL,需遍历描述符。
//遍历每个输入描述符,在必要时重定向
while ( pImportDesc->FirstThunk )
{
PSTR pszImportModuleName = MakePtr( PSTR, hModEXE,
pImportDesc->Name);
if ( lstrcmpi( pszImportModuleName, Hook->Name ) == 0 )
{
sprintf(dbBuffer,"Dll Found in module %s replace it/n",
Hook->Name );
WriteToLog(dbBuffer);
RedirectIAT( Hook, pImportDesc, (PVOID)hModEXE );
}
pImportDesc++; //继续下一个输入描述符
}
找到之后,应使用VirtualQuery( pIAT, &mbi, sizeof(mbi) )从IAT中移除写保护,这样就可以写入到内存中了。在内存打开之后,还需遍历IAT查找入口项。
while ( pIteratingIAT->u1.Function )
{
void* HookFn = 0;
if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) )
{
PIMAGE_IMPORT_BY_NAME pImportName =
MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr,
pINT->u1.AddressOfData );
//遍历挂钩函数
SFunctionHook* FHook = DLLHook->Functions;
while ( FHook->Name )
{
if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
{
sprintf(dbBuffer,"Hooked function: %s/n",
(char*)pImportName->Name );
WriteToLog(dbBuffer);
//在结构SFunctionHook中保存被替换的函数
FHook->OrigFn = (unsigned long*)pIteratingIAT->u1.Function;
HookFn = FHook->HookFn;
break;
}
FHook++;
}
}
}
现在,可替换为自己的函数了。
//在挂钩之后,替换IAT函数指针
if ( HookFn )
{
//检查是代码还是数据
//如果是代码,不应写入。
if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
{
pIteratingIAT->u1.Function = (DWORD)HookFn;
}
else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
{
//检查“桩”是否在2GB内存地址空间之上
if ( pIteratingIAT->u1.Function > (DWORD)0x80000000 )
pIteratingIAT->u1.Function = (DWORD)HookFn;
}
}
最后就是还原内存属性。
VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);
第二步
在CreateInterface方法内部,通过把我们的CreateDevice函数指针注入到虚函数表(Vtbl)中,就可以挂钩到COM接口内部,而虚函数表则由返回的ppvOut指针得到。
DirectInput8Create_Type OldFn =
(DirectInput8Create_Type)D3DHook.Functions[D3DFN_DirectInput8Create].OrigFn;
HRESULT hr = OldFn( hinst, dwVersion, riidltf, ppvOut, punkOuter );
解析此指针,直到找到指向虚函数表接口的指针,而在这个地址上,需要再次移除内存保护,以便注入自己的函数到表中,并且保存原函数指针。
接下来,把我们自己的函数指针注入到虚函数表接口内CreateDevice函数指针的偏移量上,并还原内存保护。
可以看到,CreateDevice是DirectInput接口的第4个方法,这意味着其在虚函数表内的偏移量为0x0C(指针乘3)
typedef struct IDirectInput *LPDIRECTINPUT;
#if !defined(__cplusplus) || defined(CINTERFACE)
#define IDirectInput_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
#define IDirectInput_AddRef(p) (p)->lpVtbl->AddRef(p)
#define IDirectInput_Release(p) (p)->lpVtbl->Release(p)
#define IDirectInput_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c)
#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d)
#define IDirectInput_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a)
#define IDirectInput_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b)
#define IDirectInput_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
#else
#define IDirectInput_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
#define IDirectInput_AddRef(p) (p)->AddRef()
#define IDirectInput_Release(p) (p)->Release()
#define IDirectInput_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c)
#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d)
#define IDirectInput_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a)
#define IDirectInput_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b)
#define IDirectInput_Initialize(p,a,b) (p)->Initialize(a,b)
#endif
在知道从何处注入之后,下面就要考虑如何实现了。可以查看CreateDevice在dinput.h头文件中的声明,会发现它与DirectX Help中的并不匹配。
HRESULT CreateDevice(
REFGUID rguid,
LPDIRECTINPUTDEVICE *lplpDirectInputDevice,
LPUNKNOWN pUnkOuter
);
这是它在dinput.h头文件中的定义,所以我们必须添加第四个参数,其为接口指针,下面是完整的函数声明:
HRESULT __stdcall PASCAL MyCreateDevice(LPVOID *ppvOut,REFGUID rguid,
LPDIRECTINPUTDEVICE *lplpDirectInputDevice,
LPUNKNOWN pUnkOuter
另外有一点非常重要,必须在声明中使用 __stdcall调用约定。__stdcall调用约定常用于调用Win32 API函数,被调用者负责清理堆栈;而__cdecl则是C和C++程序的默认调用约定,由调用者来负责清理堆栈,这可不是我们想要的。
当查看此调用的反汇编时,会看到堆栈指针验证函数 _RTC_CheckEsp在对接口函数调用之后被调用。
if (lpdi->CreateDevice(GUID_SysKeyboard, &lpdikey, NULL)!=DI_OK)
00401365 mov esi,esp
00401367 push 0
00401369 push offset lpdikey (4552C8h)
0040136E push offset _GUID_SysKeyboard (44643Ch)
00401373 mov eax,dword ptr [lpdi (4552C4h)]
00401378 mov ecx,dword ptr [eax]
0040137A mov edx,dword ptr [lpdi (4552C4h)]
00401380 push edx
00401381 mov eax,dword ptr [ecx+0Ch]
00401384 call eax
00401386 cmp esi,esp
00401388 call _RTC_CheckEsp (4026A0h)
0040138D test eax,eax
0040138F je Game_Init+78h (401398h)
return(0);
00401391 xor eax,eax
00401393 jmp Game_Init+107h (401427h)
//设置协作级
if (lpdikey->SetCooperativeLevel(main_window_handle,DDSCL_NORMAL);
如果忘了把函数声明为 __stdcall,函数也会正常工作,但是esp指针测试将会失败,因为其会设置eax,并在函数调用之后对它进行测试。
第三步
现在,当创建设备时,调用会重定向到我们的CreateDevice函数中。另外,在我们调用原始函数之后,会在lplpDirectInputDevice中得到一个新的指针,它可以把我们直接带到设备的虚函数表中。
HRESULT hr = OldCreateDev(ppvOut,rguid,lplpDirectInputDevice,pUnkOuter);
比如说,我们要替换GetDeviceState这个函数,为得到它的偏移量,必须在DInput.dll内部查找其定义,可以看到它是第10个方法,所以偏移量为0x24。知道了偏移量,按照第二步中介绍的步骤:移除内存保护、保存原始指针、注入自己的函数、还原内存保护,一气呵成。
声明:
本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:
https://www.wpsshop.cn/w/在线问答5/article/detail/883820
推荐阅读
article
轻松搞定
RabbitMQ5
:主题机制与RPC调用_
eqmx
5启动
eqmx
-
rabbitmq
-
hook
...
轻松搞定RabbitMQ(六)——主题翻译地址:http://www.
rabbitmq
.com/tutorials/tu...
赞
踩
article
Android
逆向之旅---破解某应用
加密算法
(
动态
调试
so
和
frida
hook
so
代码)_fr...
一、样本静态分析最近有位同学发了一个样本给我,主要是有一个解密方法,把字符串加密了,加解密方法都放在
so
中,所以之前也没...
赞
踩
article
linux
系统
调用
hook
总结_
linux
系统
的
hook
...
1.
系统
调用
Hook简介
系统
调用
属于一种软中断机制(内中断陷阱),它有操作
系统
提供的功能入口(sys_call)以及C...
赞
踩
article
frida
native
hook
技术(
frida
hook
so
层函数)
_
frida
hook
...
hook
,中文译作”钩子“,”挂钩“,看起来好像和钓鱼有点关系,其实它更像一张网。想象这样一个场景:我们在河流上筑坝,只...
赞
踩
article
Linux
下
Hook
一个
共享库函数_如果
hook
修改
linux
的
hook
so...
有时程序员需要完成这类任务:假如你有
一个
二进制版的系统,例如现在流行的android,你需要为这个系统开发
一个
软件。这个...
赞
踩
article
linx
x86平台成功注入
so
并且通道
rel
进行
hook
_
so
hook
fopen
...
root@ubuntu:/home/user1/inject
so
/inject# ./
hook
so
name:/home/...
赞
踩
article
git
commit
提交的时候报错
husky
>
pre
-
commit
hook
failed
(...
git
commit
提交的时候报错
husky
>
pre
-
commit
hook
failed
(add
--
no-v...
赞
踩
article
关于解决
git
提交报错
pre
-
commit
hook
failed
(add --no-verif...
初始化一个新版本库时,Git 默认会在这个目录中放置一些示例脚本。这些脚本除了本身可以被调用外,它们还透露了被触发时所传...
赞
踩
article
Git -
husky
>
pre
-
commit
hook
failed (add --no-ver...
前言如果您 git
commit
-m “” 提交代码时,
pre
-
commit
钩子会在 Git 键入提交信息前运行 代...
赞
踩
article
代码
守护者:
用
git
pre
-
commit
-
hook
提升开发品质_
git
pre
-
commit
ho...
本文介绍了如何利
用
git
pre
-
commit
hook
在
代码
提交前执行
代码
风格检查、秘钥验证、格式化等步骤,以保证
代码
质量...
赞
踩
article
husky
-
commit
-msg
hook
exited
with
code
1 (error)...
解决git提交代码报错:
husky
-
commit
-msg
hook
exited
with
code
1 (erro...
赞
踩
article
DirectCompute
&
DirectX
11
计算
着色器
编程简介
(
翻译)...
译者注:
DirectX
一直是Windows上图形和游戏开发的核心技术。
DirectX
提供了一种在显卡上运行的程序——着色...
赞
踩
article
Introduction
to
3D
Game
Programming
with
DirectX
1...
Introduction
to
3D
Game
Programming
with
DirectX
12 学习笔记之 --...
赞
踩
article
Introduction
to
3D
Game
Programming
with
DirectX
1...
代码工程地址:https://github.com/jiabaodan/Direct12BookReadingNotes...
赞
踩
article
Introduction
to 3D Game Programming
with
DirectX
1...
学习目标熟悉蒙皮
动画
的术语;_
dx
12
角色
动画
dx
12
角色
动画
...
赞
踩
article
Introduction to
3D
Game Programming with
DirectX
1...
概述
Direct
3D
是一种底层绘图API(application programming interface,应用程序接...
赞
踩
article
Introduction
to
3D
Game
Programming
with
DirectX 1...
学习目标1
Introduction
to
3D
Game
Programming
with
DirectX 12 学习笔...
赞
踩
article
【
Visual
C++】
游戏
开发
笔记三十九 浅墨
DirectX
教程之七
他山之石
:几种
几何体
的快捷绘...
好了,别的废话咱不说了,下面直奔本周主题~ _
c++
directx
画多个
图形
c++
directx
画多个
图形
...
赞
踩
article
DirectX
图形
接口
指南
(
3
)_
directx
常用
接口
...
指南
二:演示顶点(Render Vertex) Microsoft Direct
3
D 写的应用程序使用顶点(Vertex...
赞
踩
相关标签
消息队列
android
java
git
github
npm
前端
devops
intellij-idea
操作系统
人工智能
php
Direct
游戏开发
Direct12
directx
图形
direct3d
microsoft
primitive
null
shader