TP的学习笔记:被Nt(Mm/Mi)UnmapViewOfSection狠狠的坑了。。。
本帖最后由 tangptr@126.com 于 2015-7-17 20:36 编辑不知道大家知不知道有个函数叫NtUnmapViewOfSection,曾经是一个拿来杀进程的函数。此函数本身是个强行卸载模块的函数,无奈被拿来杀进程,第一个参数填入进程句柄,第二个则填入要卸载的模块地址。一般这第二个填的是ntdll.dll的基地址,也可以是user32.dll或者kernel32.dll什么的。。。
那为什么说被坑了呢?刚开始的时候,一直把NtUnmapViewOfSection的下一步是MmUnmapViewOfSection再是MiUnmapViewOfSection,却不知真实答案却是直接调用了MiUnmapViewOfSection。曾经有段时间的确为“明明挂钩了MmUnmapViewOfSection,怎么还是被杀?!”这个问题烦恼过,当时也没太在意,直到这段时间帮某淫做一份相对较好的进程保护器的时候,用uf nt!NtUnmapViewOfSection查看了一下。。。尼玛!
就这么被坑了。。。不过既然知道错了,就得改进自己的错,把Hook MmUnmapViewOfSection改成Hook MiUnmapViewOfSection就好了。
不过话是这么说没错,但是做起来不是简单地改改就行了,主要是因为MiUnmapViewOfSection没有被ntkrnlpa.exe导出,所以,第一步,我们要获得这个函数的地址。
NtUnmapViewOfSection和MiUnmapViewOfSection都调用了MiUnmapViewOfSection,但我们选择从MmUnmapViewOfSection下手,主要有两个原因:
一、MmUnmapViewOfSection被ntkrnlpa.exe导出了,但是NtUnmapViewOfSection就没有
二、MmUnmapViewOfSection的反汇编结果很简单,其唯一的call就是MiUnmapViewOfSection。如图所示
所以我们要搜索的特征码只需满足两个条件:一、指令长度为5,二、指令的对应机器码是0xE8。查找的代码如下:
void FindMiUnmapViewOfSection()
{
PVOID MmUnmapViewOfSection=NULL;
ULONG i;
ULONG pLen;
PBYTE pOpCode;
UNICODE_STRING uniFuncName;
RtlInitUnicodeString(&uniFuncName,L"MmUnmapViewOfSection");
MmUnmapViewOfSection=MmGetSystemRoutineAddress(&uniFuncName);
for(i=(ULONG)MmUnmapViewOfSection;i<=(ULONG)MmUnmapViewOfSection+0x16;i=i+pLen)
{
pLen=SizeOfCode((PVOID)i,&pOpCode);
if(pLen==5 && *(PBYTE)i==0xE8)
{
MiAddr=*(PULONG)(pOpCode+1)+i+5;
MiUnmapViewOfSection=(PVOID)MiAddr;
}
}
}
查找到这个函数之后就简单了,直接挂钩就好了,代码如下:
void HookMiUnmapViewOfSection()
{
PMDL pMdl;
ULONG PatchSize;
BYTE g_HookCode={0xE9,0,0,0,0};//相对跳转
BYTE Jmp_Orig_Code={0xEA,0,0,0,0,0x08,0x00 }; //绝对地址跳转
PatchSize=GetPatchSize(MiUnmapViewOfSection,5);//获得要Patch的字节数
//构造Proxy_Func
Old_MiUnmapViewOfSection=ExAllocatePool(NonPagedPool,20);
memset(Old_MiUnmapViewOfSection,0x90,20);
RtlCopyMemory((PBYTE)Old_MiUnmapViewOfSection,(PBYTE)MiUnmapViewOfSection,PatchSize);//实现原函数头
*((PULONG)(Jmp_Orig_Code+1))=(ULONG)((PBYTE)MiUnmapViewOfSection+PatchSize );//原函数+N 地址
RtlCopyMemory((PBYTE)Old_MiUnmapViewOfSection+PatchSize,Jmp_Orig_Code,7);//绝对地址跳转
*((ULONG*)(g_HookCode+1))=(ULONG)fake_MiUnmapViewOfSection-(ULONG)MiUnmapViewOfSection-5;//计算JMP 地址
pMdl=IoAllocateMdl(MiUnmapViewOfSection,5,FALSE,FALSE,NULL);
if(pMdl)
{
__try
{
MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(pMdl);
}
_asm
{
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
RtlCopyMemory(MiUnmapViewOfSection, g_HookCode, 5);
_asm
{
mov eax, cr0
oreax, 10000h
mov cr0, eax
sti
}
MmUnlockPages(pMdl);
IoFreeMdl(pMdl);
}
}
其实这里申请MDL锁定页面好像没什么必要的样子,毕竟这个函数调用的不频繁。。。算了,反正不会蓝屏{:soso_e113:}
Hook了之后,退出程序需要Unhook,代码如下:
void UnhookMiUnmapViewOfSection()
{
PMDL pMdl;
pMdl=IoAllocateMdl(MiUnmapViewOfSection,5,FALSE,FALSE,NULL);
if(pMdl)
{
__try
{
MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(pMdl);
}
_asm
{
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
RtlCopyMemory(MiUnmapViewOfSection,Old_MiUnmapViewOfSection,5);
_asm
{
mov eax, cr0
oreax, 10000h
mov cr0, eax
sti
}
MmUnlockPages(pMdl);
IoFreeMdl(pMdl);
}
ExFreePool(Old_MiUnmapViewOfSection);
}
接下来的事情就是实现过滤的代码了,首先我们来看一下这个MiUnmapViewOfSection的函数原型:
typedef NTSTATUS (*MIUNMAPVIEWOFSECTION)
(
IN PEPROCESS Process,
IN PVOID BaseAddress,
IN ULONG Flags
);
如果我们是要调用这个函数,那么第三个参数貌似填零就可以了,因为根据WinDbg对Nt(Mm)UnmapViewOfSection的反汇编结果来看,第三个参数填的都是零。但是我们真正在意的只有第一个参数Process。接着便是伪函数的代码:
NTSTATUS fake_MiUnmapViewOfSection(IN PEPROCESS Process,IN HANDLE BaseAddress,IN ULONG Flags)
{
NTSTATUS st;
if(Process==ProtectedProcess)
{
st=0xC0000022;
}
else
{
st=Old_MiUnmapViewOfSection(Process,BaseAddress,Flags);
}
return st;
}
将代码编译后,加载驱动后,可以看见PCHunter里有着相关的钩子信息,但是却显示不出函数名(这函数应该很出名才对啊。。。)如图所示
拿WinDbg对MiUnmapViewOfSection反汇编一下,结果如图:
我们再制作一个利用卸载ntdll.dll实现杀进程的小工具,代码如下:
Public Function UnloadNtdll(ByVal ProcessId As Long) As Long
Dim hProc As Long, st As Long
Dim Cid As CLIENT_ID, oa As OBJECT_ATTRIBUTES
oa.Length = Len(oa)
Cid.UniqueProcess = ProcessId
st = ZwOpenProcess(hProc, PROCESS_VM_OPERATION Or PROCESS_VM_READ, oa, Cid)
If NT_SUCCESS(st) Then
st = ZwUnmapViewOfSection(hProc, GetModuleHandleA("ntdll.dll"))
ZwClose hProc
End If
UnloadNtdll = st
End Function
测试的效果也很好,如图所示:
Windows XP的测试效果:
Windows Server 2003的测试效果:
Windows 7的测试效果:
不过我相信在vista,2k8,win8等其他的win32系统也有效。
p.s.:本文所带的附件,其描述摘自Hovi.Delphic的文章。另外,本文是拿出来安利给大家的,不设阅读权限,也不设置水晶币。 非常好的教程,又学到了一个新的知识,非常感谢楼主 简单hook一下KiAttachProcess就完事了。。。 请教一下,为什么一定要hook最底层的函数?自我感觉较为上层一点的,也是可以达到效果的。或者就是上层的校验 使其无法到最底层达到函数。
页:
[1]