我们在VS C++开发程序在调试时总是从main或WinMain函数开始,这就让我们很容易误认为它们是程序的第一条指令,但是事实并非如此。main或WinMain函数需要一个调用者,在它们被调用前,编译器其实已经做了很多事情,所以main或WinMain是“语法规定的用户入口”,而不是"应用程序入口"
探寻在main函数调用前编译器做了什么事情:
在调用main()函数前编译器会添加一些初始化c语法或c++语法全局数据,初始化缓冲区溢出全局变量。
程序入口函数mainCRTStartup:
__scrt_common_main:
这个函数在内部调用了__security_init_cookie()来初始化缓冲区溢出全局变量。(可以通过变量来阻拦一下缓冲区溢出攻击)
__scrt_common_main_seh:
此函数用来初始化c或c++中的一些全局数据,初始化线程,然后调用invoke_main()。
invoke_main:
调用主函数。
x64dbg定位main函数:
IDA静态分析:
先将exe文件拖入IDA中,通过交叉引用从main反推到入口函数mainCRTStartup。
debug模式下x64程序从mian反推调用过程中汇编特征
1.main
2.jmp
3.4个call,跟进最后一个call
4.movzx
test
jz
mov
mov
call
call <--跟进
5.2个call,跟进最后一个call
6.一个call,跟进
7.jmp
x64dbg动态定位main:
首先我们将应用程序的pdb文件(调试信息)文件删除,模拟真实场景。
我们将exe文件拖入x64dbg.exe中。
可以看到我们目前还在系统文件空间ntdll.dll中。我们按下F9进入我们自己的应用程序空间。
接着我们根据上面IDA的分析反推来找到main函数
步骤如下:
1.jmp -->跟进
2.一个call,跟进
3.2个call,跟进最后一个call
4.movzx
test
jz
mov
mov
call
call <--跟进
5.4个call,跟进最后一个call
6.jmp