-
-
[原创][推荐]windows 下 exe 用户层面启动过程分析带源码
-
发表于: 2018-4-22 23:23 4546
-
【+】本次研究windows的exe的启动过程(用户层面完成,系统层面未完成)
1.创建一个简单的exe项目作为我们观察的对象。
#include<windows.h>
intmain(intargc,char*argv[]) {
return0;
}
设置debug模式下的运行库为/MTD, release模式下的运行库为/MT (c/c++的代码生成), x64平台的也配置一下
关于/MT 等的介绍运行库模式
然后我们使用x86 的windbg来调试这个程序 (windbg 在C:\Program Files (x86)\Windows Kits\10\Debuggers ), 选择对应版本
2.调试过程
这里我们选择x86 debug模式下的 exe来测试
bp exestartup!main //这里我们下个断点在main函数, g //运行程序 //发现ntdll的符号没有完全解析,所以我们重新加载一下符号 .reload kn //查看函数的调用栈 得到调用栈如下 # ChildEBP RetAddr 00 008ffce4 00e11b4e exestartup!main [c:\users\binlmmhc\desktop\exe启动过程\exestartup\exestartup\exestartup.cpp @ 3] 01 008ffcf8 00e119c0 exestartup!invoke_main+0x1e [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] 02 008ffd50 00e1185d exestartup!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 283] 03 008ffd58 00e11bc8 exestartup!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 326] 04 008ffd60 76fb8654 exestartup!mainCRTStartup+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 05 008ffd74 779a4b17 KERNEL32!BaseThreadInitThunk+0x24 06 008ffdbc 779a4ae7 ntdll!__RtlUserThreadStart+0x2f 07 008ffdcc 00000000 ntdll!_RtlUserThreadStart+0x1
3.源码查找
既然已经看到了调用栈了,我们就从上而下,从深到浅的找到运行库中的代码, CRT源码在安装VS的时候可以选择安装源码的。
我的安装在D:\Vs2017Pro\VC\Tools\MSVC\14.11.25503\crt\src\vcruntime
exestartup!invoke_main+0x1e [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] //这里我们要找到invoke_main函数
我们找到exe_common.inl这个内联文件中,找到invoke_main函数,我们发现invoke_main有4中调用模式,
分别对应main, wmian, WinMain, wWinMain这4中入口函数的调用。
因为我们这里的是main的,所以我们找到了这里 exe_common.inl @ 69
#ifdefined_SCRT_STARTUP_MAIN usingmain_policy=__scrt_main_policy; usingfile_policy=__scrt_file_policy; usingargv_policy=__scrt_narrow_argv_policy; usingenvironment_policy=__scrt_narrow_environment_policy; staticint__cdecl invoke_main()throw() { returnmain(__argc, __argv,_get_initial_narrow_environment()); } 然后我们看一下是谁调用了invoke_main, 发现是__scrt_common_main_seh 这个函数,所以我们继续寻找 02 008ffd50 00e1185d exestartup!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 283] 这个函数定义在exe_common.inl @ 230行, 函数有点长, 我这里省略一下 static __declspec(noinline) int __cdecl __scrt_common_main_seh()throw() { . . . . . . intconstmain_result=invoke_main(); . . . . . . returnmain_result; } 我们在继续找__scrt_common_main这个函数 03 008ffd58 00e11bc8 exestartup!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 326] // This is the common main implementation to which all of the CRT main functions // delegate (for executables; DLLs are handled separately). static__forceinline int __cdecl __scrt_common_main()throw() { // The /GS security cookie must be initialized before any exception handling // targeting the current image is registered. No function using exception // handling can be called in the current image until after this call: __security_init_cookie(); return__scrt_common_main_seh(); } 然后我们发现__scrt_common_main这个函数的是 mainCRTStartup这个启动函数,还有其他3中,分别与wmain, WinMain, wWinMain对应 所以我们在exe_main.cpp中找到了mainCRTStartup这个函数 extern"C"intmainCRTStartup() { return__scrt_common_main(); }
在KERNEL32和ntdll中的函数我这里就不去找了(没有源码的和没有文档化的)
4.聚焦invoke_main
我们发现invoke_main完成我们的main的调用, 我们再对__argc, __argv, _get_initial_narrow_environment 分析一下
staticint__cdecl invoke_main()throw() { returnmain(__argc, __argv,_get_initial_narrow_environment()); } 所以继续拿起我们的windbg .restart //重新启动程序 bp exestartup!invoke_main //断在invoke_main 体会到了符号的好处没有 接下来我们看一下invoke_main的汇编代码 exestartup!invoke_main: 00e11b30 55 push ebp 00e11b31 8bec mov ebp,esp 00e11b33 e8faf4ffff call exestartup!ILT+45(__get_initial_narrow_environment) (00e11032) 00e11b38 50 push eax 00e11b39 e815f7ffff call exestartup!ILT+590(___p___argv) (00e11253) 00e11b3e 8b00 mov eax,dword ptr [eax] 00e11b40 50 push eax 00e11b41 e808f7ffff call exestartup!ILT+585(___p___argc) (00e1124e) 00e11b46 8b08 mov ecx,dword ptr [eax] 00e11b48 51 push ecx 00e11b49 e832f7ffff call exestartup!ILT+635(_main) (00e11280) 00e11b4e 83c40c add esp,0Ch 00e11b51 5d pop ebp 00e11b52 c3 ret 因为我们看main的参数最终是怎么获得的,所以我们分别在下面三个地址下断点,然后分别跟入分析 00e11b33 e8faf4ffff call exestartup!ILT+45(__get_initial_narrow_environment) (00e11032) 00e11b39 e815f7ffff call exestartup!ILT+590(___p___argv) (00e11253) 00e11b41 e808f7ffff call exestartup!ILT+585(___p___argc) (00e1124e)
获取环境变量
bp 00e11b33 bp 00e11b39 bp 00e11b41 g //运行起来,直到获取环境变量这个函数断点 t p p //看到如下的汇编代码 ucrtbased!_get_initial_narrow_environment: 687db750 a1e0328e68 mov eax,dword ptr [ucrtbased!__dcrt_initial_narrow_environment (688e32e0)] ds:002b:688e32e0=03435c38 687db755 85c0 test eax,eax 687db757 750a jne ucrtbased!_get_initial_narrow_environment+0x13 (687db763) 687db759 e85e010000 call ucrtbased!common_get_or_create_environment_nolock<char> (687db8bc) 687db75e a3e0328e68 mov dword ptr [ucrtbased!__dcrt_initial_narrow_environment (688e32e0)],eax kn //看一下调用栈,发现有符号并没有解析完全,所以我们重新加载一下符号 .reload //然后得到了如下的调用栈 0:000> kn # ChildEBP RetAddr 00 (Inline) -------- ucrtbased!common_get_initial_environment [minkernel\crts\ucrt\src\desktopcrt\env\environment_initialization.cpp @ 320] 01 012ff8c4 00e11b38 ucrtbased!_get_initial_narrow_environment [minkernel\crts\ucrt\src\desktopcrt\env\environment_initialization.cpp @ 329] 02 012ff8cc 00e119c0 exestartup!invoke_main+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] 03 012ff924 00e1185d exestartup!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 283] 04 012ff92c 00e11bc8 exestartup!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 326] 05 012ff934 76fb8654 exestartup!mainCRTStartup+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 06 012ff948 779a4b17 KERNEL32!BaseThreadInitThunk+0x24 07 012ff990 779a4ae7 ntdll!__RtlUserThreadStart+0x2f 08 012ff9a0 00000000 ntdll!_RtlUserThreadStart+0x1b 我们发现了minkernel\crts\ucrt\src\desktopcrt\env\environment_initialization.cpp @ 320]这个东西,然后查资料,最后在WindowsKits里找到了源码 我的在C:\Program Files (x86)\Windows Kits\10\Source\10.0.15063.0\ucrt 这里,所以我们继续寻找 ucrtbased!common_get_initial_environment 代码 得到如下的代码 C:\Program Files (x86)\Windows Kits\10\Source\10.0.15063.0\ucrt\env\environment_initialization.cpp @ 311 template<typenameCharacter> static Character** __cdecl common_get_initial_environment()throw() { Character**&initial_environment=get_initial_environment(Character()); if(!initial_environment) { initial_environment=common_get_or_create_environment_nolock<Character>(); } returninitial_environment; } 其他的就不继续深入了关于环境变量
同理,获取命令行参数,到获取命令行参数那里的断点
t p p //得到如下汇编代码 ucrtbased!get_argv: 6881faf0 8bff mov edi,edi 6881faf2 55 push ebp 6881faf3 8bec mov ebp,esp 6881faf5 b834278e68 mov eax,offset ucrtbased!__argv (688e2734) 6881fafa 5d pop ebp 6881fafb c3 ret 看到它获取的是一个全局变量 kn //看调用栈 0:000> kn # ChildEBP RetAddr 00 012ff8c0 00e11b3e ucrtbased!get_argv [minkernel\crts\ucrt\src\appcrt\startup\argv_parsing.cpp @ 27] 01 012ff8cc 00e119c0 exestartup!invoke_main+0xe [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] 02 012ff924 00e1185d exestartup!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 283] 03 012ff92c 00e11bc8 exestartup!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 326] 04 012ff934 76fb8654 exestartup!mainCRTStartup+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 05 012ff948 779a4b17 KERNEL32!BaseThreadInitThunk+0x24 06 012ff990 779a4ae7 ntdll!__RtlUserThreadStart+0x2f 07 012ff9a0 00000000 ntdll!_RtlUserThreadStart+0x1b 所以得到如下代码C:\Program Files (x86)\Windows Kits\10\Source\10.0.15063.0\ucrt\startup\argv_parsing.cpp @ 22 int__argc =0;// The number of arguments in __argv or __wargv char**__argv =nullptr;// The arguments as narrow strings wchar_t**__wargv=nullptr;// The arguments as wide strings char*_pgmptr =nullptr;// The name of the program as a narrow string wchar_t*_wpgmptr=nullptr;// The name of the program as a wide string char*_acmdln =nullptr;// The raw command line as a narrow string wchar_t*_wcmdln=nullptr;// The raw command line as a wide string _BEGIN_SECURE_CRT_DEPRECATION_DISABLE int*__cdecl__p___argc() {return&__argc; } char***__cdecl__p___argv() {return&__argv; } wchar_t***__cdecl__p___wargv() {return&__wargv; } char**__cdecl__p__pgmptr() {return&_pgmptr; } wchar_t**__cdecl__p__wpgmptr() {return&_wpgmptr; } char**__cdecl__p__acmdln() {return&_acmdln; } wchar_t**__cdecl__p__wcmdln() {return&_wcmdln; } 其详细解析的代码在argv_parsing.cpp中, 这里我也不深入了 argc 和argv是差不多的
5.入口点问题
我们知道默认入口点有4个,就是mainCRTStartup, wmainCRTStartup, WinMainCRTStartup, wWinMainCRTStartup
然后我们看一下我们的这个程序的入口点,
bp exestartup!mainCRTStartup 得到如下 且 程序加载模块基地址是 00e00000 2 e Disable Clear 00e11bc0 0001 (0001) 0:**** exestartup!mainCRTStartup 然后我用studyPE 查看了exe的入口点偏移 发现entry = 11050 所以我们反汇编看一下 00e11050 , 得到如下汇编 exestartup!ILT+75(_mainCRTStartup): 00e11050 e96b0b0000 jmp exestartup!mainCRTStartup (00e11bc0) 看来入口点就是一个跳转 到入口函数
注:Windows Kits 下的东西,安装SDK
系统层面的接下来有时间会继续写的。。。。
赞赏
- [原创]【分析记录】疑似Confucius组织组件CuoliVXaRAT分析 12597
- [原创]ReverseRAT远控分析 13564
- [讨论]看雪论坛可不可以出一个看雪的桌面图啊 4690
- [原创]无脑暴力爆破 第二题 南冥神功 5723