【问题标题】:Read access violation using lock xcmpchg16b _InterlockedCompareExchange128使用锁 xcmpchg16b _InterlockedCompareExchange128 读取访问冲突
【发布时间】:2018-02-08 21:37:29
【问题描述】:

我正在尝试使用锁 xcmpchg16b 挂钩一个函数。我已经尝试了大约 20 种不同的方法。

预期结果:

In real func
In hook func

调试构建结果:

Exception thrown at 0x..: 0xC0000005 Access violation reading 0xFFFFFFFFFFFFFFFF

我不知道为什么它试图从 0xFFFFFFFFFFFFFFFF 读取,没有指针到那里。

发布构建中,它不会崩溃!但它也没有钩子函数。

来源:

#include <stdio.h>
#include <Windows.h>

int RealFunc()
{
    printf("In real func\n");
    return 2;
}

int HookFunc()
{
    printf("In hook func\n");
    return 1;
}

int main()
{
    DWORD dwOld;
    if (!VirtualProtect(&RealFunc, 0x1000, PAGE_EXECUTE_READWRITE, &dwOld))
    {
        printf("Unable to make mem RWX.\n");
        return 0;
    }

    RealFunc();

    __declspec(align(16)) PVOID ProcAddress = &RealFunc;
    __declspec(align(16)) LONG64 Restore[2];

    Restore[0] = 0x0000000025ff9090;    // nop, nop, jmp [rip + 0]
    Restore[1] = (LONG64)&HookFunc;

    _InterlockedCompareExchange128((LONG64*)ProcAddress, Restore[0], Restore[1], Restore);

    RealFunc();

    system("PAUSE");
    return 0;
}

这里是函数文档:https://msdn.microsoft.com/en-us/library/windows/desktop/hh972640(v=vs.85).aspx

【问题讨论】:

  • 您应该尝试两件事: (1) 确保 ICE128 在普通数据存储器上的行为符合您的预期。 (2) 在修改代码之前尝试将页面保护设置为PAGE_READWRITE,然后在调用之前设置PAGE_EXECUTE。 Windows 可能默默地拒绝使页面同时可写和可执行(也就是说,我想知道 VirtualProtect(..., PAGE_EXECUTE_READWRITE, ...) 是否可能不报告失败,但不做任何事)。
  • RW+X 页面还有其他问题,比如数据和指令缓存之间的不一致。您的代码修改可能永远不会超出 L1 数据缓存。
  • @zwol 我不能这样做 PAGE_READWRITE 因为那样 main() 将无法执行。但是,我可以以非原子方式覆盖内存,因此页面正在获得 RWX 权限。我也无法使用ICE128 更改普通数据。
  • @BenVoigt 好点,但似乎不是根本原因。我添加了FlushInstructionCache(GetCurrentProcess(), &amp;RealFunc, 0x1000);,但仍然失败。
  • InterlockedCompareExchange 仅在 *(最后一个参数)等于 *(第一个参数)时执行交换。如果所有参数都不是 16 字节对齐的,它也会严重崩溃,这在您的情况下是不必要的,并且可以解释为什么它在发布版本中不会崩溃

标签: c++ c windows hook


【解决方案1】:
_InterlockedCompareExchange128((LONG64*)ProcAddress, 
    Restore[0], Restore[1], Restore);

这当然是错误的。如果寻找函数签名

unsigned char __cdecl InterlockedCompareExchange128(
  _Inout_ LONGLONG volatile *Destination,
  _In_    LONGLONG          ExchangeHigh,
  _In_    LONGLONG          ExchangeLow,
  _Inout_ LONGLONG          *ComparandResult
);

第二个操作数是 ExchangeHigh,第三个是 ExchangeLow - 所以必须是 Restore[1], Restore[0] 而不是 Restore[0], Restore[1]ComparandResult 也必须保存原始函数数据。所以不能是Restore

接下来还要注意,来自 MSDN:

此函数的参数必须在 16 字节上对齐 边界;否则,该函数在 x64 上的行为将无法预测 系统。

但是哪些参数?全部 ?显然没有。例如 ExchangeHighExchangeLow 是按值传递的。我们可以在这里使用完全没有任何地址的直接值。所以说 align 对于第二个和第三个参数是没有意义的。真的 InterlockedCompareExchange128 被转换为 lock cmpxchg16b 指令。来自英特尔手册

注意CMPXCHG16B 要求目标(内存)操作数是 16 字节对齐。

所以只有 Destination 必须是 16 字节对齐的。 ComparandResult - 不是(它将被移动到RCX:RBX 寄存器对)

所以 __declspec(align(16)) LONG64 Restore[2]; 你根本不需要 - 你可以将直接值传递给 InterlockedCompareExchange128。那么

__declspec(align(16)) PVOID ProcAddress = &amp;RealFunc;

_InterlockedCompareExchange128((LONG64*)ProcAddress..

错误且毫无意义。 ProcAddress 的对齐有什么不同? 指向 ProcAddress 的内存必须是 16 字节对齐的。但不是ProcAddress 本身。同样,我们在这里不需要任何临时变量。我们可以直接使用

_InterlockedCompareExchange128((LONG64*)RealFunc, ...)

当然RealFunc 必须是 16 字节对齐的。否则我们得到了 0xC0000005 Access violation reading 0xFFFFFFFFFFFFFFFF 异常。

所以我猜在调试模式下RealFunc 不是 16 字节对齐的。

在发布版本中,它不会崩溃!但它没有挂钩功能 要么。

没有钩子,因为你使用Restore 代替ComparandResult,也不例外,因为RealFunc 随机16字节对齐。

因为一般函数可以有任何地址,并且不能在 16 个字节上对齐 - _InterlockedCompareExchange128 在这里根本没有用。这也仅适用于 x64,不适用于 x86

代码(无论如何都不是钩子函数,如果 RealFunc 未在 16 个字节上对齐)可能看起来像

int RealFunc()
{
    printf("In real func\n");
    return 2;
}

int HookFunc()
{
    printf("In hook func\n");
    return 1;
}

int xxx()
{
    DWORD dwOld;

    if (VirtualProtect(RealFunc, 2*sizeof(PVOID), PAGE_EXECUTE_READWRITE, &dwOld))
    {
        RealFunc();

#if defined(_M_X64)

        if (!((LONG_PTR)RealFunc & 15))
        {
            LONG64 Comparand[2] = { ((LONG64*)RealFunc)[0], ((LONG64*)RealFunc)[1] };

            InterlockedCompareExchange128((LONG64*)RealFunc, (LONG64)HookFunc, 0x0000000025ff9090, Comparand);
        }
        else
        {
            printf("bad function address %p\n", RealFunc);
        }

#elif defined(_M_IX86)

        static PVOID pvHookFunc = HookFunc;
        LARGE_INTEGER Exchange = { 0x25ff9090, (LONG)&pvHookFunc };
        LONG64 Comparand;

        memcpy(&Comparand, RealFunc, sizeof(Comparand));
        InterlockedCompareExchange64((LONG64*)RealFunc, Exchange.QuadPart, Comparand);

#else 
#error not implemented
#endif

        FlushInstructionCache(NtCurrentProcess(), RealFunc, 2*sizeof(PVOID));

        if (dwOld != PAGE_EXECUTE_READWRITE)
        {
            VirtualProtect(RealFunc, 2*sizeof(PVOID), dwOld, &dwOld);
        }

        RealFunc();
    }

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-17
    • 2019-05-25
    • 1970-01-01
    相关资源
    最近更新 更多