环境准备

测试代码如下,编译目标x86,IDE: Visual Studio Professional 2022

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("hello, world\n");
return 0;
}

分析过程

才开始我用的是Visual C++ 6.0,但是编译出的exe看不到mainCRTStartup()的调用。

image-20240326170508493

换到visual studio 2022之后就能看到了。

image-20240327135855353

image-20240327135924554

可以看到OEP等于基地址+RVA

在visual studio中debug,可以看到在__scrt_common_main中执行了__security_init_cookie来检查缓冲区溢出问题。

image-20240327141446243

跟进__security_init_cookie

image-20240327152635490

这里可以看到安全Cookie的默认值。

image-20240327152545136

回到__scrt_common_main跟进__scrt_common_main_seh()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
static __declspec(noinline) int __cdecl __scrt_common_main_seh()
{
// 初始化crt
if (!__scrt_initialize_crt(__scrt_module_type::exe))
// 初始化失败则退出程序
__scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

bool has_cctor = false;
__try
{
bool const is_nested = __scrt_acquire_startup_lock();

if (__scrt_current_native_startup_state == __scrt_native_startup_state::initializing)
{
__scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
}
else if (__scrt_current_native_startup_state == __scrt_native_startup_state::uninitialized)
{
__scrt_current_native_startup_state = __scrt_native_startup_state::initializing;

// 全局和静态变量、对象初始化_initterm_e会检查返回值。而_initterm不会检查,因此_initterm还会初始化没有返回值的对象
if (_initterm_e(__xi_a, __xi_z) != 0)
return 255;

_initterm(__xc_a, __xc_z);

__scrt_current_native_startup_state = __scrt_native_startup_state::initialized;
}
else
{
has_cctor = true;
}

__scrt_release_startup_lock(is_nested);

// If this module has any dynamically initialized __declspec(thread)
// variables, then we invoke their initialization for the primary thread
// used to start the process:
_tls_callback_type const* const tls_init_callback = __scrt_get_dyn_tls_init_callback();
if (*tls_init_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_init_callback))
{
(*tls_init_callback)(nullptr, DLL_THREAD_ATTACH, nullptr);
}

// If this module has any thread-local destructors, register the
// callback function with the Unified CRT to run on exit.
_tls_callback_type const * const tls_dtor_callback = __scrt_get_dyn_tls_dtor_callback();
if (*tls_dtor_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_dtor_callback))
{
_register_thread_local_exe_atexit_callback(*tls_dtor_callback);
}

//
// 初始化完成,调用main函数
//

int const main_result = invoke_main();

//
// main has returned; exit somehow...
//

if (!__scrt_is_managed_app())
exit(main_result);

if (!has_cctor)
_cexit();

// 终止CRT
// 本来我打算用ollydbg一把梭的,但跟进mainCRTStartup会直接跳到这里,暂时还没研究明白,被迫用vs2022。
__scrt_uninitialize_crt(true, false);
return main_result;
}
__except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
{
// Note: We should never reach this except clause.
int const main_result = GetExceptionCode();

if (!__scrt_is_managed_app())
_exit(main_result);

if (!has_cctor)
_c_exit();

return main_result;
}
}

跟进invoke_main(),这里调用了我们的main函数。

1
2
3
4
static int __cdecl invoke_main()
{
return main(__argc, __argv, _get_initial_narrow_environment());
}

image-20240327164301930