本帖最后由 xiaoly99 于 2014-7-10 20:41 编辑
VB小子玩转驱动程序(4):HOOK
作者:0.0
0.0 导论
Ring3:
Private Sub Command1_Click()
On Error Resume Next
Dim TID As Long
TID = CLng(Text1.Text)
With DrvController
Call .IoControl(.CTL_CODE_GEN(&H801), VarPtr(TID), 4, 0, 0)'这句代码将TID以&H801的IOCTL代码传入Ring0 以SysEnter进入
End With
Command1.Enabled = False
End Sub
------------------------------------------------------------------------------------------------------------------------------------------
Ring0:
DispatchIoctl----------Switch(IOCTLCode)----------Case(StartProt)
memcpy(&mytid,pIoBuffer,sizeof(mytid)); //将Ring3传来的TID的地址写入mytid变量
PsLookupThreadByThreadId((HANDLE)mytid,&myet)) //得到EThread
myep=IoThreadToProcess(myet); //得到EProcess
*(PULONG)((ULONG)myep+0x248)=0x4; //设置进程已退出
*(PULONG)((ULONG)myet+0x248)=0x10; //设置线程为系统线程
HookMmIsAddressValid(); //Hook函数
HookKeAttachProcess() //Hook函数
这里特别讲一下两个函数
第一个函数的作用是判断一个地址是否有效 而冰刃是也是用MmIsAddressValid来判断地址有效性
小知识1:冰刃还Hook了KeBugCheckEx确保结束DKOM了BreakOnTermination标志的进程不会蓝屏 不过Hook KeBugCheck2会更好些 但是KeBugCheck2是未导出函数 所以要搜索函数或者暴搜内核 这个下篇讲
第二个函数的作用是进入进程 ReadProcessMemory和WriteProcessMemory都是依靠KeAttachProcess
但是没有Hook KeStackAttachProcess这个函数,所以可以通过某些YY方法PspExitProcess
最好是Hook KiAttachProcess+0x5 这样好一些
-----------------------------------------------------------
现在讲如何Hook
先关内存写保护 就是对cr0寄存器的操作
在计算jmp偏移量
偏移量的计算公式是 : 跳转地址 - 当前地址 - 5
然后RtlCopyMemory把偏移Copy到一个数组中
还未Hook前的Byte数组是这样的 0xE9,0,0,0,0
填入后提高IQRL到DPC级别 注意:DPC不是一个级别 而是指和DPC一样的优先级
再获取函数地址 用RtlCopyMemory把Byte数组Copy到函数中
再降低IQRL到Passive级
关内存写保护
两个实例
inlineNtOpenProcess.rar
(79.6 KB, 下载次数: 8250)
inlineObReferenceObjectByHandle.rar
(157.26 KB, 下载次数: 8037)
1.1 准备
获取未导出函数的准备是WinDbg和适用于系统的符号包,可以根据下列表格下载 注:下载的统一为Xp x86版本,其它版本请自行搜索 1.2 查找特征码
假设符号包安装在d:\Symbol,那么打开Windbg,在"lkd>"命令行后输入命令: .sympath SRV*d:\Symbol*http://msdl.microsoft.com/download/symbols
!sym noisy
!lmi nt
.reload /f nt
现在可以用u 函数名获得未导出函数地址了,但是在我们的驱动函数中不可以直接调用或Hook未导出函数,我们加载好符号先测试一下,输入命令"u KeBugCheck2"
lkd> u KeBugCheck2 l 5
nt!KeBugCheck2:
804f9226 8bff mov edi,edi
804f9228 55 push ebp
804f9229 8bec mov ebp,esp
804f922b 81ecc8030000 sub esp,3C8h
804f9231 a1c0be5480 mov eax,dword ptr [nt!__security_cookie (8054bec0)]
在本机上,KeBugCheck2的地址为804f9226,但在驱动中怎么获取这个地址呢?可以搜索导出函数KeBugCheckEx
lkd> u kebugcheckex l 10
nt!KeBugCheckEx:
804f9caa 8bff mov edi,edi
804f9cac 55 push ebp
804f9cad 8bec mov ebp,esp
804f9caf 6a00 push 0
804f9cb1 ff7518 push dword ptr [ebp+18h]
804f9cb4 ff7514 push dword ptr [ebp+14h]
804f9cb7 ff7510 push dword ptr [ebp+10h]
804f9cba ff750c push dword ptr [ebp+0Ch]
804f9cbd ff7508 push dword ptr [ebp+8]
804f9cc0 e861f5ffff call nt!KeBugCheck2 (804f9226)
804f9cc5 5d pop ebp
804f9cc6 c21400 ret 14h
在804f9cc0的位置调用了KeBugCheck2,从中我们可以知道特征码是e8########5d
这里是61f5ffff,为什么不是804f9226呢?因为它是一个偏移值,我们先把它以ULONG的形式读出:fffff561,这里用到一个公式:当前地址+偏移量+5=Call地址
804f9cc0 + fffff561 + 5 = 804f9226 = KeBugCheck2
------------------------------------------------------------------------------------
2.1 驱动搜索 #include "LDasm.h" PVOIDGetFunctionAddr(IN PCWSTR FunctionName){UNICODE_STRINGUniCodeFunctionName;RtlInitUnicodeString(&UniCodeFunctionName,FunctionName);returnMmGetSystemRoutineAddress(&UniCodeFunctionName);}
VOID GetKeBugCheck2() {
UCHAR *cPtr, *pOpcode;
ULONG Length;
for (cPtr =(PUCHAR)GetFunctionAddr(L"KeBugCheckEx");cPtr <(PUCHAR)GetFunctionAddr(L"KeInsertQueueApc") + 32;cPtr += Length)
{
Length = SizeOfCode(cPtr, &pOpcode);
if (!Length) break;
if (*pOpcode == 0xE8 && *(PUSHORT)(pOpcode + 5) == 0x5D)
{
KeBugCheck2 = (KEBUGCHECK2)(*(PULONG)(pOpcode+1)+(ULONG)cPtr + 5);//注意 为什么有两个KeBugCheck2呢?因为C是大小写敏感的
break;
}
}
}//此段代码选自IceFreak->Kill.h 2.2 动态调用未导出函数 先声明类型(typedef):typedef VOID (*KEBUGCHECK2)(
IN ULONGBugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4,
IN ULONG_PTR BugCheckParameterNew
);
再声明函数:KEBUGCHECK2 KeBugCheck2 = NULL;
这样就可以调用了,不过在调用之前先Call一次GetKeBugCheck2函数,以便获得KeBugCheck2的地址
2.3 Hook
知道地址后就可以向普通的函数一样Hook了,注意:在Hook了FastCall类型的函数是,要注意堆栈平衡,最好不要直接在其内部调用外部函数和全局变量,代码如下:
pushfd
pushad
call MyFuction
popad
popfd |