king

网络安全编程:内核驱动进程遍历

king 安全防护 2022-12-15 346浏览 0

内核驱动在安全方面占据重要的地位。本文实现一个枚举进程的函数。枚举进程不能在用户态下进行,需要到内核态下进行,这样就必须使用驱动程序来完成。先用WinDbg完成一次手动的枚举过程,再通过代码来完成。

1. 配置VMware和WinDbg进行驱动调试

使用WinDbg调试驱动程序或内核,需要双机进行调试。所谓双机,就是两台电脑。通常情况下,大部分人往往只有一台电脑。那么,解决的方法就是安装虚拟机,然后对虚拟机进行一些设置,也是可以通过WinDbg进行调试的。虚拟机选择使用VMware,下面介绍如何对虚拟机进行配置。

安装好VMware,并在VMware中安装好操作系统,然后对安装好的虚拟机进行一些设置。通过此设置可以达到调试器与虚拟机的连接。单击菜单“VM”→“Settings”命令,弹出“Virtual Machine Settings”对话框,如图1所示。

网络安全编程:内核驱动进程遍历

图1 “Virtual Machine Settings”对话框

单击“Add”按钮,打开“Add Hardware Wizard”(添加硬件向导)对话框,如图2所示。

网络安全编程:内核驱动进程遍历

图2 “Add Hardware Wizard”对话框1

在该对话框中选择“Serial Port”选项,也就是串口,然后单击“Next”按钮,弹出“Add Hardware Wizard”对话框的第二个界面,如图3所示。

网络安全编程:内核驱动进程遍历

图3 “Add Hardware Wizard”对话框2

在该界面中选择“Output to named pipe”单选按钮,也就是命名管道。命名管道是Windows下进程通信的一种方法。选中该项后继续单击“Next”按钮,进入下一个界面,也是设置的最后一个界面,如图4所示。

网络安全编程:内核驱动进程遍历

图4 “Add Hardware Wizard”对话框3

在这个界面中对命名管道进行设置,然后单击“Finish”按钮即可。至此,已经完成了一半的设置。接着,启动虚拟机配置Windows的Boot.ini文件。Boot.ini文件原内容如下:

[bootloader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operatingsystems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="MicrosoftWindowsXPProfessional"/fa
stdetect/NoExecute=AlwaysOff

将最后一行复制,然后放到最后面,并进行修改。修改后的内容如下:

[bootloader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operatingsystems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="MicrosoftWindowsXPProfessional"/fa
stdetect/NoExecute=AlwaysOff
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="MicrosoftWindowsXPProfessional"/fa
stdetect/NoExecute=optin/debug/debugport=com1/baudrate=115200

去掉Boot.ini文件的只读属性,然后保存Boot.ini文件。在下次需要对驱动进行调试,或者对内核进行调试时,选择启动Debug模式的Windows。

这里只介绍了针对Windows XP系统的配置方法。关于其他版本系统的配置方法,请自行参考相关内容。

至此,所有的配置工作都做好了,但是使用WinDbg进行连接时,还是要有连接参数的。先在桌面上创建一个WinDbg的快捷方式,然后在WinDbg快捷方式上单击右键,在弹出的快捷菜单中选择“属性”命令,弹出“属性”对话框,将“目标”位置改为:

F:\WinDDK\7600.16385.0\Debuggers\windbg.exe-b-kcom:port=\\.\pipe\com_1,baud=115200,pipe

这样就可以用WinDbg连接虚拟机中调试状态下的Windows XP了。

2. EPROCESS和手动遍历进程

Windows中有一个非常大的与进程有关的结构体——EPROCESS。每个进程对应一个EPROCESS结构,但EPROCESS是一个系统未公开的结构体,在WDK中只能找到说明,而找不到其结构体的具体定义,因此需要通过WinDbg来查看。这次使用WinDbg和VMware进行调试。按照前面的方法,使WinDbg和VMware可以连接。当WinDbg出现调试界面时,在其命令处输入dt _eprocess命令来查看该结构体,如图5所示。

网络安全编程:内核驱动进程遍历

图5 WinDbg显示的部分EPROCESS结构体

从图中可以看出,EPROCESS结构体显示出非常多的内容,从WinDbg调试界面只能看到部分成员变量,而且偏移已经到了0x258,非常多。看一下WinDbg的全部内容。

kd>dt_eprocess
nt!_EPROCESS
+0x000Pcb:_KPROCESS//进程控制块
+0x06cProcessLock:_EX_PUSH_LOCK
+0x070CreateTime:_LARGE_INTEGER
+0x078ExitTime:_LARGE_INTEGER
+0x080RundownProtect:_EX_RUNDOWN_REF
+0x084UniqueProcessId:Ptr32Void//进程ID
+0x088ActiveProcessLinks:_LIST_ENTRY//活动进程链表
+0x090QuotaUsage:[3]Uint4B
+0x09cQuotaPeak:[3]Uint4B
+0x0a8CommitCharge:Uint4B
+0x0acPeakVirtualSize:Uint4B
+0x0b0VirtualSize:Uint4B
+0x0b4SessionProcessLinks:_LIST_ENTRY
+0x0bcDebugPort:Ptr32Void
+0x0c0ExceptionPort:Ptr32Void
+0x0c4ObjectTable:Ptr32_HANDLE_TABLE
+0x0c8Token:_EX_FAST_REF
+0x0ccWorkingSetLock:_FAST_MUTEX
+0x0ecWorkingSetPage:Uint4B
+0x0f0AddressCreationLock:_FAST_MUTEX
+0x110HyperSpaceLock:Uint4B
+0x114ForkInProgress:Ptr32_ETHREAD
+0x118HardwareTrigger:Uint4B
+0x11cVadRoot:Ptr32Void
+0x120VadHint:Ptr32Void
+0x124CloneRoot:Ptr32Void
+0x128NumberOfPrivatePages:Uint4B
+0x12cNumberOfLockedPages:Uint4B
+0x130Win32Process:Ptr32Void
+0x134Job:Ptr32_EJOB
+0x138SectionObject:Ptr32Void
+0x13cSectionBaseAddress:Ptr32Void
+0x140QuotaBlock:Ptr32_EPROCESS_QUOTA_BLOCK
+0x144WorkingSetWatch:Ptr32_PAGEFAULT_HISTORY
+0x148Win32WindowStation:Ptr32Void
+0x14cInheritedFromUniqueProcessId:Ptr32Void
+0x150LdtInformation:Ptr32Void
+0x154VadFreeHint:Ptr32Void
+0x158VdmObjects:Ptr32Void
+0x15cDeviceMap:Ptr32Void
+0x160PhysicalVadList:_LIST_ENTRY
+0x168PageDirectoryPte:_HARDWARE_PTE
+0x168Filler:Uint8B
+0x170Session:Ptr32Void
+0x174ImageFileName:[16]UChar//进程名
+0x184JobLinks:_LIST_ENTRY
+0x18cLockedPagesList:Ptr32Void
+0x190ThreadListHead:_LIST_ENTRY
+0x198SecurityPort:Ptr32Void
+0x19cPaeTop:Ptr32Void
+0x1a0ActiveThreads:Uint4B
+0x1a4GrantedAccess:Uint4B
+0x1a8DefaultHardErrorProcessing:Uint4B
+0x1acLastThreadExitStatus:Int4B
+0x1b0Peb:Ptr32_PEB//进程环境块
+0x1b4PrefetchTrace:_EX_FAST_REF
+0x1b8ReadOperationCount:_LARGE_INTEGER
+0x1c0WriteOperationCount:_LARGE_INTEGER
+0x1c8OtherOperationCount:_LARGE_INTEGER
+0x1d0ReadTransferCount:_LARGE_INTEGER
+0x1d8WriteTransferCount:_LARGE_INTEGER
+0x1e0OtherTransferCount:_LARGE_INTEGER
+0x1e8CommitChargeLimit:Uint4B
+0x1ecCommitChargePeak:Uint4B
+0x1f0AweInfo:Ptr32Void
+0x1f4SeAuditProcessCreationInfo:_SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8Vm:_MMSUPPORT
+0x238LastFaultCount:Uint4B
+0x23cModifiedPageCount:Uint4B
+0x240NumberOfVads:Uint4B
+0x244JobStatus:Uint4B
+0x248Flags:Uint4B
+0x248CreateReported:Pos0,1Bit
+0x248NoDebugInherit:Pos1,1Bit
+0x248ProcessExiting:Pos2,1Bit
+0x248ProcessDelete:Pos3,1Bit
+0x248Wow64SplitPages:Pos4,1Bit
+0x248VmDeleted:Pos5,1Bit
+0x248OutswapEnabled:Pos6,1Bit
+0x248Outswapped:Pos7,1Bit
+0x248ForkFailed:Pos8,1Bit
+0x248HasPhysicalVad:Pos9,1Bit
+0x248AddressSpaceInitialized:Pos10,2Bits
+0x248SetTimerResolution:Pos12,1Bit
+0x248BreakOnTermination:Pos13,1Bit
+0x248SessionCreationUnderway:Pos14,1Bit
+0x248WriteWatch:Pos15,1Bit
+0x248ProcessInSession:Pos16,1Bit
+0x248OverrideAddressSpace:Pos17,1Bit
+0x248HasAddressSpace:Pos18,1Bit
+0x248LaunchPrefetched:Pos19,1Bit
+0x248InjectInpageErrors:Pos20,1Bit
+0x248VmTopDown:Pos21,1Bit
+0x248Unused3:Pos22,1Bit
+0x248Unused4:Pos23,1Bit
+0x248VdmAllowed:Pos24,1Bit
+0x248Unused:Pos25,5Bits
+0x248Unused1:Pos30,1Bit
+0x248Unused2:Pos31,1Bit
+0x24cExitStatus:Int4B
+0x250NextPageColor:Uint2B
+0x252SubSystemMinorVersion:UChar
+0x253SubSystemMajorVersion:UChar
+0x252SubSystemVersion:Uint2B
+0x254PriorityClass:UChar
+0x255WorkingSetAcquiredUnsafe:UChar
+0x258Cookie:Uint4B

上面就是EPROCESS结构体的全部。对于遍历进程列表来说,有用的只有几个内容,首先是偏移0x84处的进程ID,然后是偏移0x88处的进程链表,最后一个是偏移0x174的进程名。下面手动进行一次遍历。

在WinDbg的命令输入提示处输入! Process 0 0命令,得到进程的列表,如图6所示。

网络安全编程:内核驱动进程遍历

图6 进程信息

PROCESS后面给出的值就是当前进程中EPROCESS的地址,选择explorer.exe进程给出的地址0xff364708来解析EPROCESS。输入命令dt _eprocess ff364708,输出如下:

kd>dt_eprocessff364708
nt!_EPROCESS
+0x000Pcb:_KPROCESS
+0x06cProcessLock:_EX_PUSH_LOCK
+0x070CreateTime:_LARGE_INTEGER0x1cb6af5`91d56cea
+0x078ExitTime:_LARGE_INTEGER0x0
+0x080RundownProtect:_EX_RUNDOWN_REF
+0x084UniqueProcessId:0x00000600
+0x088ActiveProcessLinks:_LIST_ENTRY[0xff2b44b0-0xff3640a8]
<部分省略>
+0x174ImageFileName:[16]"explorer.exe"
<部分省略>
+0x1b0Peb:0x7ffde000_PEB
<后面省略>

可以看到,按照EPROCESS结构体解析ff364708地址,输出了需要的内容。接着,通过ActiveProcessLinks获取下一个进程的信息。输入命令dd ff364708 + 0x88,输出如下:

kd>ddff364708+0x88
ff364790ff2b44b0ff3640a80000294000021944
ff3647a000000a920000394000024cb400000bf8
ff3647b000000a9205e040000563a000ff2b44dc
ff3647c0ff3640d400000000e15b6eb8e1ce2640
ff3647d0e166f38900000001f39a544000000000
ff3647e00004000100000000ff3647e8ff3647e8
ff3647f00000003d000059ca00000001f39a5440
ff364800000000000004000100000000ff36480c

ff364790地址处保存了下一个EPROCESS结构体ActiveProcessLinks的地址。要得到下一个EPROCESS的地址,必须减去0x88才行。输入命令dt _eprocess (ff2b44b0 – 0x88),输出如下:

kd>dt_eprocess(ff2b44b0-0x88)
nt!_EPROCESS
+0x000Pcb:_KPROCESS
+0x06cProcessLock:_EX_PUSH_LOCK
+0x070CreateTime:_LARGE_INTEGER0x1cb6af5`95026ecc
+0x078ExitTime:_LARGE_INTEGER0x0
+0x080RundownProtect:_EX_RUNDOWN_REF
+0x084UniqueProcessId:0x000006b8
+0x088ActiveProcessLinks:_LIST_ENTRY[0xff2b7580-0xff364790]
<后面省略>
+0x174ImageFileName:[16]"VMwareTray.exe"
<后面省略>

将输出结果和图6中的结果对比,explorer.exe的下一个进程为VMwareTray.exe。可见遍历方法是正确的。

3. 编程实现进程遍历

上面介绍的手动遍历过程就是指导用户如何编写代码的,只要能够掌握上面的手动遍历过程,那么代码的编写也就不是问题了。下面直接看代码:

NTSTATUSDriverEntry(
PDRIVER_OBJECTpDriverObject,
PUNICODE_STRINGpRegistryPath)
{
PEPROCESSpEprocess=NULL;
PEPROCESSpFirstEprocess=NULL;
ULONGulProcessName=0;
ULONGulProcessId=0;
pDriverObject->DriverUnloadDriverUnload=DriverUnload;
pEprocess=PsGetCurrentProcess();
if(pEprocess==0)
{
KdPrint(("PsGetcurrentProcessError!\r\n"));
returnSTATUS_SUCCESS;
}
pFirstEprocess=pEprocess;
while(pEprocess!=NULL)
{
ulProcessName=(ULONG)pEprocess+0x174;
ulProcessId=*(ULONG*)((ULONG)pEprocess+0x84);
KdPrint(("ProcessName=%s,ProcessId=%d\r\n",ulProcessName,ulProcessId));
pEprocess=(ULONG)(*(ULONG*)((ULONG)pEprocess+0x88)-0x88);
if(pEprocess==pFirstEprocess||(*(LONG*)((LONG)pEprocess+0x84))<0)
{
break;
}
}
returnSTATUS_SUCCESS;
}

代码中用到了一个函数,就是PsGetCurrentProcess()。这个函数是用来获取当前进程的EPROCESS指针的,其定义如下:

PEPROCESSPsGetCurrentProcess(VOID);

通过PsGetCurrentProcess()函数获得的是system进程的EPROCESS,大多数内核模式系统线程都在system进程中。除了这个函数没有接触过以外,剩下的部分就是对EPROCESS结构体的操作,这里不做过多的介绍。实现进程内DLL文件的隐藏方法是将指定DLL在DLL链表中“脱链”。为了隐藏进程,同样可以将指定进程的EPROCESS结构体在进程链表中“脱链”,以达到隐藏的目的。

继续浏览有关 安全 的文章
发表评论