如何在RtlAllocateHeap中获取或存储堆大小以供以后在RtlFreeHeap中使用?

时间:2015-03-15 23:24:22

标签: c++ windows heap hook

核心问题:旧的Windows XP游戏在较新的Windows版本上冻结,除非它在Windows XP兼容模式下运行。

即使为此提供代码修复并不是必需的,我仍然希望对此冻结进行修复,因为noobie用户经常在这个问题上运行,并且每次都会询问他们如何修复它。应用程序可执行文件当然没有源代码,因此调试冻结并不是直截了当的。但是,自Windows XP compat mode" fixes"它,我想知道如何。

所以我在网上查了一下,发现有一个名为Shim Engine的东西通过AppHelp.dll提供各种修复。我安装了Windows Compat Toolkit并跟踪了名为FaultTolerantHeap的Shim删除了应用程序冻结。到目前为止一切都很好。

关于这个FTH没有太多信息,但this video与来自微软的Calinoiu先生在他们所做的事情以及FTH中的4个核心策略中提供了足够好的内容:

  • 始终将内存归零
  • 为每个分配附加8个字节以帮助缓冲区溢出
  • 延迟堆释放
  • 禁用正常关机时的免费

如视频中所述,这是在来自ntdll的RtlAllocateHeapRtlFreeHeap等功能上完成的。所以我虽然试一试。

下一步我将这两个功能与Microsoft Detours联系起来,并且可以毫无问题地获得访问权限。我将来自我的钩子函数的调用直接重定向到ntdll中的原始函数。游戏运行良好,钩子很好。

然后,我尝试了归零记忆:冻结仍然发生。为每个堆分配附加8个字节:冻结仍然发生。禁用内存释放:冻结消失了! AHA!

所以推迟释放微软的FTH似乎是"治愈"为了冻结。所以下一步是实现延迟免费。我们需要的是一个队列,它接受最大字节数,然后下一个空闲将清除并释放队列中的第一个条目。 Microsoft为其队列使用4MB缓冲区。所以我可以做同样的想法。但是我有一个很大的问题:我需要跟踪将自由编写的堆分配的大小,否则无法判断我的队列有多大。另外,我不想排队超大堆,例如位图图像的缓冲区非常大。

对于微软的FTH,他声称他们在每个堆分配的最后添加了他们的魔术字节。像30个字节+ 8个字节填充。非常多。我认为我可以通过添加一些4字节值作为"只是足够好"来简化它。用于从我的RtlAllocateHeap挂钩中识别堆的模式(因为我在运行时挂钩)和另外4个字节来存储每个分配的大小,以便在分配达到RtlFreeHeap时可以访问它。目前的代码:

#define FTH_MAGIC_BYTES 0xFEDCBA98ul
#define FTH_ALLOC_PADDING 0

...

static PVOID __stdcall RtlAllocateHeap_hook(PVOID HeapHandle, ULONG Flags, SIZE_T Size)
{
    if(Size==0)
    {
        return RtlAllocateHeap_orig(HeapHandle, Flags, Size);
    }
    // For our allocations we assume 32bit application:
    // treat everything with size of 4 bytes
    Size += 8+FTH_ALLOC_PADDING;
    PVOID HeapBase = RtlAllocateHeap_orig(HeapHandle, Flags, Size);

    if(HeapBase)
    {
        *(uint32*)((uint32)HeapBase+4) = FTH_MAGIC_BYTES;
        *(uint32*)((uint32)HeapBase) = Size;
        return (PVOID)((uint32)HeapBase+8);
    }
    return HeapBase;
};


static BOOL __stdcall RtlFreeHeap_hook(PVOID HeapHandle, ULONG Flags, PVOID HeapBase)
{
    if(HeapBase==0)
    {
        return RtlFreeHeap_orig(HeapHandle, Flags, HeapBase);
    }
    if(*(uint32*)((uint32)HeapBase-4)==FTH_MAGIC_BYTES)
    {
        HeapBase = (PVOID)((uint32)HeapBase-8);
    }
    return RtlFreeHeap_orig(HeapHandle, Flags, HeapBase);
};

它在启动时崩溃了应用程序。有趣的是,当我运行Debug时,它不会崩溃。在发布它确实。我在Visual Studio中完成了大会,看不出问题。我还监视新分配的以查看字节是否写入堆中 - 它们是在我期望的位置。然而它抛弃了应用程序,我不明白为什么。当我在HeapBase返回时不更改RtlAllocateHeap_hook的偏移量时,代码将运行正常。

    if(HeapBase)
    {
        //*(uint32*)((uint32)HeapBase+4) = FTH_MAGIC_BYTES;
        //*(uint32*)((uint32)HeapBase) = Size;
        return HeapBase;
    }

一旦我返回偏移量,应用程序就会再次崩溃。我可以在最后存储我的字节,然后返回没有偏移的HeapBase,但是当我们点击RtlFreeHeap_hook时,找到那个对齐将是疯狂的。我不知道微软是怎么做到的,除非他们知道堆的大小。我看到的唯一解决方案是使上述方法正确,找出如何以Microsoft方式检索堆的大小,或者试着挂钩malloc或new。

有什么想法吗?也许我错过了一些非常简单的事情: - )

感谢。

Callstack示例:

  

NTDLL.DLL!76f030bd()
      [下面的框架可能不正确和/或缺失,没有为ntdll.dll加载符号]       MSVCRT.DLL!7498f480()
      MSVCRT.DLL!7498f4e6()
      MSVCRT.DLL!7498f52c()
      COMCTL32.DLL!73656c62()
      game.dat!00413b90()
      game.dat!00413fc8()
      MSVCRT.DLL!7499edc8()
      MSVCRT.DLL!7498a53a()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a6a9()
      MSVCRT.DLL!7498d255()
      MSVCRT.DLL!7498d272()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a53a()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498a5d6()
      MSVCRT.DLL!7498dfd5()
      game.dat!0041d4ed()
      game.dat!006f0048()
      game.dat!004af57d()
      game.dat!00413b90()
      game.dat!00413fc8()
      game.dat!00413b90()
      game.dat!00413fc8()
      game.dat!004140d8()
      game.dat!008fa9ad()
      game.dat!0041f018()

     

EAX = 0262CE78 EBX = 00000000 ECX = 7C7EC8D9 EDX = 00000088 ESI = 00B70000 EDI = 00000000 EIP = 76F030BD ESP = 001886F8 EBP = 00188704 EFL = 00210202

     

7C7EC8E9 = ????

1 个答案:

答案 0 :(得分:1)

经过更多测试和阅读后,我在MSDN中看到kernel32.dll也导出了Heap函数。 Kernel32有一个函数HeapSize,它返回我们需要的东西。似乎内核函数只是通过ntdll对应的。

因此我怀疑ntdll应该与HeapSize对应。我查看了ntdll的导出并找到了RtlSizeHeap。此功能在MSDN上未记录。

谷歌搜索返回this page from ntinternals.net。因此,您可以使用内核函数HeapSize或ntdll函数RtlSizeHeap,使其指针位于GetProcAddress