Why Is The PE Entry Point Not The Same As Main Understanding __security_init_cookie and __scrt_common_main_seh (Patreon)
Downloads
Content
In this tutorial we discuss the __security_init_cookie and __scrt_common_main_seh functions and why the PE Entry Point on an MSVC binary does not point to Main.
This is an important concept to understand when first learning how to reverse engineer windows binaries, especially when using a debugger. Often it can be confusing when the debugger breaks on the PE Entry Point only to find some code that is not related to the main functionality of the binary. This code is often called "boilerplate" code and is inserted automatically by the MSVC compiler. As a reverse engineer it is useful to be able to identify and navigate this code.
MSVC Console Application Entry Point
The Entry Point on an MSVC console application servers two purposes, it calls the __security_init_cookie function then it jumps to the __scrt_common_main_seh thunk. The __scrt_common_main_seh thunk then performs some setup for the binary including some structured exception handler (SEH) setup and then calls main.
__security_init_cookie
The __security_init_cookie function is not too interesting to us from a reverse engineering perspective but it's important to be able to recognize it as a pattern as it can be used to "localize" ourselves if we are looking at PE file in memory. The purpose of this cookie is best described by MSDN: Initializes the global security cookie.
__scrt_common_main_seh
The __scrt_common_main_seh thunk is used to setup some SEH stuff for the binary and then call into main. When analyzing malware that has been compile with MSVC this code cn Abe ignored and quickly scanned to identify where it calls main. Main is where the actual developed code begins.
Looking at this code without labels (for example in x64dbg) it can be confusing to identify where main is. Luckily MSVC console applications all have the same main function prototype.
main(int argc, const char **argv, const char **envp)
This function prototype can be used to identify the call to main in __scrt_common_main_seh, simply by looking for the three arguments that are passed to main: argc, argv, envp. In 64-bit binaries these arguments are compiled into a series of three mov instructions moving the arguments into the registers RCX, RDX, R8.
In 32-bit binaries these arguments are compiled into a series of three push instructions pushing the arguments onto the stack.
These patterns can be relied on to identify main.
Self Study Examples
Attached to this post are the 64-bit sample we used in the tutorial and it's 32-bit counterpart. As a self-study exercise, unzip these files and copy them onto your FLARE-VM virtual machine.
** The samples are dynamically linked so if you are not using the FLARE-VM with Windows 10, you will need to have VCRUNTIME140.dll installed.
Once you have the file copied over to your VM attempt the following.
- Open the 64-bit binary in IDA and identify where __scrt_common_main_seh calls main.
- Open the 64-bit binary in x64dbg and run until the entry point
- Step over __security_init_cookie and into __scrt_common_main_seh.
- Examine __scrt_common_main_seh and try to identify where it calls main -- if you get stuck remember to look for the arguments moved to registers for main.
Now try the same steps with the 32-bit binary.
- Open the 32-bit binary in IDA and identify where __scrt_common_main_seh calls main.
- Open the 32-bit binary in x32dbg and run until the entry point
- Step over __security_init_cookie and into __scrt_common_main_seh.
- Examine __scrt_common_main_seh and try to identify where it calls main -- if you get stuck remember to look for the arguments pushed for main.