----- stage3 在进程中创建线程-----
此时,进程对象的有关内容已经全部就绪。它就像一个空的茶壶,里面还没有茶水,所以什么都不能做。
终于,CreateProcess[房主]在用PspCreateProcess创建了进程后[茶壶],继续调用NtCreateProcess创建线程
[往茶壶里倒茶水]不过此时的茶水不凉的,它需要加热才会沸腾,才会有活力。即此时的线程创建后一直处于
暂停状态,直到整个进程被完全的初始化完毕。
CreateProcess-->NtCreateProcess-->NtCreateProcessEx-->PspCreateProcess, PspCreateProcess,它要做的事:
■进程对象中的线程计数增加
■ETHREAD被创建并初始化
■线程ID被创建
■用户进程空间中的TEB被设置
■调用KeInitThread函数来设置KTHREAD结构
■创建线程时会检查权限.对于远程创建的线程,如果没有调试权限,则创建失败
■最终,线程完整,等待它的新生
----- stage4 通知WINDOWS子系统已经创建了一个新进程-----
这时候所有的进程和线程对象都已经继续.Kernel32.dll发送消息到WINDOWS子系统,以便让它设置新的进程和线程.消息如下:
■进程和线程句柄
■Creation Flags的入口
■父进程的ID
■Flag标志,表明此程序是否有窗体,以便CSRSS决定是否创建漏斗状的鼠标,表明有程序在后台运行
■The Csrss thread block is allocated and initialized.[没怎么看明白,CSRSS咋还有新的线程块呢?]
■CreateProcess 在进程的线程表中插入新的线程
■进程的关闭等级设定为 0x280
■The new process block is inserted into the list of Windows subsystemwide processes. The per-process data
structure used by the kernel-mode part of the Windows subsystem (W32PROCESS structure) is allocated and
initialized.[没完全理解]
----- stage6 在进程和线程的context里,完成地址空间的初始化[eg.加载需要的DLL],开始执行程序-----
线程经过漫长的等待后终于开始了它的生命之旅,这要拜KiThreadStartup函数所赐.
KiThreadStartup降低IRQL[从DPC到APC],然后调用PspUserThreadStartup函数.它再降低IRQL到PASSIVE.然后...
最后,PspUserThreadStartup queues a user-mode APC to run the image loader initialization routine
(LdrInitializeThunk in Ntdll.dll). The APC will be delivered when the thread attempts to return to user mode.
// Queue the initial APC to the thread
KeRaiseIrql (APC_LEVEL, &OldIrql);
KiInitializeUserApc (PspGetBaseExceptionFrame (Thread),
PspGetBaseTrapFrame (Thread),
PspSystemDll.LoaderInitRoutine,
NULL,
PspSystemDll.DllBase,
NULL);
KeLowerIrql (PASSIVE_LEVEL);
当PspUserThreadStartup函数返回到KiThreadStartup中时,APC已经被传送出去了,LdrInitializeThunk也被调用[All threads
start with an APC at LdrInitializeThunk,它负责初始化loader,heap manager,NLS表,TLS(线程局部存储)数组,critial
section结构,然后加载任何需要的DLL,并调用DLL中的代码]
最终, 当the loader initialization返回到user mode APC dispatcher时,用户模式下的可执行文件开始真正的执行...
WOW,终于把进程相关的内容大致的写完了。偶写的这些不单单是WINDWOS INTERNALS 上的内容,还参考了前辈们的一些文章,当然还有WRK、OSREACT!
<二> 线程相关
线程的代表便是ETHREAD,它保存在系统地址空间中。TEB(线程环境块)在用户进程空间中.CSRSS为每个进程的所有线程保留一份类似的结构(这么多进程,那得多少线程啊,不晓得这些备份的结构究竟是如何存储在CSRSS中的);Win32k.sys[CSRSS和内核交互的接口]保留一份ETHREAD指向的per-thread数据结构[W32THREAD]
关键词:ETHREAD、KTHREAD、TEB[TLS/GDI/OpenGL]
形象化:
0x044
ETHREAD ---------→EPROCESS
0x000↓ ↓0x000
KTHREAD KPROCESS kernel mode
-----------------------------------------------
0x020↓ | user mode
TEB |
0x30 ↓ 0x1b0 |
PEB ←------------------ |
_ETHREAD的有用信息很多,简单了解下
nt!_ETHREAD
+0x000 Tcb : _KTHREAD // 利于系统调度和同步线程的结构体
...
+0x1ec Cid : _CLIENT_ID // 指向进程ID
...
+0x210 IrpList : _LIST_ENTRY // I/O信息 IRPS
+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS // 指向进程的EPROCESS
+0x224 StartAddress : Ptr32 Void // 线程的起始地址
+0x228 Win32StartAddress : Ptr32 Void
...
+0x238 ThreadLock : _EX_PUSH_LOCK
...
+0x248 SystemThread : Pos 4, 1 Bit
...
① 与线程相关的函数
CreateThread-->NtCreateThread-->PspCreateThread [和创建进程同出一辙.这里不详细说明了.如果您有耐心看WRK的源码的话,相信会理解函数内部的很多东西]
宏观上由CreateThread[Kernel32.dll]完成线程的创建:
■在进程的地址空间创建一个用户堆栈
■初始化线程的hardware context
struct _CONTEXT, 25 elements, 0x2cc bytes
+0x000 ContextFlags : Uint4B
+0x004 Dr0 : Uint4B
+0x008 Dr1 : Uint4B
+0x00c Dr2 : Uint4B
+0x010 Dr3 : Uint4B
+0x014 Dr6 : Uint4B
+0x018 Dr7 : Uint4B
+0x01c FloatSave : struct _FLOATING_SAVE_AREA, 9 elements, 0x70 bytes
+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B
+0x09c Edi : Uint4B
+0x0a0 Esi : Uint4B
+0x0a4 Ebx : Uint4B
+0x0a8 Edx : Uint4B
+0x0ac Ecx : Uint4B
+0x0b0 Eax : Uint4B
+0x0b4 Ebp : Uint4B
+0x0b8 Eip : Uint4B
+0x0bc SegCs : Uint4B
+0x0c0 EFlags : Uint4B
+0x0c4 Esp : Uint4B
+0x0c8 SegSs : Uint4B
+0x0cc ExtendedRegisters : [512] UChar
■创建线程对象,通知CSRSS让它进一步设置
■返回线程句柄、ID到调用者
②
---->>[抢占式的环境]量子时间[quantum]<<----
"量子时间"即是在系统检测是否有其他同优先级线程处于等待状态前当前线程运行的一小段时间。如果没有其他等待线程,系统允许当前线程继续运行一个quantum.
后面讲的都是关于线程优先级、线程调度和系统的算法问题,这些基本都是些原理性的东西,没必要翻译。而且N多关于操作系统的书上对这些问题都有非常详细的描述。偶就不扯淡了~~~
---->>中断等级[IRQL] VS 线程优先级<<----
用户模式中的线程运行的IRQL是PASSIVE[0]和APC[1].所以无论它的优先级多高都不会产生硬件中断;
内核模式中的线程可以增加IRQL.如图:
<三> Jobs
JOB内核对象有名字、可共享得到,用来控制一个或多个进程。把他们组合形成一个group。它的主要功能是保证多个进程被统一管理操作。一个进程只能属于一个job对象。
偶觉得job实在没啥好讲的。略过~~~~
------------------------------- <over><写的很仓促,不正确的地方还请指出>-----------------------------------------