进程
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
ActiveProcessLinks 断链
获得这个位置+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);//释放
获得字段 ActiveProcessLinks
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;
}
断链成功
蓝屏可能
断链前的链表是这个样子的
断链后变成了这个样子
当断链的进程被结束的时候,会重新根据锻炼的进程的指向重新串联链表,但是此时链表已经不包含要断链的那个进程了,所以又可能蓝屏。
所以后面还要将断链了的进程的链表初始化一下(这个在上面的代码中已经初始化了)