逆向exe文件:CRT 初始化流程详细分析

前言

这是2026年御网杯的一道逆向题目。笔者拿来用于以题为例教学逆向exe文件:CRT 初始化流程详细分析。

这个exe文件用IDA解包后,发现其入口逻辑大概是:start → sub_140001180(CRT 初始化)→ sub_1400014FB(main)

笔者这里主要讲:sub_140001180(CRT 初始化)。

该函数是一个 CRT Startup 初始化包装函数,负责在进入真正用户逻辑之前完成运行库初始化、全局构造、TLS 回调、异常处理器设置、命令行参数准备,并最终调用真正的入口函数。

反编译:

1. 函数定位

原函数:

c 复制代码
__int64 sub_140001180()
{
  signed __int64 v1; // rsi
  signed __int64 v2; // rax
  signed int v3; // edi
  int v4; // ebx
  __int64 v5; // rdi
  _QWORD *v6; // rax
  __int64 v7; // rbp
  __int64 v8; // r12
  signed __int64 v9; // rdi
  __int64 v10; // rbx
  size_t v11; // rax
  size_t v12; // rsi
  void *v13; // rax
  const void *v14; // rdx
  _QWORD *v15; // rdi
  __int64 v16; // rcx
  __int64 result; // rax

  _RBX = &unk_140007040;
  v1 = *(_QWORD *)(__readgsqword(0x30u) + 8);
  while ( 1 )
  {
    v2 = _InterlockedCompareExchange((volatile signed __int64 *)&unk_140007040, v1, 0i64);
    if ( !v2 )
    {
      v3 = 0;
      if ( unk_140007048 == 1 )
        goto LABEL_20;
      goto LABEL_6;
    }
    if ( v1 == v2 )
      break;
    ((void (__fastcall *)(signed __int64))Sleep)(1000i64);
  }
  v3 = 1;
  if ( unk_140007048 == 1 )
LABEL_20:
    sub_1400028B0(31i64);
LABEL_6:
  if ( unk_140007048 )
  {
    dword_140007008 = 1;
  }
  else
  {
    unk_140007048 = 1;
    initterm(&unk_140004A60, &unk_140004A70);
  }
  if ( unk_140007048 == 1 )
  {
    initterm(&unk_140004A48, &unk_140004A58);
    unk_140007048 = 2;
    if ( v3 )
      goto LABEL_10;
  }
  else if ( v3 )
  {
    goto LABEL_10;
  }
  _RAX = 0i64;
  __asm { xchg    rax, [rbx] }
LABEL_10:
  if ( TlsCallback_0 )
    TlsCallback_0(0i64, 2i64, 0i64);
  sub_140001A80();
  qword_1400070D0 = (__int64)SetUnhandledExceptionFilter(TopLevelExceptionFilter);
  set_invalid_parameter_handler(Handler);
  sub_140001890();
  v4 = dword_140007028;
  v5 = dword_140007028 + 1;
  v6 = malloc(8 * v5);
  v7 = (__int64)v6;
  if ( v4 <= 0 )
  {
    v15 = v6;
  }
  else
  {
    v8 = qword_140007020;
    v9 = 8 * v5 - 8;
    v10 = 0i64;
    do
    {
      v11 = strlen(*(const char **)(v8 + v10));
      v12 = v11 + 1;
      v13 = malloc(v11 + 1);
      *(_QWORD *)(v7 + v10) = v13;
      v14 = *(const void **)(v8 + v10);
      v10 += 8i64;
      memcpy(v13, v14, v12);
    }
    while ( v9 != v10 );
    v15 = (_QWORD *)(v7 + v9);
  }
  *v15 = 0i64;
  qword_140007020 = v7;
  sub_140001690();
  v16 = (unsigned int)dword_140007028;
  *(_QWORD *)off_140003078 = qword_140007018;
  result = sub_1400014FB(v16, qword_140007020);
  dword_140007010 = result;
  if ( !dword_14000700C )
    exit(result);
  if ( !dword_140007008 )
  {
    cexit();
    result = (unsigned int)dword_140007010;
  }
  return result;
}

从整体结构看,该函数不是业务函数,而是程序启动阶段的运行库初始化函数。它具备以下典型 CRT Startup 特征:

  1. 使用全局锁防止 CRT 初始化重入;
  2. 使用全局状态变量记录 CRT 初始化阶段;
  3. 调用 initterm() 执行初始化函数表;
  4. 调用 TLS Callback;
  5. 设置 SetUnhandledExceptionFilter()
  6. 设置 set_invalid_parameter_handler()
  7. 准备 argc / argv / envp
  8. 调用真实入口函数;
  9. 根据运行模式调用 exit()cexit()

核心用户入口调用点:

c 复制代码
result = sub_1400014FB(v16, qword_140007020);

其中:

c 复制代码
v16 = (unsigned int)dword_140007028;
qword_140007020 = argv;

因此 sub_1400014FB() 很可能对应:

c 复制代码
main(argc, argv)

或编译器生成的 main 包装函数。

2. 整体执行流程

该函数可以抽象为以下伪代码:

c 复制代码
int CRTStartup()
{
    acquire_startup_lock();

    if (crt_state == INITIALIZING)
        runtime_error_exit(31);

    if (crt_state == UNINITIALIZED)
    {
        crt_state = INITIALIZING;
        call_c_initializers();
        call_cpp_initializers();
        crt_state = INITIALIZED;
    }
    else
    {
        already_initialized = true;
    }

    release_startup_lock_if_needed();

    call_tls_callback_if_exists();

    setup_runtime_environment();
    setup_exception_filter();
    setup_invalid_parameter_handler();

    duplicate_argv();
    setup_environment_pointer();

    result = main(argc, argv);

    if (!managed_mode)
        exit(result);

    if (!already_initialized)
        cexit();

    return result;
}

3. 初始化锁的设置

对应代码:

c 复制代码
_RBX = &unk_140007040;
v1 = *(_QWORD *)(__readgsqword(0x30u) + 8);
while ( 1 )
{
  v2 = _InterlockedCompareExchange((volatile signed __int64 *)&unk_140007040, v1, 0i64);
  if ( !v2 )
  {
    v3 = 0;
    if ( unk_140007048 == 1 )
      goto LABEL_20;
    goto LABEL_6;
  }
  if ( v1 == v2 )
    break;
  ((void (__fastcall *)(signed __int64))Sleep)(1000i64);
}


### 3.1 `unk_140007040` 的含义

`unk_140007040` 被作为 `_InterlockedCompareExchange()` 的目标地址使用:

```c
_InterlockedCompareExchange(&unk_140007040, v1, 0);

这说明它是一个全局同步变量,作用类似:

c 复制代码
static volatile LONG_PTR startup_lock;

它用于保证 CRT 初始化代码同一时间只被一个线程执行

3.2 _InterlockedCompareExchange 的含义

原子操作逻辑为:

c 复制代码
old = *lock;
if (*lock == 0)
    *lock = current_thread_or_fiber_id;
return old;

对应本函数:

c 复制代码
v2 = _InterlockedCompareExchange(&unk_140007040, v1, 0);

含义是:

  • 如果 unk_140007040 == 0,说明当前没有线程持有初始化锁;
  • unk_140007040 设置为 v1
  • 当前线程获得初始化锁;
  • 如果返回值 v2 != 0,说明锁已经被其他执行流持有。

3.3 v1 的来源

c 复制代码
v1 = *(_QWORD *)(__readgsqword(0x30u) + 8);

__readgsqword() 用于读取 x64 Windows 下 GS 段中的线程环境结构。这里取出的值被用作初始化锁的拥有者标识。

在 CRT Startup 代码中,这类值通常用于判断:

c 复制代码
if (startup_lock == current_thread_id)
{
    // 当前线程递归进入初始化流程
}

3.4 锁竞争逻辑

代码:

c 复制代码
if ( !v2 )
{
  v3 = 0;
  ...
}
if ( v1 == v2 )
  break;
Sleep(1000);

含义如下:

条件 含义
v2 == 0 当前执行流成功获得 CRT 初始化锁
v2 == v1 当前执行流已经持有锁,属于递归进入
v2 != 0 && v2 != v1 其他线程正在初始化 CRT,当前线程等待

等待方式为:

c 复制代码
Sleep(1000);

也就是每次等待 1000 毫秒后重新尝试获取锁。

3.5 v3 的含义

c 复制代码
v3 = 0;

表示当前线程是首次成功获得锁,需要在后面释放锁。

后面还有:

c 复制代码
v3 = 1;

表示当前线程是递归进入初始化流程,不应该重复释放锁。

因此可以推测:

c 复制代码
v3 == 0  // 当前调用负责释放初始化锁
v3 == 1  // 当前调用不负责释放初始化锁

4. CRT 初始化状态机

核心状态变量:

c 复制代码
unk_140007048

相关代码:

c 复制代码
if ( unk_140007048 == 1 )
LABEL_20:
  sub_1400028B0(31i64);
LABEL_6:
if ( unk_140007048 )
{
  dword_140007008 = 1;
}
else
{
  unk_140007048 = 1;
  initterm(&unk_140004A60, &unk_140004A70);
}
if ( unk_140007048 == 1 )
{
  initterm(&unk_140004A48, &unk_140004A58);
  unk_140007048 = 2;
  if ( v3 )
    goto LABEL_10;
}
else if ( v3 )
{
  goto LABEL_10;
}

unk_140007048 很可能对应 CRT 初始化状态:

含义
0 尚未初始化
1 正在初始化
2 初始化完成

4.1 检测非法重入

代码:

c 复制代码
if ( unk_140007048 == 1 )
  sub_1400028B0(31i64);

如果状态为 1,表示 CRT 正在初始化过程中。如果此时再次进入初始化流程,则属于异常情况。

sub_1400028B0(31) 很可能是运行库错误退出函数,类似:

c 复制代码
_amsg_exit(31);

该错误通常表示 CRT 初始化阶段发生非法重入或初始化顺序错误。

5. C 初始化函数调用

代码:

c 复制代码
if ( unk_140007048 )
{
  dword_140007008 = 1;
}
else
{
  unk_140007048 = 1;
  initterm(&unk_140004A60, &unk_140004A70);
}

unk_140007048 == 0 时,说明 CRT 尚未初始化,于是:

c 复制代码
unk_140007048 = 1;
initterm(&unk_140004A60, &unk_140004A70);

也就是将状态设置为"初始化中",然后调用一段初始化函数表。

5.1 initterm() 的作用

initterm() 是 MSVC CRT 中常见函数,用于遍历函数指针区间并逐个调用

典型形式:

c 复制代码
void initterm(_PVFV *begin, _PVFV *end)
{
    while (begin < end)
    {
        if (*begin != NULL)
            (**begin)();
        ++begin;
    }
}

在本函数中:

c 复制代码
initterm(&unk_140004A60, &unk_140004A70);

含义是调用地址区间:

c 复制代码
[0x140004A60, 0x140004A70)

中的函数指针。

这一段通常对应 C 初始化表,例如 .CRT$XIA ~ .CRT$XIZ

6. C++ 全局对象构造函数调用

代码:

c 复制代码
if ( unk_140007048 == 1 )
{
  initterm(&unk_140004A48, &unk_140004A58);
  unk_140007048 = 2;
  if ( v3 )
    goto LABEL_10;
}

在 C 初始化函数执行完成后,如果状态仍为 1,继续调用第二组初始化函数:

c 复制代码
initterm(&unk_140004A48, &unk_140004A58);

该区间通常用于 C++ 全局对象构造函数,例如:

c 复制代码
class A {
public:
    A() { ... }
};

A g_obj;

类似 g_obj 这样的全局对象,其构造函数会在进入 main() 之前执行。

执行完成后:

c 复制代码
unk_140007048 = 2;

表示 CRT 初始化完成。

7. 释放 CRT 初始化锁

代码:

c 复制代码
_RAX = 0i64;
__asm { xchg    rax, [rbx] }

前面有:

c 复制代码
_RBX = &unk_140007040;

因此该汇编等价于:

c 复制代码
unk_140007040 = 0;

即释放 CRT 初始化锁。

不过释放锁前有条件控制:

c 复制代码
if ( v3 )
  goto LABEL_10;

如果 v3 == 1,说明当前调用是递归进入,不是锁的实际拥有者,因此不释放锁。

如果 v3 == 0,说明当前调用成功获取了锁,因此需要释放。

8. TLS Callback 调用

代码:

c 复制代码
LABEL_10:
  if ( TlsCallback_0 )
    TlsCallback_0(0i64, 2i64, 0i64);

TLS Callback 的标准原型为:

c 复制代码
void NTAPI TlsCallback(
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
);

这里传入参数:

c 复制代码
TlsCallback_0(NULL, 2, NULL);

第二个参数 2 对应 Windows 中的:

c 复制代码
DLL_THREAD_ATTACH

虽然 DLL_THREAD_ATTACH 通常用于 DLL 线程附加通知,但在某些 CRT 启动代码、EXE TLS 初始化或保护壳场景中,可能会手动触发 TLS 回调。

需要重点关注 TlsCallback_0,因为 TLS Callback 经常用于:

  1. 早于 main() 执行初始化逻辑;
  2. 设置反调试逻辑;
  3. 解密代码段或数据段;
  4. 检查运行环境;
  5. 初始化全局状态。

如果逆向目标是恶意样本或加壳程序,TLS Callback 是高优先级分析点。

9. 异常处理与运行库处理器设置

代码:

c 复制代码
sub_140001A80();
qword_1400070D0 = (__int64)SetUnhandledExceptionFilter(TopLevelExceptionFilter);
set_invalid_parameter_handler(Handler);
sub_140001890();

9.1 sub_140001A80()

该函数位于异常处理器设置之前,可能用于:

  1. 初始化安全 Cookie;
  2. 初始化运行库内部结构;
  3. 初始化 I/O、堆或环境;
  4. 初始化反调试或异常相关上下文。

需要结合该函数内部代码进一步确认。

9.2 设置顶层异常过滤器

代码:

c 复制代码
qword_1400070D0 = (__int64)SetUnhandledExceptionFilter(TopLevelExceptionFilter);

等价于:

c 复制代码
old_filter = SetUnhandledExceptionFilter(TopLevelExceptionFilter);

作用是注册新的顶层异常处理函数。

当程序发生未处理异常时,例如:

c 复制代码
int *p = NULL;
*p = 1;

如果没有其他异常处理捕获,系统会调用:

c 复制代码
TopLevelExceptionFilter

返回值保存到:

c 复制代码
qword_1400070D0

它可能用于后续恢复旧异常处理器,或者在当前异常处理器无法处理时继续调用旧处理器。

逆向时应重点分析 TopLevelExceptionFilter,因为它可能包含:

  1. 崩溃日志记录;
  2. 自定义异常恢复;
  3. 反调试逻辑;
  4. SEH/VEH 相关控制流混淆;
  5. 解密或跳转到隐藏代码。

9.3 设置非法参数处理器

代码:

c 复制代码
set_invalid_parameter_handler(Handler);

MSVC CRT 的安全函数在遇到非法参数时,会触发 invalid parameter handler。例如:

c 复制代码
strcpy_s(NULL, 10, "abc");
printf(NULL);
fopen_s(NULL, "a.txt", "r");

默认情况下,CRT 可能会弹窗、终止进程或调用默认错误处理逻辑。

此处设置自定义 Handler,说明程序希望自己接管这类异常情况。

需要重点分析:

c 复制代码
Handler

它可能用于:

  1. 忽略 CRT 参数错误;
  2. 统一退出;
  3. 记录日志;
  4. 混淆异常行为;
  5. 配合反调试绕过默认错误路径。

10. 命令行参数 argv 的复制

代码:

c 复制代码
v4 = dword_140007028;
v5 = dword_140007028 + 1;
v6 = malloc(8 * v5);
v7 = (__int64)v6;

这里 dword_140007028 大概率是:

c 复制代码
argc

v5 = argc + 1,然后:

c 复制代码
malloc(8 * (argc + 1));

x64 下指针大小是 8 字节,因此这是在分配:

c 复制代码
char **argv_copy = malloc(sizeof(char *) * (argc + 1));

10.1 无参数或 argc <= 0 的情况

代码:

c 复制代码
if ( v4 <= 0 )
{
  v15 = v6;
}

如果 argc <= 0,则没有参数需要复制,直接让 v15 指向新分配的数组开头。

后面会执行:

c 复制代码
*v15 = 0i64;

也就是:

c 复制代码
argv_copy[0] = NULL;

10.2 有参数时逐个复制字符串

代码:

c 复制代码
else
{
  v8 = qword_140007020;
  v9 = 8 * v5 - 8;
  v10 = 0i64;
  do
  {
    v11 = strlen(*(const char **)(v8 + v10));
    v12 = v11 + 1;
    v13 = malloc(v11 + 1);
    *(_QWORD *)(v7 + v10) = v13;
    v14 = *(const void **)(v8 + v10);
    v10 += 8i64;
    memcpy(v13, v14, v12);
  }
  while ( v9 != v10 );
  v15 = (_QWORD *)(v7 + v9);
}

变量含义:

变量 含义
v8 原始 argv 指针数组
v7 新分配的 argv_copy 指针数组
v10 当前参数偏移,单位字节,每次加 8
v11 当前参数字符串长度
v12 当前参数字符串长度 + 1,包括 \0
v13 为当前参数新分配的字符串缓冲区
v14 原始参数字符串地址
v9 最后一个参数指针的偏移

循环逻辑等价于:

c 复制代码
for (int i = 0; i < argc; i++)
{
    size_t len = strlen(argv[i]) + 1;
    argv_copy[i] = malloc(len);
    memcpy(argv_copy[i], argv[i], len);
}

10.3 添加 argv 结束 NULL

代码:

c 复制代码
*v15 = 0i64;
qword_140007020 = v7;

等价于:

c 复制代码
argv_copy[argc] = NULL;
qword_140007020 = argv_copy;

C 标准中 argv[argc] 必须为 NULL,因此这里是在保证复制后的参数数组符合标准格式。

11. 环境变量和运行环境设置

代码:

c 复制代码
sub_140001690();
v16 = (unsigned int)dword_140007028;
*(_QWORD *)off_140003078 = qword_140007018;

11.1 sub_140001690()

该函数位于参数复制之后、调用 main 之前,可能用于进一步初始化运行时环境,例如:

  1. 初始化环境变量;
  2. 初始化当前目录信息;
  3. 初始化 locale;
  4. 初始化标准 I/O;
  5. 整理 argv/envp 指针;
  6. 设置全局运行库变量。

11.2 设置环境变量指针

代码:

c 复制代码
*(_QWORD *)off_140003078 = qword_140007018;

这里 qword_140007018 很可能是环境变量指针,例如:

c 复制代码
envp

off_140003078 可能是某个 CRT 全局环境指针的地址,例如:

c 复制代码
_environ

因此这句可以理解为:

c 复制代码
_environ = qword_140007018;

或:

c 复制代码
*__p__environ() = envp;

12. 调用真正用户入口函数

代码:

c 复制代码
result = sub_1400014FB(v16, qword_140007020);
dword_140007010 = result;

其中:

c 复制代码
v16 = (unsigned int)dword_140007028;
qword_140007020 = argv_copy;

因此该调用等价于:

c 复制代码
result = sub_1400014FB(argc, argv_copy);

高度疑似:

c 复制代码
result = main(argc, argv);

或:

c 复制代码
result = invoke_main(argc, argv);

返回值保存到:

c 复制代码
dword_140007010

也就是程序退出码。

逆向分析时,sub_1400014FB 是本函数之后最重要的分析目标,因为它很可能开始进入真正的用户逻辑。

13. 程序退出路径

代码:

c 复制代码
if ( !dword_14000700C )
  exit(result);
if ( !dword_140007008 )
{
  cexit();
  result = (unsigned int)dword_140007010;
}
return result;

13.1 dword_14000700C

该变量用于判断是否直接调用 exit()

如果:

c 复制代码
dword_14000700C == 0

则:

c 复制代码
exit(result);

exit() 会执行完整的 CRT 退出流程,包括:

  1. 调用 atexit() 注册函数;
  2. 调用全局对象析构函数;
  3. 刷新并关闭标准 I/O;
  4. 终止进程。

如果:

c 复制代码
dword_14000700C != 0

则不直接 exit(),而是继续执行后面的清理逻辑。

该变量可能表示:

  1. 是否为托管应用;
  2. 是否由外部宿主控制退出;
  3. 是否只返回退出码而不终止进程。

13.2 dword_140007008

前面代码中:

c 复制代码
if ( unk_140007048 )
{
  dword_140007008 = 1;
}

说明如果进入函数时 CRT 已经初始化过,则设置:

c 复制代码
dword_140007008 = 1;

退出时:

c 复制代码
if ( !dword_140007008 )
{
  cexit();
  result = (unsigned int)dword_140007010;
}

含义是:

  • 如果本次调用负责 CRT 初始化,则本次调用也负责 CRT 清理;
  • 如果 CRT 在进入本函数之前已经初始化,则不重复清理。

13.3 cexit() 的作用

cexit() 是 CRT 清理函数,但通常不会直接终止当前进程。它主要用于执行 CRT 清理逻辑,包括:

  1. 调用终止函数表;
  2. 调用 C++ 全局析构;
  3. 刷新运行库状态。

exit() 的区别是:

函数 是否终止进程 是否执行 CRT 清理
exit()
cexit()

14. 全局变量含义推测

变量 推测含义 依据
unk_140007040 CRT 初始化锁 _InterlockedCompareExchange 原子修改
unk_140007048 CRT 初始化状态 判断 0/1/2 初始化阶段
dword_140007008 CRT 是否已初始化标志 影响是否调用 cexit()
dword_14000700C 是否直接 exit() 的模式标志 控制 exit(result) 路径
dword_140007010 主函数返回值 / exit code 保存 sub_1400014FB 返回值
dword_140007028 argc 被传给主函数作为第一个参数
qword_140007020 argv 被复制并传给主函数
qword_140007018 envp / 环境指针 被写入 off_140003078 指向的位置
qword_1400070D0 旧的顶层异常处理器 保存 SetUnhandledExceptionFilter 返回值
TlsCallback_0 TLS 回调函数 按 TLS Callback 形式调用
TopLevelExceptionFilter 顶层异常处理函数 传给 SetUnhandledExceptionFilter
Handler CRT 非法参数处理器 传给 set_invalid_parameter_handler
sub_1400014FB 真正用户入口 argc, argv 形式调用

15. 结合代码的完整伪代码还原

根据当前反编译代码,可以还原为:

c 复制代码
int sub_140001180(void)
{
    int nested_or_reentered;
    int argc;
    char **new_argv;
    int result;

    startup_lock_ptr = &unk_140007040;
    current_owner = get_current_thread_or_fiber_id();

    while (true)
    {
        old_owner = InterlockedCompareExchange(
            &unk_140007040,
            current_owner,
            0
        );

        if (old_owner == 0)
        {
            nested_or_reentered = 0;

            if (crt_state == INITIALIZING)
                runtime_error_exit(31);

            break;
        }

        if (old_owner == current_owner)
        {
            nested_or_reentered = 1;
            break;
        }

        Sleep(1000);
    }

    if (crt_state != UNINITIALIZED)
    {
        already_initialized = 1;
    }
    else
    {
        crt_state = INITIALIZING;
        initterm(c_init_begin, c_init_end);
    }

    if (crt_state == INITIALIZING)
    {
        initterm(cpp_init_begin, cpp_init_end);
        crt_state = INITIALIZED;

        if (!nested_or_reentered)
            release_startup_lock();
    }
    else
    {
        if (!nested_or_reentered)
            release_startup_lock();
    }

    if (TlsCallback_0 != NULL)
        TlsCallback_0(NULL, DLL_THREAD_ATTACH, NULL);

    sub_140001A80();

    old_exception_filter = SetUnhandledExceptionFilter(
        TopLevelExceptionFilter
    );

    set_invalid_parameter_handler(Handler);

    sub_140001890();

    argc = dword_140007028;
    new_argv = malloc(sizeof(char *) * (argc + 1));

    for (int i = 0; i < argc; i++)
    {
        size_t len = strlen(qword_140007020[i]) + 1;
        new_argv[i] = malloc(len);
        memcpy(new_argv[i], qword_140007020[i], len);
    }

    new_argv[argc] = NULL;
    qword_140007020 = new_argv;

    sub_140001690();

    *off_140003078 = qword_140007018;

    result = sub_1400014FB(argc, qword_140007020);
    dword_140007010 = result;

    if (!dword_14000700C)
        exit(result);

    if (!dword_140007008)
    {
        cexit();
        result = dword_140007010;
    }

    return result;
}

16. 总结

sub_140001180() 是一个典型的 CRT 初始化启动函数。它的主要作用不是实现程序业务,而是为真正的用户入口函数准备运行环境。

它完成了:

  1. CRT 初始化锁控制;
  2. CRT 初始化状态管理;
  3. C 初始化函数调用;
  4. C++ 全局构造函数调用;
  5. TLS Callback 调用;
  6. 顶层异常过滤器设置;
  7. CRT 非法参数处理器设置;
  8. argc / argv / envp 准备;
  9. 调用真实入口函数;
  10. 根据运行模式执行退出清理。
c 复制代码
sub_140001180()

应被命名为类似:

c 复制代码
CRTStartup

或:

c 复制代码
runtime_startup_wrapper

而真正的程序逻辑入口应重点关注:

c 复制代码
sub_1400014FB(argc, argv)

该函数才是后续题目分析的主要入口。比赛时,这个函数大致过一遍,找到main()函数就可以跳过去了。这里只是用于分析才长篇大论的展开去介绍。

相关推荐
问心无愧05132 小时前
ctf show web入门71
android·前端·笔记
夜勤月3 小时前
AQS 与 ThreadPoolExecutor 深度拆解:JDK 高并发底层设计精髓
android·java·开发语言
Yeyu3 小时前
Android 卡顿诊断 SDK:从痛点出发的设计思考
android
上天_去_做颗惺星 EVE_BLUE4 小时前
Ubuntu Android 虚拟机安装使用教程
android·linux·测试工具·ubuntu·安卓
我命由我123454 小时前
Android 开发问题:Could not find com.github.PicnicSupermarket:FingerPaintView:1.2.
android·github·android studio·安卓·android jetpack·android-studio·android runtime
黄林晴6 小时前
Google Play 全面进化:AI 驱动增长,从上架到收入全链路重构
android·google
qq3621967056 小时前
Android 12/13/14/15 Google Play 兼容性检查指南:设备不兼容怎么办?2026最新解决方案
android·gitee
韩曙亮6 小时前
【错误记录】flutter attach 附加设备 执行报错 ( 附加设备注意事项 )
android·javascript·flutter·flutter attach