看了梦无极的ssdt_hook教程,虽然大牛讲得很细,但是很多细节还是要自己去体会,才会更加深入。在这里我总结一下我的分析过程,若有不对的地方,希望大家指出来。
首先我们应该认识 ssdt是什么?从梦无极的讲解过程中,我联想到了这样一个场景:
古时候有一户人家,姓李,住在皇宫外面,他们家有一个女儿进皇宫当宫女。有一天,李家的老大爷要送东西给他女儿,我们假定他可以进皇宫,于是就开始了这样一个过程。进皇宫只有一条路,那就是走正门,于是李大爷带着东西从家走到了城外,进皇宫前必须从这门口进去,通过一条路后才能到达皇宫,守门的侍卫挺友善的,把老大爷的东西让一个侍卫拿到一个地方去放着,然后叫了一个手下带着老大爷往里走,当走到皇宫门口时,守城门的侍卫就离开了,让另一个人带着老大爷继续走,然后老大爷就进了皇宫,进了皇宫后,有一个侍卫来问李大爷的女儿是在哪边,这里皇宫只有东边和西边。于是李大爷跟他说在东边,于是侍卫把李大爷带到了东边,然后见到一个总管,李大爷给出女儿是几号房的,总管根据一个皇室总表,找到了李大爷的女儿住址,于是李大爷就找到了他女儿,总管还告诉他,你带的东西可以在哪 里去取。
这里拿OpenProcess函数比作李大爷,NtOpenProcess比作李大爷的女儿,OpenProcess通过普通的调用规则进行调用来到城外,再通过ZwOpenProcess进行接见,带着它往皇宫走,然后遇到KiFastSystemCall,带他进入了皇宫。进了ring0后,需要知道是ssdt还是shawssdt,于是分东边的西边,我们假定ssdt就是东西。于是从东边 的路走过去,遇到总管KiFastCallEntry,给出函数偏移号,总管手里的皇室总表就是ssdt这个数据结构,于是找到了NtOPenProcess,而OpenProcess的参数即李大爷带的东西在ParamTableBase这里去找。
好吧,故事讲完了,我们继续分析。
ssdt竟然是一个数组,那么我们就需要先得到这个数组的首地址,于是这个结构就出来了。
代码:
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
这个结构只需要知道两个参数的作用,一个是ServiceTableBase,这个参数是ssdt数组的基址,有了它,我们再给出具体函数的偏移,就能找到正确的函数地址了。
另一个是NumberOfService,这个是ssdt这个数组的最大值,也就是ssdt中函数的个数。
有了这个结构过后,按照偏移就可以找到想要Hook的函数的地址了。但是hook之前,需要修改内核保护,否则会无情蓝屏。
代码:
void PageProtectOff()
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
Cli表示让其他人都休息,看我一个人表演。
Cro这个寄存器就保存了内核保护的标志位,用 10000h取反再和他进行与运算,就使标志位从1变成0了,就可以修改内核了。当然,完成后得修改回来,否则会出现这样的情况,你在上面跳舞,别人看你表演,你跳完了,别人还一直盯着舞台,以为你还在表演。
代码:
void PageProtectOn()
{
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
修改了内核保护后,就可以把自己定义的函数地址写在ssdt里面了吗?当然可以,但是为了系统安全,我们需要把之前的函数地址保存下来,在我们卸载驱动的时候恢复回去,以免导致系统不能正常工作。
还是以OpenProcess函数作参考 吧。
在我电脑里这个函数的偏移是122,如图:
点击图片以查看大图 图片名称: 3.png 查看次数: 6 文件大小: 30.9 KB 文件 ID : 81351
代码:
NTSTATUS ssdt_hook()
{
O_NtOpenProcess=KeServiceDescriptorTable.ServiceTableBase[122];
PageProtectOff();
KeServiceDescriptorTable.ServiceTableBase[122]=(unsigned int)MyNtOpenProcess;
PageProtectOn();
return STATUS_SUCCESS;
}
为了使驱动完成指定的任务,比如我们要保护记事本这个进程不被OpenProcess打开,于是我们在自己的NtOpenProcess函数中进行过滤,那么该怎样进行过滤呢?来看看NtOpenProcess函数的声明
代码:
NTSTATUS MyNtOpenProcess (
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in_opt PCLIENT_ID ClientId
)
这里有一个参数:ClientId,这个按我的理解是目标进程的一个结构,目标进程也就是其它进程想到打开的进程。这个结构体是这样定义的
代码:
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID;
我们可以通过他的第一个成员UniqueProcess找到该进程的进程对象,就是eprocess,那么该怎么得到eprocess这个结构体呢,于是有了下面这个函数。
代码:
NTSTATUS
PsLookupProcessByProcessId(
IN HANDLE ProcessId,
OUT PEPROCESS *Process
);
EPROCESS结构体打开是这个样子的
代码:
lkd> dt _eprocess
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE_X86
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
得到进程的EPROCESS结构体后,就可以用windbg找到基中的imagebase成员,在我电脑里是偏移0x174,这个成员就表示为目标进程的名字,我们可以通过这个名字与想要保护的进程的名字进行比较,如果是一样的,则直接返回打开不成功,这样就保护到了我们想要保护的进程。
代码:
if(ProtectProcess(ClientId->UniqueProcess,"notepad.exe"))
{
KdPrint(("hooked %s",(char *)PsGetCurrentProcess()+0x174));
return STATUS_UNSUCCESSFUL;
}
status=PsLookupProcessByProcessId(ProcessId,&process_obj);
if(!NT_SUCCESS(status))
{
KdPrint(("error code:%X---ProcessId:%d",status,ProcessId));
return FALSE;
}
if(!strcmp((char *)process_obj+0x174,str_ProtectObjName))
{
ObDereferenceObject(process_obj);
return TRUE;
}
为了让其它进程不受影响,我们在处理完这些东西后,应该再用原来的函数基址进行正常调用,使不被保护的进程能够正常工作
代码:
//KdPrint(("Hook Success!"));
return ((MYNTOPENPROCESS)O_NtOpenProcess)(ProcessHandle,
DesiredAccess,
ObjectAttributes,
ClientId);
}
写到这里我们的任务就完成了,但是为了让我们驱动卸载后,系统还能正常工作,我们需要把保存到的以前的函数地址还原到ssdt结构中,于是有了下面的代码 。
代码:
void UnHookSsdt()
{
PageProtectOff();
KeServiceDescriptorTable.ServiceTableBase[122]=O_NtOpenProcess;
PageProtectOn();
}
好了,分析就到这里,写完这篇日志,我又对ssdt_hook加深了印象。
介于零散的代码大家看起来很乱, 这里我把整 个代码 都 发出来,就容易学习了。
代码:
#include "ntddk.h"
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
NTSTATUS
PsLookupProcessByProcessId(
IN HANDLE ProcessId,
OUT PEPROCESS *Process
);
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
typedef NTSTATUS(*MYNTOPENPROCESS)(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK AccessMask,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId );//定义一个指针函数,用于下面对O_NtOpenProcess进行强制转换
ULONG O_NtOpenProcess;
BOOLEAN ProtectProcess(HANDLE ProcessId,char *str_ProtectObjName)
{
NTSTATUS status;
PEPROCESS process_obj;
if(!MmIsAddressValid(str_ProtectObjName))//这个条件是用来判断目标进程名是否有效
{
return FALSE;
}
if(ProcessId==0)//这个条件是用来排除System Idle Process进程的干扰
{
return FALSE;
}
status=PsLookupProcessByProcessId(ProcessId,&process_obj);//这句用来获取目标进程的EPROCESS结构
if(!NT_SUCCESS(status))
{
KdPrint(("我错了,这个是错误号:%X---这个是进程ID:%d",status,ProcessId));
return FALSE;
}
if(!strcmp((char *)process_obj+0x174,str_ProtectObjName))//进行比较
{
ObDereferenceObject(process_obj);//对象计数器减1,为了恢复对象管理器计数,便于回收
return TRUE;
}
ObDereferenceObject(process_obj);
return FALSE;
}
NTSTATUS MyNtOpenProcess (
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in_opt PCLIENT_ID ClientId
)
{
//KdPrint(("%s",(char *)PsGetCurrentProcess()+0x174));
if(ProtectProcess(ClientId->UniqueProcess,"calc.exe"))
{
KdPrint(("%s想打开我吗?不可能。哈哈。。",(char *)PsGetCurrentProcess()+0x174));
return STATUS_UNSUCCESSFUL;
}
//KdPrint(("Hook Success!"));
return ((MYNTOPENPROCESS)O_NtOpenProcess)(ProcessHandle,//处理完自己的任务后,调用原来的函数,让其它进程正常工作
DesiredAccess,
ObjectAttributes,
ClientId);
}
void PageProtectOff()//关闭页面保护
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
void PageProtectOn()//打开页面保护
{
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
void UnHookSsdt()
{
PageProtectOff();
KeServiceDescriptorTable.ServiceTableBase[122]=O_NtOpenProcess;//恢复ssdt中原来的函数地址
PageProtectOn();
}
NTSTATUS ssdt_hook()
{
//int i;
//for(i=0;i<KeServiceDescriptorTable.NumberOfServices;i++)
//{
// KdPrint(("NumberOfService[%d]-------%x",i,KeServiceDescriptorTable.ServiceTableBase[i]));
//}
O_NtOpenProcess=KeServiceDescriptorTable.ServiceTableBase[122];//保存原来的函数地址
PageProtectOff();
//将原来ssdt中所要hook的函数地址换成我们自己的函数地址
KeServiceDescriptorTable.ServiceTableBase[122]=(unsigned int)MyNtOpenProcess;
PageProtectOn();
return STATUS_SUCCESS;
}
void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UnHookSsdt();
KdPrint(("Driver Unload Success !"));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegsiterPath)
{
DbgPrint("This is My First Driver!");
ssdt_hook();
pDriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
附件中的工程文件是我用vs2010配置好的,如果是新手的话,只需要在电脑里安装vs2010,和winddk,这里winddk我用的是7600.16385.1这个版本,安装在C盘,就可以直接编译我的这个工程 文件了,不需要再设置那些麻烦的东西。
http://pan.baidu.com/share/link?shareid=3264102389&uk=3895584076
菜鸟开始学习SSDT HOOK((附带源码)的更多相关文章
-
【Hook技术】实现从";任务管理器";中保护进程不被关闭 + 附带源码 + 进程保护知识扩展
[Hook技术]实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展 公司有个监控程序涉及到进程的保护问题,需要避免用户通过任务管理器结束掉监控进程,这里使用 ...
-
[spring源码学习]二、IOC源码——配置文件读取
一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...
-
一起学习jQuery2.0.3源码—1.开篇
write less,do more jQuery告诉我们:牛逼的代码不仅精简而且高效! 2006年1月由美国人John Resig在纽约的barcamp发布了jQuery,吸引了来自世界各地众多Ja ...
-
OAuth2学习及DotNetOpenAuth部分源码研究
OAuth2学习及DotNetOpenAuth部分源码研究 在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. 一.什么是OAuth2 OAuth是 ...
-
Log4Net 日志配置[附带源码]
前述 园子里有许多人对log4net这款开源的日志记录控件有很多介绍.在这里个人再做一次总结,希望对以后有所帮助,需要的时候可以直接使用,减少查阅资料的时间.利用log4net可以方便地将日志信息记录 ...
-
SpringMVC关于json、xml自动转换的原理研究[附带源码分析 --转
SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 原文地址:http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-c ...
-
memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
-
memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
-
iOS 指南针的制作 附带源码
iOS 指南针的制作 附带源码 代码下载地址: http://vdisk.weibo.com/s/HK4yE http://pan.baidu.com/share/link?shareid=7 ...
随机推荐
-
PHP陷阱,一些注意事项
判断的一些注意事项 count(false) > 0 // true count(0) > 0 // true "随便一个字符串" == 0 // true " ...
-
设置redis主从出现的问题
314:S 05 Jan 15:12:17.433 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc ...
-
夺命雷公狗—angularjs—10—angularjs里面的内置函数
我们没学一门语言或者框架,几乎里面都有各自的语法和内置函数,当然,强悍的angularjs也不例外,他的方法其实常用的没多少,因为很多都可以用源生jis几乎都能完成一大部分.. <!doctyp ...
-
201521123082 《Java程序设计》第5周学习总结
201521123082 <Java程序设计>第5周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规 ...
-
认识 Less
CSS(层叠样式表)是一项出色的技术,它使得网页的表现与内容完全分离,使网站维护工作变得更容易,不会因为内容的改变而影响表现,也不会因为表现的改变而影响内容. 作为一门标记性语言,CSS 的先天性优点 ...
-
C++ gui程序附加dos输出窗口
C++ gui程序附加console qtcreator 1:在.pro文件中加入一句: CONFIG+= console 2:在运行设置里勾选在终端运行的选项 vs 1.新建gui项目 2.连接器( ...
-
Add Languages to Your Xamarin Apps with Multilingual App Toolkit
With Xamarin, you can extend your cross-platform apps with support for native speakers, reaching mar ...
-
1.2.2 Excel中手机号或身份证号批量加密星号
在对应的单元格中我们输入公式: =LEFT(C4,3)&"****"&RIGHT(C4,4)或=MID(C4,1,3)&"****"&a ...
-
【OSX】多个JDK共存时选择要使用的JDK版本
10.5以后的$JAVA_HOME没有被在.bash_profile中设置的话会被默认设置为 /usr/libexec/java_home. 如果一台mac里面安装了多个JDK, 可以通过/usr/l ...
-
linux -bash . startup.sh Permission denied
在执行./startup.sh,或者./shutdown.sh的时候,爆出了Permission denied, 其实很简单,就是今天在执行tomcat的时候,用户没有权限,而导致无法执行, 用命令c ...