邪恶八进制信息安全团队技术讨论组's Archiver

sudami 2007-12-8 17:58

[翻译]Windows Internals---Processes,Threads,Jobs

译文作者:sudami
信息来源:邪恶八进制信息安全团队([url]www.eviloctal.com[/url])
文章备注:此文不仅仅是翻译,其中有部分为译者自己的总结,写的很烂,共勉之

+++++++++++++++++++++++++++++
+  sudami [[email]xiao_rui_119@163.com[/email]] +
+  2007.12.07                 +
+  英文翻译&学习笔记           +
+++++++++++++++++++++++++++++

这是Windows Internals第6章的内容。发现描述的还算详细。读了总比不读强。于是纪录之,老鸟飘过[s:267]----

                            <一> 进程相关
EPROCESS算是进程的代表,它还保留着与之相关的其他信息。进程是死的,其中的线程才是活的。进程就好比一个容器,装着很多活跃的线程而已[没有线程的进程注定要死亡,因为它没有活力]。对了,还有PEB、TEB[这2个结构存在于用户进程空间中,其他的都在系统的高2GB地址范围里] 。见图:
[attach]10815[/attach]

① 明确KPCR、KPRCB、ETHREAD、KTHREAD、EPROCESS、KPROCESS、TEB、PEB ----

[b]KPCR[/b](Kernel&#39;s Processor Control Region,[b]内核进程控制区域[/b])是一个不会随WINDOWS版本变动而改变的固定结构体,在它的末尾[偏移[b]0x120[/b]]指向[b]KPRCB[/b]结构。
nt!_KPCR
  +0x000 NtTib      : _NT_TIB
  +0x01c SelfPcr     : Ptr32 _KPCR
  +0x020 Prcb       : Ptr32 _KPRCB
  +0x024 Irql       : UChar
  +0x028 IRR       : Uint4B
  +0x02c IrrActive    : Uint4B
  +0x030 IDR       : Uint4B
  +0x034 KdVersionBlock  : Ptr32 Void
  +0x038 IDT       : Ptr32 _KIDTENTRY
  +0x03c GDT       : Ptr32 _KGDTENTRY
  +0x040 TSS       : Ptr32 _KTSS
  ...// 省略
[color=#ff0000]  +0x120 PrcbData     : _KPRCB[/color]
[color=#ff0000][/color]

[b]KPRCB[/b]同样是一个不会随WINDOWS版本变动而改变的固定结构体。它包含有指向当前[b]KTHREAD[/b]的指针,偏移值[b]0x004[/b]。其实也就是知道了当前的[b]ETHREAD[/b]基地址。[因为ETHREAD的第一项便是KTHREAD,ETHREAD在后面讨论,现在讨论进程相关] [通过 [b]KeGetCurrentPrcb()[/b] 函数即可得到PKPRCB,具体参见WRK]
[attach]10824[/attach]

展开KTHREAD,其中的_KAPC_STATE结构中包含当前KPROCESS的地址
nt!_KTHREAD
  +0x000 Header      : _DISPATCHER_HEADER
  ...
  
[color=#ff0000]+0x034 ApcState     : _KAPC_STATE[/color]
+0x034 ApcState     : struct _KAPC_STATE, 5 elements, 0x18 bytes
   +0x000 ApcListHead   : [2] struct _LIST_ENTRY, 2 elements, 0x8 bytes
   
[color=#ff0000]+0x010 Process[/color]
     : Ptr32 to struct
[color=#ff0000]_KPROCESS[/color]
, 29 elements, 0x6c bytes
   +0x014 KernelApcInProgress : UChar
   +0x015 KernelApcPending : UChar
   +0x016 UserApcPending  : UChar

而EPROCESS的第一项正是KPROCESS。联想我们熟悉的断EPROCESS链表隐藏进程的手法。通过[b]PsGetCurrentProcess[/b]得到的其实是当前KPROCESS的地址,而KPROCESS就是EPROCESS结构体的第一项,这样就得到了当前的EPROCESS。然后遍历整个链表。。。

---->>大致流程:
[color=#0000ff][b]PsGetCurrentProcess()函数---->_PsGetCurrentProcess()宏----->KeGetCurrentThread()函数[/b][/color]
---->>具体细节:
#define _PsGetCurrentProcess() (CONTAINING_RECORD(((KeGetCurrentThread())->ApcState.Process),EPROCESS,Pcb))
[color=#0000ff]// 很明显,KeGetCurrentThread()得到KTHREAD结构体,KTHREAD偏移0x034处的
// ApcState中process即为EPROCESS的第一项KPROCESS的地址。CONTAINING_RECORD宏
// 将此地址减去它在EPROCESS中的偏移值,得到当前EPROCESS的实际地址[/color]
FORCEINLINE
struct _KTHREAD *
NTAPI KeGetCurrentThread (VOID)
{
#if (_MSC_FULL_VER >= 13012035)
  return (struct _KTHREAD *) (ULONG_PTR) __readfsdword (FIELD_OFFSET (KPCR, PrcbData.CurrentThread));
#else
  __asm { mov eax, fs:[0] KPCR.PrcbData.CurrentThread }
#endif
}
[color=#0000ff]// fs在用户模式下指向TEB结构,在内核模式下指向KPCR[前面第一个描述的结构体][/color]
[color=#0000ff][/color]

呵呵,偶画个图更直观些,也更方便记忆
[attach]10817[/attach]

关于[b]KPROCESS[/b]。里面保存了一些有用的信息,我们来简单的瞅下。
nt!_KPROCESS
  +0x000 Header      : _DISPATCHER_HEADER
  +0x010 ProfileListHead : _LIST_ENTRY
  +0x018
[color=#ff0000]DirectoryTableBase[/color]
: [2] Uint4B  
[color=#ff0000]// 进程的页目录PDT [涉及内存管理知识]
[/color]
  +0x020
[color=#ff0000]LdtDescriptor[/color]
  : _KGDTENTRY  
[color=#ff0000]// GDT的入口
[/color]
  +0x028
[color=#ff0000]Int21Descriptor[/color]
: _KIDTENTRY   
[color=#ff0000]// IDT的入口[/color]
  +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
[color=#ff0000]ThreadListHead[/color]
  : _LIST_ENTRY
[color=#ff0000]// 指向KTHREAD链[/color]
  +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

[b]PEB[/b]是很有用的东西,写shellcode、定位EPROCESS等都可以用到它。PEB在EPROCESS偏移0x1b0处
nt!_EPROCESS
  +0x000 Pcb       : _KPROCESS
  ...
  +0x084 UniqueProcessId : Ptr32 Void
  +0x088 ActiveProcessLinks : _LIST_ENTRY
  ...
  +0x160 PhysicalVadList : _LIST_ENTRY
  +0x168 PageDirectoryPte : _HARDWARE_PTE
  ...
  
[color=#ff0000]+0x1b0 Peb       : Ptr32 _PEB[/color]
  ...

---->>获得PEB的地址是非常简单的。可以通过EPROCESS的偏移,也可以用硬编码实现[不同进程的PEB高位都是一样的]
xor esi, esi           ;  FS寄存器 -> TEB结构,TEB+0x30 -> PEB结构
mov esi, fs:[esi + 30H]  ;  而PEB中包含有_PEB_LDR_DATA。通过一系列的
mov eax, esi         ;  偏移可以定位到Kernel32.dll基地址。。。
ret              ;  呵呵,参看gz1X大虾的文章:
[url=http://hi.baidu.com/gz1x/blog/item/c3c8f8f8b028040cd9f9fd90.html]
[color=#800080]WIN下获取kernel基址的shellcode探讨[/color]
[/url]

当然可以直接用Windbg来查看当前的PEB的结构 lkd>dt _peb
lkd> !peb
[color=#ff0000]PEB at 7ffd[/color]
c000  
[color=#000000]//高位都是7ffd
[/color]
  InheritedAddressSpace:  No
  ReadImageFileExecOptions: No
  BeingDebugged:      No
  ImageBaseAddress:     01000000
  Ldr            00191e90
  Ldr.Initialized:     Yes
  Ldr.InInitializationOrderModuleList: 00191f28 . 00193330
  Ldr.InLoadOrderModuleList:      00191ec0 . 00193320
  Ldr.InMemoryOrderModuleList:     00191ec8 . 00193328
  ...// 省略

② 与进程相关的一些内核变量、计数、函数 ----
[attach]10818[/attach]
关于PspCidTable参见gz1X大虾的:[url=http://hi.baidu.com/gz1x/blog/item/d99aeefa4d1c92ddb48f31b9.html][color=#800080]基于pspCidTable的进程检测技术[/color][/url]

这些变量的申明保存在WRK的[b]Psinit.c[/b]中。先看下[b]PspCreateProcess[/b]的参数
PspCreateProcess(
  OUT PHANDLE ProcessHandle,
  IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  IN HANDLE ParentProcess OPTIONAL,
[color=#0000ff]//如果没有指定,表明此进程没有父进程。是系统进程[/color]
  IN ULONG Flags,
  IN HANDLE SectionHandle OPTIONAL,
  IN HANDLE DebugPort OPTIONAL,
  IN HANDLE ExceptionPort OPTIONAL,
  IN ULONG JobMemberLevel
  )

系统启动时,[b]PspInitPhase0()[/b] 函数会做很多事情,这里只是例举与上图全局变量相关的细节:
初始化Queue header:[b]InitializeListHead[/b](&PsActiveProcessHead);
初始化PsIdleProcess[这个是系统进程的EPROCESS]
[attach]10823[/attach]

创建系统进程,并且把系统进程的EPROCESS保存在[b]PsInitialSystemProcess[/b]中。见代码:
InitializeObjectAttributes (&ObjectAttributes,
                NULL,
                0,
                NULL,
                NULL);
  if (!NT_SUCCESS (PspCreateProcess (&PspInitialSystemProcessHandle,
                    PROCESS_ALL_ACCESS,
                    &ObjectAttributes,
                    
[color=#ff0000]NULL, // 注意这里。没有指定,所以创建的是系统进程
[/color]
                    0,
                    NULL,
                    NULL,
                    NULL,
                    0))) {
    return FALSE;
  }
  if (!NT_SUCCESS (ObReferenceObjectByHandle (PspInitialSystemProcessHandle,
                        0L,
                        PsProcessType,
                        KernelMode,
                        
[color=#ff0000]&PsInitialSystemProcess[/color]
, // 系统的EPROCESS保存于此
                        NULL))) {
    return FALSE;
  }
  strcpy((char *) &PsIdleProcess->ImageFileName[0], "Idle");
  strcpy((char *) &PsInitialSystemProcess->ImageFileName[0], "System");
// EPROCESS OFFEST+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
  PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName =
    ExAllocatePoolWithTag (PagedPool,
                sizeof(OBJECT_NAME_INFORMATION),
                &#39;aPeS&#39;);
  if (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName != NULL) {
    RtlZeroMemory (PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName,
            sizeof (OBJECT_NAME_INFORMATION));
  } else {
    return FALSE;
  }

下面几个全局变量定义在WRK的[b]Psp.h[/b]文件中:
ULONG PspCreateProcessNotifyRoutineCount;
EX_CALLBACK PspCreateProcessNotifyRoutine[8];
ULONG PspLoadImageNotifyRoutineCount;
EX_CALLBACK PspLoadImageNotifyRoutine[8];
extern PHANDLE_TABLE PspCidTable;

[attach]10816[/attach] [attach]10819[/attach]

相关的函数就更多了,具体请参见原版WINDWOS INTERNALS 6[附件里有]

③ 一个进程的诞生,CreateProcess的步骤 ----
用户程序可以调用[b]CreateProcess、CreateProcessAsUser、CreateProcessWithTokenWor、CreateProcessWithLogonW[/b]创建进程。而进程的创建主要由3部分参合进来完成的:[b]Kernel32.dll、the Windows executive、子系统进程CSRSS.exe。[/b]

[color=#ff0000][b]CreateProcess创建进程的大致步骤:
1.打开文件[.exe]。
2.创建进程内核对象
3.在进程中创建线程[堆栈、线程上下文、线程内核对象]
4.通知WINDOWS子系统已经创建了一个新进程,便于它进一步的初始化
5.如果标致不是CREATE_ SUSPENDED,那么就开始执行进程中的线程
6.在进程和线程的context里,完成地址空间的初始化[eg.加载需要的DLL],开始执行程序[/b][/color]

[attach]10820[/attach]

CreateProcess在打开文件运行之前,会检查参数中的flags,决定如何设置新进程的优先级。

---->> stage 1: 打开并运行文件<<----

如果是PE文件,直接运行;如果不是PE格式,系统根据相应的格式选择相应的加载措施:
  POSIX的程序         -- 调用 Posix.exe
  OS/2 1.x的程序       -- 调用 Os2.exe
  .bat/.cmd的程序       -- 调用 Cmd.exe
  16位/MS-DOS .exe/.com/.pif -- 调用 Ntvdm.exe
如果还是不能加载,CreateProcess就会失败.
[attach]10821[/attach]
与Ntvdm.exe相关的注册表在HKLM\SYSTEM\CurrentControlSet\Control\WOW下
[attach]10822[/attach]

到此,CreateProcess已经成功的打开了一个有效的文件,并且为它创建了一个section object。但并没有映射到内存中,仅仅是打开了而已。a section object被成功的创建了并不能说明此文件是一个有效的Windows image,因为文件可能是DLL或者POSIX。

接着检查注册表[b]IFEO[/b],系统如果发现某个程序文件在IFEO列表中,它就会首先来读取Debugger参数,如果该参数不为空,系统则会把Debugger参数里指定的程序文件名作为用户试图启动的程序执行请求来处理,而仅仅把用户试图启动的程序作为Debugger参数里指定的程序文件名的参数发送过去。 联想到了很久的映像劫持病毒。

---->>stage 2: 创建进程内核对象<<----

[b]NtCreateProcess-->NtCreateProcessEx-->;PspCreateProcess来创建进程内核对象[/b],如下:
1.设置EPROCESS
2.创建进程地址空间
3.初始化KPROCESS // 偶在源码里面没有找到耶,太多了,反正没找到
4.完成进程地址空间的设置 // 原文中写着此时Ntdll.dll被映射到进程地址空间中
5.设置PEB        
6.完成进程内核对象的创建,审核收尾工作

----- stage2.1 设置EPROCESS-----
1.>创建EPROCESS
  
[color=#0000ff]//系统在启动时在PspInitPhase0()中初始化进程/线程对象的对象类型[Object types][/color]
  // Create Object types for Thread and Process Objects.
  RtlInitUnicodeString (&NameString, L"Process");
  ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_PROCESS_PAGED_CHARGE;
  ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_PROCESS_NONPAGED_CHARGE;
  ObjectTypeInitializer.DeleteProcedure = PspProcessDelete;
  ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;
  ObjectTypeInitializer.GenericMapping = PspProcessMapping;
  if (!NT_SUCCESS ([b]ObCreateObjectType[/b] (&NameString,
                     &ObjectTypeInitializer,
                     (PSECURITY_DESCRIPTOR) NULL,
                     &PsProcessType))) {
    return FALSE;
[color=#0000ff]// PsProcessType是在Psinit.c中定义的全局变量 POBJECT_TYPE PsProcessType;
[/color]
  }      
[color=#0000ff]   // 用它来保存对象类型的信息[/color]
  
[color=#0000ff]// 函数PspCreateProcess中创建EPROCESS[/color]
  // Create the process object
  Status =[b] ObCreateObject[/b] (PreviousMode,
[color=#0000ff]// PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
[/color]
               PsProcessType,
[color=#0000ff]// 就是上面描述的那个全局变量
[/color]
               ObjectAttributes,
[color=#0000ff]// 函数的参数[/color]
               PreviousMode,
               NULL,
               sizeof (EPROCESS),
               0,
               0,
               &Process);  
[color=#0000ff]// 创建后的EPROCESS保存在此[/color]
  RtlZeroMemory (Process, sizeof(EPROCESS));
  ExInitializeRundownProtection (&Process->RundownProtect);
  PspInitializeProcessLock (Process);
  InitializeListHead (&Process->ThreadListHead);

2.> 设置进程Working set size的最值[20-45]
WorkingSetMinimum = PsMinimumWorkingSet; // 20
WorkingSetMaximum = PsMaximumWorkingSet; // 45
3.> 从父进程那里继承一些属性[略过]

----- stage2.2 创建进程地址空间-----
1.> page directory 即是PDT相关。 嘿嘿,想必大家都看过操作系统之类的书,上面介绍的有关内存管理的东西必须好好的看
前置知识是必须充分了解[b]JT、MBT、PMT、PMTR、PTE、PDE[/b]等其中页表(PTE映射在系统高2GB中--0xC0000000~0xC03FFFFF的4MB空间,页目录映射在0xC0300000~...)

PS: 进程的地址空间分为4种:
  1. [b]Boot Process[/b]--Address space is initialized during MmInit. Parent is not specified
  2. [b]System Process[/b]--系统地址空间,看看前面介绍的这个全局变量PspInitialSystemProcess,熟悉了吧
  3. [b]User Process (Cloned Address Space)[/b] 从指定的进程复制的
  4. [b]User Process (New Image Address Space)[/b] 新的
------------------------------------------------------
if (SectionHandle != NULL) {
    // User Process (New Image Address Space).
    Status = [b]MmInitializeProcessAddressSpace [/b](Process,
                         NULL,
                         SectionObject,
                         &Flags,
                         &(Process->SeAuditProcessCreationInfo.ImageFileName));
...//省略
  } else if (Parent != NULL) {
[color=#0000ff]//如果存在父进程[/color]
    if (Parent != PsInitialSystemProcess) {
[color=#0000ff]// 如果父进程不是系统进程,说明是一个普通的父进程创建的[/color]
      Process->SectionBaseAddress = Parent->SectionBaseAddress;
      // User Process ( Cloned Address Space )
  
      Status = MmInitializeProcessAddressSpace (Process,
                           Parent,
                           NULL,
                           &Flags,
                           NULL);
      // A cloned process isn&#39;t started from an image file, so we give it the name
      // of the process of which it is a clone, provided the original has a name.
   // 暂时不重要,省略掉
    } else {
      // System Process
      Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
      Status = MmInitializeProcessAddressSpace (Process,
                           NULL,
                           NULL,
                           &Flags,
                           NULL);
  }

2.> Hyperspace page
3.> Working set list

----- stage2.5 设置PEB----
if (Parent && CreatePeb) {
    RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));
   
[color=#0000ff]// PspCreateProcess中申明的局部变量 INITIAL_PEB InitialPeb;
[/color]
    InitialPeb.Mutant = (HANDLE)(-1);
    InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages;
      
    if (SectionHandle != NULL) {
[color=#0000ff]// 如果要创建新的进程.PEB也得从新创建
[/color]
      Status =
[color=#ff0000] MmCreatePeb[/color]
(Process, &InitialPeb, &Process->;Peb);
[color=#ff0000]      Peb = Process->;Peb;[/color]
    } else {
[color=#0000ff]// 如果是CLONE进程,PEB只要从父进程里面拷贝一份就可以了
[/color]
      SIZE_T BytesCopied;
      InitialPeb.InheritedAddressSpace = TRUE;
      
[color=#ff0000]Process->;Peb = Parent->;Peb;[/color]
      
[color=#ff0000]MmCopyVirtualMemory [/color]
(CurrentProcess,
                 &InitialPeb,
                 Process,
                 Process->;Peb,
                 sizeof (INITIAL_PEB),
                 KernelMode,
                 &BytesCopied);
    }
  }
  Peb = Process->;Peb;

----- stage2.6 完成进程内核对象的创建-----
1.> Audit the process creation
if (SeDetailedAuditingWithToken (NULL)) {
    SeAuditProcessCreation (Process); // Process为当前的EPROCESS
  }
2.> See if the parent has a job. If so reference the job and add the process in.
3.> 将EPROCESS插入的系统的active process链表中 [联想的断链和复制链]
  PspLockProcessList (CurrentThread);
  InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
  PspUnlockProcessList (CurrentThread);
4.> 线程创建的时间被设定,然后当线程的句柄可用的时候,返回到最初的调用者CreateProcess中[Kernel32.dll]


----- stage3 在进程中创建线程-----
此时,进程对象的有关内容已经全部就绪。它就像一个空的茶壶,里面还没有茶水,所以什么都不能做。终于,CreateProcess[房主]在用PspCreateProcess创建了进程后[茶壶],继续调用NtCreateProcess创建线程[往茶壶里倒茶水]不过此时的茶水不凉的,它需要加热才会沸腾,才会有活力。即此时的线程创建后一直处于暂停状态,直到整个进程被完全的初始化完毕。

[color=#0000ff]CreateProcess-->NtCreateProcess-->NtCreateProcessEx-->PspCreateProcess, PspCreateProcess[/color], 它要做的事:
  ■进程对象中的线程计数增加
  ■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函数所赐.
[b]KiThreadStartup[/b]降低[b]IRQL[/b][从[b]DPC[/b]到[b]APC[/b]],然后调用[b]PspUserThreadStartup[/b]函数.它再降低IRQL到[b]PASSIVE[/b].然后...
最后,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);
    [b]KiInitializeUserApc[/b] (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(线程局部存储)数组,critialsection结构,然后加载任何需要的DLL,并调用DLL中的代码]

最终, 当the loader initialization返回到user mode APC dispatcher时,用户模式下的可执行文件开始真正的执行...

WOW,终于把进程相关的内容大致的写完了。偶写的这些不单单是WINDWOS INTERNALS 上的内容,还参考了前辈们的一些文章,当然还有WRK、OSREACT!

                            <二> 线程相关
线程的代表便是[b]ETHREAD[/b],它保存在系统地址空间中。TEB(线程环境块)在用户进程空间中.CSRSS为每个进程的所有线程保留一份类似的结构(这么多进程,那得多少线程啊,不晓得这些备份的结构究竟是如何存储在CSRSS中的);[b]Win32k.sys[/b][CSRSS和内核交互的接口]保留一份ETHREAD指向的[b]per-thread[/b]数据结构[[b]W32THREAD[/b]]

关键词:[b]ETHREAD、KTHREAD、TEB[/b][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 [color=#0000FF]// 利于系统调度和同步线程的结构体[/color]
  ...
  +0x1ec Cid       : _CLIENT_ID [color=#0000FF]// 指向进程ID[/color]
  ...
  +0x210 IrpList     : _LIST_ENTRY [color=#0000FF]// I/O信息 IRPS[/color]
  +0x218 TopLevelIrp   : Uint4B
  +0x21c DeviceToVerify  : Ptr32 _DEVICE_OBJECT
  +0x220 ThreadsProcess  : Ptr32 _EPROCESS [color=#0000FF]// 指向进程的EPROCESS[/color]
  +0x224 StartAddress   : Ptr32 Void   [color=#0000FF]// 线程的起始地址[/color]
  +0x228 Win32StartAddress : Ptr32 Void
  ...
  +0x238 ThreadLock    : _EX_PUSH_LOCK
  ...
  +0x248 SystemThread   : Pos 4, 1 Bit
  ...

① 与线程相关的函数
[attach]10826[/attach]

[b]CreateThread-->NtCreateThread-->PspCreateThread[/b] [和创建进程同出一辙.这里不详细说明了.如果您有耐心看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到调用者


---->>[抢占式的环境][b]量子时间[/b][quantum]
后面讲的都是关于线程优先级、线程调度和系统的算法问题,这些基本都是些原理性的东西,没必要翻译。而且N多关于操作系统的书上对这些问题都有非常详细的描述。偶就不扯淡了~~~

---->>中断等级[IRQL] VS 线程优先级
[attach]10825[/attach]

                                 <三> Jobs
JOB内核对象有名字、可共享得到,用来控制一个或多个进程。把他们组合形成一个group。它的主要功能是保证多个进程被统一管理操作。一个进程只能属于一个job对象。
[attach]10827[/attach]

偶觉得job实在没啥好将的。略过~~~~
------------------------------- <over><写的很仓促,不正确的地方还请指出>-----------------------------------------
[b][url=http://hi.baidu.com/sudami/blog/item/f7262627f62e1700908f9d5c.html]http://hi.baidu.com/sudami/blog/item/f7262627f62e1700908f9d5c.html[/url][/b]

页: [1]
© 1999-2008 EvilOctal Security Team