怎么寻找入口点Main函数
受到不同因素影响 位置不一样
发行版本 或者debug x86 x64 编译器版本都有可能影响main 函数位置
纯真(老)版本 入口点
- push
- push
- push
- call
入口点位置
fastcall 约定(在以前记录里能找到)
RCX
RDX
R8
入口点
怎么找main 函数
使用的实验代码
我使用vs2015 版本的 代码 (MTD) 进行逆向
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("hello,world");
system("pause");
return 0;
}
此代码 拖入 ida 进行分析 直接进入main函数 (有).plb直接进入 main_0界面
; Attributes: thunk
; int __cdecl main_0(int argc, const char **argv, const char **envp)
_main_0 proc near
argc= dword ptr 4
argv= dword ptr 8
envp= dword ptr 0Ch
jmp _main
_main_0 endp
jmp main 解释跳转到main 函数的地方 双击就可以
非可视化界面 使用ctrl + "+"找到main 双击进入即可进入 主函数
__cdecl main_0
但是 我们目的是什么 我们要探索该代码的规则 所以查看调用的 函数也就是**__cdecl main_0**
使用交叉引用就可以 找到调用到上个 函数 快捷键位ctrl + x
__cdecl invoke_main()
; Attributes: library function bp-based frame
; int __cdecl invoke_main()
invoke_main proc near
push ebp
mov ebp, esp
call j___get_initial_narrow_environment
push eax ; envp
call j____p___argv
mov eax, [eax]
push eax ; argv
call j____p___argc
mov ecx, [eax]
push ecx ; argc
call _main_0 ;<---- 这个就是上个main_0 函数
add esp, 0Ch
pop ebp
retn
invoke_main endp
一共三个 call 第三个 call 就是mian _0 我在注释上写 了
但是咱们不是往深处找 往 回找 找到最初的路线 cdecl invoke_mai 就是咱们要找的 点击进去然后 继续使用交叉引用跳转到

这里
invoke main 这里 就是刚才的入口 然后发现太麻烦了 可以选择变成普通模式
空格键可以变普通模式 like this ↓

FTP & _scrt_common_main_seh函数
add esp, 4
.text:00450EE6 movzx eax, al
.text:00450EE9 test eax, eax
.text:00450EEB jz short loc_450EFB
.text:00450EED mov ecx, [ebp+tls_dtor_callback]
.text:00450EF0 mov edx, [ecx]
.text:00450EF2 push edx ; _Callback
.text:00450EF3 call j___register_thread_local_exe_atexit_callback
.text:00450EF8 add esp, 4
.text:00450EFB
.text:00450EFB loc_450EFB: ; CODE XREF: __scrt_common_main_seh+128↑j
.text:00450EFB ; __scrt_common_main_seh+13B↑j
.text:00450EFB call invoke_main ; main函数
这里是个特殊的代码区 没有第二个 通过指令判断 这是什么 ? --> C++程序启动和线程局部存储(Thread Local Storage, TLS)
向上翻找到特殊的关键字看到main 的时候**__scrt_common_main**
__scrt_common_main_seh proc near ; CODE XREF: __scrt_common_main+8↑p
.text:00450DB0
.text:00450DB0 var_40 = dword ptr -40h
.text:00450DB0 var_3C = dword ptr -3Ch
.text:00450DB0 var_38 = dword ptr -38h
.text:00450DB0 return_code = dword ptr -34h
.text:00450DB0 xcptnum = dword ptr -30h
.text:00450DB0 main_result = dword ptr -2Ch
.text:00450DB0 Target = dword ptr -28h
.text:00450DB0 tls_dtor_callback= dword ptr -24h
.text:00450DB0 tls_init_callback= dword ptr -20h
.text:00450DB0 is_nested = byte ptr -1Ah
.text:00450DB0 has_cctor = byte ptr -19h
.text:00450DB0 ms_exc = CPPEH_RECORD ptr -18h
.text:00450DB0
.text:00450DB0 ; __unwind { // __except_handler4
.text:00450DB0
继续交叉引用 跳转到这里 这个函数
scrt_common_main()
scrt_common_main proc near ; CODE XREF: _mainCRTStartup+3↓p
.text:00450D90 push ebp
.text:00450D91 mov ebp, esp
.text:00450D93 call j____security_init_cookie
.text:00450D98 call __scrt_common_main_seh ;<--- 刚才那个函数上面那个的call 入口
.text:00450D9D pop ebp
.text:00450D9E retn
.text:00450D9E __scrt_common_main endp
这个第二个call 就是 根据 scrt_common_main 反向推 继续交叉引用
你会寻找到唯一的call __scrt_common_main 这就是刚才的入口继续找\
mainCRTStartup()
mainCRTStartup proc near ; CODE XREF: start_1↑j
.text:004510B0 push ebp
.text:004510B1 mov ebp, esp
.text:004510B3 call __scrt_common_main
.text:004510B8 pop ebp
.text:004510B9 retn
.text:004510B9 _mainCRTStartup endp
这个是真正的入口点 vs 编译的入口点真正的入口点
mainCRTStartup然后继续往上推进 交叉引用
就会找到第一层
int __cdecl start_1()
.text:0044A730
.text:0044A730 ; int __cdecl start_1()
.text:0044A730 public start_1
.text:0044A730 start_1 proc near
.text:0044A730 jmp _mainCRTStartup
.text:0044A730 start_1 endp
.text:0044A730
哎 你再交叉引用 你就没了
这就是第一层了 没了
动态调试查看 代码 (没有注释版本)
为啥要这么弄呢

.pdb 是符号 文件
咱们能看见这些函数名字都是因为他 所以咱们 把他删掉的时候 函数都会变成一串的数字
删除ilk 和pdb 然后
使用动态调试调试 一下 之前咱们调试过发现 搜索main 即可进入
但是咱们现在 把pdb删除了
现在进入ntdll.dll 了没有进入该程序 使用f9 进入断点 进入程序入口
那么入口就是咱们的__cdecl start_1 里面的jmp _mainCRTStartup

如图所示
F9 运行
F8 单步步过
- F7单步步进
- F2 是断点
然后使用F8 进入JMP 函数

这个 呢就是 mainCRTStartup
进入这个projecx 里面 f8 到call 这里f7 这里就是 __scrt_common_main() __的里面了
可以返回看为什么是2个call 对上了 第二个call 就是 下一个入口 第二个是下一个main 的入口点

然后进入下一个点寻找符合TLS 的函数来寻找下一个入口点的 _scrt_common_main_seh 区域 (自己回去找)

也就是说第二个call 就是main函数入口点 下个断点试试看
然后进入第二个call 发现了这个
- 其实是__cdecl invoke_main()函数

第四个call 就是 正确的call入口
进入后发现了大家很熟悉的东西

hello world 这就是入口点
也就是说可以写个类似的东西 就仿照一下就可以猜出来