广海社区

 找回密码
 立即注册
搜索
查看: 104|回复: 6

用读取内核内存的方式枚举进程

[复制链接]
累计签到:56 天
连续签到:2 天

74

主题

75

帖子

533

积分

知名人士 Rank: 4

威望
0
热心值
0
贡献
453
交易币
0
发表于 2019-6-11 11:45:39 | 显示全部楼层 |阅读模式
用读取内核内存的方式枚举进程
隐藏进程的Rootkit总是很恶心的东西,有的Rootkit甚至还会阻止ARK启动,仅仅是用任务管理器是肯定看不到的。那么我们在这里,用VB写一个检测进程的工具。
为了演示方便,我直接用ZwSystemDebugControl来读内核内存,故只支持Windows XP。要支持其他的操作系统,需要编写一个读写内核内存的驱动
首先要实现读取进程内存,代码如下:

  1. Public Sub ReadKernelMemory(ByVal dest As Long, ByVal src As Long, ByVal cch As Long)
  2. Dim mc As MEMORY_CHUNKS
  3. Dim st As Long, ret As Long
  4. With mc
  5.     .Address = src
  6.     .pData = dest
  7.     .nSize = cch
  8. End With
  9. st = ZwSystemDebugControl(8, VarPtr(mc), Len(mc), 0, 0, ret)
  10. End Sub
复制代码
其中src是要读取的地址,dest是复制到的位置,cch是长度。
让我们先回顾到最早期的隐藏进程的方式,它是通过Hook SSDT的NtQueryInformationProcess实现的枚举进程,对抗这种方式,我们可以通过枚举进程活动链实现枚举。先看看Windows XP下的EPROCESS结构体:


可见在+0x88的位置就是活动进程链的偏移量。那么我们可以通过遍历双向链表的方式实现枚举,先讲原理:
双向链表是以手拉手的方式将多个结构体链接起来的,比如ABCDE这五个进程,它们的排列方式就是:
<-A-><-B-><-C-><-D-><-E->
有了原理,就可以写代码了,代码如下:

  1. Public Sub EnumProcess(ByVal LvItem As ListView)
  2. Dim txps As Long
  3. Dim PID As Long
  4. Dim ListEntry As LIST_ENTRY
  5. Dim NodePoint As Long
  6. Dim LvX As ListItem
  7. Dim LvC As Long: LvC = 0
  8. txps = PsGetCurrentProcess
  9. ReadKernelMemory VarPtr(ListEntry), txps + &H88, 8
  10. NodePoint = ListEntry.BLink - &H88
  11. Do
  12.     LvC = LvC + 1
  13.     ReadKernelMemory VarPtr(PID), txps + &H84, 4
  14.     Set LvX = LvItem.ListItems.Add(LvC, , PID)
  15.     LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
  16.     ReadKernelMemory VarPtr(ListEntry), txps + &H88, 8
  17.     txps = ListEntry.FLink - &H88
  18. Loop Until txps = NodePoint
  19. MsgBox LvC & "Processes Detected!", vbInformation
  20. End Sub
复制代码
其中,有个函数叫做PsGetCurrentProcess,这是我自己写的函数,用于返回自己进程的EPROCESS,实现方法是枚举系统全部句柄,然后比较*(PULONG)(pObject+0x84)的值是不是自身进程的PID,如果是,就返回pObject的值。代码如下:
  1. Public Function PsGetCurrentProcess() As Long
  2. Dim st As Long
  3. Dim PID As Long
  4. Dim NumOfHandle As Long
  5. Dim i As Long
  6. st = 0
  7. Dim bytBuf() As Byte
  8. Dim arySize As Long: arySize = 1
  9. Do
  10.     ReDim bytBuf(arySize)
  11.     st = ZwQuerySystemInformation(SystemHandleInformation, VarPtr(bytBuf(0)), arySize, 0&)
  12.     If (Not NT_SUCCESS(st)) Then
  13.         If (st <> STATUS_INFO_LENGTH_MISMATCH) Then
  14.             Erase bytBuf
  15.             Exit Function
  16.         End If
  17.     Else
  18.         Exit Do
  19.     End If
  20.     arySize = arySize * 2
  21.     ReDim bytBuf(arySize)
  22. Loop
  23. NumOfHandle = 0
  24. Call CopyMemory(VarPtr(NumOfHandle), VarPtr(bytBuf(0)), Len(NumOfHandle))
  25. Dim h_info() As SYSTEM_HANDLE_TABLE_ENTRY_INFO
  26. ReDim h_info(NumOfHandle)
  27. Call CopyMemory(VarPtr(h_info(0)), VarPtr(bytBuf(0)) + Len(NumOfHandle), Len(h_info(0)) * NumOfHandle)
  28. For i = LBound(h_info) To UBound(h_info)
  29.     With h_info(i)
  30.         ReadKernelMemory VarPtr(PID), .pObject + &H84, 4
  31.         If PID = GetCurrentProcessId Then
  32.             PsGetCurrentProcess = .pObject
  33.         End If
  34.     End With
  35. Next
  36. End Function
复制代码
然而枚举活动进程链这招并不好使,因为摘除活动进程链是相当老的技术了,某个驱动开发教程甚至把摘除活动链隐藏进程放了进去。。。以至于人人都会摘除活动进程链隐藏进程。。。前些时间我还在VBGOOD论坛上混的时候,曾发过摘链隐藏进程的帖子,地址:摘除ActiveProcessLinks实现隐藏进程
既然枚举活动进程链不好使,就必须另辟蹊径,我们发现在EPROCESS的下面还有个叫做SessionProcessLinks的对象,翻译过来就是会话进程链表,它位于+0xB4的位置,如图所示:


那么只需要对枚举活动链的代码稍作修改,就变成了枚举会话进程链表的代码,代码如下:
  1. Public Sub EnumProcess(ByVal LvItem As ListView)
  2. Dim txps As Long
  3. Dim PID As Long
  4. Dim ListEntry As LIST_ENTRY
  5. Dim NodePoint As Long
  6. Dim LvX As ListItem
  7. Dim LvC As Long: LvC = 0
  8. txps = PsGetCurrentProcess
  9. ReadKernelMemory VarPtr(ListEntry), txps + &HB4, 8
  10. NodePoint = ListEntry.BLink - &HB4
  11. Do
  12.     LvC = LvC + 1
  13.     ReadKernelMemory VarPtr(PID), txps + &H84, 4
  14.     Set LvX = LvItem.ListItems.Add(LvC, , PID)
  15.     LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
  16.     ReadKernelMemory VarPtr(ListEntry), txps + &HB4, 8
  17.     txps = ListEntry.FLink - &HB4
  18. Loop Until txps = NodePoint
  19. MsgBox LvC & "Processes Detected!", vbInformation
  20. End Sub
复制代码
有个缺陷就是只能枚举会话ID相同的进程,若进程的会话ID不同,则枚举不到会话不同的进程。好在Windows XP下的所有进程的会话ID都是0,那么枚举进程也就不存在会话ID的问题。但是在NT6下,会有会话ID不同的情况出现,如图所示:
  
于是呵呵哒,可见遍历会话进程链表在NT5是可靠的,但NT6就不一样了,那么就选择一条新的路:枚举句柄表
这个句柄表不是PspCidTable,那张表有点麻烦,还有一级表二级表啥啥的,还要去定位。。。这里呢指的是对象表。我们再看看EPROCESS的结构,可见在+0xC4的位置上就是进程对应的句柄表,如图所示:
  
再看看这里的_HANDLE_TABLE结构:
  
可以发现在+0x4的位置是对应进程的EPROCESS,在0x1C的位置则是个双向链表,看到双向链表,就可以枚举了,但绝对不是上面的代码中的偏移量就行了,因为这里我们其实遍历得到的是_HANDLE_TABLE。代码如下:

  1. Public Sub EnumProcess(ByVal LvItem As ListView)
  2. Dim txps As Long
  3. Dim PID As Long
  4. Dim ListEntry As LIST_ENTRY
  5. Dim HandleTable As Long
  6. Dim NodePoint As Long
  7. Dim LvX As ListItem
  8. Dim LvC As Long: LvC = 0
  9. txps = PsGetCurrentProcess
  10. ReadKernelMemory VarPtr(HandleTable), txps + &HC4, 4
  11. ReadKernelMemory VarPtr(ListEntry), HandleTable + &H1C, 8
  12. NodePoint = ListEntry.BLink - &H1C
  13. Do
  14.     LvC = LvC + 1
  15.     ReadKernelMemory VarPtr(PID), HandleTable + &H8, 4
  16.     ReadKernelMemory VarPtr(txps), HandleTable + &H4, 4
  17.     Set LvX = LvItem.ListItems.Add(LvC, , PID)
  18.     LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
  19.     ReadKernelMemory VarPtr(ListEntry), HandleTable + &H1C, 8
  20.     HandleTable = ListEntry.FLink - &H1C
  21. Loop Until HandleTable = NodePoint
  22. MsgBox LvC & "Processes Detected!", vbInformation
  23. End Sub
复制代码
上述三个方法,遍历进程的效果都不错,如果我们编写个读取内核内存的驱动,那么只需注意硬编码的问题就好。这里给出动态获取活动进程链表的解决方案:首先观察一下EPROCESS的结构,可以发现ActiveProcessLinks这个成员总是UniqueProcessId成员的下一个,我们再观察一下PsGetProcessId的反汇编结果:

可以发现第五行的汇编代码是mov eax,dword ptr[eax+84h],也就是8B8084000000,因此UniqueProcessId的偏移量是在+0xA的位置上,读取这个值后进而算出ActiveProcessLinks的偏移量。
代码如下:

  1. ULONG DynamicGetOffsetOfActiveProcessLinks()
  2. {
  3.         PVOID Function;
  4.         UNICODE_STRING uniFuncName;
  5.         ULONG offset;
  6.         RtlInitUnicodeString(&uniFuncName,L"PsGetProcessId");
  7.         Function=MmGetSystemRoutineAddress(&uniFuncName);
  8.         offset=*(PULONG)((ULONG)Function+10);
  9.         offset=offset+4;
  10.         return offset;
  11. }
复制代码
还有一种算法:取得当前进程的EPROCESS,并用PsGetProcessId取得当前进程的PID,随后查找结构中值等于PID的位置,这个位置的内存地址相对于起始地址就是UniqueProcessId的偏移量。代码如下:
  1. ULONG DynamicGetOffsetOfActiveProcessLinks()
  2. {
  3.         ULONG PID;
  4.         PEPROCESS txps;
  5.         ULONG i;
  6.         ULONG offset;
  7.         PVOID pAddr;
  8.         txps=PsGetCurrentProcess();
  9.         PID=PsGetProcessId(txps);
  10.         for(i=(ULONG)txps;(ULONG)txps+0x1000;i=i+4)
  11.         {
  12.                 pAddr=(PVOID)i;
  13.                 if(MmIsAddressValid(pAddr))
  14.                 {
  15.                         if(PID==*(PULONG)pAddr)
  16.                         {
  17.                                 offset=(ULONG)pAddr-(ULONG)txps+4;
  18.                                 return offset;
  19.                         }
  20.                 }
  21.         }
  22. }
复制代码
此外就是程序还打包了结束进程的代码,方法就是把多个进程放置到同一个作业里然后结束作业。由于和本文无关,不贴代码了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

累计签到:29 天
连续签到:1 天

39

主题

44

帖子

274

积分

小有名气 Rank: 3Rank: 3

威望
0
热心值
0
贡献
230
交易币
0
发表于 2019-6-12 15:15:55 | 显示全部楼层
真的可以吗
回复

使用道具 举报

尚未签到

0

主题

92

帖子

184

积分

正式会员 Rank: 2

威望
0
热心值
0
贡献
92
交易币
0
发表于 2019-6-14 21:38:35 | 显示全部楼层
我只是路过,不发表意见
回复

使用道具 举报

尚未签到

0

主题

73

帖子

146

积分

正式会员 Rank: 2

威望
0
热心值
0
贡献
73
交易币
0
发表于 2019-6-15 09:20:24 | 显示全部楼层
顶一个!
回复

使用道具 举报

尚未签到

0

主题

81

帖子

162

积分

正式会员 Rank: 2

威望
0
热心值
0
贡献
81
交易币
0
发表于 2019-6-16 14:57:30 | 显示全部楼层
听楼主一席话,省我十本书!
回复

使用道具 举报

尚未签到

0

主题

69

帖子

138

积分

正式会员 Rank: 2

威望
0
热心值
0
贡献
69
交易币
0
发表于 2019-6-16 15:03:50 | 显示全部楼层
今天怎么了,什么人都出来了!
回复

使用道具 举报

尚未签到

0

主题

69

帖子

138

积分

正式会员 Rank: 2

威望
0
热心值
0
贡献
69
交易币
0
发表于 2019-6-18 09:38:20 | 显示全部楼层
楼主是在找骂么?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|Archiver|手机版|小黑屋|广海社区 |格子

GMT+8, 2019-10-23 20:34 , Processed in 0.090804 second(s), 46 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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