进程启动时主线程创建过程分析

VS2013编译以下代码:

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int test = 1;
    test = 2;

    return 0;
}

使用CFF Explorer或者PEID打开编译后生成的exe文件,查看imageBase和OEP,如下所示:

OEP:  0x00011073

image

ImageBase:0x00400000

image

所以该程序的启动点在:0x00411073

该程序的区段信息如下所示:

image

将其载入OD,其载入地址并非00400000,所以载入OD后,alt+e,先看看主模块的载入地址:

image

一般进程运行时都是先载入该exe文件,所以exe总能以其imagebase载入,但这里不知这个是为什么,这里可以不用管这个,直接以其载入地址0x00390000进行后面的分析,但是鄙人有强迫症,总想搞清楚这是为什么,所以先来看下为什么不能以imagebase载入。

联想到安全机制ASLR,难道是这个原因?多次载入,果然发现多次载入时主模块的载入地址每次都不同。。。

隐约记得VC6.0是不支持ASLR的,所以下面用VC6.0重新编译以下代码
#include "stdafx.h"

int main(int argc, char* argv[])
{
    printf("Hello World! ");
    return 0;
}

果然,每次载入地址都是00400000,那么VS2013的ASLR是不是也可以关闭呢?记得好像哪里有说过可以关闭,找一找。。。

强大的网友:

---------------------------------------------------------------------------------------------------

通过加载程序时候不再使用固定的基址加载,从而干扰shellcode定位的一种保护机制

windows Vista出现后,ASLR 才真正开始发挥作用

VS2005 添加 /dynmicbase 就可以支持 ASLR

VS2008 linker->Randomized Base Address 设置

包含 映像随机化,堆栈随机化,PEB+TEB随机化

---------------------------------------------------------------------------------------------------

image

果然,将其关闭,再次测试

备注:关闭后,少了一个区段reloc,这个此处不分析为什么

image

搞定,此处只是为了便于多次分析,所以关闭ASLR,正常情况,为了安全还是要将ASLR打开的



下面从基地址(0x00400000)开始分析,如下所示:

我们知道PE文件在内库中是以1000h(一页)对其,所以先看该页:

image

。。。。。。

image

。。。。。。

end

这部分数据全是PE文件的结构数据,各种头部,区段数据等,和本主题没关系,继续向下第二页:

0x00401000,PEID和OD都可以知道这部分数据是.textbbs,

image

0x00401000---0x00402000这一页内存,结果全是0,《程序员的自我修养》中说“.bbs”段存放的是为初始化的全局变量和局部静态变量,这里没有用到未初始化的全局变量和局部静态变量,所以全是0.

下一个区段:.text(0x00411000

段在OEP,看OEP的代码

image

这个是OD自动断下来的第一条指令,查看堆栈:

image

去0x75DF3388看看

image

在此处3388处下F2断点,Ctrl+F2重启进程,在此处断下来,发现堆栈中没有任何信息,继续向前走,我们在0x75DF3378和0x75DF337A处下断点,重启进程

image

先看从3385~338B

push 主线程参数;

call 主线程函数;(通过OEP做了一次跳板)

push 主线程返回值;

call ntdll.RtlExitUserThread

其源代码应是:RtlExitUserThread((OEP[jmp到主线程入口main])(参数));

通过Windbg调试发现jnz kernel32.75DF6202处其实是:kernel32!RegSetValueExW+0x655 (75df6202),其功能自行百度,此处具体做了什么先不分析了。。。好复杂。。。

此时的堆栈信息如下:

image

此时第一条堆栈信息是当前函数的名字:即0x75DF3378是kernel32.BaseThreadInitThunk函数

在这四条堆栈信息中函数过程处下断点,Crtl+F2

然而结果如下:

image

前面的位置断不下来,所以从最后一条堆栈信息查看,结果如下:

image

原来此处是ntdll的入口,应该是类似于dllmain的,继续看第二条堆栈信息:

image

表示不认识,第一条:

image

依旧不认识。

但是调试发现0x75DF3378会被调用两次,第一次其实并未走到OEP,第二次才会call oep,所以此处再调试一次,等第二次调用0x75DF3378时,堆栈信息如下:

image

我们去0x77E49F40处看看:

image

初始化了异常处理链SEH,最终调用BaseThreadInitThunk,但是谁调用了0x77E49F2A?堆栈是空的?jmp?

上IDA?

结果发现ntdll不是以imagebase载入的,所以需要换算:77E49F2A-77E10000=39F2A

image

ntdll的imagebase:

image

所以7DE70000+39F2A=7DEA9F2A就是IDA中77E49F2A的位置。

结果如下:

image

所以,创建主线程的入口应该是ntdll!RtlUserThreadStart,至于其具体做了什么,可以在IDA中详细查看,

这里再不分析了。

感觉好像丢了什么,算了,就这样吧,睡了。

另附:Windows核心编程(第5版)153页的以下代码:

Void RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam)

{

__try{

               ExitThread((pfnStartAddr)(pvParam));

         }

__except(UnhandledExceptionFilter(GetExceptionInformation())){

         ExitProcess(GetExceptionCode());

//NOTE: We Never get here.

}

}

原文地址:https://www.cnblogs.com/sevensd/p/5877754.html