【问题标题】:How to redirect reads and writes to a variable?如何将读取和写入重定向到变量?
【发布时间】:2016-05-03 02:56:17
【问题描述】:

我有一个使用 MSVC 6.0 构建的已编译 32 位 Windows 应用程序。我想注入代码并将读取或写入重定向到变量。对于简单的情况,例如:

DWORD& dword_995344 = *(DWORD*)0x995344;

可以工作。但我也希望将代码编译为自己的独立 exe。显然这是行不通的,因为 0x995344 不太可能是有效地址。例如,它也不适用于 char buffer[256]; // at address 0xdeadbeef

然后我想也许我可以想出一些模板包装器:

template<DWORD VarAddr, class VarType>
class Var
{
public:
    using VarTypePtr = VarType*;

    Var()
    {
        mVar = (VarTypePtr)VarAddr;
    }

    Var(const Var&) = delete;
    Var& operator = (const Var&) = delete;

    operator VarType()
    {
        return *mVar;
    }

    void operator = (VarType v)
    {
        *mVar = v;
    }


private:
    VarTypePtr mVar;
};

Var<0x00995344, DWORD> dword_995344_;

void Test()
{
    dword_995344_ = 7;
    DWORD y = dword_995344_;
}

但再次处理所有类型和案例并不简单,或者在某些情况下也可能不可能。是否有其他技术(无论多么 hacky :))可以捕获读取和写入,因此我可以执行以下操作:

DWORD dword_995344 = 0;

void InstallVarHooks()
{
   RedirectReadWrites(&dword_995344, 0x995344);
}

【问题讨论】:

  • 您的意思是一个应用程序监视另一个应用程序写入地址或监视一个应用程序内吗?
  • 在一个应用程序中,我的 dll 被注入到在给定地址具有 DWORD var 的目标中(因此 0xwhatever 在我当前的进程地址空间中)
  • 这些变量是栈还是堆?

标签: c++ windows reverse-engineering


【解决方案1】:

您可能会考虑使用 source 代码来收集您需要的信息。这可以通过program transformation systems 完成。

这些工具可以让你编写本质上说的模式,

when you see <this>, replace it by <that>

以编译器准确的方式(不是字符串黑客,它永远不会在真实代码上工作)。在你的情况下,你想说的话,

when you see a <write of varX>,
replace it by <store varX to known location, then do write varX>

如何做到这一点的灵感可以在我的技术论文Branch Coverage for Arbitrary Languages made Easy中找到。

要使用 C++ 执行此操作,请参阅 https://softwareengineering.stackexchange.com/a/257441/12135

【讨论】:

  • 这太……疯狂了。所以你是说我需要使用 LibTool/clang 的东西编写某种编译器扩展,这将改变我的编译源以读取/写入 var 到某个地址?
  • 当然,你没有必须这样做;欢迎你做任何真正适合你的事情。如果您希望能够以常规方式捕获任意变量,这是一种合理的方式;只有完整的语言前端对数据类型、标识符/范围等以及您的语言 (C++) 的复杂性有足够的了解。要在二进制文件上执行此操作,您需要以某种方式管理目标代码的修补,并且确实不清楚您将如何处理复杂类型的变量或处理优化(如果涉及变量的代码被消除了怎么办?)
【解决方案2】:

我不确定我是否理解您的问题,您想在写入变量时拦截,如果是,则将写入重定向到其他内存位置,读取也是如此。这部分理论上可以通过调试断点来完成,有一个项目https://github.com/mmorearty/hardware-breakpoints 应该会给你提示如何完成它。它看起来很陈旧,但由于您想在 VS6.0 应用程序上使用它,它实际上可能会起作用。您必须使用您的 DLL 注入代码进入您的目标应用程序才能使用它。

这实际上只是一个工具,它应该允许您在读取/写入变量时截取时刻,您仍然需要编写可以适当修改变量的代码。

无论如何,看起来像很多黑客攻击。

【讨论】:

  • 这看起来可以工作.. 但是似乎有 4 个硬件断点的限制?
【解决方案3】:

虽然语法很混乱,但完全可以引用数组:

char (&TheArray)[256] = *reinterpret_cast<char(*)[256]>(0xDEADBEEF);

在 C++11 中,您可以使用 auto,从而避免重复两次:

auto &TheArray = *reinterpret_cast<char(*)[256]>(0xDEADBEEF);

还有一种更简单的方法是只使用指向地址的指针:

char *TheArray = *reinterpret_cast<char*>(0xDEADBEEF);

由于指针在 C(以及因此也是 C++)中的作用类似于数组,因此上述效果基本相同。

因此,如果您需要的是对任意地址的某个对象的引用,即使它是任意复杂类型,该方法也确实有效。

如果您要访问的变量不在常量地址(现在很少有变量),您可以用一个任意复杂的表达式替换常量0xDEADBEEF,该表达式可以计算出所述变量的地址,这可能还需要调用GetModuleHandle,以获取主可执行文件的地址。

【讨论】:

  • 这种方法的问题是编译为自己的二进制意味着将它们更改为“char TheArray[256]”,除非有某种方法可以强制链接器将 TheArray 放在地址 0xDEADBEEF?
  • 顺便说一句“char (&TheArray)[256] = reinterpret_cast)[256]>(0xDEADBEEF);”无法在 MSVC2013 中编译:错误 C2101: '&' on constant
猜你喜欢
  • 2011-06-12
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2012-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多