进程

2.进程

KPROCESS

这里我们使用驱动管理程序

kd> dt _kprocess 86de0d20  
ntdll!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead  : _LIST_ENTRY [ 0x86de0d30 - 0x86de0d30 ]//基本都是空的
   +0x018 DirectoryTableBase : 0xbeda71e0//CR3
   +0x01c LdtDescriptor    : _KGDTENTRY
   +0x024 Int21Descriptor  : _KIDTENTRY
   +0x02c ThreadListHead   : _LIST_ENTRY [ 0x87025a50 - 0x87025a50 ]
   +0x034 ProcessLock      : 0
   +0x038 Affinity         : _KAFFINITY_EX//亲核性
   +0x044 ReadyListHead    : _LIST_ENTRY [ 0x86de0d64 - 0x86de0d64 ]//就绪链表
   +0x04c SwapListEntry    : _SINGLE_LIST_ENTRY//换入换出
   +0x050 ActiveProcessors : _KAFFINITY_EX//当前线程的亲核性,跑在那个核上面(活动的)
   +0x05c AutoAlignment    : 0y0//状态当前进程是否对齐(通常为0)
   +0x05c DisableBoost     : 0y0//状态
   +0x05c DisableQuantum   : 0y0//状态,线程的时间碎片
   +0x05c ActiveGroupsMask : 0y1//写死的,不用管
   +0x05c ReservedFlags    : 0y0000000000000000000000000000 (0)
   +0x05c ProcessFlags     : 0n8
   +0x060 BasePriority     : 8 ''//创建的线程的优先级继承自这里,初始的优先级是8
   +0x061 QuantumReset     : 18 ''//时间碎片,每次时间中断会减少时间碎片,可以增减线程的时间碎片
   +0x062 Visited          : 0 ''
   +0x063 Unused3          : 0 ''
   +0x064 ThreadSeed       : [1] 0
   +0x068 IdealNode        : [1] 0
   +0x06a IdealGlobalNode  : 0
   +0x06c Flags            : _KEXECUTE_OPTIONS
   +0x06d AddressPolicy    : 0 ''
   +0x06e IopmOffset       : 0x20ac
   +0x070 Unused4          : 0
   +0x074 StackCount       : _KSTACK_COUNT
   +0x078 ProcessListEntry : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x080 CycleTime        : 0//执行了多少个时间周期
   +0x088 KernelTime       : 0//内核时间,这个记录的不是创建的时间,而是死亡时间,死了后才有值
   +0x08c UserTime         : 0//用户时间,这个记录的不是创建的时间,而是死亡时间,死了后才有值
   +0x090 VdmTrapcHandler  : (null) 

亲核性 Affinity

+0x038 Affinity : _KAFFINITY_EX//亲核性这个亲核性代表如果CPU有2核,线程运行在那个核上,是由这个决定的

这个是一个结构

kd> dt _KAFFINITY_EX
ntdll!_KAFFINITY_EX
   +0x000 Count            : Uint2B
   +0x002 Size             : Uint2B
   +0x004 Reserved         : Uint4B
   +0x008 Bitmap           : [1] Uint4B

其中有一个位图(Bitmap)当他是1的时候,因为这个是位图,如果是0的位置是1的话,代表这个CPU运行跑在第0号CPU上面,如下图

这个进程结构体中的亲核性是标准亲核性如果这个进程创建了一个线程,那么这个线程的初始亲核性就是从这个进程中继承来的

注意,内核中的线程如果不指定的话是继承自system

就绪链表 ReadyListHead

如果一个进程创建了3个线程,只有一个线程在运行,另外两个待机,那么没有运行的线程就放在这个就绪链表中

换入换出 SwapListEntry

当这个进程没有线程再运行的时候,这个进程可能被放到磁盘上,当真正有线程再运行的时候这个进程才会被换回来。

这个链表就是用来记录这个进程有那块空间是被交换出去的

EPROCESS

kd> dt _Eprocess 86de0d20  
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK//进程锁,修改的时候要加把锁,避免多人修改
   +0x0a0 CreateTime       : _LARGE_INTEGER 0x01d9335a`793d1b20//进程创建的时间
   +0x0a8 ExitTime         : _LARGE_INTEGER 0x0//进程死亡的时间
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF//运行的保护锁
   +0x0b4 UniqueProcessId  : 0x00000a1c Void//进程id,十六进制
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY [ 0x8414fd90 - 0x86f32dd8 ]//当前有多少个进程是活动的,想想断链
   +0x0c0 ProcessQuotaUsage : [2] 0xca8//统计信息
   +0x0c8 ProcessQuotaPeak : [2] 0xce4//统计信息
   +0x0d0 CommitCharge     : 0xec//统计信息
   +0x0d4 QuotaBlock       : 0x889acd80 _EPROCESS_QUOTA_BLOCK//统计信息
   +0x0d8 CpuQuotaBlock    : (null) //统计信息
   +0x0dc PeakVirtualSize  : 0x341f000//统计信息
   +0x0e0 VirtualSize      : 0x341e000//统计信息
   +0x0e4 SessionProcessLinks : _LIST_ENTRY [ 0x96472010 - 0x86f32e04 ]//当前用户的进程链表
   +0x0ec DebugPort        : (null) //如果有值,代表被调试
   +0x0f0 ExceptionPortData : 0x8a207570 Void//崩溃的时候调试对象
   +0x0f0 ExceptionPortValue : 0x8a207570//崩溃的时候调试对象
   +0x0f0 ExceptionPortState : 0y000
   +0x0f4 ObjectTable      : 0xacfc62d0 _HANDLE_TABLE//句柄表
   +0x0f8 Token            : _EX_FAST_REF//当前登录的用户有什么权限
   +0x0fc WorkingSetPage   : 0x94aa4//进程的工作集,当前进程用了多少个页
   +0x100 AddressCreationLock : _EX_PUSH_LOCK
   +0x104 RotateInProgress : (null) 
   +0x108 ForkInProgress   : (null) 
   +0x10c HardwareTrigger  : 0
   +0x110 PhysicalVadRoot  : (null) 
   +0x114 CloneRoot        : (null) 
   +0x118 NumberOfPrivatePages : 0xc0
   +0x11c NumberOfLockedPages : 0
   +0x120 Win32Process     : 0xfe8a9be0 Void
   +0x124 Job              : (null) 
   +0x128 SectionObject    : 0x992cfda0 Void
   +0x12c SectionBaseAddress : 0x00400000 Void
   +0x130 Cookie           : 0x4c57da61
   +0x134 Spare8           : 0
   +0x138 WorkingSetWatch  : (null) 
   +0x13c Win32WindowStation : 0x0000002c Void
   +0x140 InheritedFromUniqueProcessId : 0x0000057c Void
   +0x144 LdtInformation   : (null) 
   +0x148 VdmObjects       : (null) 
   +0x14c ConsoleHostProcess : 0
   +0x150 DeviceMap        : 0x8f748470 Void
   +0x154 EtwDataSource    : (null) 
   +0x158 FreeTebHint      : 0x7ffdf000 Void
   +0x160 PageDirectoryPte : _HARDWARE_PTE_X86
   +0x160 Filler           : 0
   +0x168 Session          : 0x96472000 Void
   +0x16c ImageFileName    : [15]  "q???"//当前进程的名字
   +0x17b PriorityClass    : 0x2 ''
   +0x17c JobLinks         : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x184 LockedPagesList  : (null) 
   +0x188 ThreadListHead   : _LIST_ENTRY [ 0x87025ad8 - 0x87025ad8 ]//线程链表,当前的进程有多少个线程
   +0x190 SecurityPort     : (null) 
   +0x194 PaeTop           : 0x87da71e0 Void
   +0x198 ActiveThreads    : 1
   +0x19c ImagePathHash    : 0xfff0af2
   +0x1a0 DefaultHardErrorProcessing : 1
   +0x1a4 LastThreadExitStatus : 0n0
   +0x1a8 Peb              : 0x7ffd4000 _PEB
   +0x1ac PrefetchTrace    : _EX_FAST_REF
   +0x1b0 ReadOperationCount : _LARGE_INTEGER 0x1
   +0x1b8 WriteOperationCount : _LARGE_INTEGER 0x0
   +0x1c0 OtherOperationCount : _LARGE_INTEGER 0xe7
   +0x1c8 ReadTransferCount : _LARGE_INTEGER 0x3e50
   +0x1d0 WriteTransferCount : _LARGE_INTEGER 0x0
   +0x1d8 OtherTransferCount : _LARGE_INTEGER 0x28a
   +0x1e0 CommitChargeLimit : 0
   +0x1e4 CommitChargePeak : 0xec
   +0x1e8 AweInfo          : (null) 
   +0x1ec SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO//完整的进程名
   +0x1f0 Vm               : _MMSUPPORT
   +0x25c MmProcessLinks   : _LIST_ENTRY [ 0x84156290 - 0x86f32f7c ]
   +0x264 HighestUserAddress : 0x7fff0000 Void
   +0x268 ModifiedPageCount : 2
   +0x26c Flags2           : 0x202d000
   +0x26c JobNotReallyActive : 0y0
   +0x26c AccountingFolded : 0y0
   +0x26c NewProcessReported : 0y0
   +0x26c ExitProcessReported : 0y0
   +0x26c ReportCommitChanges : 0y0
   +0x26c LastReportMemory : 0y0
   +0x26c ReportPhysicalPageChanges : 0y0
   +0x26c HandleTableRundown : 0y0
   +0x26c NeedsHandleRundown : 0y0
   +0x26c RefTraceEnabled  : 0y0
   +0x26c NumaAware        : 0y0
   +0x26c ProtectedProcess : 0y0//进程保护
   +0x26c DefaultPagePriority : 0y101
   +0x26c PrimaryTokenFrozen : 0y1
   +0x26c ProcessVerifierTarget : 0y0
   +0x26c StackRandomizationDisabled : 0y1
   +0x26c AffinityPermanent : 0y0
   +0x26c AffinityUpdateEnable : 0y0
   +0x26c PropagateNode    : 0y0
   +0x26c ExplicitAffinity : 0y0
   +0x26c Spare1           : 0y0
   +0x26c ForceRelocateImages : 0y0
   +0x26c DisallowStrippedImages : 0y0
   +0x26c LowVaAccessible  : 0y1
   +0x26c RestrictIndirectBranchPrediction : 0y0
   +0x26c AddressPolicyFrozen : 0y0
   +0x270 Flags            : 0x144d0801
   +0x270 CreateReported   : 0y1//进程创建的时候是否上报调试器
   +0x270 NoDebugInherit   : 0y0
   +0x270 ProcessExiting   : 0y0//进程是否在退出中
   +0x270 ProcessDelete    : 0y0//在R3是否存在
   +0x270 Wow64SplitPages  : 0y0
   +0x270 VmDeleted        : 0y0
   +0x270 OutswapEnabled   : 0y0
   +0x270 Outswapped       : 0y0
   +0x270 ForkFailed       : 0y0
   +0x270 Wow64VaSpace4Gb  : 0y0
   +0x270 AddressSpaceInitialized : 0y10
   +0x270 SetTimerResolution : 0y0
   +0x270 BreakOnTermination : 0y0
   +0x270 DeprioritizeViews : 0y0
   +0x270 WriteWatch       : 0y0
   +0x270 ProcessInSession : 0y1
   +0x270 OverrideAddressSpace : 0y0
   +0x270 HasAddressSpace  : 0y1
   +0x270 LaunchPrefetched : 0y1
   +0x270 InjectInpageErrors : 0y0
   +0x270 VmTopDown        : 0y0//如果申请内存的话,从后(0xffffff)开始申请
   +0x270 ImageNotifyDone  : 0y1
   +0x270 PdeUpdateNeeded  : 0y0
   +0x270 VdmAllowed       : 0y0
   +0x270 CrossSessionCreate : 0y0
   +0x270 ProcessInserted  : 0y1 //如果是0,系统就不向这个进程插入句柄(进程保护)
   +0x270 DefaultIoPriority : 0y010//是否使用了默认的线程优先级
   +0x270 ProcessSelfDelete : 0y0
   +0x270 SetTimerResolutionLink : 0y0
   +0x274 ExitStatus       : 0n259//状态码,判断进程是否退出
   +0x278 VadRoot          : _MM_AVL_TABLE
   +0x298 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x2a8 TimerResolutionLink : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x2b0 RequestedTimerResolution : 0
   +0x2b4 ActiveThreadsHighWatermark : 1
   +0x2b8 SmallestTimerResolution : 0
   +0x2bc TimerResolutionStackRecord : (null) 
   +0x2c0 SequenceNumber   : 0x6b
   +0x2c8 CreateInterruptTime : 0x00000015`b73ae2b0
   +0x2d0 CreateUnbiasedInterruptTime : 0x00000015`b73ae2b0
   +0x2d8 SecurityDomain   : 0x00000001`0000000a

获得这个位置+0x0b8 ActiveProcessLinks可以使用+0x0b4 UniqueProcessId来获得,这个获取进程id是个导出函数

可以看到这里面有个+0b4h这个和结构体的偏移是对应的,所以可以通过硬编码获得这个偏移然后+4来得到ActiveProcessLinks这个函数,这样少些硬编码兼容性更好

进程的默认申请位置VmTopDown

+0x270 VmTopDown: 0y0当我们做内存隐藏的时候,如果在小地址有一块内存被隐藏了,但是进程不知道,如果进程需要申请内存的话,一旦认为到我们隐藏的那块内存是空闲的,在那申请内存的话,就会崩溃掉,所以我们做完进程隐藏后,需要让进程默认从大地址开始找空闲内存申请,这样就不会遍历到我们内存隐藏的那块内存

32位下比较危险,64位可以这么用

OBJECT_HEADER

当前进程的进程地址-0x18就是对象头

kd> dt _object_header 86de0d20-0x18
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n35//引用次数,为0的时候才可以释放
   +0x004 HandleCount      : 0n2//句柄引用
   +0x004 NextToFree       : 0x00000002 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0x7 ''//索引号
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0x8 ''
   +0x00f Flags            : 0 ''//标志位,进程保护
   +0x010 ObjectCreateInfo : 0x889acd80 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x889acd80 Void
   +0x014 SecurityDescriptor : 0x99253de2 Void
   +0x018 Body             : _QUAD

eb 86de0d20-0x18+f 4这里将FLAGS标志位改成4

做进程保护

安全没有副作用

代码实现 进程保护

#include <ntifs.h>

//使用未文档化的函数先声明
EXTERN_C UCHAR* PsGetProcessImageFileName(
	__in PEPROCESS Process
);

//开启保护 传进去进程对象
VOID SetObjectHeaderFlags(PEPROCESS pRocess)
{
	/*
	kd> dt _object_header 86de0d20-0x18
	nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n35
   +0x004 HandleCount      : 0n2
   +0x004 NextToFree       : 0x00000002 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0x7
   +0x00d TraceFlags       : 0
   +0x00e InfoMask         : 0x8
   +0x00f Flags            : 0
	*/

	//初始化一个PUCHAR
	PUCHAR ObjectHeader = NULL;
#ifdef _WIN64
	//如果是编译成64位的
	//_object_header = 获取到的进程结构-0x30(64位)
	ObjectHeader = ((PUCHAR)pRocess - 0x30);
	//_object_header + 0x01b得到flag标志位,将标志位的值(这里使用指针获取地址指向的值)改成4
	//因为这里不确定值是否是4,所以这里使用或等于,如果是4的话不变,不是的话变成4
	*(ObjectHeader + 0x1B) |= 4;
#else
	//如果是编译成32位的
	//_object_header = 获取到的进程结构-0x18(32位)
	ObjectHeader = ((PUCHAR)pRocess - 0x18);
	//_object_header + 0x00f得到flag标志位,将标志位的值(这里使用指针获取地址指向的值)改成4
	//因为这里不确定值是否是4,所以这里使用或等于,如果是4的话不变,不是的话变成4
	*(ObjectHeader + 0xf) |= 4;
#endif
}

//关闭保护 传进去进程对象
VOID CloseObjectHeaderFlags(PEPROCESS pRocess)
{

	//初始化一个PUCHAR
	PUCHAR ObjectHeader = NULL;
#ifdef _WIN64
	//如果是编译成64位的
	//_object_header = 获取到的进程结构-0x30(64位)
	ObjectHeader = ((PUCHAR)pRocess - 0x30);
	//_object_header + 0x01b得到flag标志位,将标志位的值(这里使用指针获取地址指向的值)改成4
	//关闭保护,取反
	*(ObjectHeader + 0x1B) &= ~4;
#else
	//如果是编译成32位的
	//_object_header = 获取到的进程结构-0x18(32位)
	ObjectHeader = ((PUCHAR)pRocess - 0x18);
	//_object_header + 0x00f得到flag标志位,将标志位的值(这里使用指针获取地址指向的值)改成4
	//关闭保护,取反
	*(ObjectHeader + 0xf) &= ~4;
#endif

}
//根据名字找进程结构 不区分大小写
PEPROCESS FindProcessByName(char* name)
{
	//DbgBreakPoint();
	//用来存放我们找到的进程的结构
	PEPROCESS FindProcess = NULL;
	//根据进程id的编号遍历
	for (int i = 4; i < 0x1000000; i += 4)
	{
		PEPROCESS pRocess = NULL;//提供一个结构体
		//根据进程id获取进程结构
		NTSTATUS status = PsLookupProcessByProcessId((HANDLE)i, &pRocess);
		if (!NT_SUCCESS(status))
		{
			continue;
		}
		//根据当前遍历到的进程结构获得名字
		PUCHAR processName = PsGetProcessImageFileName(pRocess);
		//如果存在而且名字和我们指定的名字一样(不区分大小写比较)
		if (processName && _stricmp(processName, name) == 0)
		{
			//保存我们找到的进程结构
			FindProcess = pRocess;
			break;
		}
		//使用后要减少引用计数,防止一直占用,泄露内存
		ObDereferenceObject(pRocess);
	}
	return FindProcess;
}

VOID Unload(PDRIVER_OBJECT pDriver)
{
	//通过进程名字获取进程结构
	//不区分大小写
	PEPROCESS pRocess = FindProcessByName("dbgview.exe");
	if (!pRocess)
	{
		return;
	}
	//关掉保护
	CloseObjectHeaderFlags(pRocess);
	//使用后要减少引用计数,防止一直占用,泄露内存
	ObDereferenceObject(pRocess);
	DbgPrint("unload\r\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//通过进程名字获取进程结构
	//不区分大小写
	PEPROCESS pRocess = FindProcessByName("dbgview.exe");
	//如果没找到,返回失败
	if (!pRocess)
	{
		return STATUS_UNSUCCESSFUL;
	}
	//开启保护
	SetObjectHeaderFlags(pRocess);
	DbgPrint("开启保护成功\r\n");
	// 使用后要减少引用计数,防止一直占用,泄露内存
	ObDereferenceObject(pRocess);
	//DbgBreakPoint();
	//DbgPrint("TEST_Entry\r\n");
	return STATUS_SUCCESS;
}

原理

#define OB_FLAG_NEW_OBJECT              0x01
#define OB_FLAG_KERNEL_OBJECT           0x02
#define OB_FLAG_CREATOR_INFO            0x04
#define OB_FLAG_EXCLUSIVE_OBJECT        0x08
#define OB_FLAG_PERMANENT_OBJECT        0x10
#define OB_FLAG_DEFAULT_SECURITY_QUOTA  0x20
#define OB_FLAG_SINGLE_HANDLE_ENTRY     0x40
#define OB_FLAG_DELETED_INLINE          0x80

可以看到0x4代表了OB_FLAG_CREATOR_INFO

将这个改成0x4代表这个OBJECT_HEADER+0x010 ObjectCreateInfo

再插入对象的时候有个判断,如果这个位被设置成这个,那么无法插入对象

断链2

找到指定进程的结构

//通过全路径查找指定进程
//imageFilename只有15个字节,但是完整的进程名字可能超过整个大小,所以要使用全路径
//传进去大写的短路径跟全路径比较
PEPROCESS FindProcByFullName(PWCH name)
{
	PEPROCESS process = NULL;
	PEPROCESS FindProcess = NULL;

	for (int i = 4; i < 0x100000; i += 4)
	{
		NTSTATUS status = PsLookupProcessByProcessId((HANDLE)i, &process);
		if (!NT_SUCCESS(status))
		{
			continue;
		}

		PUNICODE_STRING procName = NULL;
		//获取全路径使用这个函数
		status = SeLocateProcessImageName(process, &procName);
		if (!NT_SUCCESS(status))
		{
			//释放内存
			ObDereferenceObject(process);
			continue;
		}

		//判断是否获取到了名字,名字不为空,
		if (procName->Length)
		{
			//转大写比较
			_wcsupr(procName->Buffer);
			//判断指定的短路经是否被全路径包含
			if (wcsstr(procName->Buffer, name) != 0)
			{
				FindProcess = process;
				//释放
				ExFreePoolWithTag(procName, 0);
				break;
			}
		}
		//释放
		ExFreePoolWithTag(procName, 0);
		//释放内存
		ObDereferenceObject(process);
	}
	return FindProcess;
}

调用

PEPROCESS process = FindProcByFullName(L"DBGVIEW.EXE");
ObDereferenceObject(process);//释放
kd> dt _eprocess 88b3f340 
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK
   +0x0a0 CreateTime       : _LARGE_INTEGER 0x01d933fc`1cbbb809
   +0x0a8 ExitTime         : _LARGE_INTEGER 0x0
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF
   +0x0b4 UniqueProcessId  : 0x000005e0 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY

ActiveProcessLinks字段存储着链表,但是这个字段我们买法直接获得

但是+0x0b4 UniqueProcessId整个字段我们可以获得,可以看到在导出函数里面有一个这个函数

可以看到在这个函数的汇编代码中存在一个mov eax,[eax+0b4h]

这个+0b4h正好和UniqueProcessId这个字段在_eprocess中的偏移一致,那么我们就可以通过修改这个偏移,获得我们想要的ActiveProcessLinks

实现思路:

在这个函数里面特征码搜索->定位到mov eax,[eax+0b4h]->再+4就可以获得ActiveProcessLinks

代码

//获取ActiveProcessLinks链表的偏移
ULONG GetProceActiveProcessLinksOffset()
{
	//定义一个静态变量
	static ULONG offset = 0;
	//如果offset有值直接返回
	if (offset) return offset;
	//因为我们是通过导出表拿到的这个函数,这个函数在某些编译器上只是一个指针
	UNICODE_STRING name;
	//定义要取的函数名
	RtlInitUnicodeString(&name, L"PsGetProcessId");
	//取到这个函数地址
	PUCHAR func = (PUCHAR)MmGetSystemRoutineAddress(&name);

	//遍历这个函数的字节码
	//在这个函数内部搜索硬编码
	for (int i = 0; i < 100; i++)
	{
		//定位到下面这行硬编码上
		/*8B 80 B4 00 00 00    mov     eax, [eax+0B4h]*/
		if (func[i] == 0x8B && func[i + 1] == 0x80)
		{
			//强转,取4个整数字节
			//+i 代表func到当前的位置(定位到这行硬编码),+2代表去掉了8B 80
			//这样取到的就是B4 00 00 00 也就是偏移+0B4h
			offset = *(PULONG)(func + i + 2);
			//加上4偏移,这样汇编代码就变成了mov eax,[eax+0B8h]取得ActiveProcessLinks
			offset += 4;
			break;
		}
	}
	return offset;
}

调用ULONG offset = GetProceActiveProcessLinksOffset();

完整代码

#include <ntifs.h>

//通过全路径查找指定进程
//imageFilename只有15个字节,但是完整的进程名字可能超过整个大小,所以要使用全路径
//传进去大写的短路径跟全路径比较
PEPROCESS FindProcByFullName(PWCH name)
{
	PEPROCESS process = NULL;
	PEPROCESS FindProcess = NULL;

	for (int i = 4; i < 0x100000; i += 4)
	{
		NTSTATUS status = PsLookupProcessByProcessId((HANDLE)i, &process);
		if (!NT_SUCCESS(status))
		{
			continue;
		}

		PUNICODE_STRING procName = NULL;
		//获取全路径使用这个函数
		status = SeLocateProcessImageName(process, &procName);
		if (!NT_SUCCESS(status))
		{
			//释放内存
			ObDereferenceObject(process);
			continue;
		}

		//判断是否获取到了名字,名字不为空,
		if (procName->Length)
		{
			//转大写比较
			_wcsupr(procName->Buffer);
			//判断指定的短路经是否被全路径包含
			if (wcsstr(procName->Buffer, name) != 0)
			{
				FindProcess = process;
				//释放
				ExFreePoolWithTag(procName, 0);
				break;
			}
		}
		//释放
		ExFreePoolWithTag(procName, 0);
		//释放内存
		ObDereferenceObject(process);
	}
	return FindProcess;
}
//获取ActiveProcessLinks链表的偏移
ULONG GetProceActiveProcessLinksOffset()
{
	//定义一个静态变量
	static ULONG offset = 0;
	//如果offset有值直接返回
	if (offset) return offset;
	//因为我们是通过导出表拿到的这个函数,这个函数在某些编译器上只是一个指针
	UNICODE_STRING name;
	//定义要取的函数名
	RtlInitUnicodeString(&name, L"PsGetProcessId");
	//取到这个函数地址
	PUCHAR func = (PUCHAR)MmGetSystemRoutineAddress(&name);

	//遍历这个函数的字节码
	//在这个函数内部搜索硬编码
	for (int i = 0; i < 100; i++)
	{
		//定位到下面这行硬编码上
		/*8B 80 B4 00 00 00    mov     eax, [eax+0B4h]*/
		if (func[i] == 0x8B && func[i + 1] == 0x80)
		{
			//强转,取4个整数字节
			//+i 代表func到当前的位置(定位到这行汇编),+2代表去掉了8B 80
			//这样取到的就是B4 00 00 00 也就是偏移+0B4h
			offset = *(PULONG)(func + i + 2);
			//加上4偏移,这样汇编代码就变成了mov eax,[eax+0B8h]取得ActiveProcessLinks
			offset += 4;
			break;
		}
	}
	return offset;
}


VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	//传入大写短路径(WCHAR)
	PEPROCESS process = FindProcByFullName(L"DBGVIEW.EXE");
	//获取偏移
	ULONG offset = GetProceActiveProcessLinksOffset();
	//定位到链表的位置然后进行断链
	RemoveEntryList((PUCHAR)process + offset);
	//不初始化会有蓝屏可能
	//将链表初始化
	InitializeListHead((PUCHAR)process + offset);
	//释放
	ObDereferenceObject(process);
	//DbgBreakPoint();


	DbgPrint("TEST_Entry\r\n");
	return STATUS_SUCCESS;
}

断链成功

蓝屏可能

断链前的链表是这个样子的

断链后变成了这个样子

当断链的进程被结束的时候,会重新根据锻炼的进程的指向重新串联链表,但是此时链表已经不包含要断链的那个进程了,所以又可能蓝屏。

所以后面还要将断链了的进程的链表初始化一下(这个在上面的代码中已经初始化了)

posted @ 2024-05-18 17:23  MuRKuo  阅读(41)  评论(0编辑  收藏  举报