|
本帖最后由 tangptr@126.com 于 2015-7-17 20:36 编辑
不知道大家知不知道有个函数叫NtUnmapViewOfSection,曾经是一个拿来杀进程的函数。此函数本身是个强行卸载模块的函数,无奈被拿来杀进程,第一个参数填入进程句柄,第二个则填入要卸载的模块地址。一般这第二个填的是ntdll.dll的基地址,也可以是user32.dll或者kernel32.dll什么的。。。
那为什么说被坑了呢?刚开始的时候,一直把NtUnmapViewOfSection的下一步是MmUnmapViewOfSection再是MiUnmapViewOfSection,却不知真实答案却是直接调用了MiUnmapViewOfSection。曾经有段时间的确为“明明挂钩了MmUnmapViewOfSection,怎么还是被杀?!”这个问题烦恼过,当时也没太在意,直到这段时间帮某淫做一份相对较好的进程保护器的时候,用uf nt!NtUnmapViewOfSection查看了一下。。。尼玛!
uf nt!NtUnmapViewOfSection的反汇编结果
就这么被坑了。。。不过既然知道错了,就得改进自己的错,把Hook MmUnmapViewOfSection改成Hook MiUnmapViewOfSection就好了。
不过话是这么说没错,但是做起来不是简单地改改就行了,主要是因为MiUnmapViewOfSection没有被ntkrnlpa.exe导出,所以,第一步,我们要获得这个函数的地址。
NtUnmapViewOfSection和MiUnmapViewOfSection都调用了MiUnmapViewOfSection,但我们选择从MmUnmapViewOfSection下手,主要有两个原因:
一、MmUnmapViewOfSection被ntkrnlpa.exe导出了,但是NtUnmapViewOfSection就没有
二、MmUnmapViewOfSection的反汇编结果很简单,其唯一的call就是MiUnmapViewOfSection。如图所示
MmUnmapViewOfSection的反汇编结果
所以我们要搜索的特征码只需满足两个条件:一、指令长度为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[5]={0xE9,0,0,0,0};//相对跳转
- BYTE Jmp_Orig_Code[7]={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
- or eax, 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
- or eax, 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里有着相关的钩子信息,但是却显示不出函数名(这函数应该很出名才对啊。。。)如图所示
PCHunter的显示结果
拿WinDbg对MiUnmapViewOfSection反汇编一下,结果如图:
WinDbg的结果
我们再制作一个利用卸载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的测试效果:
XP的杀进程效果
Windows Server 2003的测试效果:
2k3的杀进程效果
Windows 7的测试效果:
windows7的测试效果
不过我相信在vista,2k8,win8等其他的win32系统也有效。
p.s.:本文所带的附件,其描述摘自Hovi.Delphic的文章。另外,本文是拿出来安利给大家的,不设阅读权限,也不设置水晶币。 |
评分
-
查看全部评分
|