5

简短说明:

在汇编程序中我的段的第一行设置断点.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

如果我尝试在 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  

在反汇编中可以看到完整的程序,但问题仍然存在。尽管我的程序从预期的地址开始,并且在反汇编中显示了第一个断点,但它仍然被跳过。将 anint 3作为第一行仍然会导致以下行:

00400FFF  add         ah,cl  

并且不会停止执行,再次在反汇编中重新破坏我的程序的视图。然后我的程序的下一行位于 location 00401001,我认为这是有道理的,因为int 3它是一个单字节指令,但为什么它会在反汇编中消失?

即使使用“Step Into (F11)”命令启动程序也不允许我在第一行中断。事实上,在没有断点的情况下,使用 F11 启动程序根本不会停止执行。

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

4

3 回答 3

3

01370FFF 添加字节 ptr [ecx+6],bh

至少我可以解释一个谜。注意地址,0x1370fff。CODE 段永远不会从这样的地址开始,段开始于 0x1000 的倍数的地址。这使得起始地址的最后 3 个十六进制数字始终为 0。调试器感到困惑并开始反汇编错误地址的代码,减一。实际起始地址为 0x1371000。反汇编开始很糟糕,因为 0x1370fff 处有一个 0。那是一条多字节的 ADD 指令。所以它会显示一段时间的垃圾,直到它偶然赶上真正的机器代码指令。

您需要帮助它并给它一个命令,以便在正确的地址开始反汇编。在 VS 的地址框中,键入“0x1371000”。

另一个值得注意的怪癖是起始地址的奇怪值。进程通常从地址 0x400000 开始。您启用了一项名为 ASLR 的功能,即地址空间布局随机化。它是一种防病毒功能,可以使程序从不可预知的起始地址开始。不错的功能,但它并不能完全帮助调试程序。目前尚不清楚您是如何构建此代码的,但您需要 /DYNAMICBASE:NO 链接器选项将其关闭。

您需要记住的另一个重要的调试器怪癖是它们设置断点的方式。他们通过修补代码来做到这一点,用指令替换指令的起始字节int 3。当断点命中时,它会迅速用原始机器代码指令字节替换该字节。所以你永远不会看到这个。如果你选择了错误的地址来设置断点,就会出错,比如在多字节指令的中间。它现在不再破坏代码,改变的字节弄乱了原始指令。当你从一个糟糕的拆卸开始时,你很容易落入这个陷阱。

好吧,这样做是正确的方式。改为使用调试器的 STEP 命令开始调试。

于 2012-11-01T21:12:12.927 回答
1

我发现了问题的根源是什么,但我不知道为什么会这样。

创建另一个 MASM 项目后,我注意到新项目在程序的第一行中断,并且反汇编似乎没有被破坏或更改。因此,我将其属性与我的原始项目(用于调试配置)进行了比较。我发现的唯一区别是我的原始项目禁用了增量链接。具体来说,它添加/INCREMENTAL:NO到链接器命令行。

从命令行中删除此选项(从而启用增量链接)导致程序在调试期间按预期运行;我在反汇编窗口中显示的代码保持不变,我可以在main过程的第一行打断点,并且int 3指令也可以作为第一行正确执行。

于 2012-12-05T06:22:53.327 回答
0

如果按 F+11(步入)而不是 Start Debugging,调试器将在第一行停止。

可能存在一些混乱的断点设置。删除项目目录中的所有 *.suo 文件以重置所有断点。

请注意,如果您的项目具有主要功能,则其中将包含一个秘密标题和内容。要在真正的入口点设置断点,请使用:Debug + New Breakpoint + Break at Function -> wWinMainCRTStartup 用于 Windows 程序或 ma​​inCRTStartup 或 wmainCRTStartup 用于控制台程序。

于 2012-11-01T20:31:26.720 回答