首页
论坛
课程
招聘
Tariq
雪    币: 67
活跃值: 活跃值 (12)
能力值: ( LV6,RANK:90 )
在线值:
发帖
2
回帖
20
粉丝
0

科锐三阶段项目---线程创建流程分析

2010-6-11 17:38 8577

科锐三阶段项目---线程创建流程分析

2010-6-11 17:38
8577
    Windows NT系统在创建进程之后,紧接着创建线程。本文以反汇编wrkx86.exe为主,来分析线程创建流程!
    工作环境: Windows Server 2003 R2
    调试软件: WinDbg , IDA


    在OD中调试到如下地址:


    然后在WinDbg下断bp nt!ntCreateThread即进入内核线程创建开始位置。

    一.线程创建流程分析:
    1.NtCreateThread函数中检查地址参数是否合法和可写,保存Teb作为PspCreateThread传入参数。
    2.StartRoutine是否有值来决定当前模式是内核/用户模式。为NULL,通过ETHREAD->Tcb获得运行模式。
    3.由ProcessHandle参数获得相应的进程对象,保存在局部变量Process。
    4.调用ObCreateObject 创建ETHREAD的一个对象。
    5.初始线程的停止保护锁(&Thread->RundownProtect), 用于跨线程初始化TEB,挂起线程。
    6.设置线程的进程CID, 线程的CID句柄。函数在PspCidTable句柄表创建句柄表项。
    7.初始读取的族大小
    初始化LPC信号量对象
    初始化跨进程通信LPC
    初始化所有正在处理单尚末完成的I/O请求<IRP对象>
    初始化配置管理器等级注册表的变化通知
    初始化线程锁/时间旋转锁/当前线程的所有定时器
    8.根据ThreadContext的值来确认此次创建是用户模式线程<非NULL>,或者系统线程<NULL>.
    用户模式线程: 创建一个TEB,并用InitialTeb初始化,接着初始线程的启动地址,WINDOWS子系统的启动地址。
    9.调用KeInitThread函数 <继续初始新线程属性。> 同步Header, WaitBlock,系统服务表 ,APC ,定时器, 线程的内核栈等。
    10.禁用当前线程内核APC的。且锁定进程。 确保当前进程的状态不是退出或正在终止。进程中Flags标记位判断当前进程是否是死进程。CrossThreadFlags跨线程访问的标志位。
    包括Terminated 线程已执行终止操作 创建失败 等信息。
    11.进程的活动线程数+1。挂入目标进程(EPROCESS中)的线程队列。
    12.启动该线程运行 KeStartThread函数; 并再次初始化末完成的域,设置线程的优先级, 时限设置等。
    13.局部变量OldActiveThreads 判断当前创建的线程是否是第一个线程。当为第一个线程: 通知线程创建标注的注册程序.
    14.检测当前新创建线程的进程是否正处于在一个作业中。
    15.线程对象引用数+2, 一个是当前创建的操作, 另一个返回线程的句柄。
    16.CreateSuspended为TURE 挂起目标线程 不让其参与调度。KeSuspendThread挂起目标线程 , KeForceResumeThread 线程唤醒。
    17.SeCreateAccessStateEx 创建ACCESS_STATE结构 用来插入进程的句柄表中,通过ObInsertObject函数将新线程对象插入。
    18.KeQuerySystemTime 查询线程创建的时间。 PS_SET_THREAD_CREATE_TIME 设置线程创建的时间。
    19.目标线程需要根据安全属性描述块确定其允许的访问权限.ObGetObjectSecurity 得到线程SD 。成员GrantedAccess赋值。
    注: 已被挂起的线程即使处于就绪状态也不会被调度运行,而要到被解除挂起时才能被调度运行 KeReadyThread函数将线程进入就绪状态。
    20.最后ObDereferenceObject将引用计数-1,操作完成。线程创建结束。


       
    二.        线程创建总体流程图:


    三.        NtCreateThread函数分析:

    NTSTATUS
    NtCreateThread(                     // <相关参数说明>
        __out PHANDLE ThreadHandle,     //返回创建线程的句柄
        __in ACCESS_MASK DesiredAccess, //对新线程的访问权限
        __in_opt POBJECT_ATTRIBUTES ObjectAttributes, //指定了线程对象的属性
        __in HANDLE ProcessHandle,      //进程句柄
        __out PCLIENT_ID ClientId, 		 //返回新线程的ClientId 结构
        __in PCONTEXT ThreadContext, 	 //新线程的执行环境
        __in PINITIAL_TEB InitialTeb,   //提供新线程的TEB初始值
        __in BOOLEAN CreateSuspended    //新创建的线程是否要先被挂起
    )


        这个函数主要检查传入参数是否可写, <即验证传入的参数是否合法>。如果不能,则直接返回异常值<STATUS_INVALID_PARAMETER>。 完成参数的检测后,此函数调用创建线程的函数PspCreateThread。
    IDA关键分析点:
      1.验证用户模式和内核模式下,地址的合法性。
        32系统中,用户模式的2GB和内核模式的4GB虚拟内存地址空间中,其地址位置0x7fff0000~0x7fffffff <容量为64KB>不能访问。
    IDA处理思路 :
       定义全局变量_MmUserProbeAddress = 0x7fff0000;对变量地址相减如发生借位操作,即合法。 代码如下:
    mov     eax, _MmUserProbeAddress 
    ; 用户使允许用的最高断地址  = 7FFF0000 检测标记
    mov     ecx, [ebp+ThreadHandle]
    cmp     ecx, eax
    jb      short ThreadHandleProc 
    ; 判断threadhand传出参数的地址是否在用户所允许申请的空间
    ;通过相减 得出的结果是否进位来判断是否越界

      2.结构体的对齐。
       结构体的开始地址判断必须是4的倍数开始,有利于对齐操作,因为此函数参数结构体成员占4个字节。
    IDA处理思路 :
    对地址的低2位进行检测,即test bl,3。代码如下:
    mov     [ebp+SaveClientID], ebx 
    ;保存传入进程结构体地址指针  ClientId
    test    bl, 3          
    ; 等于0 bl=4   对ClientId这个结构体进行对齐判断标记
               ; 结构体的开始地址末尾从00 04 08偏移 方便成员对齐
               ; 用TEST bl,3起到对齐作用 因为这个结构体成员是以4字节对齐
    jnz     short MisalignmentProc 
    ; 结构体首地址如果不是从00 04 08开始
                ; 导致成员变量对齐问题  因为系统遵循对齐规则
                ;  进入末对齐异常处理函数
    参数处理后,进入PspCreateThread线程创建.

    四.        PspCreateThread函数分析:

    NTSTATUS
    PspCreateThread(                       //<相关参数说明>
        OUT PHANDLE ThreadHandle,          //返回创建线程的句柄
        IN ACCESS_MASK DesiredAccess,		 //对新线程的访问权限
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,  
    										 //指定了线程对象的属性
        IN HANDLE ProcessHandle,			 //进程句柄
        IN PEPROCESS ProcessPointer,		 //指向所属进程的EPROCESS对象         
        OUT PCLIENT_ID ClientId OPTIONAL,   //返回新线程的ClientId 结构
        IN PCONTEXT ThreadContext OPTIONAL, //新线程的执行环境
        IN PINITIAL_TEB InitialTeb OPTIONAL,//提供新线程的TEB初始值
        IN BOOLEAN CreateSuspended,         //新创建的线程是否要先被挂起
        IN PKSTART_ROUTINE StartRoutine OPTIONAL, //系统线程启动函数地址
        IN PVOID StartContext               //系统线程启动函数的执行环境
    )
    参数其它说明:
       ProcessPointer 创建系统线程时指向全局的PsInitialSystemProcess 其余情况为NULL.ThreadContext 为NULL,表示将创建一个系统线程。
      IDA反汇编分析:
    1.        PreviousMode分析:进行创建时,首先判断当前运行的模式。
    cmp   [ebp+StartRoutine], esi 
    ; 判断是否是系统线程启动函数的地址
    jz    short NoKelnelModeProc 
    ; StartRoutine有值代表线程当前是内核模式
    mov   byte ptr [ebp+PreviousMode], 0 
    ; 0代表内核模式
    jmp   short GetCurModeProc
    NoKelnelModeProc:
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+1D j
    mov     al, [eax+_ETHREAD.Tcb.___u33._s2.PreviousMode] 
    ; StartRoutine为空  则通过当前ETHREAD->Tcb的值获得前运行模式
    mov     byte ptr [ebp+PreviousMode], al

    2.        获得进程对象
    xor     ebx, ebx                    ;清零
    mov     [ebp+Process], ebx          ;初始化为零
    cmp     [ebp+ProcessHandle], esi    ;比较值
    jz      short NoProcessHandleProc
    push    esi             ; HandleInformation
    lea     eax, [ebp+TargetProcess] ; 保存进程对象的地址
    push    eax             ; Object
    push    [ebp+PreviousMode] ; AccessMode
    push    _PsProcessType     ; ObjectType
    push    2                  ; DesiredAccess
    push    [ebp+ProcessHandle] ; ProcessHandle保存从3环传入的进程句柄
    call    _ObReferenceObjectByHandle@24  ; ObReferenceObjectByHandle 
    ;在当前进程的句柄表或内核句柄表中找到相应的表项,并通过参数返回指向目标对象<数据结构>的指针 
    mov     ebx, [ebp+TargetProcess]
    mov     [ebp+Process], ebx    ;另保存通过函数获得进程对象

    3.        当模式为内核,且当前进程为系统初始进程,退出创建并返回
    cmp     byte ptr [ebp+PreviousMode], 0 ;判断是否内核模式
    jz      short ModeAndObject_OK   
    cmp     ebx, _PsInitialSystemProcess   ;全局变量,记录系统的初始进程
    jnz     short ModeAndObject_OK 
    mov     esi, STATUS_INVALID_HANDLE     ;满足条件即返回错误码

    4.        初始线程保护锁,线程的进程CID,获取PspCidTable中CID句柄表.
    and     dword ptr [esi+_ETHREAD.RundownProtect.___u0], eax 
    ; 变量L_EThreadRundPtOffset     线程的停止保护锁 用于跨线程初始化TEB,挂起线程
    mov     [esi+_ETHREAD.ThreadsProcess], ebx 
    ; 变量L_EThreadProcessOffset   设置线程的进程CID。
    mov     eax, [ebx+_EPROCESS.UniqueProcessId] 
    ; 变量L_EProcessUniqueProcessIdOffset
    mov     [esi+_ETHREAD.Cid.UniqueProcess], eax 
    ; 变量L_ETreadCidUPSOffset  == Thread->Cid.UniqueProcess 的偏移地址
    mov     dword ptr [ebp+CidEntry.___u0], esi 
    ; 相当于___u0保存的线程对象<CidEntry.Object> 创建线程的CID句柄。
    and     dword ptr [ebp+CidEntry.___u1], 0 ; 赋值0
    lea     eax, [ebp+CidEntry]
    push    eax                               ; HandleTableEntry
    push    _PspCidTable                      ; HandleTable  句柄表
    ; PspCidTable是个独立的句柄表,CID即进程号+线程号是这个句柄表中的句柄
    call    _ExCreateHandle@8 
    ; ExCreateHandle(x,x)通过句柄表创建句柄的入口地址       
    mov     [esi+_ETHREAD.Cid.UniqueThread], eax       ; 保存句柄

    5.        链表成员的初始化. PLIST_ENTRY链表结构体包括2个节点成员.Flink, 指向下一个成员节点。Blink 指向头一个成员节点。
    lea     eax, [esi+_ETHREAD.___u2.LpcReplyChain.Flink] 
    ; _EThread结构体中一个共用体结构的首地址
    ; 1._ETHREAD.ExitTime
    ; 2._ETHREAD.LpcReplyChain   //用于跨进程通信<lpc>
    ; 3._ETHREAD.KeyedWaitChain
    mov     [eax+4], eax   
    ; _EThread.LpcReplyChain.Blink指向_EThread共用体中的LpcReplyChain首地址
    mov     [eax], eax     
    ; _EThread.LpcReplyChain.Flink 指向_EThread共用体中的LpcReplyChain首地址
    lea     eax, [esi+_ETHREAD.IrpList] 
    ;  _ETHREAD.IrpList的地址
    // 初始化所有正在处理单尚末完成的I/O请求<IRP对象>
    mov     [eax+4], eax   
    ;  _EThread.IrpList.Blink  链表地址指向_EThread.IrpList
    mov     [eax], eax     
     			;  _EThread.IrpList.Flink 链表地址指向_EThread.IrpList
    lea     eax, [esi+_ETHREAD.PostBlockList] 
    ;  _ETHREAD.PostBlockList的地址   
    // 初始化配置管理器等级注册表的变化通知
    mov     [eax+4], eax    
    ;  _EThread.PostBlockList.Blink  链表地址指向_EThread.PostBlockList
    mov     [eax], eax      
    ;  _EThread.PostBlockList.Flink  链表地址指向_EThread.PostBlockList
    mov     dword ptr [esi+_ETHREAD.ThreadLock.___u0], edi 
    ; _EThread.ThreadLock  // 初始化线程锁
    mov     [esi+_ETHREAD.ActiveTimerListLock], edi 
    ; _EThread.ActiveTimerListLock   初始化时间旋转锁
    lea     eax, [esi+_ETHREAD.ActiveTimerListHead] 
    ; 初始化当前线程的所有定时器
    mov     [eax+4], eax    
    ; _EThread.ActiveTimerListHead.Blink 指向_EThread.ActiveTimerListHead
    mov     [eax], eax      
    ;_EThread.ActiveTimerListHead.Flink 指向_EThread.ActiveTimerListHead


    6.        RundownProtection 线程保护锁
      当进行跨线程初始化TEB,挂起线程等操作. 通过RundownProtection值锁定线程以确保当前线程不是处在退出或正终止的状态中。
    lea     edi, [ebx+_EPROCESS.RundownProtect] 
    ; RundownProtection跨进程<线程>访问标志
    mov     ecx, [edi]
    and     ecx, 0FFFFFFFEh      ; 提取最低位
    lea     edx, [ecx+2]			 
    mov     eax, ecx
    lock cmpxchg [edi], edx 
    ; 检测RundownProtect是否为0或1  如果不是则调用ExfAcquireRundownProtection求值
    ;  X不为0或1(>=2)           eax = X的最地位<0或1>        ECX = X的值<最低位为0>    最后值 = EAX   不一定等于0
    ;  X为0或1(0 1)     eax = X                ECX = X                   最后的值为X的第2位 = 0
    ; <ExfAcquireRundownProtection == X>

    7.        通过检测ThreadContext来判断是创建用户系统还是系统线程。
    cmp     [ebp+ThreadContext], 0 ;  线程上下文  3环传入参数
    jz      THreadContext_NoExist 
    ; ThreadContext是否存在值  存在即用此值创建内核态的对象<用户模式线程>
    ; 否则用内核态Context创建内核对象<系统线程>
    lea     eax, [ebp+Teb]
    push    eax             ; Base
    lea     eax, [esi+_ETHREAD.Cid]
    push    eax             ; ClientId
    push    [ebp+InitialTeb] ; InitialTeb
    push    ebx             ; TargetProcess
    call    _MmCreateTeb@16 ; MmCreateTeb(x,x,x,x)  创建对象TEB
    mov     [ebp+Status], eax ; 此例程将创建一个TEB加入它的目标进程内页并复制初始TEB

    8.        初始化线程启动地址和Windows子系统的启动地址
    mov     eax, [ebp+ThreadContext] 
    ; 通过用户模式的线程来初始化内核线程对象
    mov     ecx, [eax+_CONTEXT._Eip]
    ; ThreadContext->Eip  初始化线程的启动地址
    mov     [esi+_ETHREAD.StartAddress], ecx
    mov     ecx, [eax+_CONTEXT._Eax] 
    ; ThreadContext->Eax  初始化WINDOWS子系统的启动地址
    mov     [esi+_ETHREAD.___u17.Win32StartAddress], ecx


    9.        通过KeInitThread初始线程对象
      当创建用户线程赋值PspUserThreadStartup 当线程创建完毕后,在3环下启动线程时,即通过此函数入口点执行。
    当创建系统线程赋值PspSystemThreadStartup
    push    ebx             ; Process
    push    [ebp+Teb]       ; Teb
    push    eax             ; ThreadContext
    push    [esi+_ETHREAD.StartAddress]
    push    ecx             ; NULL
    push    offset _PspUserThreadStartup@8 
    ; PspUserThreadStartup(x,x) 启动调用用户态的线程
    push    ecx             ; NULL
    jmp     short Call_KeInitThread
    THreadContext_NoExist:                  
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+1A3 j
    xor     eax, eax        
    ; 进入没有创建新线程的情况下处理流程  即用内核线程创建系统线程
         mov     [ebp+Teb], eax  ; Teb = NULL;
         push    10h
         pop     ecx             ; PS_CROSS_THREAD_FLAGS_SYSTEM
         lea     edx, [esi+_ETHREAD.CrossThreadFlags]
         lock or [edx], ecx     
    ; 永远受系统线程的CrossThreadFlags标志位的控制
         mov     ecx, [ebp+StartRoutine] 
    ; 从这里开始为KeInitThread传参   用内核线程来创建内核对象
         mov     [esi+_ETHREAD.StartAddress], ecx
         push    ebx             ; Process
         push    eax             ; Teb
         push    eax             ; ContextFrame
         push    [ebp+StartContext] ; StartContext
         push    ecx             ; StartRoutine
         push    offset _PspSystemThreadStartup@8 ; SystemRoutine
         push    eax             ; KernelStack
    Call_KeInitThread:                      
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+24E j
         push    esi             ; Thread
         call    _KeInitThread@32 ; KeInitThread(x,x,x,x,x,x,x,x)
    ; 初始化线程对象 <继续初始新线程属性。> 同步Header, WaitBlock,
    ; 系统服务表 ,APC ,定时器, 线程的内核栈等


    10.        Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD 跨线程访问的标志位 包括Terminated 线程已执行终止操作 创建失败 等.
    IDA 主要代码:
    dec     [edi+_KTHREAD.___u29._s0.KernelApcDisable] 
    ; KeEnterCriticalRegionThread (&CurrentThread->Tcb);
               ; 将此值设为-1   关闭当前线程的内核APC
           lea     eax, [ebx+_EPROCESS.ProcessLock.___u0]
           mov     [ebp+PushLock], eax
           mov     eax, 0
           mov     ecx, [ebp+PushLock] ; 锁定进程
               ; 确保当前进程的状态不是退出或正在终止。
               ; Process->Flags 判断进程是否是死进程。
               ; CurrentThread->CrossThreadFlags跨线程访问的标志位
               ; 包括Terminated 线程已执行终止操作 创建失败 等。
           lock bts [ecx], eax
           setb    al
           test    al, al
           jz      short PushLock_OK ; 跳转成功
           mov     ecx, [ebp+PushLock] ; PushLock
           call @ExfAcquirePushLockExclusive@4 
    ; ExfAcquirePushLockExclusive(x) 获得PUSHLOCK值
    
    PushLock_OK:                            
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+2DB j
            test    byte ptr [ebx+_EPROCESS.Flags], 8 
    ; PS_PROCESS_FLAGS_PROCESS_DELETE 检测Flags标记位
            jnz     NoRightFlagsExitProc
     ; 进程和线程的标志位至少一个必须为0  否则引发错误
            test    byte ptr [edi+_ETHREAD.CrossThreadFlags], 1
    ;PS_CROSS_THREAD_FLAGS_TERMINATED 检测CrossThreadFlags位


    11.        挂入目标进程(EPROCESS中)的线程队列 并调用KeStartThread设置线程的优先级等,启动该线程运行。
    lea     eax, [ebx+_EPROCESS.ActiveThreads] ;记录进程的活动线程数
    mov     ecx, [eax]
    mov     [ebp+L_OldActiveThreads], ecx ; 保存原ActiveThreads
    inc     ecx             ; 进程的活动线程数+1
    mov     [eax], ecx
    lea     eax, [esi+_ETHREAD.ThreadListEntry]
    lea     ecx, [ebx+_EPROCESS.ThreadListHead]
    mov     edx, [ecx+4]    ; _EPROCESS.ThreadListHead.blink
    mov     [eax], ecx      ; _EPROCESS.ThreadListEntry.Flink
    mov     [eax+4], edx    ; _ETHREAD.ThreadListEntry.blink
    mov     [edx], eax
    mov     [ecx+4], eax    
    ; 1.从_EPROCESS.ThreadListHead => _ETHREAD.ThreadListHead
    ; 2._EPROCESS.ThreadListHead .blink =>_EPROCESS.ThreadListHead .Flink
    ; 挂入目标进程(EPROCESS中)的线程队列
    push    esi             ; Thread
    call    _KeStartThread@4 ; KeStartThread  启动线程
    ; 并再次初始化末完成的域,设置线程的优先级, 时限设置等

    12.        回调处理 当创建的是第一个线程,通知线程创建标注的注册程序.
    cmp     [ebp+L_OldActiveThreads], 0
    ; 局部变量OldActiveThreads  判断当前创建的线程是否是第一个线程。
    ;当为第一个线程:  通知线程创建标注的注册程序
    ; 线程创建成功时得到通报内核成员 在数组登记一个通知函数 以新线程CID(进程号和线程号)为参数
    jnz     short ProcessAndThreads_OK ; 跳转成功
    mov     dl, 1           					; Create
    mov     ecx, ebx        					; Process
    call    @WmiTraceProcess@8 ; WmiTraceProcess  鉴定当前进程的通知例程
    cmp     _PspCreateProcessNotifyRoutineCount, 0 ; 创建进程的个数
    jz      short ProcessAndThreads_OK
    mov     [ebp+l_CPNotifyRoutineAddr],
     offset _PspCreateProcessNotifyRoutine ; 保存首地址
    mov     [ebp+L_nCount], 8 ; 循环总次数
    repetitionProc_P:                       
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+3F4 j
    push    [ebp+l_CPNotifyRoutineAddr] 		; CallBack
    call    _ExReferenceCallBackBlock@4 		; ExReferenceCallBackBlock
    ;_PspCreateProcessNotifyRoutine[L_nCount]每个成员的信号是否被阻止回调
    mov     [ebp+CallBack], eax
    test    eax, eax
    jz      short BlockCall_P ; 当为NULL 阻止
    push    eax             					; CallBackBlock
    call    _ExGetCallBackBlockRoutine@4 ; ExGetCallBackBlockRoutine(x)
    ; 当不阻止时  关联起信号
    push    1
    push    [ebx+_EPROCESS.UniqueProcessId]
    push    [ebx+_EPROCESS.InheritedFromUniqueProcessId]
    call    eax
    push    [ebp+CallBack]  					; CallBackBlock
    push    [ebp+l_CPNotifyRoutineAddr] ; CallBack
    call    _ExDereferenceCallBackBlock@8 
    ; ExDereferenceCallBackBlock(x,x)       恢复先前的
    BlockCall_P:                           
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+3CA j
    add     [ebp+l_CPNotifyRoutineAddr], 4 	; 依次遍历数组中每个成员
    dec     [ebp+L_nCount]  				 	; 总数决定循环的中止
    jnz     short repetitionProc_P			 	; 重复处理  for();


    13.        创建ACCESS_STATE结构  插入对象目录以及当前进程的句柄表
    mov     eax, _PsThreadType  获得线程类型结构
    add     eax, 68h        ; &PsThreadType->TypeInfo.GenericMapping
    push    eax             ; GenericMapping
    push    [ebp+DesiredAccess] ; DesiredAccess
    lea     eax, [ebp+AuxData]
    push    eax             ; AuxData
    lea     eax, [ebp+LocalAccessState]
    push    eax             ; AccessState
    push    edi             ; Process
    push    0               ; Thread
    call    _SeCreateAccessStateEx@24 
    ; SeCreateAccessStateEx(x,x,x,x,x,x) 初始化ACCESS_STATE结构体
            ; 创建ACCESS_STATE结构 用来插入进程的句柄表中
            ; 通过ObInsertObject函数将新线程对象插入
    mov     edi, eax
    test    edi, edi
    jge     short NOAccessState
    push    2
    pop     eax
    lea     ecx, [esi+_ETHREAD.CrossThreadFlags] ; 失败后恢复先前设置
    lock or [ecx], eax      ; 失败,设置标记位表示该线程死亡
    cmp     [ebp+CreateSuspended], 0
    jz      short CurNOSuspendedStatus ; 是否是挂起状态下
    push    esi             ; Thread
    call    _KeResumeThread@4 ;  KeResumeThread(x) 恢复挂起的线程
    CurNOSuspendedStatus:                   
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+596 j
    push    esi             ; Thread
    call    _KeReadyThread@4 ; KeReadyThread(x)  准备执行线程
           ; Dispatch 调度线程  解除挂起以后,出于就绪状态 当该线程被调度运行时 自行退出
           ; 将目标线程挂入就绪线程队列


    14.        查询系统当前时间并得到SD。
    lea     eax, [ebp+CreateTime] 
    push    eax             		; CurrentTime
    call    _KeQuerySystemTime@4 ; KeQuerySystemTime(x) 查询当前系统时间
    mov     eax, dword ptr [ebp+CreateTime]
    mov     dword ptr [esi+_ETHREAD.CreateTime], eax 
    ; 初始化赋值_Ethread.Creatime结构体  当前创建线程的时间
    mov eax, dword ptr [ebp+CreateTime+4] ; (Thread)->CreateTime.QuadPart
    mov     [esi+_ETHREAD.CreateTime.u.HighPart], eax
    lea     edi, [esi+_ETHREAD.CrossThreadFlags]
    test    byte ptr [edi], 2 ; PS_CROSS_THREAD_FLAGS_DEADTHREAD
    jnz     DropThreadFlagProc 
    ; (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0
    lea     eax, [ebp+MemoryAllocated]
    push    eax             ; MemoryAllocated
    lea     eax, [ebp+SecurityDescriptor]
    push    eax             ; SecurityDescriptor
    push    esi             ; Object
    call    _ObGetObjectSecurity@12 
    ; ObGetObjectSecurity(x,x,x)  得到对象的安全属性//得到线程SD

    15.        目标线程根据安全属性描述块确定其允许的访问权限
    mov     [ebp+SubjectContext.ProcessAuditId], ebx; 保存进程
    push    ebx             ; Process
    call    _PsReferencePrimaryToken@4 
    ; PsReferencePrimaryToken(x) 返回指向主进程的标记  且保护指针的标记数目++
    mov     [ebp+SubjectContext.PrimaryToken], eax    ;保存保护指针的标记
    and     [ebp+SubjectContext.ClientToken], 0      
    lea     edi, [esi+_ETHREAD.GrantedAccess]    ;获取GranteAccess地址
    lea     eax, [ebp+accesst]                  ;获取accesst地址
    push    eax             ; AccessStatus
    push    edi             ; GrantedAccess
    push    [ebp+PreviousMode] ; AccessMode
    mov     eax, _PsThreadType                  ;线程类型数组首地址
    add     eax, 68h ;求取偏移68H值&PsThreadType->TypeInfo.GenericMapping
    push    eax             ; GenericMapping
    xor     eax, eax        ;清零 传参
    push    eax             ; Privileges
    push    eax             ; PreviouslyGrantedAccess
    push    2000000h        ; DesiredAccess
    push    eax             ; SubjectContextLocked
    lea     eax, [ebp+SubjectContext]
    push    eax             ; SubjectSecurityContext
    push    [ebp+SecurityDescriptor] ; SecurityDescriptor
    call    _SeAccessCheck@40
    ; SeAccessCheck(x,x,x,x,x,x,x,x,x,x) 入口进行检查
    mov     [ebp+AccessCheck], al
    lea     ecx, [ebx+_EPROCESS.Token] ; FastRef
    mov     edx, [ebp+SubjectContext.PrimaryToken] ; Object
    call    @ObFastDereferenceObject@8 
    ; ObFastDereferenceObject         快速解除对象的引用
    push    [ebp+MemoryAllocated] ; MemoryAllocated
    push    [ebp+SecurityDescriptor] ; SecurityDescriptor
    call    _ObReleaseObjectSecurity@8 
    ; ObReleaseObjectSecurity(x,x) 安全的释放对象
    cmp     [ebp+AccessCheck], 0
    jnz     short AccessCheckValue
    and     dword ptr [edi], 0 ; Thread->GrantedAccess = 0
    AccessCheckValue:                       
    ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+6FD j
    or      dword ptr [edi], 61h 
    ; Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION)   ; 设置最小权限


    五.KeInitThread函数关键点分析:
         此函数根据进程对象中的信息来初始化新线程的一些属性。根据ThreadContext的值来决定是创建用户模式线程还是系统线程。如果创建用户线程,则传参PspUserThreadStartup.系统线程,传参PspSystemThreadStartup.
    另外此函数根据所提供的参数信息调用KiInitializeContextThread.完成于特定处理器相关执行环境的初始化。
    1.        WaitListHead的初始化:
    mov    [esi+_KTHREAD.Header.___u0._s0.Type], 6   ; ThreadObject
    mov    [esi+_KTHREAD.Header.___u0._s0.Size], 6Eh 
    ; sizeof(KTHREAD) / sizeof(LONG)
    lea     eax, [esi+_KTHREAD.Header.WaitListHead]
    ; 初始化WaitListHead  当一个线程正在等待执行时  加入此链表
    mov     [eax+_LIST_ENTRY.Blink], eax   ;初始链表头
    mov     [eax+_LIST_ENTRY.Flink], eax   ;初始链表尾


    2.        内存对齐算法 <AutoAlignment>
    通过移位操作来设置线程的标记位<内存访问对齐> 。
    lea     eax, [esi+_KTHREAD.ThreadFlags]    取得线程首地址
    mov     ebx, [ebp+Process]                 取得进程地址
    mov     ecx, [ebx+_KPROCESS.ProcessFlags]  进程标志位
    shl     ecx, 1Fh     ;左移31位 
    sar     ecx, 1Fh		;求出最低位
    xor     ecx, [eax]
    and     ecx, 1
    xor     [eax], ecx		
    ; Thread->AutoAlignment = Process->AutoAlignment;
    ; 位段  最低位段为AutoAlignment
    ; AutoAlignment 内存访问对齐标志 继承自EPROCESS

    3.        此函数主要也是初始化线程的成员。<Header, WaitBlock,系统服务表 ,APC ,定时器, 线程的内核栈等>  具体赋值部分请查看wrkx86.idb中KeInitThread函数。

    六.PspUserThreadStartup函数关键点函数分析
      当创建的线程是用户模式线程时,在调用KeInitThread函数时,作为参数进行传入,在三环下启动线程时,调用应用程序指定PspUserThreadStartup<初始线程启动函数>,将此启动函数地址压入用户栈开始执行。
    1.        调试线程的创建部分
    test    byte ptr [esi+_ETHREAD.CrossThreadFlags], 6
    ;此标记位用来检测调试状态
    jnz     short jump_ok_1 
    ; 如果当前新创建线程的进程是在调试状态或有调试通告 则须创建调试线程
    push    [ebp+StartContext] ; StartAddress
    push    esi             ; Thread
    call    _DbgkCreateThread@8 ; DbgkCreateThread(x,x)   创建调试线程


    2.        获取TrapFrame陷阱框架地址
    call    ds:__imp_@KfRaiseIrql@4
     ; KeRaiseIrql  这个函数的IRQL降低到指定的值
    push    ebx             ; SystemArgument2
    push    ds:SystemArgument1 ; SystemArgument1
    push    ebx             ; NormalContext
    push    ds:NormalRoutine ; NormalRoutine
    mov     eax, [esi+_KTHREAD.InitialStack]
    sub     eax, 29Ch       
    ; PSPALIGN_UP(sizeof(KTRAP_FRAME),4) + sizeof(FX_SAVE_AREA)
    ; => sizeof(KTRAP_FRAME)+sizeof(FX_SAVE_AREA) => 29Ch
    ; TrapFrame陷阱框架地址
    push    eax             ; TrapFrame
    push    ebx             ; ExceptionFrame
    call    _KiInitializeUserApc@24 
    ; KiInitializeUserApc(x,x,x,x,x,x) 初始化一个用户模式APC的背景


    3.        在系统中填写的cookie处理。由SharedUserData->Cookie来决定
    CooikeZero:                             
    ; CODE XREF: PspUserThreadStartup(x,x)+143 j
           lea     eax, [ebp+Time] ; 获取保存时间的地址
           push    eax             ; CurrentTime
           call    _KeQuerySystemTime@4 
    ; KeQuerySystemTime(x)  获取系统时间
           mov     eax, large fs:20h ; //获取当前处理器块地址
           mov     ecx, [eax+_KPRCB.MmPageFaultCount]
           xor     ecx, [eax+_KPRCB.InterruptTime]
           xor     ecx, dword ptr [ebp+Time+4] ; Time.HighPart
           xor     ecx, dword ptr [ebp+Time] ; Time.LowPart
           lea     eax, [ebp+Time] ; 获取保存时间的地址
           xor     ecx, eax        
           mov     edx, 0FFDF0330h
    ;Time.LowPart^Time.HighPart^Prcb->InterruptTime^Prcb->MmPageFaultCount ^ (ULONG)(ULONG_PTR)&Time = 0FFDF0330h;
           xor     eax, eax
           lock cmpxchg [edx], ecx ; 交换指令
    
    ExitFun: 
           cmp     ds:0FFDF0330h, ebx ; SharedUserData->Cookie   ;判断此值来决定是否填写Cookie


    七.KeStartThread整体分析
       功能:  KeStartThread函数启动该线程运行;插入线程到进程的线程链表中。并再次初始化末完成的域,<设置线程的优先级, 时限设置等>   
    函数初始流程IDA分析:
    mov     esi, [edi+_ETHREAD.Tcb.___u6.ApcState.Process] ; 得到进程
    mov     ecx, [esi+60h]  			; Process->DisableBoost
    shl     ecx, 1Eh                  ;获取标志位
    lea     eax, [edi+0A0h]			; Thread->DisableBoost
    sar     ecx, 1Eh                  ;获取标志位
    xor     ecx, [eax]      ;  Thread->DisableBoost = Process->DisableBoost;
                          ; 线程调度过程中优先级的提升
        lea     edx, [ebp+LockHandle] ; 调用函数的第二个参数
    and     ecx, 2
    xor     [eax], ecx
    mov     al, [esi+_EPROCESS.Pcb.Iopl]
    mov     [edi+_ETHREAD.Tcb.Iopl], al ; Thread->Iopl = Process->Iopl;   I/O优先级
    mov     al, [esi+_KPROCESS.QuantumReset] 
    ; // Initialize the thread quantum and set system affinity false.
    mov     [edi+_ETHREAD.Tcb.___u57._s1.Quantum], al
     ; Thread->Quantum = Process->QuantumReset; 初始化线程量子数量
    mov     al, [esi+_KPROCESS.QuantumReset]
    lea     ecx, [esi+_KPROCESS.ProcessLock] ; 调用函数的第一个参数
    mov     [edi+_ETHREAD.Tcb.___u57._s2.QuantumReset], al
     		; Thread->QuantumReset = Process->QuantumReset;  线程的基本时间重置值
    mov     [edi+_KTHREAD.___u33._s3.SystemAffinityActive], 0 
    ; Thread->SystemAffinityActive = FALSE; 系统亲和力设置
    call    ds:__imp_@KeAcquireInStackQueuedSpinLockRaiseToSynch@8 
    ; KeAcquireInStackQueuedSpinLockRaiseToSynch(x,x)
        ; 提高的IRQL到SYNCH_LEVEL获得栈队列中的旋转锁
    mov     al, [esi+_KPROCESS.BasePriority] ; 设置线程的基本优先级
    mov     [edi+_KTHREAD.BasePriority], al
    mov     [edi+_KTHREAD.Priority], al ; 设置线程的优先级 动态微调
    mov     eax, [esi+_EPROCESS.Pcb.Affinity]
    mov     [edi+_KTHREAD.Affinity], eax
    mov     eax, [esi+_EPROCESS.Pcb.Affinity]
    mov     [edi+_KTHREAD.UserAffinity], eax ; 设置线程的亲和力
    movzx   eax, [esi+_EPROCESS.Pcb.IdealNode] ; 进程选择优先的处理器节点
    movzx   edx, [esi+_EPROCESS.Pcb.ThreadSeed] ; 进程选择理想的处理器
    mov     eax, _KeNodeBlock[eax*4] ; =>  KeNodeBlock[Process->IdealNode]
    mov     ecx, _KiProcessorBlock[edx*4] ; => KiProcessorBlock[Process->ThreadSeed];
    mov     eax, [eax+10h]  ; KeNodeBlock[Process->IdealNode]->ProcessorMask
    mov     ecx, [ecx+550h] ; KiProcessorBlock[IdealProcessor]->MultiThreadProcessorSet
    and     eax, [esi+_EPROCESS.Pcb.Affinity]
    not     ecx
    and     ecx, eax        ; 2个数组的值相与
    jz      short ValueExist
    mov     eax, ecx
    
    ValueExist:                             ; CODE XREF: KeStartThread(x)+97 j
    push    eax             ; Set
    push    edx             ; Number
    call    _KeFindNextRightSetAffinity@8 ; KeFindNextRightSetAffinity(x,x)
    mov     ecx, large fs:20h
    mov     [esi+_EPROCESS.Pcb.ThreadSeed], al
    mov     ebx, 418h
    add     ecx, ebx        ; LockQueue
    mov     [edi+_ETHREAD.Tcb.UserIdealProcessor], al
    mov     [edi+_ETHREAD.Tcb.IdealProcessor], al
    call    @KeAcquireQueuedSpinLockAtDpcLevel@4 
    ; KeAcquireQueuedSpinLockAtDpcLevel(x)
    ; 在当前IRQL级别中选择指定栈队列里自旋锁
    lea     ecx, [esi+_EPROCESS.Pcb.ThreadListHead]
    mov     edx, [ecx+_LIST_ENTRY.Blink]
    lea     eax, [edi+_ETHREAD.Tcb.ThreadListEntry]
    mov     [eax+_LIST_ENTRY.Flink], ecx
    mov     [eax+_LIST_ENTRY.Blink], edx
    mov     [edx], eax
    mov     [ecx+4], eax    ; 从尾部插入链表  插入线程到进程列表和增加内核线程
    mov     ecx, large fs:20h
    inc     [esi+_EPROCESS.Pcb.StackCount] 
    ; //解锁调度数据库,释放锁的过程,降低IRQL到其以前的值
    add     ecx, ebx        ; LockQueue
    call    @KeReleaseQueuedSpinLockFromDpcLevel@4 
    ; KeReleaseQueuedSpinLockFromDpcLevel(x)


    八.关系图


    相关参考资料:
    Windows 内核原理与实现 潘爱民
    Windows 内核情景分析 毛德操


    注:
      因本人水平有限,加上时间仓促,难免存在错误与纰漏之处,恳请各位高手给予指正!
      科锐五期 tariq


      [公告]看雪论坛2020激励机制上线了!发帖不减雪币了!如何获得积分快速升级?

      上传的附件:
      最新回复 (3)
      besterChen
      雪    币: 117
      活跃值: 活跃值 (15)
      能力值: ( LV9,RANK:180 )
      在线值:
      发帖
      10
      回帖
      180
      粉丝
      0
      besterChen 活跃值 4 2010-6-11 18:31
      2
      0
      又是我的沙发 ~~ 哈哈
      小天狼星
      雪    币: 270
      能力值: (RANK:10 )
      在线值:
      发帖
      4
      回帖
      239
      粉丝
      0
      小天狼星 活跃值 2010-7-1 22:16
      3
      0
      顶楼上的肺。。。。
      顺便问下楼主怎么配合ida来分析来?
      besterChen
      雪    币: 117
      活跃值: 活跃值 (15)
      能力值: ( LV9,RANK:180 )
      在线值:
      发帖
      10
      回帖
      180
      粉丝
      0
      besterChen 活跃值 4 2010-7-1 22:32
      4
      0
      哈哈, 头像是嫂子么?
      游客
      登录 | 注册 方可回帖
      返回