找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 9612|回复: 3

TP的学习笔记:被Nt(Mm/Mi)UnmapViewOfSection狠狠的坑了。。。

[复制链接]

77

主题

192

回帖

9

精华

贵宾会员

积分
15602
发表于 2015-7-17 20:32:04 | 显示全部楼层 |阅读模式
本帖最后由 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的反汇编结果

uf nt!NtUnmapViewOfSection的反汇编结果

就这么被坑了。。。不过既然知道错了,就得改进自己的错,把Hook MmUnmapViewOfSection改成Hook MiUnmapViewOfSection就好了。
不过话是这么说没错,但是做起来不是简单地改改就行了,主要是因为MiUnmapViewOfSection没有被ntkrnlpa.exe导出,所以,第一步,我们要获得这个函数的地址。
NtUnmapViewOfSection和MiUnmapViewOfSection都调用了MiUnmapViewOfSection,但我们选择从MmUnmapViewOfSection下手,主要有两个原因:
一、MmUnmapViewOfSection被ntkrnlpa.exe导出了,但是NtUnmapViewOfSection就没有
二、MmUnmapViewOfSection的反汇编结果很简单,其唯一的call就是MiUnmapViewOfSection。如图所示

MmUnmapViewOfSection的反汇编结果

MmUnmapViewOfSection的反汇编结果

所以我们要搜索的特征码只需满足两个条件:一、指令长度为5,二、指令的对应机器码是0xE8。查找的代码如下:
  1. void FindMiUnmapViewOfSection()
  2. {
  3.         PVOID MmUnmapViewOfSection=NULL;
  4.         ULONG i;
  5.         ULONG pLen;
  6.         PBYTE pOpCode;
  7.         UNICODE_STRING uniFuncName;
  8.         RtlInitUnicodeString(&uniFuncName,L"MmUnmapViewOfSection");
  9.         MmUnmapViewOfSection=MmGetSystemRoutineAddress(&uniFuncName);
  10.         for(i=(ULONG)MmUnmapViewOfSection;i<=(ULONG)MmUnmapViewOfSection+0x16;i=i+pLen)
  11.         {
  12.                 pLen=SizeOfCode((PVOID)i,&pOpCode);
  13.                 if(pLen==5 && *(PBYTE)i==0xE8)
  14.                 {
  15.                         MiAddr=*(PULONG)(pOpCode+1)+i+5;
  16.                         MiUnmapViewOfSection=(PVOID)MiAddr;
  17.                 }
  18.         }
  19. }
复制代码

查找到这个函数之后就简单了,直接挂钩就好了,代码如下:
  1. void HookMiUnmapViewOfSection()
  2. {
  3.         PMDL pMdl;
  4.         ULONG PatchSize;
  5.         BYTE g_HookCode[5]={0xE9,0,0,0,0};//相对跳转
  6.         BYTE Jmp_Orig_Code[7]={0xEA,0,0,0,0,0x08,0x00 }; //绝对地址跳转
  7.         PatchSize=GetPatchSize(MiUnmapViewOfSection,5);//获得要Patch的字节数
  8.         //构造Proxy_Func
  9.         Old_MiUnmapViewOfSection=ExAllocatePool(NonPagedPool,20);
  10.         memset(Old_MiUnmapViewOfSection,0x90,20);
  11.         RtlCopyMemory((PBYTE)Old_MiUnmapViewOfSection,(PBYTE)MiUnmapViewOfSection,PatchSize);//实现原函数头
  12.         *((PULONG)(Jmp_Orig_Code+1))=(ULONG)((PBYTE)MiUnmapViewOfSection+PatchSize );//原函数+N 地址
  13.         RtlCopyMemory((PBYTE)Old_MiUnmapViewOfSection+PatchSize,Jmp_Orig_Code,7);//绝对地址跳转
  14.         *((ULONG*)(g_HookCode+1))=(ULONG)fake_MiUnmapViewOfSection-(ULONG)MiUnmapViewOfSection-5;//计算JMP 地址
  15.         pMdl=IoAllocateMdl(MiUnmapViewOfSection,5,FALSE,FALSE,NULL);
  16.         if(pMdl)
  17.         {
  18.                 __try
  19.                 {
  20.                         MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
  21.                 }
  22.                 __except(EXCEPTION_EXECUTE_HANDLER)
  23.                 {
  24.                         IoFreeMdl(pMdl);
  25.                 }
  26.                 _asm
  27.                 {
  28.                         cli              
  29.                         mov eax, cr0   
  30.                         and eax, not 10000h
  31.                         mov cr0, eax     
  32.                 }
  33.                 RtlCopyMemory(MiUnmapViewOfSection, g_HookCode, 5);
  34.                 _asm
  35.                 {
  36.                         mov eax, cr0   
  37.                         or  eax, 10000h            
  38.                         mov cr0, eax              
  39.                         sti                  
  40.                 }
  41.                 MmUnlockPages(pMdl);
  42.                 IoFreeMdl(pMdl);
  43.         }
  44. }
复制代码

其实这里申请MDL锁定页面好像没什么必要的样子,毕竟这个函数调用的不频繁。。。算了,反正不会蓝屏{:soso_e113:}
Hook了之后,退出程序需要Unhook,代码如下:
  1. void UnhookMiUnmapViewOfSection()
  2. {
  3.         PMDL pMdl;
  4.         pMdl=IoAllocateMdl(MiUnmapViewOfSection,5,FALSE,FALSE,NULL);
  5.         if(pMdl)
  6.         {
  7.                 __try
  8.                 {
  9.                         MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
  10.                 }
  11.                 __except(EXCEPTION_EXECUTE_HANDLER)
  12.                 {
  13.                         IoFreeMdl(pMdl);
  14.                 }
  15.                 _asm
  16.                 {
  17.                         cli              
  18.                         mov eax, cr0   
  19.                         and eax, not 10000h
  20.                         mov cr0, eax     
  21.                 }
  22.                 RtlCopyMemory(MiUnmapViewOfSection,Old_MiUnmapViewOfSection,5);
  23.                 _asm
  24.                 {
  25.                         mov eax, cr0   
  26.                         or  eax, 10000h            
  27.                         mov cr0, eax              
  28.                         sti                  
  29.                 }
  30.                 MmUnlockPages(pMdl);
  31.                 IoFreeMdl(pMdl);
  32.         }
  33.         ExFreePool(Old_MiUnmapViewOfSection);
  34. }
复制代码

接下来的事情就是实现过滤的代码了,首先我们来看一下这个MiUnmapViewOfSection的函数原型:
  1. typedef NTSTATUS (*MIUNMAPVIEWOFSECTION)
  2. (
  3. IN PEPROCESS Process,
  4. IN PVOID BaseAddress,
  5. IN ULONG Flags
  6. );
复制代码

如果我们是要调用这个函数,那么第三个参数貌似填零就可以了,因为根据WinDbg对Nt(Mm)UnmapViewOfSection的反汇编结果来看,第三个参数填的都是零。但是我们真正在意的只有第一个参数Process。接着便是伪函数的代码:
  1. NTSTATUS fake_MiUnmapViewOfSection(IN PEPROCESS Process,IN HANDLE BaseAddress,IN ULONG Flags)
  2. {
  3.         NTSTATUS st;
  4.         if(Process==ProtectedProcess)
  5.         {
  6.                 st=0xC0000022;
  7.         }
  8.         else
  9.         {
  10.                 st=Old_MiUnmapViewOfSection(Process,BaseAddress,Flags);
  11.         }
  12.         return st;
  13. }
复制代码

将代码编译后,加载驱动后,可以看见PCHunter里有着相关的钩子信息,但是却显示不出函数名(这函数应该很出名才对啊。。。)如图所示

PCHunter的显示结果

PCHunter的显示结果

拿WinDbg对MiUnmapViewOfSection反汇编一下,结果如图:

WinDbg的结果

WinDbg的结果

我们再制作一个利用卸载ntdll.dll实现杀进程的小工具,代码如下:
  1. Public Function UnloadNtdll(ByVal ProcessId As Long) As Long
  2. Dim hProc As Long, st As Long
  3. Dim Cid As CLIENT_ID, oa As OBJECT_ATTRIBUTES
  4. oa.Length = Len(oa)
  5. Cid.UniqueProcess = ProcessId
  6. st = ZwOpenProcess(hProc, PROCESS_VM_OPERATION Or PROCESS_VM_READ, oa, Cid)
  7. If NT_SUCCESS(st) Then
  8.     st = ZwUnmapViewOfSection(hProc, GetModuleHandleA("ntdll.dll"))
  9.     ZwClose hProc
  10. End If
  11. UnloadNtdll = st
  12. End Function
复制代码

测试的效果也很好,如图所示:
Windows XP的测试效果:

XP的杀进程效果

XP的杀进程效果

Windows Server 2003的测试效果:

2k3的杀进程效果

2k3的杀进程效果

Windows 7的测试效果:

windows7的测试效果

windows7的测试效果

不过我相信在vista,2k8,win8等其他的win32系统也有效。
p.s.:本文所带的附件,其描述摘自Hovi.Delphic的文章。另外,本文是拿出来安利给大家的,不设阅读权限,也不设置水晶币。

HookMiUnmapViewOfSection.zip

62.39 KB, 下载次数: 3509

下载代码不回帖是一种很欠扁的行为

评分

参与人数 1水晶币 +60 收起 理由
Tesla.Angela + 60 赞一个!

查看全部评分

0

主题

11

回帖

0

精华

铜牌会员

积分
134
发表于 2015-7-17 22:37:43 | 显示全部楼层
非常好的教程,又学到了一个新的知识,非常感谢楼主

856

主题

2630

回帖

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

积分
36108
发表于 2015-7-18 00:51:39 | 显示全部楼层
简单hook一下KiAttachProcess就完事了。。。

1

主题

16

回帖

0

精华

初来乍到

积分
21
发表于 2021-1-6 23:08:27 | 显示全部楼层
请教一下,为什么一定要hook最底层的函数?自我感觉较为上层一点的,也是可以达到效果的。或者就是上层的校验 使其无法到最底层达到函数。
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表