TP的学习笔记:用读取内核内存的方式枚举进程
本帖最后由 tangptr@126.com 于 2015-7-11 21:46 编辑隐藏进程的Rootkit总是很恶心的东西,有的Rootkit甚至还会阻止ARK启动,仅仅是用任务管理器是肯定看不到的。那么我们在这里,用VB写一个检测进程的工具。
为了演示方便,我直接用ZwSystemDebugControl来读内核内存,故只支持Windows XP。要支持其他的操作系统,需要编写一个读写内核内存的驱动
首先要实现读取进程内存,代码如下:
Public Sub ReadKernelMemory(ByVal dest As Long, ByVal src As Long, ByVal cch As Long)
Dim mc As MEMORY_CHUNKS
Dim st As Long, ret As Long
With mc
.Address = src
.pData = dest
.nSize = cch
End With
st = ZwSystemDebugControl(8, VarPtr(mc), Len(mc), 0, 0, ret)
End Sub
其中src是要读取的地址,dest是复制到的位置,cch是长度。
让我们先回顾到最早期的隐藏进程的方式,它是通过Hook SSDT的NtQueryInformationProcess实现的枚举进程,对抗这种方式,我们可以通过枚举进程活动链实现枚举。先看看Windows XP下的EPROCESS结构体:
可见在+0x88的位置就是活动进程链的偏移量。那么我们可以通过遍历双向链表的方式实现枚举,先讲原理:
双向链表是以手拉手的方式将多个结构体链接起来的,比如ABCDE这五个进程,它们的排列方式就是:
<-A-><-B-><-C-><-D-><-E->
有了原理,就可以写代码了,代码如下:
Public Sub EnumProcess(ByVal LvItem As ListView)
Dim txps As Long
Dim PID As Long
Dim ListEntry As LIST_ENTRY
Dim NodePoint As Long
Dim LvX As ListItem
Dim LvC As Long: LvC = 0
txps = PsGetCurrentProcess
ReadKernelMemory VarPtr(ListEntry), txps + &H88, 8
NodePoint = ListEntry.BLink - &H88
Do
LvC = LvC + 1
ReadKernelMemory VarPtr(PID), txps + &H84, 4
Set LvX = LvItem.ListItems.Add(LvC, , PID)
LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
ReadKernelMemory VarPtr(ListEntry), txps + &H88, 8
txps = ListEntry.FLink - &H88
Loop Until txps = NodePoint
MsgBox LvC & "Processes Detected!", vbInformation
End Sub
其中,有个函数叫做PsGetCurrentProcess,这是我自己写的函数,用于返回自己进程的EPROCESS,实现方法是枚举系统全部句柄,然后比较*(PULONG)(pObject+0x84)的值是不是自身进程的PID,如果是,就返回pObject的值。代码如下:
Public Function PsGetCurrentProcess() As Long
Dim st As Long
Dim PID As Long
Dim NumOfHandle As Long
Dim i As Long
st = 0
Dim bytBuf() As Byte
Dim arySize As Long: arySize = 1
Do
ReDim bytBuf(arySize)
st = ZwQuerySystemInformation(SystemHandleInformation, VarPtr(bytBuf(0)), arySize, 0&)
If (Not NT_SUCCESS(st)) Then
If (st <> STATUS_INFO_LENGTH_MISMATCH) Then
Erase bytBuf
Exit Function
End If
Else
Exit Do
End If
arySize = arySize * 2
ReDim bytBuf(arySize)
Loop
NumOfHandle = 0
Call CopyMemory(VarPtr(NumOfHandle), VarPtr(bytBuf(0)), Len(NumOfHandle))
Dim h_info() As SYSTEM_HANDLE_TABLE_ENTRY_INFO
ReDim h_info(NumOfHandle)
Call CopyMemory(VarPtr(h_info(0)), VarPtr(bytBuf(0)) + Len(NumOfHandle), Len(h_info(0)) * NumOfHandle)
For i = LBound(h_info) To UBound(h_info)
With h_info(i)
ReadKernelMemory VarPtr(PID), .pObject + &H84, 4
If PID = GetCurrentProcessId Then
PsGetCurrentProcess = .pObject
End If
End With
Next
End Function
然而枚举活动进程链这招并不好使,因为摘除活动进程链是相当老的技术了,某个驱动开发教程甚至把摘除活动链隐藏进程放了进去。。。以至于人人都会摘除活动进程链隐藏进程。。。前些时间我还在VBGOOD论坛上混的时候,曾发过摘链隐藏进程的帖子,地址:摘除ActiveProcessLinks实现隐藏进程
既然枚举活动进程链不好使,就必须另辟蹊径,我们发现在EPROCESS的下面还有个叫做SessionProcessLinks的对象,翻译过来就是会话进程链表,它位于+0xB4的位置,如图所示:
那么只需要对枚举活动链的代码稍作修改,就变成了枚举会话进程链表的代码,代码如下:
Public Sub EnumProcess(ByVal LvItem As ListView)
Dim txps As Long
Dim PID As Long
Dim ListEntry As LIST_ENTRY
Dim NodePoint As Long
Dim LvX As ListItem
Dim LvC As Long: LvC = 0
txps = PsGetCurrentProcess
ReadKernelMemory VarPtr(ListEntry), txps + &HB4, 8
NodePoint = ListEntry.BLink - &HB4
Do
LvC = LvC + 1
ReadKernelMemory VarPtr(PID), txps + &H84, 4
Set LvX = LvItem.ListItems.Add(LvC, , PID)
LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
ReadKernelMemory VarPtr(ListEntry), txps + &HB4, 8
txps = ListEntry.FLink - &HB4
Loop Until txps = NodePoint
MsgBox LvC & "Processes Detected!", vbInformation
End Sub
有个缺陷就是只能枚举会话ID相同的进程,若进程的会话ID不同,则枚举不到会话不同的进程。好在Windows XP下的所有进程的会话ID都是0,那么枚举进程也就不存在会话ID的问题。但是在NT6下,会有会话ID不同的情况出现,如图所示:
于是呵呵哒,可见遍历会话进程链表在NT5是可靠的,但NT6就不一样了,那么就选择一条新的路:枚举句柄表
这个句柄表不是PspCidTable,那张表有点麻烦,还有一级表二级表啥啥的,还要去定位。。。这里呢指的是对象表。我们再看看EPROCESS的结构,可见在+0xC4的位置上就是进程对应的句柄表,如图所示:
再看看这里的_HANDLE_TABLE结构:
可以发现在+0x4的位置是对应进程的EPROCESS,在0x1C的位置则是个双向链表,看到双向链表,就可以枚举了,但绝对不是上面的代码中的偏移量就行了,因为这里我们其实遍历得到的是_HANDLE_TABLE。代码如下:
Public Sub EnumProcess(ByVal LvItem As ListView)
Dim txps As Long
Dim PID As Long
Dim ListEntry As LIST_ENTRY
Dim HandleTable As Long
Dim NodePoint As Long
Dim LvX As ListItem
Dim LvC As Long: LvC = 0
txps = PsGetCurrentProcess
ReadKernelMemory VarPtr(HandleTable), txps + &HC4, 4
ReadKernelMemory VarPtr(ListEntry), HandleTable + &H1C, 8
NodePoint = ListEntry.BLink - &H1C
Do
LvC = LvC + 1
ReadKernelMemory VarPtr(PID), HandleTable + &H8, 4
ReadKernelMemory VarPtr(txps), HandleTable + &H4, 4
Set LvX = LvItem.ListItems.Add(LvC, , PID)
LvX.ListSubItems.Add 1, , "0x" & Hex(txps)
ReadKernelMemory VarPtr(ListEntry), HandleTable + &H1C, 8
HandleTable = ListEntry.FLink - &H1C
Loop Until HandleTable = NodePoint
MsgBox LvC & "Processes Detected!", vbInformation
End Sub
上述三个方法,遍历进程的效果都不错,如果我们编写个读取内核内存的驱动,那么只需注意硬编码的问题就好。这里给出动态获取活动进程链表的解决方案:首先观察一下EPROCESS的结构,可以发现ActiveProcessLinks这个成员总是UniqueProcessId成员的下一个,我们再观察一下PsGetProcessId的反汇编结果:
可以发现第五行的汇编代码是mov eax,dword ptr,也就是8B8084000000,因此UniqueProcessId的偏移量是在+0xA的位置上,读取这个值后进而算出ActiveProcessLinks的偏移量。
代码如下:
ULONG DynamicGetOffsetOfActiveProcessLinks()
{
PVOID Function;
UNICODE_STRING uniFuncName;
ULONG offset;
RtlInitUnicodeString(&uniFuncName,L"PsGetProcessId");
Function=MmGetSystemRoutineAddress(&uniFuncName);
offset=*(PULONG)((ULONG)Function+10);
offset=offset+4;
return offset;
}
还有一种算法:取得当前进程的EPROCESS,并用PsGetProcessId取得当前进程的PID,随后查找结构中值等于PID的位置,这个位置的内存地址相对于起始地址就是UniqueProcessId的偏移量。代码如下:
ULONG DynamicGetOffsetOfActiveProcessLinks()
{
ULONG PID;
PEPROCESS txps;
ULONG i;
ULONG offset;
PVOID pAddr;
txps=PsGetCurrentProcess();
PID=PsGetProcessId(txps);
for(i=(ULONG)txps;(ULONG)txps+0x1000;i=i+4)
{
pAddr=(PVOID)i;
if(MmIsAddressValid(pAddr))
{
if(PID==*(PULONG)pAddr)
{
offset=(ULONG)pAddr-(ULONG)txps+4;
return offset;
}
}
}
}
此外就是程序还打包了结束进程的代码,方法就是把多个进程放置到同一个作业里然后结束作业。由于和本文无关,不贴代码了。
p.s:附件的描述摘自于Hovi.Delphic的帖子。由于本文是安利给大家的,所以既不设置水晶币,也不设置阅读权限{:soso_e113:} {:soso_e113:} {:soso_e113:} 文章很不错!只不过在7、8年前才有价值!!!唉。。。 现在有价值的好文章越来越少了 妹妹啊,怎么全是VB代码 xtfpg 发表于 2016-4-28 12:03
妹妹啊,怎么全是VB代码
习惯了在Ring3用VB写程序了,我几乎没有用除VB之外的语言写EXE了 tangptr@126.com 发表于 2016-4-28 12:34
习惯了在Ring3用VB写程序了,我几乎没有用除VB之外的语言写EXE了
算你狠,你有驱动枚举进程的么?支持全系统的 xtfpg 发表于 2016-4-28 13:20
算你狠,你有驱动枚举进程的么?支持全系统的
用ZwQuerySystemInformation的要不要23333333333333兼容性爆表 tangptr@126.com 发表于 2016-4-28 22:11
用ZwQuerySystemInformation的要不要23333333333333兼容性爆表
哈哈,我已经找到群主的相关代码了,很简单,好用的爆表
页:
[1]