Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
SHADOW SSDT HOOK
HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还有一个就是吧跳转函数写在什么位置,SSDT的时候是写在蓝屏函数里了。
一、获得 w KeServiceDescriptorTableShadow的地址
这个跟获得 KeServiceDescriptorTable 差不多,唯一不同就是特征码:
ULONGLONG GetKeServiceDescriptorTableShadow64()
{
PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
PUCHAR i = NULL;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0;
ULONGLONG addr=0;
for(i=StartSearchAddress;i<EndSearchAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x4c && b2==0x8d && b3==0x1d ) //4c8d1d
{
memcpy(&templong,i+3,4);
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
}
二、根据 X INDEX 获得 T SSSDT 函数在内核里的地址
原理跟获得 SSDT 函数在在内核里的地址差不多,先获得 W32pServiceTable的地址,然后再获得每个函数的偏移地址,在把偏移地址与 W32pServiceTable相加。为什么下面的计算公式是 W32pServiceTable + 4 * (index-0x1000)呢?其实这只是个理解上的问题。SSDT 函数的起始 INDEX 是 0x0,SSSDT 函数的起始 INDEX 是 0x1000,但函数地址在 W32pServiceTable 是从基址开始记录
的(假设 W32pServiceTable 的地址是 0xfffff800~80000000,第 0 个函数的地址就记录在 0xfffff800~80000000,第 1 个函数的地址就记录在0xfffff800~80000004,第 2 个函数的地址就记录在 0xfffff800~80000008,以此类推)。
ULONGLONG GetSSSDTFuncCurAddr64(ULONG64 Index)
{
ULONGLONG W32pServiceTable=0, qwTemp=0;
LONG dwTemp=0;
PSYSTEM_SERVICE_TABLE pWin32k;
pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));
W32pServiceTable=(ULONGLONG)(pWin32k->ServiceTableBase);
ul64W32pServiceTable = W32pServiceTable;
qwTemp = W32pServiceTable + 4 * (Index-0x1000); //这里是获得偏移地址的位置,要HOOK的话修改这里即可
dwTemp = *(PLONG)qwTemp;
dwTemp = dwTemp >> 4;
qwTemp = W32pServiceTable + (LONG64)dwTemp;
return qwTemp;
}
三、修改SSSDT里的地址
还是跟 SSDT 类似,修改 W32pServiceTable+4*index 地址的 DWORD 值(偏移地址值)。
VOID ModifySSSDT(ULONG64 Index, ULONG64 Address)
{
ULONGLONG W32pServiceTable=0, qwTemp=0;
LONG dwTemp=0;
PSYSTEM_SERVICE_TABLE pWin32k;
KIRQL irql;
pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE)); //4*8
W32pServiceTable=(ULONGLONG)(pWin32k->ServiceTableBase);
qwTemp = W32pServiceTable + 4 * (Index-0x1000);
dwTemp = (LONG)(Address - W32pServiceTable);
dwTemp = dwTemp << 4;
irql=WPOFFx64();
*(PLONG)qwTemp = dwTemp;
WPONx64(irql);
}
第一个代理函数用机器码写成,总共就 14 个字节,前 6 字节为 ff 25 00 00 00 00,后 8 字节为第二个代理函数的地址(JMP QWORD PTR)。
VOID ModifySSSDT(ULONG64 Index, ULONG64 Address)
{
ULONGLONG W32pServiceTable=0, qwTemp=0;
LONG dwTemp=0;
PSYSTEM_SERVICE_TABLE pWin32k;
KIRQL irql;
pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE)); //4*8
W32pServiceTable=(ULONGLONG)(pWin32k->ServiceTableBase);
qwTemp = W32pServiceTable + 4 * (Index-0x1000);
dwTemp = (LONG)(Address - W32pServiceTable);
dwTemp = dwTemp << 4;
irql=WPOFFx64();
*(PLONG)qwTemp = dwTemp;
WPONx64(irql);
}
代理函数地址:
ULONG64 ProxyNtUserPostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if( NtUserQueryWindow(hWnd,0)==MyProcessId && PsGetCurrentProcessId()!=(HANDLE)MyProcessId )
{
DbgPrint("Do not fuck with me!");
return 0;
}
else
{
DbgPrint("OriNtUserPostMessage called!");
return NtUserPostMessage(hWnd,Msg,wParam,lParam);
}
}
注意UnHOOK就是把真正的函数地址给填写回去
VOID UNHOOK_SSSDT()
{
ModifySSSDT(IndexOfNtUserPostMessage, (ULONG64)NtUserPostMessage);
DbgPrint("UNHOOK_SSSDT OK!");
}
执行效果测试
以下是窗口攻击函数:
int main2()
{
DWORD pid, wpid, i, j;
HWND hWnd;
gogogo:
printf("pid: ");
scanf("%ld", &pid);
for (i = 100; i<0xffffff; i += 2)
{
GetWindowThreadProcessId((HWND)i, &wpid);
if (wpid == pid && IsWindowVisible((HWND)i) == 1)
{
hWnd = (HWND)i;
for (j = 0; j<0x10000; j++)
{
PostMessage(hWnd, j, 0, 0);
}
}
}
printf("Ok!");
getchar();
getchar();
goto gogogo;
return 0;
}
通过HOOK PostMessage 来保护自己不被干掉:
至于Un SSSDT HOOK 是和SSDT思路一样的,也是自己加载相关内核模块,得到一些地址,然后在通过驱动通讯,在内核里获得一些地址,最后计算出来函数的真正地址,然后把原地址填写回去(这个地方就是继续hook一遍)就行了。直接把资料相关代码粘贴过来吧:
R3的代码:
#include <stdio.h>
#include <direct.h>
#include <Windows.h>
#include "EnumDrv.h"
#include "DrvCtrl.h"
HANDLE hMyDrv;
void PrintAddressByIndex()
{
LONG64 id=0;
ULONG64 addr=0;
st:
printf("Input index (HEX without \"0x\" like: 1000, input -1 to exit): ");
scanf("%llx",&id);
if (id<0) return;
IoControl(hMyDrv ,CTL_CODE_GEN(0x807), &id, 8, &addr, 8);
printf("%llx\n",addr);
getchar();
goto st;
}
DWORD FileLen(char *filename)
{
WIN32_FIND_DATAA fileInfo={0};
DWORD fileSize=0;
HANDLE hFind;
hFind = FindFirstFileA(filename ,&fileInfo);
if(hFind != INVALID_HANDLE_VALUE)
{
fileSize = fileInfo.nFileSizeLow;
FindClose(hFind);
}
return fileSize;
}
CHAR *LoadDllContext(char *filename)
{
DWORD dwReadWrite, LenOfFile=FileLen(filename);
HANDLE hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
PCHAR buffer=(PCHAR)malloc(LenOfFile);
SetFilePointer(hFile, 0, 0, FILE_BEGIN);
ReadFile(hFile, buffer, LenOfFile, &dwReadWrite, 0);
CloseHandle(hFile);
return buffer;
}
return NULL;
}
ULONG64 GetWin32kImageBase()
{
PIMAGE_NT_HEADERS64 pinths64;
PIMAGE_DOS_HEADER pdih;
char *NtosFileData=NULL;
NtosFileData=LoadDllContext("c:\\win32k.dll");
pdih=(PIMAGE_DOS_HEADER)NtosFileData;
pinths64=(PIMAGE_NT_HEADERS64)(NtosFileData+pdih->e_lfanew);
return pinths64->OptionalHeader.ImageBase;
}
void GetOriAddress()
{
ULONG64 W32pServiceTable, Win32kBase, Win32kImageBase, Win32kInProcess=0, retv;
IoControl(hMyDrv ,CTL_CODE_GEN(0x806), NULL, 0, &W32pServiceTable, 8);
Win32kBase = GetWin32kBase();
CopyFileA("c:\\windows\\system32\\win32k.sys","c:\\win32k.dll",0);
Win32kImageBase = GetWin32kImageBase();
printf("W32pServiceTable: %llx\n", W32pServiceTable);
printf("WIN32K.SYS base: %llx\n", Win32kBase);
printf("WIN32K.SYS image base: %llx\n\n\n", Win32kImageBase);
ULONG index=0;
if ( Win32kInProcess==0 )
Win32kInProcess = (ULONGLONG)LoadLibraryExA("c:\\win32k.dll",0, DONT_RESOLVE_DLL_REFERENCES);
for(index=0;index<825;index++) //825是WIN7X64上SSSDT的函数个数
{
ULONGLONG RVA=W32pServiceTable-Win32kBase;
ULONGLONG temp=*(PULONGLONG)(Win32kInProcess+RVA+8*(ULONGLONG)index);
ULONGLONG RVA_index=temp-Win32kImageBase;
retv = RVA_index+Win32kBase;
printf("Shadow SSDT Function[%ld]: %llx\n",index,retv);
if(index % 100 ==0)
{
printf("Press any key to continue......\n");
getchar();
}
}
}
int main()
{
hMyDrv=openDriver();
GetOriAddress();
uninstallDriver();
return 0;
}
宋孖健,13
Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)的更多相关文章
- Win64 驱动内核编程-20.UnHook SSDT
UNHOOK SSDT 要恢复 SSDT,首先要获得 SSDT 各个函数的原始地址,而 SSDT 各个函数的原始地址,自然是存储在内核文件里的.于是,有了以下思路: 1.获得内核里 KiService ...
- Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook
Ring0 InLineHook 和UnHook 如果是要在R0里hook,作者的建议是InLine HOOK,毕竟SSDT HOOK 和 SHADOW SSDT HOOK比较麻烦,不好修改.目前R3 ...
- Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...
- Win64 驱动内核编程-34.对抗与枚举MiniFilter
对抗与枚举MiniFilter MiniFilter 是目前杀毒软件用来实现"文件系统自我保护"和"文件实时监控"的方法. 由于 MiniFilter 模型简单 ...
- Win64 驱动内核编程-31.枚举与删除映像回调
枚举与删除映像回调 映像回调可以拦截 RING3 和 RING0 的映像加载.某些游戏保护会用此来拦截黑名单中的驱动加载,比如 XUETR.WIN64AST 的驱动.同理,在反游戏保护的过程中,也可以 ...
- Win64 驱动内核编程-30.枚举与删除线程回调
枚举与删除线程回调 进程回调可以监视进程的创建和退出,这个在前面的章节已经总结过了.某些游戏保护的驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退出.而线程回调则通常用 ...
- Win64 驱动内核编程-29.强制解锁文件
强制解锁文件 强制解锁因其他进程占用而无法删除的文件. 1.调用 ZwQuerySystemInformation 的 16 功能号来枚举系统里的句柄 2.打开拥有此句柄的进程并把此句柄复制到自己的进 ...
- Win64 驱动内核编程-18.SSDT
SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...
- Win64 驱动内核编程-7.内核里操作进程
在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...
随机推荐
- Linux-mysql服务级别对DB的操作要领[导出-导入(执行SQL)]及修改数据库名称
A:docker容器的mysql docker exec -it mysql bash -- 进入容器 备份脚本 mysqldump -uroot -p123456 --databases dbNam ...
- web图像化服务管理工具
在 CentOS 8 中安装 Cockpit Web 控制台 Cockpit 是红帽开发的网页版图像化服务管理工具,优点是无需中间层,且可以管理多种服务. 根据其项目主站描述,Cockpit 有如下特 ...
- day03---Vue(04)
一.组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界 ...
- python中gzip模块的使用
gzip模块能够直接压缩和解压缩bytes-like类型的数据,同时也能实现对应格式文件的压缩与解压缩 一.数据压缩与解压缩 压缩 函数-gzip.compress(data, compresslev ...
- SpringBoot中整合Redis、Ehcache使用配置切换 并且整合到Shiro中
在SpringBoot中Shiro缓存使用Redis.Ehcache实现的两种方式实例 SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这sh ...
- oCPC中转化率模型与校准
翻看日历时间已经来到了2021年,也是共同战役的第二年,许久没有更新文章了,在与懒惰进行过几次斗争都失利之后,今天拿出打工人最后的倔强,终于收获了一场胜利.闲话不多说,今天咱们重点聊聊oCPC中转化率 ...
- 通过lms.samples熟悉lms微服务框架的使用
经过一段时间的开发与测试,终于发布了Lms框架的第一个正式版本(1.0.0版本),并给出了lms框架的样例项目lms.samples.本文通过对lms.samples的介绍,简述如何通过lms框架快速 ...
- 什么是 Jenkins? 运用Jenkins持续集成
[注]本文译自:https://www.edureka.co/blog/what-is-jenkins/ 持续集成是 DevOps 最重要的部分,用于集成各个 DevOps 阶段.Jenkins ...
- React 错误边界组件
这是React16的内容,并不是最新的技术,但是用很少被讨论,直到通过文档发现其实也是很有用的一部分内容,还是总结一下- React中的未捕获的 JS 错误会导致整个应用的崩溃,和整个组件树的卸载.从 ...
- Android Studio的基本开发环境,配置阿里云源
原创文章,转发请注明出处. 安装Android Studio 安装文件下载地址:https://developer.android.google.cn/studio/ 下载Gradle 由于国内的网络 ...