Visual Studio只在第二行程序集中断?

时间:2012-10-26 02:45:15

标签: visual-studio-2010 assembly masm

简短说明:

在程序集程序中.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启动程序根本不会停止执行。

除了我在这里详述的内容之外,我还不确定我还能尝试解决这个问题。这超出了我目前对汇编和调试器的理解。

3 个答案:

答案 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用于控制台程序。