Re: INT3的问题
《软件调试》答疑
INT3的问题
casechen
2008-09-03, 17:16 下午
张老师:
您在书中说了,系统对INT3的特殊优待。比如说特殊的单字节指令,EIP=EIP-1等。
我的问题是EIP=EIP-1上。对于调试器落实断点时,这个EIP的调整我可以理解,因为可以替换为原来的指令让其继续执行。但是如果要替换的指令本来 就是INT3,比如说直接在程序中写入的INT3、调用DebugBreak等API函数等情况。这时下条指令仍然是INT3,结果不是造成了循环执行 INT3指令了吗?
我也考虑了您所说的继续单步执行一条指令的情况,但逻辑上依然是EIP指向INT3指令,还是不能解释。
问题比较简单,还请张老师或各位大侠不吝赐教。
谢谢!
Re: INT3的问题
格蠹老雷
2008-09-03, 22:21 下午
CaseChen,
这个问题问得非常好,说明你已经思考的非常深入。
首先强调一下调整EIP是Windows操作系统的中断处理函数(KiTrap03)所采取的动作(不是CPU层面的动作),即《软件调试》78页所引用的汇编指令。因为INT3导致的是陷阱类异常,当软件得知异常时,EIP指针已经指向INT3的下一条指令,所以KiTrap03将其调整会再指向INT3。这样做是假定导致中断异常的一定是INT 3指令,但其实我们知道81页讨论的特殊情况也会导致中断异常,但是这是特例,Windows系统没有考虑这个特例。
对于你说的本来就是INT 3指令的情况,当程序在调试中执行时,调试器会有一个特别处理,当调试器恢复被调试程序执行时,它如果看到下一条指令是INT 3,那么它会把EIP指针加1跳过它(^_^)。
你可以亲自观察一下这个过程:
使用一个WinDBG打开HiInt3.exe(附带代码\bin\hiint3.exe),然后执行,观察到因为INT 3触发中断停下来,记住此时的EIP指针值:
eax=cccccccc ebx=7ffdb000 ecx=00000000 edx=00371448 esi=016cf764 edi=0012ff80
eip=00401028 esp=0012ff34 ebp=0012ff80 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
HiInt3!main+0x18:
00401028 cc int 3
执行.dbgdbg启动一个CDB来调试当前的WinDBG(也可以手工再打开一个WinDBG,然后Attach到第一个WinDBG上,但是没有这个感觉惬意)。
在CDB中埋下如下断点:
bp ntdll!ZwSetContextThread
执行g命令,恢复WinDBG运行
到WinDBG中按F5恢复执行
此时ZwSetContextThread断点会命中,使用kv命令观察栈回溯:
ChildEBP RetAddr Args to Child
016cf340 7c862cc2 0000035c 01d6de00 016cf368 ntdll!NtSetContextThread (FPO: 2,0,0])
以上栈回溯显示的就是调试器在恢复调试目标执行前,要将线程的上下文信息(调试器所维护的那份)设置到线程的上下文结构中,稍后,内核在恢复IRET指令恢复异常执行前会将其中的寄存器值设置物理寄存器中。
其中01d6de00 就是CONTEXT结构的地址,也就是被调试进程(HiInt3)的线程上下文结构地址,使用DT命令观察:
0:003> dt _CONTEXT 01d6de00
ntdll!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x12ff80
+0x0a0 Esi : 0x16cf764
+0x0a4 Ebx : 0x7ffd5000
+0x0a8 Edx : 0x371448
+0x0ac Ecx : 0
+0x0b0 Eax : 0xcccccccc
+0x0b4 Ebp : 0x12ff80
+0x0b8 Eip : 0x401029
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x202
+0x0c4 Esp : 0x12ff34
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
注意,其中的Eip值是0x401029,而刚才我们在调试器中看到的是0x401028,也就是说,调试器已经帮我偷偷加1了^_^。做好事是不是应该告诉我们一下呀?NO,因为太少的程序员知道这个细节,给他们说了,他们会更困惑,所以还是不说吧。《软件调试》也有这样的窘境,不能深究所有细节,那样有些人会质问,知道这个有什么用?????(悲哉)
Re: INT3的问题
casechen
2008-09-04, 11:54 上午
谢谢张老师的详细讲解,让我豁然开朗。
其实,我能理解您说的“窘境”。在学习的过程中,我常常感觉到很多问题都是因为基础知识没有理解透彻。因此,在您书中提到“很多软件工程师的一个弱点是对硬件了解得太少”、对调试原理的强调等观点时,我能深刻体会到您对读者的良苦用心——任何求知过程都是没有捷径的,勿在浮砂筑高台。真是字字珠矶。
可是,那些浮躁、急于求成的人(这好像也是中国很多做软件人的通病吧,现在情况多少有所改善但也没有好到哪里去)却不一定能理解您的这番用心。可悲的是这些人对您、对基础重要性的不理解却没有妨碍他们的嘴巴。