【发布时间】:2019-01-01 19:34:02
【问题描述】:
我有一个应用程序,它为主程序循环的每次迭代调用一个函数。我想使用像 OllyDBG 这样的调试器来删除这个函数调用。
我想我大致定位了函数应该基于其中的一些字符串的位置。
如何在不破坏程序的情况下执行此操作?
【问题讨论】:
标签: debugging reverse-engineering
我有一个应用程序,它为主程序循环的每次迭代调用一个函数。我想使用像 OllyDBG 这样的调试器来删除这个函数调用。
我想我大致定位了函数应该基于其中的一些字符串的位置。
如何在不破坏程序的情况下执行此操作?
【问题讨论】:
标签: debugging reverse-engineering
这实际上取决于程序如何使用该功能。您需要一些基本的汇编语言才能成功地做到这一点,并且可能还需要了解不同的调用约定。
我会举几个例子,难度越来越大。
假设您要避免的函数具有以下原型:
void bad_function();
我们可以清楚地看到它没有参数,也没有返回任何值。还可以说它不会更改任何全局变量或对程序进行任何其他更改。在这种情况下,我们可以安全地“删除”对函数的调用,一切都会很好。
但是,我们不能只从流中删除机器代码字节,因为存在相对和硬编码的偏移量,我们需要根据删除的字节数进行调整。幸运的是(虽然,运气与此无关)有称为 nop 指令的特定指令只是空占位符 - 指示 CPU 执行 nothing 但继续执行下一条指令。
附注:尽管大多数指令集定义了 nop 指令,但它们通常只是已经存在但没有副作用的指令的约定。例如,常见的 x86 nop 指令是0x90,技术上是xchg eax, eax。显然,与自己交换一个寄存器没有任何作用。
现在,假设函数返回一个布尔值,确定执行是否应该继续或突然终止(例如,因为发生了程序认为不好的事情)。
函数的原型可能如下所示:
bool bad_function();
使用它的代码将是:
bool bad;
bad = bad_function();
if (bad)
{
ExitProcess();
}
生成的程序集可能类似于:
call bad_function
test eax, eax
jz not_bad_function
call ExitProcess
not_bad_function:
... some more assembly
您会注意到,我们需要确保not_bad_function(我为便于阅读而命名的标签。实际上它会有一个偏移量)下的代码执行,同时避免调用bad_function.
我们想用nops 替换不需要的指令。有时指令可能大于一个字节,我们将用几条 nop 指令代替一条指令,而不仅仅是一条。还有多字节长的nop 指令,但我们不会在这里介绍。
在下面的例子中,我们想要nop-out 所有四个指令直到not_bad_function(这是两个calls,test 和jz 指令)。
我们也可以使跳转无条件,并保持test 和calls 不变。如果我们确实想要执行bad_function,这可能会很方便,因为它确实会产生我们感兴趣的某些副作用。
现在,假设bad_function 接受参数。根据调用约定、传递的参数集、使用的编译器和目标体系结构,我们可以在调用bad_function 之前得到几条push 指令。这些用于将参数推送到堆栈,以供被调用函数使用。同样取决于调用约定,调用者可能有责任在之后清理堆栈。在这种情况下,我们需要在调用函数后删除推送指令和清理。
随着代码越来越长,我将创建一个示例作为练习留给读者。
【讨论】: