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 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...
随机推荐
- PTA甲级—常用技巧与算法
散列 1078 Hashing (25 分) Quadratic probing (with positive increments only) is used to solve the collis ...
- 【Arduino学习笔记06】上拉电阻和下拉电阻
为什么要用上拉电阻和下拉电阻?--避免输入引脚处于"悬空"状态 下图是一个没有使用上拉电阻/下拉电阻的电路图: 在按键没有按下时,要读取的输入引脚没有连接到任何东西,这种状态就称为 ...
- FreeBSD 发布 2020 年 Q3 季度报告
FreeBSD 几日前发布 Q3 季度报告,介绍了在过去第三季度里 FreeBSD 完成的工作和相关项目,涉及到架构支持.内核改进.持续集成和驱动程序优化等. 列举部分如下: FreeBSD 基金会目 ...
- java实现回溯算法
最近有在leetcode上面做算法题,已经遇到了两道回溯算法的题目,感觉一点思路都没有,现决定将java如何实现回溯算法做一次总结. 什么叫做回溯算法 (摘抄于百度百科) 回溯算法实际上一个类似枚举的 ...
- Matplotlib图例中文乱码
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 plt.rcParams['axes.unicode_minus']=False #用来正 ...
- socket 之send和recv原理剖析
认识TCP socket的发送缓冲区和接收缓冲区 当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,相当与内存中的一片空间 send原理剖析 send是不是直接把数据发送给服 ...
- menuStrip鼠标滑过自动弹出
public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } private void 退出系统T ...
- 五个问题,三大策略,手把手教你定制App性能监控方案
作者:友盟+U-APM团队 Why? 为什么要做应用性能监控? 首先,我们要知道应用性能监控具体指什么?以及目的: 监控是一套完整的"监视+报警"的系统.对于像我们这样的App开发 ...
- 利用matplotlib和cmaps根据已有的colormap,重新定义colormap
算法网上这哥们总结的还可以[1] ,但是使用matplotlib自定义colormap自己掌握的还不够,写在这里 希望达到的目标 使用什么样的颜色,可以自己定义 方便的调用其他人的色标, 使用一部分c ...
- Redis实战篇(三)基于HyperLogLog实现UV统计功能
如果现在要开发一个功能: 统计APP或网页的一个页面,每天有多少用户点击进入的次数.同一个用户的反复点击进入记为 1 次,也就是统计 UV 数据. 让你来开发这个统计模块,你会如何实现? 如果统计 P ...