能力值:
( LV2,RANK:10 )
|
-
-
2 楼
关注一下~~
|
能力值:
( LV2,RANK:10 )
|
-
-
3 楼
一般来说,线程真正的入口点第一个函数是在ntdll,然后它调用是kernel32的一个函数,然后kernel32的函数调用程序自己定义的入口,也就是说线程是被封装过的,初始化,扫尾都是系统帮你做的
exitthread的调用就是在kernel32的那个函数里,如果程序自己的代码执行完并且返回,就自行结束
前两步大概是完成一些准备工作,比如说调用Dll的DllMain之类的
|
能力值:
( LV7,RANK:100 )
|
-
-
4 楼
还是不懂 啊!
|
能力值:
( LV2,RANK:10 )
|
-
-
5 楼
再顶顶,这问题没人管了?
|
能力值:
( LV2,RANK:10 )
|
-
-
6 楼
我也关注一下这个问题,我也在看那本书!
|
能力值:
( LV7,RANK:100 )
|
-
-
7 楼
我不弄懂绝不罢手
|
能力值:
( LV12,RANK:300 )
|
-
-
8 楼
既然楼主这么锲而不舍……
其实书中说的内容,正是http://bbs.pediy.com/showthread.php?t=85910中提到的第一种方法。
“从这个过程中可以得到一个很重要的数据,那就是堆栈中的返回地址,这个地址只要在程序入口的地方用[esp]就可以将它读出”
这句话并没有错,但是这里的地址,就是指子进程的线程初始化时入口处[esp]中的内容,这个esp是指新线程堆栈里的数据,而不是指父进程堆栈里的数据。
我看来看去都没有看出罗云彬哪里说这里是指父进程的堆栈,因此这里不是他说错,而显然纯粹是楼主的误解。
不过对这个原理的描述,罗云彬的确有说得笼统可能引人误解的地方。
“当父进程要创建一个子进程的时候,它会调用Kernel32.dll 中的CreateProcess函数,CreateProcess函数在完成装载应用程序后,会将一个返回地址压入堆栈并转而执行应用程序,如果应用程序用ExitProcess 函数来终止,那么这个返回地址没有什么用途,但如果应用程序使用Ret指令的话,程序就会返回CreateProcess函数设定的地址。”
这个说法其实是有一点问题的,读者从其中很难理解系统从父进程切换到子进程的主线程的时机到底在哪。
实际上,“将一个返回地址压入堆栈并转而执行应用程序”这并不是在父进程的线程空间内做的,而是子进程的主线程在BaseProcessStartThunk中call进程序入口时,在堆栈中留下的返回地址,返回地址指向BaseProcessStartThunk函数中的下一条指令,指令内容正是push eax后call ExitThread。如果新线程不是新进程的主线程(比如远程线程的情况),那么调用线程入口点的函数则是BaseThreadStartThunk函数,这时堆找中留下的返回地址指向的那条指令内容同样是push eax后call ExitThread。
而“父进程切换到子进程的主线程的时机”,则是在父进程的CreateProcessInternalW函数调用NtCreateThread在子进程中创建线程,再调用NtResumeThread将其唤醒之后。新线程被唤醒之后,在用户态最先调用的是ntdll.dll中的LdrInitializeThunk,完成用户态进线程环境的初始化,包括TLS,以及按照导入表加载dll等(如果进程正被调试,那么这个过程中在LdrpInitializeProcess函数中会调用DbgBreakPoint触发一个Int3异常,这也是Ring3调试器所收到的初始断点位置,OD将之称为“系统断点”,OD的所谓中断在系统断点的设置,就是中断在这里),然后回到内核,第二次进入用户态的时候,就是从BaseProcessStartThunk开始的。
新进程的主线程到达BaseProcessStartThunk时,其线程上下文的初始内容,是之前由父进程的CreateProcessInternalW函数调用kernel32.dll中的BaseInitializeContext函数指定的。
CreateProcessInternalW或CreateRemoteThread在调用NtCreateThread创建新线程前,均会调用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
因此当新进程创建时主线程到达入口点时的[esp],以及远程线程到达入口点时的[esp],这两个值其实不一样(地址所处的函数不同),但是对应的指令都是push eax后call ExitThread。
以上是XP系统的情况。CreateProcess的全过程的系统表述可以参考Windows Internals第6章的相关内容,不过Windows Internals对线程初始上下文的内容的表述没有如此详细,上面关于BaseInitializeContext内容的表述是我在参考其他文章的同时自己IDA观察该函数流程后自己归纳的。
|
能力值:
( LV2,RANK:10 )
|
-
-
9 楼
|
能力值:
( LV2,RANK:10 )
|
-
-
10 楼
原来系统进程有这么多东西呀,平时看到系统里那么多进程,感觉好复杂,现在终于有点明白了。
|
能力值:
( LV7,RANK:100 )
|
-
-
11 楼
谢谢,轩辕小葱!
|
能力值:
( LV7,RANK:100 )
|
-
-
12 楼
真实太感谢您了,我还看到了Anti Virus专题,这个专题不错啊,呵呵
|
|
|