找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 7711|回复: 7

TP的学习笔记:用读取内核内存的方式枚举进程

[复制链接]

78

主题

190

回帖

9

精华

贵宾会员

积分
15605
发表于 2015-7-11 19:12:43 | 显示全部楼层 |阅读模式
本帖最后由 tangptr@126.com 于 2015-7-11 21:46 编辑

隐藏进程的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结构体:

ActiveLinks

ActiveLinks

可见在+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的位置,如图所示:

SessionLinks

SessionLinks

那么只需要对枚举活动链的代码稍作修改,就变成了枚举会话进程链表的代码,代码如下:
  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不同的情况出现,如图所示:

Session Id

Session Id

于是呵呵哒,可见遍历会话进程链表在NT5是可靠的,但NT6就不一样了,那么就选择一条新的路:枚举句柄表
这个句柄表不是PspCidTable,那张表有点麻烦,还有一级表二级表啥啥的,还要去定位。。。这里呢指的是对象表。我们再看看EPROCESS的结构,可见在+0xC4的位置上就是进程对应的句柄表,如图所示:

HandleTable1

HandleTable1

再看看这里的_HANDLE_TABLE结构:

HandleTable2

HandleTable2

可以发现在+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的反汇编结果:

PsGetProcessId

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. }
复制代码

此外就是程序还打包了结束进程的代码,方法就是把多个进程放置到同一个作业里然后结束作业。由于和本文无关,不贴代码了。
p.s:附件的描述摘自于Hovi.Delphic的帖子。由于本文是安利给大家的,所以既不设置水晶币,也不设置阅读权限{:soso_e113:} {:soso_e113:} {:soso_e113:}

EnumProcesses.zip

54.79 KB, 下载次数: 5754

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

评分

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

查看全部评分

857

主题

2632

回帖

2

精华

管理员

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

积分
36130
发表于 2015-7-11 22:07:05 | 显示全部楼层
文章很不错!只不过在7、8年前才有价值!!!唉。。。

30

主题

693

回帖

0

精华

钻石会员

积分
2815
发表于 2015-7-12 18:54:50 | 显示全部楼层
现在有价值的好文章越来越少了

8

主题

68

回帖

0

精华

钻石会员

积分
4115
发表于 2016-4-28 12:03:41 | 显示全部楼层
妹妹啊,怎么全是VB代码

78

主题

190

回帖

9

精华

贵宾会员

积分
15605
 楼主| 发表于 2016-4-28 12:34:19 | 显示全部楼层
xtfpg 发表于 2016-4-28 12:03
妹妹啊,怎么全是VB代码

习惯了在Ring3用VB写程序了,我几乎没有用除VB之外的语言写EXE了

8

主题

68

回帖

0

精华

钻石会员

积分
4115
发表于 2016-4-28 13:20:55 | 显示全部楼层
tangptr@126.com 发表于 2016-4-28 12:34
习惯了在Ring3用VB写程序了,我几乎没有用除VB之外的语言写EXE了

算你狠,你有驱动枚举进程的么?支持全系统的

78

主题

190

回帖

9

精华

贵宾会员

积分
15605
 楼主| 发表于 2016-4-28 22:11:36 | 显示全部楼层
xtfpg 发表于 2016-4-28 13:20
算你狠,你有驱动枚举进程的么?支持全系统的

用ZwQuerySystemInformation的要不要23333333333333兼容性爆表

8

主题

68

回帖

0

精华

钻石会员

积分
4115
发表于 2016-5-4 11:56:39 | 显示全部楼层
tangptr@126.com 发表于 2016-4-28 22:11
用ZwQuerySystemInformation的要不要23333333333333兼容性爆表

哈哈,我已经找到群主的相关代码了,很简单,好用的爆表
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

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