Ollydbg中F8的Bug

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ollydbg中F8的Bug相关的知识,希望对你有一定的参考价值。

用Ollydbg调试以下代码,可以展示出其F8单步步过所存在的一个Bug:

#include <windows.h>

int main()
{
    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    GetThreadContext(GetCurrentThread(), &ctx);
    ctx.Dr7 = 0x101;
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    SetThreadContext(GetCurrentThread(), &ctx);

    return 0;
}

代码中会调用SetThreadContext设置调试寄存器dr0有效(并未指定dr0的值)。

如果你用Od在call SetThreadContext处F8,程序基本上将会跑飞,不会停在下一条指令处。

 

这和Od的F8的原理有关。

重启程序,来到call GetThreadContext后按F8,如下图:

技术分享

内存区0x0012FC80是局部变量ctx的地址,内容是刚刚从GetThreadContext获得的。右边是当前调试寄存器的值。

可以看到内存偏移0x0012FC84处,即ctx.Dr0的值为0x00401023,与右边寄存器窗口中DR0的值相等。

 

Od的F8原理,若当前指令为call,则会在call指令的下一条指令即返回指令处设置一个一次性的硬件断点。

也就说,cCall GetThreadContext即0x0040101D处按F8,Od会在下一条指令即0x00401023处设置一个硬件断点,当前使用dr0存放地址。

设置硬件断点的方法是调用SetThreadContext,使得右边寄存器窗口的DR0变成0x00401023。

之后被调试程序调用GetThreadContext,得到的其实是用SetThreadContext设置过的内容,局部变量ctx.Dr0也就变成了0x00401023。

 

基于上面的道理,下面F8步过 call esi 就会导致右边窗口DR0的值发生改变,这里不要让它改变,让它依旧保持为0x00401023。

直接F9来到SetThreadContext处,先别按F8,而是按回车进入SetThreadContext内部,在第一条指令处下断。

这样做的目的是为了在SetThreadContext处按F8后能先进入其内部,而非直接运行函数。

按F8断在SetThreadContext的开头处:

技术分享

打开硬件断点列表,发现Od记录了一个一次性硬件断点,地址为0x00401041,与右边调试寄存器内DR0的值相同。

这正好是call SetThreadContext的下一条指令的地址(call SetThreadContext的返回地址)。

这印证了前面所说在call处F8会在返回地址处设硬件断点。

 

关键的来了。

如果接着在call NtSetContextThread处F8,或者不在SetThreadContext内部下断而直接在call SetThreadContext处F8,程序就会跑飞。

 

原因是这样的:

在call SetThreadContext按F8时,根据前面所述原理,Od会在下一条指令即0x00401041处设硬件断点,以期望程序运行到那里断下来。

那样就会形成我们平常见到的效果,在call处F8,接着断在下一行。

Od是这样想的,但是此时调用的函数是SetThreadContext,该函数也会设置一遍寄存器的值,会把Od设置过的内容覆盖掉。

本来Od设置了DR0为0x00401041,而被调试程序调用SetThreadContext,将DR0设置成了局部变量ctx中的内容,即0x00401023。

这使得0x00401041断点无效,程序之后将不能断在0x00401041,如果后面没有其他断点,程序将一直跑下去。

结果就是跑飞了,完全没有平常F8见到的效果。

 

但是Od很傻,它的硬件断点列表中还记录着自己设置的内容,并不是调试寄存器的真实值。

重启程序,在call SetThreadContext之后的 pop esi 处下断,然后在call SetThreadContext处按F8,会发现程序直接断在了 pop esi 处。

技术分享

就像按了F9一样。程序没有停在call SetThreadContext的下一条指令xor eax, eax处,可见Od设置的DR0失效了。

可是Od的硬件断点列表中依然有指令xor eax, eax的地址0x00401041,和右边寄存器窗口中的真实值不符。

 

接着再按F8,会发现Od又将自己硬件断点列表中的值写回调试寄存器了。

技术分享

 

这个Bug不仅Od原版存在,而且各种加插件版本的Od也有。

归根结底,这是Od内部源码设计的问题,没有考虑周全,最终变成了一种反调试的行为。

以上是关于Ollydbg中F8的Bug的主要内容,如果未能解决你的问题,请参考以下文章

vscode代码片段建议bug

集合

ollydbg - 如何保持调试的程序窗口打开

ollydbg入门记录

Debug程序调试?

OllyDbg:“标签预期”问题