IDA 反汇编生成与 Visual Studio 2017 年编译的 exe 的 ASM 文件完全不同的代码
IDA disassembly produces completely different code to ASM file for exe compiled in Visual Studio 2017
我使用 Visual Studio 2017
编译了一个简单的程序
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
我用命令行编译
cl 1.cpp /Fa1.asm
这给了我(大部分)对我有意义的汇编代码
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.14.26429.4
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
CONST SEGMENT
$SG5542 DB 'hello, world', 0aH, 00H
CONST ENDS
PUBLIC ___local_stdio_printf_options
PUBLIC __vfprintf_l
PUBLIC _printf
PUBLIC _main
PUBLIC ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
EXTRN ___acrt_iob_func:PROC
EXTRN ___stdio_common_vfprintf:PROC
; COMDAT ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA
_BSS SEGMENT
?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_BSS ENDS
; Function compile flags: /Odtp
_TEXT SEGMENT
_main PROC
; File c:\users\mr dai\documents\michael\study\cybersecurity\reverseengineering4beg\random\random\random.cpp
; Line 3
push ebp
mov ebp, esp
; Line 4
push OFFSET $SG5542
call _printf
add esp, 4
; Line 5
xor eax, eax
; Line 6
pop ebp
ret 0
_main ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT _printf
_TEXT SEGMENT
__Result$ = -8 ; size = 4
__ArgList$ = -4 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\stdio.h
; Line 954
push ebp
mov ebp, esp
sub esp, 8
; Line 957
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
; Line 958
mov ecx, DWORD PTR __ArgList$[ebp]
push ecx
push 0
mov edx, DWORD PTR __Format$[ebp]
push edx
push 1
call ___acrt_iob_func
add esp, 4
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
; Line 959
mov DWORD PTR __ArgList$[ebp], 0
; Line 960
mov eax, DWORD PTR __Result$[ebp]
; Line 961
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT __vfprintf_l
_TEXT SEGMENT
__Stream$ = 8 ; size = 4
__Format$ = 12 ; size = 4
__Locale$ = 16 ; size = 4
__ArgList$ = 20 ; size = 4
__vfprintf_l PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\stdio.h
; Line 642
push ebp
mov ebp, esp
; Line 643
mov eax, DWORD PTR __ArgList$[ebp]
push eax
mov ecx, DWORD PTR __Locale$[ebp]
push ecx
mov edx, DWORD PTR __Format$[ebp]
push edx
mov eax, DWORD PTR __Stream$[ebp]
push eax
call ___local_stdio_printf_options
mov ecx, DWORD PTR [eax+4]
push ecx
mov edx, DWORD PTR [eax]
push edx
call ___stdio_common_vfprintf
add esp, 24 ; 00000018H
; Line 644
pop ebp
ret 0
__vfprintf_l ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT ___local_stdio_printf_options
_TEXT SEGMENT
___local_stdio_printf_options PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\corecrt_stdio_config.h
; Line 85
push ebp
mov ebp, esp
; Line 87
mov eax, OFFSET ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
; Line 88
pop ebp
ret 0
___local_stdio_printf_options ENDP
_TEXT ENDS
END
但是我用 IDA 打开 exe,反汇编程序吐出一些完全无法理解的东西。
为什么会这样? visual studio 是否默认自动混淆汇编代码?我用谷歌搜索,但似乎并非如此。如果是这样,我该如何关闭混淆?
谢谢
main
不是你这个级别的程序的入口点。
C语言是一个抽象,之前main
可以运行runtime must be initialised.
C++ 更复杂,但思想是一样的:有一些代码是 运行 在 main
之前(否则谁会初始化 cout
例如?)
归根结底,所有语言都会编译成二进制 PE,熟悉这一点很重要。
我已经编译了你的程序以展示如何找到 main
。
但是请注意,根据您的代码,我假设您在查看反汇编时正在编译一个 C 文件,您似乎编译了一个 C++ 文件。
众所周知,C++ 的逆向工程更为复杂。
下面的示例与您的不同,我的是 x86 调试版本。
首先,IDA 会在 Exports 选项卡
中告诉您 PE 入口点在哪里
如果你双击它并按照 jmp
和 call
指令的路径(只有一条路径,你不会迷路)你会得到一个有两个调用的例程
VS 生成安全 cookie 作为第一件事,这就是调用的第一个函数所做的事情:
请注意,即使没有 IDA 提示,此例程也很容易识别,因为它会进行非常准确的 API 调用,您可以 Google 图片中的一些函数名称来查找文档.
评估这是安全cookie生成例程我们然后回到上一个并进入第二个调用
这是您的程序的主体,不是 main
,而是初始化和完成 CRT 的地方,包括调用 main
。
看一下左下角的流程图,大部分工作都在左边的 b运行ch 中(意味着右边的 b运行ch 是一个错误条件)。
main
通常在 _exit
或 _cexit
之前调用 call
秒。
然后我们接近这些调用:
如果将鼠标悬停在函数调用上,IDA 将显示函数代码。
单个 jmp
函数出现在调试版本中以帮助调试器,并且通常是 运行 时间函数。
第一个带圆圈的函数,悬停时显示对 "enviroment" 例程的调用,这很好,因为 main
需要程序参数(Windows 不要将参数传递给程序,那里是获得它们的特定 API)。
这看起来像是对 main
的调用,参数匹配。
事实上,如果我们输入呼叫,我们会到达 main
:
当然,您可以通过简单地在 "String view" 中查找 "Hello, world!" 字符串找到 main
(Shift + F12)但在现实世界的逆向工程场景中,这几乎总是不可能的。
制作程序然后逆向工程是一个很好的方法,如果你安装了VS,你可能有MS DIA SDK允许IDA读取pdb
文件。
这对逆向工程有很大帮助,你可以加载两个 IDA,一个有 PDB,一个没有,然后比较。
不幸的是,获得 MS DIA SDK 可能并不那么容易。
此外,IDA FLIRT 是必须的。
它是一个方法签名库,它允许 IDA 识别 运行time 函数,从而非常容易地专注于应用程序的真实代码。
虽然很难找到(也更难生成)签名,但它们是完全值得的。
最后,请注意,由于生成的代码类型不同,调试版本可能更难进行逆向工程。
如果您制作发布版本并对其进行逆向工程,您会发现更容易达到 main
.
我使用 Visual Studio 2017
编译了一个简单的程序#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
我用命令行编译
cl 1.cpp /Fa1.asm
这给了我(大部分)对我有意义的汇编代码
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.14.26429.4
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
CONST SEGMENT
$SG5542 DB 'hello, world', 0aH, 00H
CONST ENDS
PUBLIC ___local_stdio_printf_options
PUBLIC __vfprintf_l
PUBLIC _printf
PUBLIC _main
PUBLIC ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
EXTRN ___acrt_iob_func:PROC
EXTRN ___stdio_common_vfprintf:PROC
; COMDAT ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA
_BSS SEGMENT
?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_BSS ENDS
; Function compile flags: /Odtp
_TEXT SEGMENT
_main PROC
; File c:\users\mr dai\documents\michael\study\cybersecurity\reverseengineering4beg\random\random\random.cpp
; Line 3
push ebp
mov ebp, esp
; Line 4
push OFFSET $SG5542
call _printf
add esp, 4
; Line 5
xor eax, eax
; Line 6
pop ebp
ret 0
_main ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT _printf
_TEXT SEGMENT
__Result$ = -8 ; size = 4
__ArgList$ = -4 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\stdio.h
; Line 954
push ebp
mov ebp, esp
sub esp, 8
; Line 957
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
; Line 958
mov ecx, DWORD PTR __ArgList$[ebp]
push ecx
push 0
mov edx, DWORD PTR __Format$[ebp]
push edx
push 1
call ___acrt_iob_func
add esp, 4
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
; Line 959
mov DWORD PTR __ArgList$[ebp], 0
; Line 960
mov eax, DWORD PTR __Result$[ebp]
; Line 961
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT __vfprintf_l
_TEXT SEGMENT
__Stream$ = 8 ; size = 4
__Format$ = 12 ; size = 4
__Locale$ = 16 ; size = 4
__ArgList$ = 20 ; size = 4
__vfprintf_l PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\stdio.h
; Line 642
push ebp
mov ebp, esp
; Line 643
mov eax, DWORD PTR __ArgList$[ebp]
push eax
mov ecx, DWORD PTR __Locale$[ebp]
push ecx
mov edx, DWORD PTR __Format$[ebp]
push edx
mov eax, DWORD PTR __Stream$[ebp]
push eax
call ___local_stdio_printf_options
mov ecx, DWORD PTR [eax+4]
push ecx
mov edx, DWORD PTR [eax]
push edx
call ___stdio_common_vfprintf
add esp, 24 ; 00000018H
; Line 644
pop ebp
ret 0
__vfprintf_l ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT ___local_stdio_printf_options
_TEXT SEGMENT
___local_stdio_printf_options PROC ; COMDAT
; File c:\program files (x86)\windows kits\include.0.17134.0\ucrt\corecrt_stdio_config.h
; Line 85
push ebp
mov ebp, esp
; Line 87
mov eax, OFFSET ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
; Line 88
pop ebp
ret 0
___local_stdio_printf_options ENDP
_TEXT ENDS
END
但是我用 IDA 打开 exe,反汇编程序吐出一些完全无法理解的东西。
为什么会这样? visual studio 是否默认自动混淆汇编代码?我用谷歌搜索,但似乎并非如此。如果是这样,我该如何关闭混淆?
谢谢
main
不是你这个级别的程序的入口点。
C语言是一个抽象,之前main
可以运行runtime must be initialised.
C++ 更复杂,但思想是一样的:有一些代码是 运行 在 main
之前(否则谁会初始化 cout
例如?)
归根结底,所有语言都会编译成二进制 PE,熟悉这一点很重要。
我已经编译了你的程序以展示如何找到 main
。
但是请注意,根据您的代码,我假设您在查看反汇编时正在编译一个 C 文件,您似乎编译了一个 C++ 文件。
众所周知,C++ 的逆向工程更为复杂。
下面的示例与您的不同,我的是 x86 调试版本。
首先,IDA 会在 Exports 选项卡
中告诉您 PE 入口点在哪里如果你双击它并按照 jmp
和 call
指令的路径(只有一条路径,你不会迷路)你会得到一个有两个调用的例程
VS 生成安全 cookie 作为第一件事,这就是调用的第一个函数所做的事情:
请注意,即使没有 IDA 提示,此例程也很容易识别,因为它会进行非常准确的 API 调用,您可以 Google 图片中的一些函数名称来查找文档.
评估这是安全cookie生成例程我们然后回到上一个并进入第二个调用
这是您的程序的主体,不是 main
,而是初始化和完成 CRT 的地方,包括调用 main
。
看一下左下角的流程图,大部分工作都在左边的 b运行ch 中(意味着右边的 b运行ch 是一个错误条件)。
main
通常在 _exit
或 _cexit
之前调用 call
秒。
然后我们接近这些调用:
如果将鼠标悬停在函数调用上,IDA 将显示函数代码。
单个 jmp
函数出现在调试版本中以帮助调试器,并且通常是 运行 时间函数。
第一个带圆圈的函数,悬停时显示对 "enviroment" 例程的调用,这很好,因为 main
需要程序参数(Windows 不要将参数传递给程序,那里是获得它们的特定 API)。
这看起来像是对 main
的调用,参数匹配。
事实上,如果我们输入呼叫,我们会到达 main
:
当然,您可以通过简单地在 "String view" 中查找 "Hello, world!" 字符串找到 main
(Shift + F12)但在现实世界的逆向工程场景中,这几乎总是不可能的。
制作程序然后逆向工程是一个很好的方法,如果你安装了VS,你可能有MS DIA SDK允许IDA读取pdb
文件。
这对逆向工程有很大帮助,你可以加载两个 IDA,一个有 PDB,一个没有,然后比较。
不幸的是,获得 MS DIA SDK 可能并不那么容易。
此外,IDA FLIRT 是必须的。
它是一个方法签名库,它允许 IDA 识别 运行time 函数,从而非常容易地专注于应用程序的真实代码。
虽然很难找到(也更难生成)签名,但它们是完全值得的。
最后,请注意,由于生成的代码类型不同,调试版本可能更难进行逆向工程。
如果您制作发布版本并对其进行逆向工程,您会发现更容易达到 main
.