草庐IT

c - 在 2GB 范围内分配内存

coder 2024-06-06 原文

我正在编写一个函数,允许用户在指定地址的 2GB +/- 范围内分配内存。我正在查询内存以找到一个空闲页面,并在那里分配。这是x64 trampoline hooking ,因为我使用的是相对 jmp 指令。

我的问题是 NtQueryVirtualMemorySTATUS_ACCESS_VIOLATION 错误而失败,因此总是返回 0。我很困惑为什么会发生这种情况,因为 min(可能的最低地址)在我 checkin Process Explorer 时似乎是免费的。

LPVOID Allocate2GBRange(UINT_PTR address, SIZE_T dwSize)
{
    NtQueryVirtualMemory_t NtQueryVirtualMemory = (NtQueryVirtualMemory_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryVirtualMemory");

    UINT_PTR min, max;
    min = address >= 0x80000000 ? address - 0x80000000 : 0;
    max = address < (UINTPTR_MAX - 0x80000000) ? address + 0x80000000 : UINTPTR_MAX;

    MEMORY_BASIC_INFORMATION mbi = { 0 };
    while (min < max)
    {
        NTSTATUS a = NtQueryVirtualMemory(INVALID_HANDLE_VALUE, min, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), NULL);
        if (a)
            return 0;

        if (mbi.State == MEM_FREE)
        {
            LPVOID addr = VirtualAlloc(mbi.AllocationBase, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if (addr)
                return addr;
        }

        min += mbi.RegionSize;
    }
}

最佳答案

首先是几个一般性的注释(不是关于错误的)

从我的搭配来看很奇怪NtQueryVirtualMemoryVirtualAlloc .存在意义或使用

  • NtQueryVirtualMemoryNtAllocateVirtualMemory

  • VirtualQueryVirtualAlloc

NtQueryVirtualMemoryVirtualQueryEx 相比没有任何额外功能(与 NtAllocateVirtualMemory 相比,VirtualAllocEx 通过 ZeroBits 参数具有额外的功能)

那么如果已经使用 NtQueryVirtualMemory 不需要GetProcAddress - 您可以使用来自 wdkntdll.libntdllp.lib 的静态链接 - 这个 api 曾经存在,并将从 ntdll.dll 喜欢VirtualQuerykernel32.dll 导出,然后链接到 kernel32.lib 如果你想使用 GetProcAddress - 存在意义不是每次都这样做 Allocate2GBRange被调用,但是一次。和调用的主要检查结果 - 可能是 GetProcAddress返回 0 ?如果你确定 GetProcAddress永远不会失败 - 你确定 NtQueryVirtualMemory总是从 ntdll.dll 导出 - 所以使用 ntdll[p].lib 的静态链接

然后 INVALID_HANDLE_VALUE就位ProcessHandle尽管正确,但看起来很不自然。更好用NtCurrentProcess()此处宏或 GetCurrentProcess() .但无论如何,因为你使用 kernel32 api - 没有任何理由使用 NtQueryVirtualMemory相反 VirtualQuery这里

你不需要零初始化 mbi在调用之前 - 这只是参数,因为最初总是 min < max更好用do {} while (min < max)改为循环 while(min < max) {}


现在关于代码中的严重错误:

  • 使用mbi.AllocationBase - 当mbi.State == MEM_FREE - 在这个 案例mbi.AllocationBase == 0 - 所以你告诉 VirtualAlloc 任何可用空间中分配。
  • min += mbi.RegionSize; - mbi.RegionSize来自 mbi.BaseAddress - 所以将其添加到 min不正确 - 你需要使用 min = (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;
  • 然后打电话VirtualAlloc (当 lpAddress != 0 时)您必须使用 MEM_COMMIT|MEM_RESERVE相反 MEM_COMMIT仅。

以及关于传递给 VirtualAlloc 的地址- 通过 mbi.AllocationBase (只是 0)不正确。但通过 mbi.BaseAddress万一我们找到mbi.State == MEM_FREE区域也不正确。为什么 ?来自 VirtualAlloc

If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity.

这意味着VirtualAlloc真的尝试分配内存而不是通过 mbi.BaseAddress但来自 mbi.BaseAddress & ~(dwAllocationGranularity - 1) - 较小的地址。但是这个地址可能已经很忙,结果你得到了STATUS_CONFLICTING_ADDRESSES (ERROR_INVALID_ADDRESS win32 错误)状态。

举个具体的例子——让你有

[00007FF787650000, 00007FF787673000) busy memory
[00007FF787673000, ****************) free memory

最初是你的 min将在[00007FF787650000, 00007FF787673000)繁忙的内存区域 - 结果你得到了 mbi.BaseAddress == 0x00007FF787650000mbi.RegionSize == 0x23000 ,因为区域繁忙 - 您将在 mbi.BaseAddress + mbi.RegionSize; 尝试下一个区域- 所以在 00007FF787673000地址。你有mbi.State == MEM_FREE为此,但如果您尝试调用 VirtualAlloc00007FF787673000地址 - 它将此地址向下舍入为 00007FF787670000因为现在分配粒度是0x10000 .但是00007FF787670000属于[00007FF787650000, 00007FF787673000)繁忙的内存区域 - 结果 VirtualAlloc失败 STATUS_CONFLICTING_ADDRESSES (ERROR_INVALID_ADDRESS)。

所以你需要使用(BaseAddress + (AllocationGranularity-1)) & ~(AllocationGranularity-1)真的 - 地址向上舍入到最接近分配粒度的倍数。

所有代码都可以是这样的:

PVOID Allocate2GBRange(UINT_PTR address, SIZE_T dwSize)
{
    static ULONG dwAllocationGranularity;

    if (!dwAllocationGranularity)
    {
        SYSTEM_INFO si;
        GetSystemInfo(&si);
        dwAllocationGranularity = si.dwAllocationGranularity;
    }

    UINT_PTR min, max, addr, add = dwAllocationGranularity - 1, mask = ~add;

    min = address >= 0x80000000 ? (address - 0x80000000 + add) & mask : 0;
    max = address < (UINTPTR_MAX - 0x80000000) ? (address + 0x80000000) & mask : UINTPTR_MAX;

    ::MEMORY_BASIC_INFORMATION mbi; 
    do 
    {
        if (!VirtualQuery((void*)min, &mbi, sizeof(mbi))) return NULL;

        min = (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;

        if (mbi.State == MEM_FREE)
        {
            addr = ((UINT_PTR)mbi.BaseAddress + add) & mask;

            if (addr < min && dwSize <= (min - addr))
            {
                if (addr = (UINT_PTR)VirtualAlloc((PVOID)addr, dwSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE))
                    return (PVOID)addr;
            }
        }


    } while (min < max);

    return NULL;
}

关于c - 在 2GB 范围内分配内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54729401/

有关c - 在 2GB 范围内分配内存的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  3. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  4. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  5. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  6. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  7. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  8. Ruby 日期参数超出范围 - 2

    我正在尝试使用在我的代码中是动态的Time.local来安排时间。在每个月的第一天,我传递的值是Time.local(2009,9,-1,0)。在PHP中,这会将时间设置为上个月的最后一天。在ruby​​中,我只是得到“ArgumentError:参数超出范围”。是我用错了方法还是什么?谢谢。 最佳答案 您应该使用DateTime类而不是Time。(您可能需要先require'date'并安装activesupportgem。)它比Time更通用,并且可以用DateTime.civil(2009,9-1,-1,0)做你想做的事。为天

  9. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  10. ruby-on-rails - 如何将大于 5GB 的文件上传到 Amazon S3? - 2

    我目前正在使用带有Carrierwavegem的Rails3.2将文件上传到AmazonS3。现在我需要能够处理用户提交的大于5GB的文件,同时仍然使用Carrierwavegem。Carrierwave或Fog是否有任何其他gem或分支可以处理5GB以上的文件上传到S3?编辑:我不想重写一个完整的Rails上传解决方案,所以像这样的链接没有帮助:https://gist.github.com/908875. 最佳答案 我想出了如何做到这一点,并且现在可以正常工作了。在正确的config/environment文件中,添加以下内容以

随机推荐