在汇编中查找 Main
Find Main in Assembly
我有简单的 C++ 程序:
#include <iostream>
using namespace std;
void main()
{
cout << "Hello, world, from Visual C++!" << endl;
}
使用以下命令编译:cl /EHsc hello.cpp
我想开始调试可执行文件,如何在调试器中找到这个main函数对应的汇编代码? (我正在使用 x64dbg)
入口点与 Main 函数不同。
我找到了主要功能,它离入口点不近,我有字符串,我很容易找到它。
是否有任何方法或规则或最佳实践来猜测 main 对应的汇编代码在哪里?
编辑:
我有源代码,但我只是在学习 RE。
一般不能。
当你编译一个程序时,你会得到一个二进制文件和(可选的)调试符号。
如果你有调试符号,让 IDA 或你的调试器加载它们,然后你应该能够用符号计算 main
到函数的地址(例如在 IDA 中,只需按 g
并写 main
然后你会在那里。在 WinDbg 或 gdb 中你可以输入 b main
)
然而,更常见的情况是在您没有调试符号的二进制文件上找到 main
函数。在这种情况下,您不知道 main
函数在哪里,甚至不知道它是否存在。二进制文件可能不会使用入口点执行初始化然后调用 main(int argc, char *argv[], char *envp[])
.
的常见 libc
做法
但是因为你是一个聪明人,我建议阅读你认为你正在使用的 compiler/platform 的 libc
实现,并遵循平台定义的逻辑入口点,直到您看到 call main
指令。
(请注意,.NET 二进制文件和其他类型的二进制文件的行为可能完全不同。)
如果您调试自己的代码 - 在调试器下停止某处的最佳方式 - 使用下一个代码
if (IsDebuggerPresent()) __debugbreak();
因此您可以将其插入 main
或任何其他位置的开头。
如果您调试的不是自己的二进制代码 - 二进制代码根本不包含 c/c++
CRT
代码 - 所以问题变得毫无意义。然而,如果 CRT
代码存在,尽管有许多不同的实现 - 都使用通用模式并且经过一些练习 - 可能会在 CRT
代码调用 main
的地方找到。
如果标准 windows 二进制文件存在 pdb
个文件 - 这根本不是问题
虽然入口点通常不是在您的可执行文件中定义的 main
,但这仅仅是因为编译器用一些初始化代码包装 main
是很常见的。
在大多数情况下,初始化代码非常相似,并且每个编译器都有少数几个版本之一。这些函数中的大多数都有一个 IDA FLIRT signature,用 IDA 打开二进制文件会自动为您定义一个 WinMain
、main
等函数。您也可以为此使用 IDA 的免费(试用)版本。
如果不是这种情况,通过跟踪入口点函数内部一层深度的几次调用,从入口点到达 main
非常简单。 main
调用通常接近入口点函数的末尾。
这是一个示例,main
函数在底部附近被选中(请注意,这是使用 mingw 为 windows 编译的 unix 可执行文件,因此这与大多数本机 win32 可执行文件有些不同)。
我有简单的 C++ 程序:
#include <iostream>
using namespace std;
void main()
{
cout << "Hello, world, from Visual C++!" << endl;
}
使用以下命令编译:cl /EHsc hello.cpp
我想开始调试可执行文件,如何在调试器中找到这个main函数对应的汇编代码? (我正在使用 x64dbg)
入口点与 Main 函数不同。 我找到了主要功能,它离入口点不近,我有字符串,我很容易找到它。
是否有任何方法或规则或最佳实践来猜测 main 对应的汇编代码在哪里?
编辑:
我有源代码,但我只是在学习 RE。
一般不能。
当你编译一个程序时,你会得到一个二进制文件和(可选的)调试符号。
如果你有调试符号,让 IDA 或你的调试器加载它们,然后你应该能够用符号计算 main
到函数的地址(例如在 IDA 中,只需按 g
并写 main
然后你会在那里。在 WinDbg 或 gdb 中你可以输入 b main
)
然而,更常见的情况是在您没有调试符号的二进制文件上找到 main
函数。在这种情况下,您不知道 main
函数在哪里,甚至不知道它是否存在。二进制文件可能不会使用入口点执行初始化然后调用 main(int argc, char *argv[], char *envp[])
.
libc
做法
但是因为你是一个聪明人,我建议阅读你认为你正在使用的 compiler/platform 的 libc
实现,并遵循平台定义的逻辑入口点,直到您看到 call main
指令。
(请注意,.NET 二进制文件和其他类型的二进制文件的行为可能完全不同。)
如果您调试自己的代码 - 在调试器下停止某处的最佳方式 - 使用下一个代码
if (IsDebuggerPresent()) __debugbreak();
因此您可以将其插入 main
或任何其他位置的开头。
如果您调试的不是自己的二进制代码 - 二进制代码根本不包含 c/c++
CRT
代码 - 所以问题变得毫无意义。然而,如果 CRT
代码存在,尽管有许多不同的实现 - 都使用通用模式并且经过一些练习 - 可能会在 CRT
代码调用 main
的地方找到。
如果标准 windows 二进制文件存在 pdb
个文件 - 这根本不是问题
虽然入口点通常不是在您的可执行文件中定义的 main
,但这仅仅是因为编译器用一些初始化代码包装 main
是很常见的。
在大多数情况下,初始化代码非常相似,并且每个编译器都有少数几个版本之一。这些函数中的大多数都有一个 IDA FLIRT signature,用 IDA 打开二进制文件会自动为您定义一个 WinMain
、main
等函数。您也可以为此使用 IDA 的免费(试用)版本。
如果不是这种情况,通过跟踪入口点函数内部一层深度的几次调用,从入口点到达 main
非常简单。 main
调用通常接近入口点函数的末尾。
这是一个示例,main
函数在底部附近被选中(请注意,这是使用 mingw 为 windows 编译的 unix 可执行文件,因此这与大多数本机 win32 可执行文件有些不同)。