在程序集程序中.CODE
段的第一行设置断点不会停止程序的执行。
Visual Studio的调试器怎么会让它无法在程序集中编写的程序的第一行创建断点?这是调试器的一些奇怪之处,是打破多字节指令的情况,还是我只是做些傻事?
我在Visual Studio中编译并运行以下汇编程序:
; Tell MASM to use the Intel 80386 instruction set.
.386
; Flat memory model, and Win 32 calling convention
.MODEL FLAT, STDCALL
; Treat labels as case-sensitive (required for windows.inc)
OPTION CaseMap:None
include windows.inc
include masm32.inc
include user32.inc
include kernel32.inc
include macros.asm
includelib masm32.lib
includelib user32.lib
includelib kernel32.lib
.DATA
BadText db "Error...", 0
GoodText db "Excellent!", 0
.CODE
main PROC
;int 3 ; <-- If uncommented, this will not break.
mov ecx, 6 ; <-- Breakpoint here will not hit.
xor eax, eax ; <-- Breakpoint here will.
_label: add eax, ecx
dec ecx
jnz _label
cmp eax, 21
jz _good
_bad: invoke StdOut, addr BadText
jmp _quit
_good: invoke StdOut, addr GoodText
_quit: invoke ExitProcess, 0
main ENDP
END main
如果我尝试在主函数mov ecx, 6
的第一行设置断点,则忽略该断点,程序不停地执行。只有在我将其设置在xor eax, eax
或任何后续行之后的行上时才会触发断点。
我甚至尝试插入一个软件断点int 3
作为函数的第一行,它也被忽略了。
我注意到的第一件事很奇怪:在点击我的一个断点后查看反汇编会给我以下内容:
01370FFF add byte ptr [ecx+6],bh
--- [Path]\main.asm
xor eax, eax
00841005 xor eax,eax --- <-- Breakpoint is hit here
_label: add eax, ecx
00841007 add eax,ecx
dec ecx
00841009 dec ecx
jnz _label
0084100A jne _label (841007h)
cmp eax, 21
0084100C cmp eax,15h
这里有趣的是,xor
在Visual Studio的眼中是我程序中的第一个操作。缺席的是move ecx, 6
行。直接在它认为我的源头开始的地方上面是实际将ecx
设置为6的行。所以我的程序的实际开始根据反汇编被破坏了。
如果我创建了我的程序int 3
的第一行,那么我的代码在反汇编中的上面显示的行是:
00F80FFF add ah,cl
正如其中一个答案中所建议的,我关闭了ASLR,看起来反汇编更稳定了一些:
.CODE
main PROC
;mov ecx, 6
xor eax, eax
00401000 xor eax,eax --- <-- Breakpoint is present here, but not hit.
_label: add eax, ecx
00401002 add eax,ecx --- <-- Breakpoint here is hit.
dec ecx
00401004 dec ecx
完整的程序在反汇编中可见,但问题仍然存在。尽管我的程序从预期的地址开始,和反汇编中显示的第一个断点,但它仍然被跳过。将int 3
作为第一行仍然会产生以下行:
00400FFF add ah,cl
并且不会停止执行,和再次重新破坏我的程序在反汇编中的视图。我的程序的下一行是在00401001
位置,我认为这是有意义的,因为int 3
是一个单字节指令,但为什么它会在反汇编中消失?
即使使用'Step Into(F11)'命令启动程序也不允许我在第一行中断。事实上,没有断点,用F11启动程序根本不会停止执行。
除了我在这里详述的内容之外,我还不确定我还能尝试解决这个问题。这超出了我目前对汇编和调试器的理解。
答案 0 :(得分:3)
01370FFF添加字节ptr [ecx + 6],bh
至少我可以解释一个谜。注意地址0x1370fff。 CODE段永远不会从这样的地址开始,段从地址开始,其地址是0x1000的倍数。这使得起始地址的最后3个十六进制数字始终为0.调试器被混淆并开始在错误的地址上反汇编代码。实际的起始地址是0x1371000。反汇编开始很差,因为在0x1370fff处有一个0。这是一个多字节ADD指令。所以它会显示垃圾一段时间,直到它意外地赶上真正的机器代码指令。
你需要帮助它,并给它一个命令,开始在适当的地址拆卸。在VS的地址框中,键入“0x1371000”。
另一个值得注意的怪癖是起始地址的奇怪值。进程通常从地址0x400000开始。您有一个名为ASLR的功能,地址空间布局随机化。它是一种防病毒功能,使程序从一个不可预测的起始地址开始。不错的功能,但它并不完全有助于调试程序。目前尚不清楚如何构建此代码,但需要使用/ DYNAMICBASE:NO链接器选项将其关闭。
您需要记住的另一个重要的调试器怪癖是它们设置断点的方式。它们是通过修补代码来实现的,用int 3
指令替换指令的起始字节。当断点命中时,它会快速用原始机器代码指令字节替换该字节。所以你永远不会看到这个。如果选择错误的地址来设置断点,就会出错,就像在多字节指令的中间一样。它现在不再破坏代码,改变的字节会扰乱原始指令。当你开始进行糟糕的反汇编时,你很容易陷入这个陷阱。
嗯,这是正确的方法。改为使用调试器的STEP命令开始调试。
答案 1 :(得分:1)
我已经发现问题的根源是什么,但我不知道为什么会这样。
创建另一个MASM项目后,我注意到新的将在程序的第一行中断,并且反汇编似乎没有被破坏或改变。因此,我将其属性与我的原始项目进行了比较(对于Debug配置)。我发现的唯一区别是我的原始项目禁用了增量链接。具体来说,它将/INCREMENTAL:NO
添加到链接器命令行。
从命令行中删除此选项(从而启用增量链接)导致程序在调试期间按预期运行;我在反汇编窗口中显示的代码保持不变,我可以在main
过程的第一行点击断点,int 3
指令也可以作为第一行正确执行。
答案 2 :(得分:0)
如果按F + 11(步入)而不是开始调试,调试器将在第一行停止。
可能存在一些混乱的断点设置。删除项目目录中的任何* .suo文件以重置所有断点。
请注意,如果项目具有主要功能,您的项目将包含一个秘密标题和内容。要在实际入口点设置断点,请使用:Debug + New Breakpoint + Break at Function - &gt; wWinMainCRTStartup用于Windows程序或mainCRTStartup或wmainCRTStartup用于控制台程序。