为什么NASM和dumpbin.exe对同一个可执行文件的反汇编输出不同?
Why are the disassembly output by NASM and dumpbin.exe different for the same executable file?
这是我遵循的步骤。
1) 我从 Kip Irvine 的 "Assembly Language for x86 Processors" 一书中获取了三个不同小程序的汇编语言代码。
2) 我组装、链接生成了一个有效的可执行文件,在每种情况下都没有错误。
3) 对于每个可执行文件,我使用 NASM
生成反汇编
ndisasm -u -p intel add3.exe > add3_ndisasm.txt
4) 在每种情况下,我都使用 dumpbin.exe 获得了反汇编输出
dumpbin /disasm add3.exe > add3_dumpbin_disasm.txt
令人惊讶的是,我在第4步中得到的反汇编与第3步中的反汇编完全不同
这是我使用的汇编代码(在 3 种情况之一)。
; This program adds and subtracts 32-bit integers.
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
DumpRegs PROTO
.code
main PROC
mov eax,10000h ; EAX = 10000h
add eax,40000h ; EAX = 50000h
sub eax,20000h ; EAX = 30000h
call DumpRegs
INVOKE ExitProcess,0
main ENDP
END main
这是第 3 步 (NDISASM) 的反汇编示例
00000000 4D dec ebp
00000001 5A pop edx
00000002 90 nop
00000003 0003 add [ebx],al
00000005 0000 add [eax],al
00000007 000400 add [eax+eax],al
0000000A 0000 add [eax],al
0000000C FF db 0xff
0000000D FF00 inc dword [eax]
这是来自第 4 步 (dumpbin.exe)
Microsoft (R) COFF/PE Dumper Version 14.11.25508.2
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file add3.exe
File Type: EXECUTABLE IMAGE
00401000: 50 push eax
00401001: E8 EF 0F 00 00 call 00401FF5
00401006: C3 ret
00401007: 55 push ebp
00401008: 8B EC mov ebp,esp
0040100A: 83 C4 E8 add esp,0FFFFFFE8h
0040100D: 60 pushad
0040100E: 80 3D 00 40 40 00 cmp byte ptr ds:[00404000h],0
00
00401015: 75 05 jne 0040101C
我从第 3 步的输出中提取了一些指令代码,并试图在第 4 步的反汇编列表中搜索它们,但找不到它们。
5) 然后我对可执行文件进行了十六进制转储(使用 frhed)并将其中的字节值与两个步骤中的输出进行比较。
0000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 00 00 00 00 40 00 00 MZ..........ÿÿ..¸.......@..
001b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...........................
0036 00 00 00 00 00 00 d8 00 00 00 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 73 ......Ø.....º..´.Í!¸.LÍ!Ths
0051 69 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 i program cannot be run in
006c 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 5b 39 0b f3 1f 58 65 DOS mode....$.......[9.ó.Xe
我在第 5 步中看到的字节值与第 3 步中的字节值匹配,但与第 4 步中的字节值不匹配。
如何解释这些差异?
我一定是在某处遗漏了一些简单的小细节,那是什么?
简答: .exe
≠ .com
提示:注意 MZ
签名作为第 5 步输出中的前两个字节 :-P
长答案:
Microsoft 的可执行 .exe
格式不仅仅包含代码。首先,它以一个特殊的签名(格式创建者的首字母缩写)开头,然后是描述代码组织的大量信息。
相比之下,.com
文件只是一个代码,这意味着它的第一个字节是文件加载到内存后执行的内容。
你得到的第一个反汇编是错误的(是的,第一个是错误的,不是第二个!)因为它试图从第一个字节开始解析跳转到实际代码。
dumpbin
足够智能,可以正确解析 .exe
文件的 header 并开始实际代码的反汇编。
解决方案
如果你想比较反汇编输出,你要么必须确保你的 NASM 知道文件类型并正确解析它的 header 或者......简化你的life 并将 .exe
转换为 .com
在这种情况下,两个反汇编操作应该产生相同的输出(当然,除了潜在的错误)
我上次将 .exe
文件转换为 .com
是多年前使用名为 exe2bin
的实用程序。在线快速搜索显示这是在 Windows XP 时代的,并且不再随 OS 一起提供。如果你从某个地方下载它,我认为它没有理由不能工作。
一般的回答是你的期望不合理。
只有专门设计的协作汇编器和专门设计的助记符设备的反汇编器才能处理这个问题。一个简单的例子可以说明这一点。
假设你有
MOV RCX, RBX ; Intel destination then source.
这条指令有两种可能的机器代码。
汇编程序任意选择一个。所以如果你拆
并重新组装包含此指令的工作代码,您可能无法取回原始代码。
这是一个简单的例子。一旦你进入更短的 AX 特殊代码、缩放索引字节、偏移选择等等,它就会变得更糟。
http://home.hccnet.nl/a.w.m.van.der.horst/ciasdis.html
提供了这样一个汇编程序。
在上面的示例中,在这个 assembler/disassembler 组合中,您有两个明确定义的指令
MOV, X| F| CX'| R| BX|
和
MOV, X| T| BX'| R| CX|
所以你可以自己测试你打算使用的汇编器。如果它只包含一条将 BX 寄存器复制到 CX 寄存器的指令,则所有赌注都已取消。
(剧透警告,none 的知名汇编程序都可以。)
这是我遵循的步骤。
1) 我从 Kip Irvine 的 "Assembly Language for x86 Processors" 一书中获取了三个不同小程序的汇编语言代码。
2) 我组装、链接生成了一个有效的可执行文件,在每种情况下都没有错误。
3) 对于每个可执行文件,我使用 NASM
生成反汇编ndisasm -u -p intel add3.exe > add3_ndisasm.txt
4) 在每种情况下,我都使用 dumpbin.exe 获得了反汇编输出
dumpbin /disasm add3.exe > add3_dumpbin_disasm.txt
令人惊讶的是,我在第4步中得到的反汇编与第3步中的反汇编完全不同
这是我使用的汇编代码(在 3 种情况之一)。
; This program adds and subtracts 32-bit integers.
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
DumpRegs PROTO
.code
main PROC
mov eax,10000h ; EAX = 10000h
add eax,40000h ; EAX = 50000h
sub eax,20000h ; EAX = 30000h
call DumpRegs
INVOKE ExitProcess,0
main ENDP
END main
这是第 3 步 (NDISASM) 的反汇编示例
00000000 4D dec ebp
00000001 5A pop edx
00000002 90 nop
00000003 0003 add [ebx],al
00000005 0000 add [eax],al
00000007 000400 add [eax+eax],al
0000000A 0000 add [eax],al
0000000C FF db 0xff
0000000D FF00 inc dword [eax]
这是来自第 4 步 (dumpbin.exe)
Microsoft (R) COFF/PE Dumper Version 14.11.25508.2
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file add3.exe
File Type: EXECUTABLE IMAGE
00401000: 50 push eax
00401001: E8 EF 0F 00 00 call 00401FF5
00401006: C3 ret
00401007: 55 push ebp
00401008: 8B EC mov ebp,esp
0040100A: 83 C4 E8 add esp,0FFFFFFE8h
0040100D: 60 pushad
0040100E: 80 3D 00 40 40 00 cmp byte ptr ds:[00404000h],0
00
00401015: 75 05 jne 0040101C
我从第 3 步的输出中提取了一些指令代码,并试图在第 4 步的反汇编列表中搜索它们,但找不到它们。
5) 然后我对可执行文件进行了十六进制转储(使用 frhed)并将其中的字节值与两个步骤中的输出进行比较。
0000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 00 00 00 00 40 00 00 MZ..........ÿÿ..¸.......@..
001b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...........................
0036 00 00 00 00 00 00 d8 00 00 00 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 73 ......Ø.....º..´.Í!¸.LÍ!Ths
0051 69 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 i program cannot be run in
006c 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 5b 39 0b f3 1f 58 65 DOS mode....$.......[9.ó.Xe
我在第 5 步中看到的字节值与第 3 步中的字节值匹配,但与第 4 步中的字节值不匹配。
如何解释这些差异? 我一定是在某处遗漏了一些简单的小细节,那是什么?
简答: .exe
≠ .com
提示:注意 MZ
签名作为第 5 步输出中的前两个字节 :-P
长答案:
Microsoft 的可执行 .exe
格式不仅仅包含代码。首先,它以一个特殊的签名(格式创建者的首字母缩写)开头,然后是描述代码组织的大量信息。
相比之下,.com
文件只是一个代码,这意味着它的第一个字节是文件加载到内存后执行的内容。
你得到的第一个反汇编是错误的(是的,第一个是错误的,不是第二个!)因为它试图从第一个字节开始解析跳转到实际代码。
dumpbin
足够智能,可以正确解析 .exe
文件的 header 并开始实际代码的反汇编。
解决方案
如果你想比较反汇编输出,你要么必须确保你的 NASM 知道文件类型并正确解析它的 header 或者......简化你的life 并将 .exe
转换为 .com
在这种情况下,两个反汇编操作应该产生相同的输出(当然,除了潜在的错误)
我上次将 .exe
文件转换为 .com
是多年前使用名为 exe2bin
的实用程序。在线快速搜索显示这是在 Windows XP 时代的,并且不再随 OS 一起提供。如果你从某个地方下载它,我认为它没有理由不能工作。
一般的回答是你的期望不合理。
只有专门设计的协作汇编器和专门设计的助记符设备的反汇编器才能处理这个问题。一个简单的例子可以说明这一点。 假设你有
MOV RCX, RBX ; Intel destination then source.
这条指令有两种可能的机器代码。 汇编程序任意选择一个。所以如果你拆 并重新组装包含此指令的工作代码,您可能无法取回原始代码。 这是一个简单的例子。一旦你进入更短的 AX 特殊代码、缩放索引字节、偏移选择等等,它就会变得更糟。
http://home.hccnet.nl/a.w.m.van.der.horst/ciasdis.html 提供了这样一个汇编程序。
在上面的示例中,在这个 assembler/disassembler 组合中,您有两个明确定义的指令
MOV, X| F| CX'| R| BX|
和
MOV, X| T| BX'| R| CX|
所以你可以自己测试你打算使用的汇编器。如果它只包含一条将 BX 寄存器复制到 CX 寄存器的指令,则所有赌注都已取消。 (剧透警告,none 的知名汇编程序都可以。)