【问题标题】:Why does this code enable me to detect a debugger?为什么这段代码能让我检测到调试器?
【发布时间】:2012-04-22 18:35:00
【问题描述】:

为什么下面的汇编代码是反调试工具?

l1:
call l3
l2:
;some code
l3:
mov al, 0c3h
mov edi, offset l3
or ecx, -1
rep stosb

我知道C3hRETN 并且我知道stobs 根据edi 中的偏移量将al 中的值作为操作码写入,并且由于@987654329 而完成ecx 次@。

我还知道stobsstosw 将在英特尔架构上作为原始格式预取时运行。

如果我们在调试模式下运行程序,则预取无关紧要,并且 l2 标签将运行(因为它是单步的),否则如果没有调试器,它将在 l1 和 l3 之间进行乒乓,我是对的?

【问题讨论】:

标签: windows debugging assembly x86 reverse-engineering


【解决方案1】:

当程序被调试(即单步)时,预取队列在每一步(发生中断时)被刷新。但是,当正常执行时,rep stosb 不会发生这种情况。即使有内存写入缓存区域,旧处理器也不会刷新它,以支持除rep movsrep stosb 之外的已更改的自修改代码。 (IIRC 它最终在 i7 处理器中得到修复。)

这就是为什么如果有一个调试器(单步)代码将正确执行,当rep stosbret 替换时l2 将被执行。当没有调试器时,rep stosb 会继续,因为ecx 是最大可能的,它最终会写到它不应该写的地方,并且会发生异常。

this paper 中描述了这种反调试技术。

【讨论】:

    【解决方案2】:

    调试器在这里唯一要做的就是增加时间延迟。这可能是它如何工作的关键。英特尔(我假设是 AMD)手册明确表示,除非程序向 CPU 发出包含修改后指令的高速缓存行已更改的信号,否则不能保证自修改代码“工作”。这是为了使预取逻辑便宜;芯片设计人员不希望硬件不断测试指令高速缓存行的每个字节是否仍然有效。

    所以我假设调试器会发生 的情况是 l1 调用 l3,它在 rep stosb 之后存储返回,并且由于调试器在单步执行中引起的长时间延迟而执行返回,强制更改后要重新获取的包含 l3 的 cachecline。

    如果没有调试器,我会猜测 stosb 执行后的指令(未显示)。如果是跳转到“无调试器”,那么跳转成功将表明没有使用单步调试器。

    如果我在应用程序中发现此代码,我会拒绝运行它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2015-06-07
      • 2014-09-14
      • 1970-01-01
      相关资源
      最近更新 更多