进程线程篇——总结与提升(下)
写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。
看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。
🔒 华丽的分割线 🔒
进程创建浅析
同理上面的浅析,我同样使用伪代码。进程创建浅析是为了知道内核是怎样创建进程的流程,具体细节请自行挖掘,我们定位到其内核函数NtCreateProcess
:
NTSTATUS __stdcall NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, BOOLEAN InheritObjectTable, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort)
{
ULONG v8; // eax
v8 = (SectionHandle & 1) != 0;
if ( (DebugPort & 1) != 0 )
v8 |= 2u;
if ( InheritObjectTable )
v8 |= 4u;
return NtCreateProcessEx(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
ParentProcess,
v8,
SectionHandle,
DebugPort,
ExceptionPort,
0);
}
这个内核函数又会调用NtCreateProcessEx
实现功能,我们点进去看看:
NTSTATUS __stdcall NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, BOOLEAN InJob)
{
PHANDLE v9; // ecx
NTSTATUS result; // eax
if ( KeGetCurrentThread()->PreviousMode )
{
v9 = ProcessHandle;
if ( ProcessHandle >= MmUserProbeAddress )
*MmUserProbeAddress = 0;
*ProcessHandle = *ProcessHandle;
}
else
{
v9 = ProcessHandle;
}
if ( ParentProcess )
result = PspCreateProcess(
v9,
DesiredAccess,
ObjectAttributes,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
InJob);
else
result = STATUS_INVALID_PARAMETER;
return result;
}
这个函数又会调用PspCreateProcess
实现创建进程的任务,继续点击去看看:
🔒 点击查看伪代码 🔒
NTSTATUS __stdcall PspCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, HANDLE DebugPort, HANDLE ExceptionPort, BOOLEAN InJob)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v41 = KeGetCurrentThread();
AccessMode[0] = v41->PreviousMode;
process = v41->ApcState.Process;
v64 = 0;
a4[0] = 0;
a4[1] = 0;
if ( (Flags & 0xFFFFFFF0) != 0 )
return STATUS_INVALID_PARAMETER;
if ( ParentProcess )
{
result = ObReferenceObjectByHandle(ParentProcess, 0x80u, PsProcessType, AccessMode[0], &eprocess, 0);
ParentProcess_1 = eprocess;
if ( result < 0 )
return result;
if ( InJob && !eprocess->Job )
{
ObfDereferenceObject(eprocess);
return STATUS_INVALID_PARAMETER;
}
ActiveProcessors = eprocess->Pcb.Affinity;
}
else
{
ParentProcess_1 = 0;
ActiveProcessors = KeActiveProcessors;
}
a3 = ActiveProcessors;
*NewIrql = PsMinimumWorkingSet;
v47 = PsMaximumWorkingSet;
v11 = ObCreateObject(AccessMode[0], PsProcessType, ObjectAttributes, *AccessMode, 0, 608, 0, 0, &Process);
if ( v11 < 0 )
goto LABEL_97;
NewProcess = Process;
memset(Process, 0, 0x260u);
NewProcess->RundownProtect.Count = 0;
NewProcess->ProcessLock.Value = 0;
InitializeListHead(&NewProcess->ThreadListHead);
PspInheritQuota(&NewProcess->Pcb, &ParentProcess_1->Pcb);
ObInheritDeviceMap(NewProcess, ParentProcess_1);
v13 = ParentProcess_1;
if ( ParentProcess_1 )
{
NewProcess->DefaultHardErrorProcessing = ParentProcess_1->DefaultHardErrorProcessing;
NewProcess->InheritedFromUniqueProcessId = v13->UniqueProcessId;
}
else
{
NewProcess->DefaultHardErrorProcessing = 1;
NewProcess->InheritedFromUniqueProcessId = 0;
}
if ( SectionHandle )
{
v14 = ObReferenceObjectByHandle(SectionHandle, 8u, MmSectionObjectType, AccessMode[0], &Object, 0);
v61 = Object;
v11 = v14;
if ( v14 < 0 )
goto LABEL_96;
v13 = ParentProcess_1;
}
else
{
v61 = 0;
if ( v13 != PsInitialSystemProcess )
{
if ( ExAcquireRundownProtection(&v13->RundownProtect) )
{
v15 = v13->SectionObject;
v61 = v15;
if ( v15 )
ObfReferenceObject(v15);
ExReleaseRundownProtection(&v13->RundownProtect);
}
if ( !v61 )
{
v11 = STATUS_PROCESS_IS_TERMINATING;
goto LABEL_96;
}
}
}
NewProcess->SectionObject = v61;
if ( DebugPort )
{
v11 = ObReferenceObjectByHandle(DebugPort, 2u, DbgkDebugObjectType, AccessMode[0], &v44, 0);
if ( v11 < 0 )
goto LABEL_96;
NewProcess->DebugPort = v44;
if ( (Flags & 2) != 0 )
_InterlockedOr(&NewProcess->584, 2u);
}
else if ( v13 )
{
DbgkCopyProcessDebugPort(NewProcess, v13);
}
if ( ExceptionPort )
{
v11 = ObReferenceObjectByHandle(ExceptionPort, 0, LpcPortObjectType, AccessMode[0], &v45, 0);
if ( v11 < 0 )
goto LABEL_96;
NewProcess->ExceptionPort = v45;
}
NewProcess->ExitStatus = STATUS_PENDING;
v11 = PspInitializeProcessSecurity(&ParentProcess_1->Pcb, NewProcess);
if ( v11 < 0 )
goto LABEL_96;
v16 = ParentProcess_1;
if ( ParentProcess_1 )
{
if ( !MmCreateProcessAddressSpace(*NewIrql, NewProcess, a4) )
goto LABEL_59;
}
else
{
NewProcess->ObjectTable = process->ObjectTable;
MmInitializeHandBuiltProcess(NewProcess, a4);
}
_InterlockedOr(&NewProcess->584, 0x40000u);
NewProcess->Vm.MaximumWorkingSetSize = v47;
KeInitializeProcess(&NewProcess->Pcb, 8, a3, a4, NewProcess->DefaultHardErrorProcessing & 4);
NewProcess->Pcb.ThreadQuantum = PspForegroundQuantum;
NewProcess->PriorityClass = 2;
if ( v16 )
{
v17 = v16->PriorityClass;
if ( v17 == 1 || v17 == 5 )
NewProcess->PriorityClass = v17;
v18 = ObInitProcess((Flags & 4) != 0 ? ParentProcess_1 : 0, NewProcess);
}
else
{
v18 = MmInitializeHandBuiltProcess2(&NewProcess->Pcb);
}
v11 = v18;
if ( v18 < 0 )
goto LABEL_96;
v58 = 0;
if ( SectionHandle )
{
v19 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, 0, v61, &NewProcess->SeAuditProcessCreationInfo);
v11 = v19;
if ( v19 < 0 )
goto LABEL_96;
v58 = v19;
v11 = PspMapSystemDll(&NewProcess->Pcb, 0);
if ( v11 < 0 )
goto LABEL_96;
v64 = 1;
goto LABEL_58;
}
v20 = ParentProcess_1;
if ( !ParentProcess_1 )
goto LABEL_58;
if ( ParentProcess_1 == PsInitialSystemProcess )
{
v11 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, 0, 0, 0);
if ( v11 >= 0 )
{
v24 = ExAllocatePoolWithTag(PagedPool, 8u, 0x61506553u);
NewProcess->SeAuditProcessCreationInfo.ImageFileName = v24;
if ( v24 )
{
*&v24->Name.Length = 0;
v24->Name.Buffer = 0;
goto LABEL_58;
}
goto LABEL_59;
}
LABEL_96:
ObfDereferenceObject(NewProcess);
goto LABEL_97;
}
NewProcess->SectionBaseAddress = ParentProcess_1->SectionBaseAddress;
v11 = MmInitializeProcessAddressSpace(&NewProcess->Pcb, v20, 0, 0);
v64 = 1;
if ( v11 < 0 )
goto LABEL_96;
v21 = ParentProcess_1->SeAuditProcessCreationInfo.ImageFileName;
if ( v21 )
{
v22 = v21->Name.MaximumLength + 8;
v23 = ExAllocatePoolWithTag(PagedPool, v22, 0x61506553u);
NewProcess->SeAuditProcessCreationInfo.ImageFileName = v23;
if ( v23 )
{
qmemcpy(v23, ParentProcess_1->SeAuditProcessCreationInfo.ImageFileName, v22);
NewProcess->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer = &NewProcess->SeAuditProcessCreationInfo.ImageFileName[1].Name.Length;
goto LABEL_58;
}
LABEL_59:
v11 = STATUS_INSUFFICIENT_RESOURCES;
goto LABEL_96;
}
LABEL_58:
v25 = (NewProcess->Token.Value & 0xFFFFFFF8);
v26 = MmGetSessionId(NewProcess);
SeSetSessionIdToken(v25, v26);
v46[0] = NewProcess;
v46[1] = 0;
v27 = ExCreateHandle(PspCidTable, v46);
NewProcess->UniqueProcessId = v27;
if ( !v27 )
goto LABEL_59;
NewProcess->ObjectTable->UniqueProcessId = v27;
if ( SeDetailedAuditingWithToken(0) )
SeAuditProcessCreation(&NewProcess->Pcb);
if ( ParentProcess_1 )
{
v28 = ParentProcess_1->Job;
if ( v28 )
{
if ( (v28->LimitFlags & 0x1000) == 0 )
{
if ( (Flags & 1) != 0 )
{
v11 = (v28->LimitFlags & 0x800) != 0 ? 0 : STATUS_ACCESS_DENIED;
}
else
{
v11 = PspGetJobFromSet(v28, InJob, &NewProcess->Job);
if ( v11 < 0 )
goto LABEL_96;
v43 = NewProcess->Job;
v11 = PspAddProcessToJob(v43, &NewProcess->Pcb);
v29 = v43->Token;
if ( v29 )
{
v11 = SeSubProcessToken(v29, &v50, 0);
if ( v11 < 0 )
goto LABEL_96;
SeAssignPrimaryToken(&NewProcess->Pcb, v50);
ObfDereferenceObject(v50);
}
}
if ( v11 < 0 )
goto LABEL_96;
}
}
}
if ( ParentProcess_1 && v64 )
{
BaseAddress[0] = 0;
BaseAddress[1] = -1;
if ( SectionHandle )
{
v11 = MmCreatePeb(&NewProcess->Pcb, BaseAddress, &NewProcess->Peb);
if ( v11 < 0 )
{
NewProcess->Peb = 0;
goto LABEL_96;
}
}
else
{
LOBYTE(BaseAddress[0]) = 1;
v30 = ParentProcess_1->Peb;
NewProcess->Peb = v30;
MmCopyVirtualMemory(&process->Pcb, BaseAddress, &NewProcess->Pcb, v30, 8u, 0, &a7a);
}
}
v31 = v41;
--v41->KernelApcDisable;
ExAcquireFastMutexUnsafe(&PspActiveProcessMutex);
v32 = dword_48315C;
NewProcess->ActiveProcessLinks.Flink = &PsActiveProcessHead;
NewProcess->ActiveProcessLinks.Blink = v32;
v32->Flink = &NewProcess->ActiveProcessLinks;
dword_48315C = &NewProcess->ActiveProcessLinks;
ExReleaseFastMutexUnsafe(&PspActiveProcessMutex);
v34 = (*(v31 + 212))++ == -1;
if ( v34 && *(v31 + 52) != v31 + 52 )
{
*(v31 + 73) = 1;
LOBYTE(v33) = 1;
HalRequestSoftwareInterrupt(v33);
}
if ( !ParentProcess_1 || (v35 = PsInitialSystemProcess, ParentProcess_1 != PsInitialSystemProcess) )
v35 = *(v31 + 68);
v11 = SeCreateAccessStateEx(0, v35, &PassedAccessState, v37, DesiredAccess, (PsProcessType + 26));
if ( v11 < 0 )
goto LABEL_96;
v11 = ObInsertObject(NewProcess, &PassedAccessState, DesiredAccess, 1u, 0, &Handle);
v53 = v11;
SeDeleteAccessState(&PassedAccessState);
if ( v11 >= 0 )
{
NewProcess->GrantedAccess = 1;
PsSetProcessPriorityByClass(NewProcess, 0);
if ( !ParentProcess_1 || ParentProcess_1 == PsInitialSystemProcess )
{
NewProcess->GrantedAccess = 2035711;
}
else
{
v11 = ObGetObjectSecurity(NewProcess, &SecurityDescriptor, MemoryAllocated);
v53 = v11;
if ( v11 < 0 )
{
ObCloseHandle(Handle, AccessMode[0]);
goto LABEL_96;
}
SubjectSecurityContext.ProcessAuditId = NewProcess;
SubjectSecurityContext.PrimaryToken = PsReferencePrimaryToken(&NewProcess->Pcb);
SubjectSecurityContext.ClientToken = 0;
v62 = SeAccessCheck(
SecurityDescriptor,
&SubjectSecurityContext,
0,
0x2000000u,
0,
0,
(PsProcessType + 26),
AccessMode[0],
&NewProcess->GrantedAccess,
&AccessStatus);
ObFastDereferenceObject(&NewProcess->Token, SubjectSecurityContext.PrimaryToken);
ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated[0]);
if ( !v62 )
NewProcess->GrantedAccess = 0;
NewProcess->GrantedAccess |= 0x1F07FBu;
}
KeQuerySystemTime(&NewProcess->CreateTime);
*ProcessHandle = Handle;
ms_exc.registration.TryLevel = -1;
if ( v58 )
v11 = v58;
goto LABEL_96;
}
LABEL_97:
if ( ParentProcess_1 )
ObfDereferenceObject(ParentProcess_1);
return v11;
}
我们都知道,创建进程的时候一定会创建一个线程,被称之为主线程。上面都是初始化进程结构体,创建TEB
等操作,线程在哪里创建的呢?是因为这个是在3环调用的,我们来看看3环长啥样子:
BOOL __stdcall CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
return CreateProcessInternalW(
0,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
0);
}
这个函数又调用了CreateProcessInternalW
这个函数,我们来继续点击去看看,创建线程的函数就在这里面,但是为了节省篇幅,我们就给出了局部代码:
BaseInitializeContext(&ThreadContext, v157, SectionInformation, UserStack.StackBase, 0);
v161 = BaseFormatObjectAttributes(&ObjectAttributes, v141, 0);
if ( v184 && v160 && v141 )
{
DirectoryInfo.CurDirRef = v141->nLength;
v200 = v141->lpSecurityDescriptor;
v201 = v141->bInheritHandle;
v200 = 0;
v161 = BaseFormatObjectAttributes(&ObjectAttributes, &DirectoryInfo.CurDirRef, 0);
}
v20 = NtCreateThread(
&ThreadHandle,
0x1F03FFu,
v161,
ProcessHandle,
&ClientId,
&ThreadContext,
&UserStack,
1u);
进程结束浅析
结束函数没啥可分析了,给出如下IDA
伪代码:
NTSTATUS __stdcall NtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v2 = KeGetCurrentThread();
v3 = v2;
v8 = v2->ApcState.Process;
if ( ProcessHandle )
{
v11 = 1;
}
else
{
ProcessHandle = -1;
v11 = 0;
}
LOBYTE(AccessMode) = v2->PreviousMode;
result = ObReferenceObjectByHandle(ProcessHandle, 1u, PsProcessType, AccessMode, &AccessMode, 0);
v5 = AccessMode;
v6 = AccessMode;
if ( result >= 0 )
{
ProcessHandlea = &AccessMode[146];
if ( (AccessMode[146].Count & 0x2000) != 0 )
PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", AccessMode, &AccessMode[93]);
RunRef = v5 + 32;
if ( ExAcquireRundownProtection(v5 + 32) )
{
if ( v11 )
_InterlockedOr(ProcessHandlea, 8u);
ProcessHandleb = 290;
v7 = PsGetNextProcessThread(v6, 0);
if ( v7 )
{
ProcessHandleb = 0;
do
{
if ( v7 != v3 )
PspTerminateThreadByPointer(v7, ExitStatus);
v7 = PsGetNextProcessThread(v6, v7);
}
while ( v7 );
}
ExReleaseRundownProtection(RunRef);
if ( v6 == v8 )
{
if ( v11 )
{
ObfDereferenceObject(v6);
PspTerminateThreadByPointer(v3, ExitStatus);
}
}
else if ( ExitStatus == DBG_TERMINATE_PROCESS )
{
DbgkClearProcessDebugObject(v6, 0);
}
if ( ProcessHandleb == 290 || v6[1].ThreadListHead.Flink && v11 )
{
ObClearProcessHandleTable(v6);
ProcessHandleb = 0;
}
ObfDereferenceObject(v6);
result = ProcessHandleb;
}
else
{
ObfDereferenceObject(v5);
result = STATUS_PROCESS_IS_TERMINATING;
}
}
return result;
}
可以说,结束进程,也就是把它的所有的线程全部干掉,删掉进程相关记录,进程就被杀死了。
进程线程结构体扩展
之前介绍进程线程相关结构体的时候主要介绍关键的成员,但是有些成员是在说明信息的时候还是比较重要的,或者没啥用处做个记录的,这里再重新补充一下,仅供了解:
KPROCESS
其结构体如下所示:
kd> dt _KPROCESS
ntdll!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER
+0x010 ProfileListHead : _LIST_ENTRY
+0x018 DirectoryTableBase : [2] Uint4B
+0x020 LdtDescriptor : _KGDTENTRY
+0x028 Int21Descriptor : _KIDTENTRY
+0x030 IopmOffset : Uint2B
+0x032 Iopl : UChar
+0x033 Unused : UChar
+0x034 ActiveProcessors : Uint4B
+0x038 KernelTime : Uint4B
+0x03c UserTime : Uint4B
+0x040 ReadyListHead : _LIST_ENTRY
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY
+0x04c VdmTrapcHandler : Ptr32 Void
+0x050 ThreadListHead : _LIST_ENTRY
+0x058 ProcessLock : Uint4B
+0x05c Affinity : Uint4B
+0x060 StackCount : Uint2B
+0x062 BasePriority : Char
+0x063 ThreadQuantum : Char
+0x064 AutoAlignment : UChar
+0x065 State : UChar
+0x066 ThreadSeed : UChar
+0x067 DisableBoost : UChar
+0x068 PowerState : UChar
+0x069 DisableQuantum : UChar
+0x06a IdealNode : UChar
+0x06b Flags : _KEXECUTE_OPTIONS
+0x06b ExecuteOptions : UChar
ProfileListHead
性能分析相关,一般操作系统会自动处理,如下图所示,没啥用处:
ActiveProcessors
当前活动的处理器,表示在哪个核运行。
ReadyListHead
该进程处于就绪状态的所有进程链表。
SwapListEntry
被交换到磁盘上的进程的链表,如果进程被交换出去就会挂到这里。
ThreadListHead
当前进程下的所有线程的链表。
ProcessLock
进程锁,用于同步,防止被同时修改用的,给操作系统用的。
ThreadQuantum
线程默认的时间碎片。
State
表示进程交换到磁盘和内存的状态。
ThreadSeed
指示Affinity
陈述的亲核性最理想亲的核。
EPROCESS
其结构体如下所示:
kd> dt _EPROCESS
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE_X86
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB
+0x1b4 PrefetchTrace : _EX_FAST_REF
+0x1b8 ReadOperationCount : _LARGE_INTEGER
+0x1c0 WriteOperationCount : _LARGE_INTEGER
+0x1c8 OtherOperationCount : _LARGE_INTEGER
+0x1d0 ReadTransferCount : _LARGE_INTEGER
+0x1d8 WriteTransferCount : _LARGE_INTEGER
+0x1e0 OtherTransferCount : _LARGE_INTEGER
+0x1e8 CommitChargeLimit : Uint4B
+0x1ec CommitChargePeak : Uint4B
+0x1f0 AweInfo : Ptr32 Void
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
+0x238 LastFaultCount : Uint4B
+0x23c ModifiedPageCount : Uint4B
+0x240 NumberOfVads : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 HasPhysicalVad : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x248 Unused3 : Pos 22, 1 Bit
+0x248 Unused4 : Pos 23, 1 Bit
+0x248 VdmAllowed : Pos 24, 1 Bit
+0x248 Unused : Pos 25, 5 Bits
+0x248 Unused1 : Pos 30, 1 Bit
+0x248 Unused2 : Pos 31, 1 Bit
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x255 WorkingSetAcquiredUnsafe : UChar
+0x258 Cookie : Uint4B
RundownProtect
进程停运保护,可以防止他人杀死进程。
SessionProcessLinks
进程的会话子系统相关。
Token
该进程的令牌,与安全相关。
InheritedFromUniqueProcessId
指示被谁创建该进程的pid
,这东西比较有用,可以找到父进程。
SeAuditProcessCreationInfo
通过这个可以获取进程的全路径,我们举个例子:
kd> dt _EPROCESS 89a9b648
ntdll!_EPROCESS
……
+0x1f0 AweInfo : (null)
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
……
kd> dx -id 0,0,805539a0 -r1 (*((ntdll!_SE_AUDIT_PROCESS_CREATION_INFO *)0x89a9b83c))
(*((ntdll!_SE_AUDIT_PROCESS_CREATION_INFO *)0x89a9b83c)) [Type: _SE_AUDIT_PROCESS_CREATION_INFO]
[+0x000] ImageFileName : 0x89cc8a18 [Type: _OBJECT_NAME_INFORMATION *]
kd> dx -id 0,0,805539a0 -r1 ((ntdll!_OBJECT_NAME_INFORMATION *)0x89cc8a18)
((ntdll!_OBJECT_NAME_INFORMATION *)0x89cc8a18) : 0x89cc8a18 [Type: _OBJECT_NAME_INFORMATION *]
[+0x000] Name : "\Device\HarddiskVolume1\Program Files\PalmInput\Extensions\Guard\2.6.0.49\PalmInputGuard.exe" [Type: _UNICODE_STRING]
Flags
指示进程的状态,比较有用。
SubSystemMinorVersion / SubSystemMajorVersion
指示支持的子系统版本,在PE
文件的信息中是有描述的。
ExitStatus
进程退出状态。
ReadOperationCount
调用ReadFile
的次数。
WriteOperationCount
调用WriteFile
的次数。
OtherOperationCount
调用其他与IO
读写文件相关的API
次数。
KTHREAD
其结构体如下所示:
kd> dt _KTHREAD
ntdll!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 Teb : Ptr32 Void
+0x024 TlsArray : Ptr32 Void
+0x028 KernelStack : Ptr32 Void
+0x02c DebugActive : UChar
+0x02d State : UChar
+0x02e Alerted : [2] UChar
+0x030 Iopl : UChar
+0x031 NpxState : UChar
+0x032 Saturation : Char
+0x033 Priority : Char
+0x034 ApcState : _KAPC_STATE
+0x04c ContextSwitches : Uint4B
+0x050 IdleSwapBlock : UChar
+0x051 Spare0 : [3] UChar
+0x054 WaitStatus : Int4B
+0x058 WaitIrql : UChar
+0x059 WaitMode : Char
+0x05a WaitNext : UChar
+0x05b WaitReason : UChar
+0x05c WaitBlockList : Ptr32 _KWAIT_BLOCK
+0x060 WaitListEntry : _LIST_ENTRY
+0x060 SwapListEntry : _SINGLE_LIST_ENTRY
+0x068 WaitTime : Uint4B
+0x06c BasePriority : Char
+0x06d DecrementCount : UChar
+0x06e PriorityDecrement : Char
+0x06f Quantum : Char
+0x070 WaitBlock : [4] _KWAIT_BLOCK
+0x0d0 LegoData : Ptr32 Void
+0x0d4 KernelApcDisable : Uint4B
+0x0d8 UserAffinity : Uint4B
+0x0dc SystemAffinityActive : UChar
+0x0dd PowerState : UChar
+0x0de NpxIrql : UChar
+0x0df InitialNode : UChar
+0x0e0 ServiceTable : Ptr32 Void
+0x0e4 Queue : Ptr32 _KQUEUE
+0x0e8 ApcQueueLock : Uint4B
+0x0f0 Timer : _KTIMER
+0x118 QueueListEntry : _LIST_ENTRY
+0x120 SoftAffinity : Uint4B
+0x124 Affinity : Uint4B
+0x128 Preempted : UChar
+0x129 ProcessReadyQueue : UChar
+0x12a KernelStackResident : UChar
+0x12b NextProcessor : UChar
+0x12c CallbackStack : Ptr32 Void
+0x130 Win32Thread : Ptr32 Void
+0x134 TrapFrame : Ptr32 _KTRAP_FRAME
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x140 PreviousMode : Char
+0x141 EnableStackSwap : UChar
+0x142 LargeStack : UChar
+0x143 ResourceIndex : UChar
+0x144 KernelTime : Uint4B
+0x148 UserTime : Uint4B
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : UChar
+0x165 ApcStateIndex : UChar
+0x166 ApcQueueable : UChar
+0x167 AutoAlignment : UChar
+0x168 StackBase : Ptr32 Void
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY
+0x1b8 FreezeCount : Char
+0x1b9 SuspendCount : Char
+0x1ba IdealProcessor : UChar
+0x1bb DisableBoost : UChar
MutantListHead
互斥体链表相关,具体细节在同步篇进行讲解。
Alertable
指示线程是否被唤醒,具体细节在APC
篇进行讲解。
Priority
线程优先级。
EnableStackSwap
指示是否能将堆栈最为文件交换
ETHREAD
结构体如下所示:
kd> dt _ETHREAD
ntdll!_ETHREAD
+0x000 Tcb : _KTHREAD
+0x1c0 CreateTime : _LARGE_INTEGER
+0x1c0 NestedFaultCount : Pos 0, 2 Bits
+0x1c0 ApcNeeded : Pos 2, 1 Bit
+0x1c8 ExitTime : _LARGE_INTEGER
+0x1c8 LpcReplyChain : _LIST_ENTRY
+0x1c8 KeyedWaitChain : _LIST_ENTRY
+0x1d0 ExitStatus : Int4B
+0x1d0 OfsChain : Ptr32 Void
+0x1d4 PostBlockList : _LIST_ENTRY
+0x1dc TerminationPort : Ptr32 _TERMINATION_PORT
+0x1dc ReaperLink : Ptr32 _ETHREAD
+0x1dc KeyedWaitValue : Ptr32 Void
+0x1e0 ActiveTimerListLock : Uint4B
+0x1e4 ActiveTimerListHead : _LIST_ENTRY
+0x1ec Cid : _CLIENT_ID
+0x1f4 LpcReplySemaphore : _KSEMAPHORE
+0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
+0x208 LpcReplyMessage : Ptr32 Void
+0x208 LpcWaitingOnPort : Ptr32 Void
+0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
+0x210 IrpList : _LIST_ENTRY
+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS
+0x224 StartAddress : Ptr32 Void
+0x228 Win32StartAddress : Ptr32 Void
+0x228 LpcReceivedMessageId : Uint4B
+0x22c ThreadListEntry : _LIST_ENTRY
+0x234 RundownProtect : _EX_RUNDOWN_REF
+0x238 ThreadLock : _EX_PUSH_LOCK
+0x23c LpcReplyMessageId : Uint4B
+0x240 ReadClusterSize : Uint4B
+0x244 GrantedAccess : Uint4B
+0x248 CrossThreadFlags : Uint4B
+0x248 Terminated : Pos 0, 1 Bit
+0x248 DeadThread : Pos 1, 1 Bit
+0x248 HideFromDebugger : Pos 2, 1 Bit
+0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
+0x248 SystemThread : Pos 4, 1 Bit
+0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
+0x248 BreakOnTermination : Pos 6, 1 Bit
+0x248 SkipCreationMsg : Pos 7, 1 Bit
+0x248 SkipTerminationMsg : Pos 8, 1 Bit
+0x24c SameThreadPassiveFlags : Uint4B
+0x24c ActiveExWorker : Pos 0, 1 Bit
+0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit
+0x24c MemoryMaker : Pos 2, 1 Bit
+0x250 SameThreadApcFlags : Uint4B
+0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit
+0x250 LpcExitThreadCalled : Pos 1, 1 Bit
+0x250 AddressSpaceOwner : Pos 2, 1 Bit
+0x254 ForwardClusterOnly : UChar
+0x255 DisablePageFaultClustering : UChar
CrossThreadFlags
表示线程的状态和身份,可以设置状态为系统线程可以使普通权限杀不死。
线程创建浅析
之前我们分析创建进程的时候,是通过NtCreateThread
这个函数进行创建主线程,我们来看看里面有什么:
NTSTATUS __stdcall NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext, PINITIAL_TEB UserStack, BOOLEAN CreateSuspended)
{
struct _INITIAL_TEB *v8; // eax
int *v9; // ebx
int v11; // ecx
int v12[6]; // [esp+Ch] [ebp-38h] BYREF
_KTHREAD *v13; // [esp+24h] [ebp-20h]
CPPEH_RECORD ms_exc; // [esp+2Ch] [ebp-18h]
ms_exc.registration.TryLevel = 0;
v13 = KeGetCurrentThread();
if ( v13->PreviousMode )
{
if ( ThreadHandle >= MmUserProbeAddress )
*MmUserProbeAddress = 0;
*ThreadHandle = *ThreadHandle;
if ( ClientId )
{
v12[5] = ClientId;
if ( ClientId >= MmUserProbeAddress )
*MmUserProbeAddress = 0;
if ( (ClientId & 3) != 0 )
ExRaiseDatatypeMisalignment();
LOBYTE(ClientId->UniqueProcess) = ClientId->UniqueProcess;
LOBYTE(ClientId->UniqueThread) = ClientId->UniqueThread;
}
if ( !ThreadContext )
{
ms_exc.registration.TryLevel = -1;
return STATUS_INVALID_PARAMETER;
}
if ( (ThreadContext & 3) != 0 )
ExRaiseDatatypeMisalignment();
v8 = MmUserProbeAddress;
if ( ThreadContext >= MmUserProbeAddress )
{
*MmUserProbeAddress = 0;
v8 = MmUserProbeAddress;
}
v9 = UserStack;
if ( (UserStack & 3) != 0 )
ExRaiseDatatypeMisalignment();
if ( UserStack >= v8 )
v8->PreviousStackBase = 0;
}
else
{
v9 = UserStack;
}
v12[0] = *v9;
v11 = v9[1];
v12[1] = v11;
if ( !v12[0] && !v11 )
qmemcpy(v12, v9, 0x14u);
ms_exc.registration.TryLevel = -1;
return PspCreateThread(
ThreadHandle,
DesiredAccess,
ObjectAttributes,
ProcessHandle,
0,
ClientId,
ThreadContext,
v12,
CreateSuspended,
0,
0);
}
浏览一遍,发现最终是通过PspCreateThread
这个函数进行创建线程实现,点击去看看:
🔒 点击查看伪代码 🔒
MACRO_STATUS __stdcall PspCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PVOID a5, PCLIENT_ID ClientId, PCONTEXT ThreadContext, int a8, BOOLEAN CreateSuspended, int a10, int a11)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
CurrentThread = KeGetCurrentThread();
CurrentThread_1 = CurrentThread;
if ( a10 )
AccessMode[0] = 0;
else
AccessMode[0] = CurrentThread->PreviousMode;
v70 = 0;
v12 = 0;
v56 = 0;
if ( ProcessHandle )
{
result = ObReferenceObjectByHandle(ProcessHandle, 2u, PsProcessType, AccessMode[0], &Object, 0);
v12 = Object;
v56 = Object;
}
else if ( a10 )
{
v12 = a5;
ObfReferenceObject(a5);
v56 = a5;
result = STATUS_WAIT_0;
}
else
{
result = STATUS_INVALID_HANDLE;
}
if ( result >= STATUS_WAIT_0 )
{
if ( AccessMode[0] && v12 == PsInitialSystemProcess )
{
v14 = STATUS_INVALID_HANDLE;
LABEL_15:
ObfDereferenceObject(v12);
return v14;
}
v15 = ObCreateObject(AccessMode[0], PsThreadType, ObjectAttributes, *AccessMode, 0, 600, 0, 0, &v59);
if ( v15 < 0 )
{
v14 = v15;
goto LABEL_15;
}
thread = v59;
memset(v59, 0, 0x258u);
thread->RundownProtect.Count = 0;
thread->ThreadsProcess = v12;
thread->Cid.UniqueProcess = v12->UniqueProcessId;
v60[0] = thread;
v60[1] = 0;
v17 = ExCreateHandle(PspCidTable, v60);
thread->Cid.UniqueThread = v17;
if ( !v17 )
{
v18 = STATUS_INSUFFICIENT_RESOURCES;
LABEL_38:
ObfDereferenceObject(thread);
return v18;
}
thread->ReadClusterSize = MmReadClusterSize;
KeInitializeSemaphore(&thread->LpcReplySemaphore, 0, 1);
thread->ExitTime.HighPart = &thread->456;
thread->ExitTime.LowPart = &thread->456;
thread->IrpList.Blink = &thread->IrpList;
thread->IrpList.Flink = &thread->IrpList;
thread->PostBlockList.Blink = &thread->PostBlockList;
thread->PostBlockList.Flink = &thread->PostBlockList;
thread->ThreadLock.Value = 0;
KeInitializeSpinLock(&thread->ActiveTimerListLock);
thread->ActiveTimerListHead.Blink = &thread->ActiveTimerListHead;
thread->ActiveTimerListHead.Flink = &thread->ActiveTimerListHead;
v45 = &v12->RundownProtect;
if ( !ExAcquireRundownProtection(&v12->RundownProtect) )
{
v18 = STATUS_PROCESS_IS_TERMINATING;
goto LABEL_38;
}
if ( ThreadContext )
{
v18 = MmCreateTeb(&v12->Pcb, a8, &thread->Cid, &v70);
if ( v18 < 0 )
{
v19 = v45;
LABEL_37:
ExReleaseRundownProtection(v19);
goto LABEL_38;
}
ms_exc.registration.TryLevel = 0;
thread->StartAddress = ThreadContext->Eip;
thread->LpcReceivedMessageId = ThreadContext->Eax;
ms_exc.registration.TryLevel = -1;
v20 = KeInitThread(thread, 0, PspUserThreadStartup, 0, thread->StartAddress, ThreadContext, v70, v12);
}
else
{
v70 = 0;
_InterlockedOr(&thread->584, 0x10u);
thread->StartAddress = a10;
v20 = KeInitThread(thread, 0, PspSystemThreadStartup, a10, a11, 0, 0, v12);
}
v18 = v20;
if ( v20 < 0 )
{
if ( v70 )
{
MmDeleteTeb(&v12->Pcb, v70);
thread->Tcb.Teb = 0;
}
LABEL_36:
v19 = &v12->RundownProtect;
goto LABEL_37;
}
v21 = CurrentThread_1;
--CurrentThread_1->KernelApcDisable;
v53 = &v12->ProcessLock;
_ECX = &v12->ProcessLock;
_EDX = 2;
__asm { cmpxchg [ecx], edx }
if ( (v12->Flags & 8) != 0 )
{
v50 = &v12->ProcessLock;
_ECX = &v12->ProcessLock;
_EDX = 0;
__asm { cmpxchg [ecx], edx }
v26 = (*(v21 + 212))++ == -1;
if ( v26 && *(v21 + 52) != v21 + 52 )
{
*(v21 + 73) = 1;
LOBYTE(_ECX) = 1;
HalRequestSoftwareInterrupt(_ECX);
}
KeUninitThread(thread);
if ( v70 )
MmDeleteTeb(&v12->Pcb, v70);
v18 = STATUS_PROCESS_IS_TERMINATING;
goto LABEL_36;
}
v27 = v12->ActiveThreads;
v12->ActiveThreads = v27 + 1;
v28 = v12->ThreadListHead.Blink;
thread->ThreadListEntry.Flink = &v12->ThreadListHead;
thread->ThreadListEntry.Blink = v28;
v28->Flink = &thread->ThreadListEntry;
v12->ThreadListHead.Blink = &thread->ThreadListEntry;
KeStartThread(thread);
v48 = &v12->ProcessLock;
_ECX = &v12->ProcessLock;
_EDX = 0;
__asm { cmpxchg [ecx], edx }
v31 = CurrentThread_1;
v26 = CurrentThread_1->KernelApcDisable++ == -1;
if ( v26 && *(v31 + 52) != v31 + 52 )
{
CurrentThread_1->ApcState.KernelApcPending = 1;
LOBYTE(_ECX) = 1;
HalRequestSoftwareInterrupt(_ECX);
}
ExReleaseRundownProtection(&v12->RundownProtect);
if ( !v27 )
{
WmiTraceProcess(&v12->Pcb);
if ( PspCreateProcessNotifyRoutineCount )
{
v58 = &PspCreateProcessNotifyRoutine;
v51 = 8;
do
{
v32 = ExReferenceCallBackBlock(v58);
v33 = v32;
if ( v32 )
{
v34 = ExGetCallBackBlockRoutine(v32);
v34(v12->InheritedFromUniqueProcessId, v12->UniqueProcessId, 1);
ExDereferenceCallBackBlock(v58, v33);
}
v58 += 4;
--v51;
}
while ( v51 );
}
}
v35 = v12->Job;
if ( v35 && *(v35 + 196) && (v12->JobStatus & 5) == 0 )
{
_InterlockedOr(&v12->JobStatus, 4u);
--CurrentThread_1->KernelApcDisable;
Resource = (v35 + 32);
ExAcquireResourceSharedLite((v35 + 32), 1u);
v36 = *(v35 + 196);
if ( v36 )
IoSetIoCompletion(v36, *(v35 + 200), v12->UniqueProcessId, 0, 6, 0);
ExReleaseResourceLite(Resource);
v37 = CurrentThread_1;
v26 = CurrentThread_1->KernelApcDisable++ == -1;
if ( v26 && *(v37 + 52) != v37 + 52 )
{
*(v37 + 73) = 1;
LOBYTE(v37) = 1;
HalRequestSoftwareInterrupt(v37);
}
}
WmiTraceThread(thread, a8, 1);
if ( PspCreateThreadNotifyRoutineCount )
{
v57 = &PspCreateThreadNotifyRoutine;
v49 = 8;
do
{
v38 = ExReferenceCallBackBlock(v57);
RunRef = v38;
if ( v38 )
{
v39 = ExGetCallBackBlockRoutine(v38);
v39(thread->Cid.UniqueProcess, thread->Cid.UniqueThread, 1);
ExDereferenceCallBackBlock(v57, RunRef);
}
v57 += 4;
--v49;
}
while ( v49 );
}
ObReferenceObjectEx(thread, 2);
if ( ThreadContext )
{
P = ExAllocatePoolWithTag(NonPagedPool, 0x30u, 'aCsP');
if ( !P )
{
_InterlockedOr(&thread->584, 2u);
LABEL_64:
v18 = STATUS_INSUFFICIENT_RESOURCES;
LABEL_77:
KeReadyThread(thread);
ObDereferenceObjectEx(thread, 2);
return v18;
}
KeInitializeApc(P, &thread->Tcb, OriginalApcEnvironment, IopDeallocateApc, 0, dword_598B5C, 1, 0);
if ( !KeInsertQueueApc(P, BaseAddress, 0, 0) )
{
_InterlockedOr(&thread->584, 2u);
ExFreePoolWithTag(P, 0);
goto LABEL_64;
}
}
if ( CreateSuspended )
{
ms_exc.registration.TryLevel = 1;
KeSuspendThread(&thread->Tcb);
ms_exc.registration.TryLevel = -1;
if ( (thread->CrossThreadFlags & 1) != 0 )
KeForceResumeThread(&thread->Tcb);
}
if ( ThreadContext )
v40 = &CurrentThread_1->ApcState.Process->Pcb;
else
v40 = &v12->Pcb;
v63 = SeCreateAccessStateEx(0, v40, &PassedAccessState, v44, DesiredAccess, (PsThreadType + 26));
if ( v63 < 0 )
{
_InterlockedOr(&thread->584, 2u);
if ( CreateSuspended )
KeResumeThread(thread);
v18 = v63;
goto LABEL_77;
}
v63 = ObInsertObject(thread, &PassedAccessState, DesiredAccess, 0, 0, &Handle);
SeDeleteAccessState(&PassedAccessState);
if ( v63 >= 0 )
{
ms_exc.registration.TryLevel = 2;
*ThreadHandle = Handle;
if ( ClientId )
*ClientId = thread->Cid;
ms_exc.registration.TryLevel = -1;
}
else
{
_InterlockedOr(&thread->584, 2u);
if ( CreateSuspended )
KeResumeThread(thread);
}
KeQuerySystemTime(&CurrentTime);
v41 = CurrentTime.QuadPart >> 29;
thread->CreateTime.LowPart = 8 * CurrentTime.LowPart;
thread->CreateTime.HighPart = v41;
if ( (thread->CrossThreadFlags & 2) != 0 )
{
thread->GrantedAccess = 2032639;
}
else
{
v63 = ObGetObjectSecurity(thread, &SecurityDescriptor, MemoryAllocated);
if ( v63 < 0 )
{
_InterlockedOr(&thread->584, 2u);
if ( CreateSuspended )
KeResumeThread(thread);
KeReadyThread(thread);
ObfDereferenceObject(thread);
ObCloseHandle(Handle, AccessMode[0]);
goto LABEL_95;
}
SubjectSecurityContext.ProcessAuditId = v12;
SubjectSecurityContext.PrimaryToken = PsReferencePrimaryToken(&v12->Pcb);
SubjectSecurityContext.ClientToken = 0;
v42 = &thread->GrantedAccess;
v64 = SeAccessCheck(
SecurityDescriptor,
&SubjectSecurityContext,
0,
0x2000000u,
0,
0,
(PsThreadType + 26),
AccessMode[0],
&thread->GrantedAccess,
&AccessStatus);
ObFastDereferenceObject(&v12->Token, SubjectSecurityContext.PrimaryToken);
ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated[0]);
if ( !v64 )
*v42 = 0;
*v42 |= 0x61u;
}
KeReadyThread(thread);
ObfDereferenceObject(thread);
LABEL_95:
result = v63;
}
return result;
}
流程和进程线程创建了流程套路几乎一样,初始化线程结构体,创建TEB
,插入结构体,但里面有APC
相关的知识,具体细节就不再赘述了。
DPC
DPC
是什么?它的英文全称为Deferred Procedure Call
,即延迟过程调用。它最初作用是设计为中断服务程序的一部分,用来解决中断服务处理时间过长的问题。因为每次触发中断,都会关中断,然后执行中断服务例程。由于关中断了,所以中断服务例程必须短小精悍,不能消耗过多时间,否则会导致系统丢失大量其他中断。但是有的中断,其中断服务例程要做的事情本来就很多,那怎么办?于是,可以在中断服务例程中先执行最紧迫的那部分工作,然后把剩余的相对来说不那么重要的工作移入到DPC
函数中去执行。
每当触发一个中断时,中断服务例程可以在当前CPU
中插入一个DPC
,当执行完ISR
,退出ISR
后, CPU
就会扫描它的DPC
队列,依次执行里面的每个DPC
,当执行完DPC
后,才又回到当前线程的中断处继续执行。
既然提到ISR
,那么它是什么?它的英文全称为Interrupt Service Routines
,即中断服务处理。在Windows
中如果有认为不太重要的操作会被打包成一个KDPC
结构体,如下所示:
kd> dt _KDPC
ntdll!_KDPC
+0x000 Type : Int2B
+0x002 Number : UChar
+0x003 Importance : UChar
+0x004 DpcListEntry : _LIST_ENTRY
+0x00c DeferredRoutine : Ptr32 void
+0x010 DeferredContext : Ptr32 Void
+0x014 SystemArgument1 : Ptr32 Void
+0x018 SystemArgument2 : Ptr32 Void
+0x01c Lock : Ptr32 Uint4B
打包完毕后,会插入到KPCRB
的DpcListHead
成员中,等待触发调用时机。KPCRB
结构如下所示,只展示与DPC
相关的成员:
kd> dt _KPRCB
ntdll!_KPRCB
……
+0x4b0 DpcTime : Uint4B
+0x4b4 DebugDpcTime : Uint4B
……
+0x860 DpcListHead : _LIST_ENTRY
+0x868 DpcStack : Ptr32 Void
+0x86c DpcCount : Uint4B
+0x870 DpcQueueDepth : Uint4B
+0x874 DpcRoutineActive : Uint4B
+0x878 DpcInterruptRequested : Uint4B
+0x87c DpcLastCount : Uint4B
+0x880 DpcRequestRate : Uint4B
……
+0x8a0 DpcLock : Uint4B
……
+0x8c0 CallDpc : _KDPC
……
非时钟中断的中断、时钟中断、主动调用API
,相关函数会去轮询DPC
链表进行回调。对于用户或者驱动创建的DPC
,还可以加定时器,到指定时间进行触发,但不会挂到KPCR
当中,会挂到时钟任务当中。
介绍了上面的概念,我们来介绍结构体的相关成员含义:
KDPC
Type
指明该结构体的类型,值为0x13
。
Number
指明属于哪个KPCR
。
Importance
优先级,取值0-2
,0最低,2最高,优先级越高越优先执行,初始化默认值为1。
DpcListEntry
DPC
链表,和进程线程链表一样,挂到腰上。
DeferredRoutine
DPC
的回调函数地址。
DeferredContext
回调函数上下文,非必须。
SystemArgument1 / SystemArgument2
回调函数的参数,非必须。
Lock
DPC
结构体的锁。
DPC 的初始化
学习DPC
,首先我们了解一下它的初始化,我们用IDA
定位到该函数:
; void __stdcall KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext)
public _KeInitializeDpc@12
_KeInitializeDpc@12 proc near ; CODE XREF: IopInitializeIrpStackProfiler()+29↑p
; VdmpDelayInterrupt(x)+26B↓p ...
Dpc = dword ptr 8
DeferredRoutine = dword ptr 0Ch
DeferredContext = dword ptr 10h
mov edi, edi
push ebp
mov ebp, esp
mov eax, [ebp+Dpc]
mov ecx, [ebp+DeferredRoutine]
and dword ptr [eax+1Ch], 0
mov [eax+0Ch], ecx
mov ecx, [ebp+DeferredContext]
mov word ptr [eax], 13h
mov byte ptr [eax+2], 0
mov byte ptr [eax+3], 1
mov [eax+10h], ecx
pop ebp
retn 0Ch
_KeInitializeDpc@12 endp
这个函数十分简单,用伪代码展示如下:
void __stdcall KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext)
{
Dpc->Lock = 0;
Dpc->DeferredRoutine = DeferredRoutine;
Dpc->Type = 19;
Dpc->Number = 0;
Dpc->Importance = 1;
Dpc->DeferredContext = DeferredContext;
}
DPC 的插入
然后我们来看看DPC
的插入流程,由于涉及IRQL
提权操作,我们先看一下枚举:
为了增强可读性,如下是我重命名好的IDA
的伪代码:
BOOLEAN __stdcall KeInsertQueueDpc(PRKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2)
{
_KPRCB *kprcb; // esi
bool Importance; // zf
_LIST_ENTRY *DpcListHead; // ecx
_LIST_ENTRY *DpcListEntry; // eax
_LIST_ENTRY *v9; // edx
_LIST_ENTRY *v10; // edx
KIRQL NewIrql; // [esp+Fh] [ebp-1h]
NewIrql = KfRaiseIrql(0x1Fu);
kprcb = MEMORY[0xFFDFF020];
_ECX = &Dpc->Lock;
_EDX = MEMORY[0xFFDFF020] + 0x8A0; // DpcLock
__asm { cmpxchg [ecx], edx }
++kprcb->DpcCount;
++kprcb->DpcQueueDepth;
Importance = Dpc->Importance == 2;
Dpc->SystemArgument1 = SystemArgument1;
Dpc->SystemArgument2 = SystemArgument2;
DpcListHead = &kprcb->DpcListHead;
DpcListEntry = &Dpc->DpcListEntry;
if ( Importance )
{
v9 = DpcListHead->Flink;
DpcListEntry->Flink = DpcListHead->Flink;
Dpc->DpcListEntry.Blink = DpcListHead;
v9->Blink = DpcListEntry;
DpcListHead->Flink = DpcListEntry;
}
else
{
v10 = kprcb->DpcListHead.Blink;
DpcListEntry->Flink = DpcListHead;
Dpc->DpcListEntry.Blink = v10;
v10->Flink = DpcListEntry;
kprcb->DpcListHead.Blink = DpcListEntry;
}
if ( !kprcb->DpcRoutineActive
&& !kprcb->DpcInterruptRequested
&& (Dpc->Importance
|| kprcb->DpcQueueDepth >= kprcb->MaximumDpcQueueDepth
|| kprcb->DpcRequestRate < kprcb->MinimumDpcRate) )
{
LOBYTE(DpcListHead) = 2;
kprcb->DpcInterruptRequested = 1;
HalRequestSoftwareInterrupt(DpcListHead);
}
KfLowerIrql(NewIrql);
return 1;
}
可以看出,如果DPC
的优先级高,就会插到DPC
链表的前面,如果低就会插到末尾。在多核的情况下,其伪代码会有所不一样,如下所示:
BOOLEAN __stdcall KeInsertQueueDpc(PRKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2)
{
unsigned __int8 Number; // al
_KPRCB *kpcrb; // esi
char v5; // bl
bool v6; // zf
_LIST_ENTRY *v7; // ecx
_LIST_ENTRY *v8; // eax
_LIST_ENTRY *v9; // edx
_LIST_ENTRY *v10; // edx
signed __int32 v12; // [esp+Ch] [ebp-8h]
KIRQL NewIrql; // [esp+13h] [ebp-1h]
NewIrql = KfRaiseIrql(0x1Fu);
Number = Dpc->Number;
if ( Number < 0x20u )
{
v5 = Dpc;
kpcrb = KeGetPcr()->Prcb;
}
else
{
kpcrb = KiProcessorBlock[Number];
v5 = Number - 32;
}
KiAcquireSpinLock();
v12 = _InterlockedCompareExchange(&Dpc->Lock, &kpcrb->DpcLock, 0);
if ( !v12 )
{
++kpcrb->DpcCount;
++kpcrb->DpcQueueDepth;
v6 = Dpc->Importance == 2;
Dpc->SystemArgument1 = SystemArgument1;
Dpc->SystemArgument2 = SystemArgument2;
v7 = &kpcrb->DpcListHead;
v8 = &Dpc->DpcListEntry;
if ( v6 )
{
v9 = v7->Flink;
v8->Flink = v7->Flink;
Dpc->DpcListEntry.Blink = v7;
v9->Blink = v8;
v7->Flink = v8;
}
else
{
v10 = kpcrb->DpcListHead.Blink;
v8->Flink = v7;
Dpc->DpcListEntry.Blink = v10;
v10->Flink = v8;
kpcrb->DpcListHead.Blink = v8;
}
if ( !kpcrb->DpcRoutineActive && !kpcrb->DpcInterruptRequested )
{
if ( kpcrb == KeGetPcr()->Prcb )
{
if ( Dpc->Importance
|| kpcrb->DpcQueueDepth >= kpcrb->MaximumDpcQueueDepth
|| kpcrb->DpcRequestRate < kpcrb->MinimumDpcRate )
{
LOBYTE(v7) = 2;
kpcrb->DpcInterruptRequested = 1;
HalRequestSoftwareInterrupt(v7); //处理 DPC
}
}
else if ( Dpc->Importance == 2 || kpcrb->DpcQueueDepth >= kpcrb->MaximumDpcQueueDepth )
{
kpcrb->DpcInterruptRequested = 1;
KiIpiSend(1 << v5, 2u);
}
}
}
KiReleaseSpinLock(&kpcrb->DpcLock);
KfLowerIrql(NewIrql);
return v12 == 0;
}
这里提一句,想要获取多核调试环境极其内核文件,必须把虚拟机的配置配置成多核再重新安装操作系统。但是我给的符号是单核的,多核的我也踏破铁鞋也找不到,上面的伪代码一些结构体我是通过单核的生成的,至于函数是参考资料命名的。如果您有多核的符号,希望您能够提供,感谢支持。
对于多核的情况,具体细节将会在同步篇进行讲解。含有SpinLock
函数的这个东西成为自旋锁。发现这份代码和单核的会多一些判断和一个函数KiIpiSend
,这个函数作用是通知另一个KPCR
执行DPC
,了解即可。
DPC 的移除
对于移除DPC
代码很简单,如下是其伪代码:
BOOLEAN __stdcall KeRemoveQueueDpc(PRKDPC Dpc)
{
unsigned int *Lock; // esi
_disable(); // cli
Lock = Dpc->Lock;
if ( Lock )
{
--*(Lock - 12);
RemoveEntryList(&Dpc->DpcListEntry);
Dpc->Lock = 0;
}
_enable(); // sti
return Lock != 0;
}
对于多核下,代码如下,和单核区别不大,只是加入了自旋锁:
BOOLEAN __stdcall KeRemoveQueueDpc(PRKDPC Dpc)
{
unsigned int *Lock; // edi
_disable();
Lock = Dpc->Lock;
if ( Lock )
{
KiAcquireSpinLock(Dpc->Lock);
if ( Lock == Dpc->Lock )
{
--*(Lock - 12);
RemoveEntryList(&Dpc->DpcListEntry);
Dpc->Lock = 0;
}
KiReleaseSpinLock(Lock);
}
_enable();
return Lock != 0;
}
DPC 的执行
执行DPC
是通过KiRetireDpcList
执行的,而调用该函数的是通过KiDispatchInterrupt
和KiIdleLoop
执行的,我们通过伪代码进行简单了解:
int __usercall KiRetireDpcList@<eax>(_KPCR *kpcr@<ebx>, _LIST_ENTRY *DPCListHead@<ebp>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v13 = 0;
if ( PPerfGlobalGroupMask && (*(PPerfGlobalGroupMask + 4) & 0x80) != 0 )
v13 = 1;
do
{
MEMORY[0xFFDFFBC4] = &v11; // 这里有错误,原汇编:
// mov ds:0FFDFF994h, esp
// 可能是插件的 Bug
// 指向:DpcRoutineActive
do
{
dpc = DPCListHead->Flink;
v3 = DPCListHead->Flink->Flink;
DPCListHead->Flink = v3;
v3->Blink = DPCListHead;
dpc = (dpc - 4); // 挂到腰上了,把指针指到头部
DeferredRoutine = dpc->DeferredRoutine;
SystemArgument1 = *&dpc->SystemArgument1;
DeferredContext = dpc->DeferredContext;
dpc_1 = dpc;
dpc->Lock = 0;
--kpcr->PrcbData.DpcQueueDepth;
_enable();
if ( v13 ) // 日志性能分析,忽略
{
v6 = WmiGetCpuClock(DeferredRoutine);
DeferredRoutine = v7;
v11 = v6;
v12 = v7;
}
result = (DeferredRoutine)(dpc_1, DeferredContext, SystemArgument1, HIDWORD(SystemArgument1));// 执行 DPC
if ( v13 )
result = PerfInfoLogDpc(v12, v11, SHIDWORD(v11));
_disable();
}
while ( !IsListEmpty(DPCListHead) );
kpcr->PrcbData.DpcRoutineActive = 0;
kpcr->PrcbData.DpcInterruptRequested = 0;
}
while ( !IsListEmpty(DPCListHead) );
return result;
}
DPC 的使用
既然学习了DPC
,没有应用肯定不行,我们做几个实验体验一下DPC
。我们新建一个驱动项目,怎么配置我就不说了,代码如下:
#include <ntifs.h>
#include <ntddk.h>
KDPC dpc = { 0 };
VOID DPCRoutine(_In_ struct _KDPC* Dpc, _In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2)
{
DbgPrint("DPC Running……\n");
}
NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unloaded Successfully!");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("Loaded Successfully!");
DriverObject->DriverUnload = UnloadDriver;
KeInitializeDpc(&dpc, DPCRoutine, NULL);
KeInsertQueueDpc(&dpc, NULL, NULL);
return STATUS_SUCCESS;
}
编译然后加载后,就会出现如下结果:
那么继续搞得高级点,不是驱动或者用户创建的DPC
支持定时器吗?我们来搞一下:
#include <ntifs.h>
#include <ntddk.h>
KDPC dpc = { 0 };
KTIMER timer = { 0 };
LARGE_INTEGER duringtime = { 0 };
VOID DPCRoutine(_In_ struct _KDPC* Dpc, _In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2)
{
DbgPrint("DPC Running……\n");
KeSetTimer(&timer, duringtime, &dpc);
}
VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
{
KeCancelTimer(&timer);
DbgPrint("Unloaded Successfully!");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("Loaded Successfully!");
DriverObject->DriverUnload = UnloadDriver;
KeInitializeTimer(&timer);
KeInitializeDpc(&dpc, DPCRoutine, NULL);
duringtime.QuadPart = -30 * 1000 * 1000; //负号表示相对时间,此处间隔为3秒
KeSetTimer(&timer, duringtime, &dpc);
return STATUS_SUCCESS;
}
实现的效果如下:
上面实现的就是DPC
定时器。
Windows 架构
学习系统内核,最好了解一些该系统的系统架构,示意图如下,来自潘爱民的《Windows内核原理与实现》:
如有兴趣了解其细节,请自行翻找,注意该书是基于WRK
的,里面的结构体相关或者代码相关的请以逆向结果为准。
下一篇
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15963678.html