Win32系统中进程主线程初始化时,ebx指向PEB的问题

我相信大家在用OD的过程中也早已发现,如果不使用Create_SUSPENDED,初始化线程在跑到入口点后,其ebx同样指向PEB结构。 

显然,我们必须进入Win32系统新进程创建的过程来找这个真相。由于我们知道初始化线程被创建后(尚未Resume)ebx就已经指向PEB了,所以要不然就是在NtCreateThread过程中,要不然就是在它前面。同时也应该在NtCreatePeb之后。因此我就在这个范围内找寻。 

应该说这里我犯了个错误,就是没有详细看CreateProcess的流程,而是使劲往NtCreateThread的内核代码看去,N多函数调用关系看得我眼花,虽然这些内核函数对线程的Context有诸多操作,但是均并不涉及其Ebx。 

最后终于回过头来看CreateProcessInternalW在NtCreateThread之前的过程。《Windows Internals 4th》在这里一句话带过了: 

Before the thread can be created, it needs a stack and a context in which to run, so these are set up now. 

原来是恰恰在NtCreateThread之前,在用户态初始化了新线程的Context,再结合网上其他的文章,终于找到关键函数:kernel32!BaseInitializeContext。 

BaseInitializeContext(PCONTEXT Context, // 0x200 bytes 
PPEB Peb, 
PVOID EntryPoint, 
DWORD StackTop, 
int Type // union (Process, Thread, Fiber) 
); 

IDA中看一下这个函数: 

.text:7C810443 
.text:7C810443 __stdcall BaseInitializeContext(x, x, x, x, x) proc near 
.text:7C810443                                         ; CODE XREF: CreateRemoteThread(x,x,x,x,x,x,x)+84 p 
.text:7C810443                                         ; CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+690 p 
.text:7C810443                                         ; CreateFiberEx(x,x,x,x,x)+82 p 
.text:7C810443 
.text:7C810443 Context         = dword ptr 8 
.text:7C810443 PPeb            = dword ptr 0Ch 
.text:7C810443 EntryPoint      = dword ptr 10h 
.text:7C810443 StackTop        = dword ptr 14h 
.text:7C810443 Type            = dword ptr 18h 
.text:7C810443 
.text:7C810443 ; FUNCTION CHUNK AT .text:7C81508E SIZE 00000019 BYTES 
.text:7C810443 ; FUNCTION CHUNK AT .text:7C82FF86 SIZE 0000000F BYTES 
.text:7C810443 
.text:7C810443                 mov     edi, edi 
.text:7C810445                 push    ebp 
.text:7C810446                 mov     ebp, esp 
.text:7C810448                 mov     eax, [ebp+Context] 
.text:7C81044B                 mov     ecx, [ebp+EntryPoint] 
.text:7C81044E                 and     [eax+CONTEXT.SegGs], 0 
.text:7C810455                 cmp     [ebp+Type], 1 
.text:7C810459                 mov     [eax+CONTEXT.Eax], ecx 
.text:7C81045F                 mov     ecx, [ebp+PPeb] 
.text:7C810462                 mov     [eax+CONTEXT.Ebx], ecx 
.text:7C810468                 push    20h 
.text:7C81046A                 pop     ecx 
.text:7C81046B                 mov     [eax+CONTEXT.SegEs], ecx 
.text:7C810471                 mov     [eax+CONTEXT.SegDs], ecx 
.text:7C810477                 mov     [eax+CONTEXT.SegSs], ecx 
.text:7C81047D                 mov     ecx, [ebp+StackTop] 
.text:7C810480                 mov     [eax+CONTEXT.SegFs], 38h 
.text:7C81048A                 mov     [eax+CONTEXT.SegCs], 18h 
.text:7C810494                 mov     [eax+CONTEXT.EFlags], 3000h 
.text:7C81049E                 mov     [eax+CONTEXT.Esp], ecx 
.text:7C8104A4                 jnz     loc_7C81508E 
.text:7C8104A4 
.text:7C8104AA                 mov     dword ptr [eax+CONTEXT.Eip], offset BaseThreadStartThunk(x,x) 
.text:7C8104AA 
.text:7C8104B4 
.text:7C8104B4 loc_7C8104B4:                           ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+4C5F j 
.text:7C8104B4                                         ; BaseInitializeContext(x,x,x,x,x)+1FB4D j 
.text:7C8104B4                 add     ecx, 0FFFFFFFCh 
.text:7C8104B7                 mov     [eax+CONTEXT.ContextFlags], 10007h 
.text:7C8104BD                 mov     [eax+CONTEXT.Esp], ecx 
.text:7C8104C3                 pop     ebp 
.text:7C8104C4                 retn    14h 
.text:7C8104C4 
.text:7C8104C4 __stdcall BaseInitializeContext(x, x, x, x, x) endp 
.text:7C8104C4 

.text:7C81508E 
.text:7C81508E loc_7C81508E:                           ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+61 j 
.text:7C81508E                 cmp     [ebp+Type], 2 
.text:7C815092                 jz      loc_7C82FF86 
.text:7C815092 
.text:7C815098                 mov     dword ptr [eax+CONTEXT.Eip], offset BaseProcessStartThunk(x,x) 
.text:7C8150A2                 jmp     loc_7C8104B4 
.text:7C8150A2 

.text:7C82FF86 
.text:7C82FF86 loc_7C82FF86:                           ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+4C4F j 
.text:7C82FF86                 mov     dword ptr [eax+CONTEXT.Eip], offset BaseFiberStart() 
.text:7C82FF90                 jmp     loc_7C8104B4 
.text:7C82FF90 

这个函数就是新线程的线程上下文初始化的关键。下面这两行代码: 

.text:7C81045F                 mov     ecx, [ebp+PPeb] 
.text:7C810462                 mov     [eax+CONTEXT.Ebx], ecx 

将其Ebx指向了PEB。同时这个函数也对几个段选择子进行了初始化,因此诸如为什么Ring3进线程初始化时这几个段选择子总是那样的值的问题,也一并解决了。 

因为BaseInitializeContext在CreateRemoteThread中也被调用(CreateThread则又调用了CreateRemoteThread),所以Ring3下用这些常规方式创建的进线程,初始化时都经过这个函数,其线程上下文的相关内容便也保持一致的特点。 

Update: 

之前的说法还是有点不足之处,BaseInitializeContext的第二个参数,准确地来说应该是PPebOrPParameter。当被CreateProcessInternalW调用时,此处传入的是新进程的PEB指针(由父进程对新进程调用NtQueryInformationProcess得到);当被CreateRemoteThread调用时,这里传入的是CreateRemoteThread的第五个输入参数,也就是lpParameter,也就是为新线程指定的传入参数。 

BaseInitializeContext函数将相应CONTEXT结构中的Eip字段指定为BaseProcessStartThunk(新进程创建时CreateProcessInternalW调用的时候)或BaseThreadStartThunk(创建非主线程时,由CreateRemoteThread调用的时候),将Eax字段内容指定为线程入口点,将Ebx字段内容指定为新进程的PEB指针(新进程创建时CreateProcessInternalW调用的时候)或创建远程线程时传给新线程的参数(创建非主线程时,由CreateRemoteThread调用的时候)。 


新进程的主线程进入BaseProcessStartThunk后: 
xor ebp, ebp 
push eax; 线程入口点 
push 0 
jmp BaseProcessStart 

BaseProcessStart之后使用这个push eax时压入栈的线程入口点值来调用入口点函数,当入口点函数返回时,使用其返回值调用ExitThread退出线程: 
call     dword ptr [ebp+8] ; 之前压入栈的线程入口点 
push     eax              ; 进入主线程入口点时,[esp]的值就是这行指令的地址 
call     ExitThread 

远程线程进入BaseThreadStartThunk后的行为有点类似,但push的参数中多了一个ebx,即传入参数: 
xor ebp, ebp 
push ebx; 传入参数 
push eax; 线程入口点 
push 0 
jmp BaseThreadStart 

BaseThreadStart之后使用前面压入栈的线程入口点值和传入参数来调用入口点函数,当入口点函数返回时,使用其返回值调用ExitThread退出线程: 
push     dword ptr [ebp+0Ch] ; 之前压入栈的传入参数 
call     dword ptr [ebp+8] ; 之前压入栈的线程入口点 
push     eax              ; 进入远程线程入口点时,[esp]的值就是这行指令的地址 
call     ExitThread 

另外除了进程和线程这两种类型之外,BaseInitializeContext还会被CreateFiberEx调用,相应的对象称为纤程,我还不知道这个纤程具体是怎么回事。  




有什么问题可以加群,100852896
点击这里加入此群 在线提问
文章来自: 本站原创
Tags:
评论: 0 | 查看次数: 6281
博主QQ: 友情链接请找我
QQ群: 灰色档案
返回顶部 关闭