【问题标题】:VirtualQueryEx unable to read memory, so can't dereference pointer (LPCVOID/uint8_t*)VirtualQueryEx 无法读取内存,因此无法取消引用指针 (LPCVOID/uint8_t*)
【发布时间】:2021-07-10 21:55:26
【问题描述】:

我有一个读取应用程序内存的函数

template<class T>
std::unordered_map<uint8_t, T> find_byte(T find) {
    std::cout << "Searching for value\n";

    std::unordered_map<uint8_t, T> mapping;

    // Turn the T bytes to a vector to use nice C++ functions
    std::vector<uint8_t> byte_check;
    if constexpr (std::is_same_v<T, std::string>) {
        byte_check = std::vector<uint8_t>(find.begin(), find.end());
    }
    else {
        uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(&find));
        byte_check    = std::vector<uint8_t>(data, data + sizeof(find));
    }

    MEMORY_BASIC_INFORMATION info;
    for (uint8_t* addr = nullptr; VirtualQueryEx(m_proc, addr, &info, sizeof(info)) == sizeof(info); addr += info.RegionSize) {
        if (info.State == MEM_COMMIT && (info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE)) {
            size_t read{};
            std::vector<uint8_t> mem_chunk;
            mem_chunk.resize(info.RegionSize);
            if (ReadProcessMemory(m_proc, addr, mem_chunk.data(), mem_chunk.size(), &read)) {
                mem_chunk.resize(read);
                for (auto pos = mem_chunk.begin();
                     mem_chunk.end() != (pos = std::search(pos, mem_chunk.end(), byte_check.begin(), byte_check.end()));
                     pos++) {
                    uint8_t* int_addr_ptr  = (addr + (pos - mem_chunk.begin()));
                    mapping[*int_addr_ptr] = find;
                }
            }
        }
    }
    return mapping;
}

它编译得很好,但是它崩溃了它试图取消引用int_addr_ptr 指针。 使用调试器单步执行后,我注意到从VirtualQueryEx 返回的addr 无法读取。

我认为问题在于我如何取消引用,但我不知道如何解决它。 我累了:

auto lpcvoid       = (addr + (pos - mem_chunk.begin()));
auto int_addr_ptr = reinterpret_cast<const uint8_t*>(lpcvoid);

来自here,但没有产生任何结果。

我想指出,如果我返回 &lt;uint8_t, T&gt; 的地图,它可以正常工作,但要避免指针

【问题讨论】:

  • addr - 远程进程地址空间中的地址。结果 int_addr_ptr (基于 addr )也是。所以 *int_addr_ptr 错误
  • @RbMm 如何将远程地址空间表示为 int?我的意思是我假设地址仍然是可以表示为十六进制/整数的内存地址,即使相对于不同的地址空间
  • 地址不适合 int 以防 64 位。例如,可能使用 INT_PTR(将地址投射到此)。

标签: c++ winapi memory


【解决方案1】:

当您查询内存区域时,您应该从其BaseAddress 开始扫描其内容,而不是从您实际查询的address 开始。 VirtualQueryEx() 可能需要四舍五入地址。 info.RegionSize 是相对于info.BaseAddress 而言的,所以当调用ReadProcessMemory() 时,mem_chunk.size() 将有太多字节无法读取,如果addr &gt; info.BaseAddress,您需要从正在读取的大小中减去该偏移量。

std::search() 循环中,pos 应该增加正在搜索的find 的大小,而不是增加 1 个字节。 addr 应该又是 info.BaseAddress

但更重要的是,为什么 int_addr_ptr 被取消引用?它的值代表一个内存地址在另一个进程,而不是在你自己的进程。因此,您不能简单地取消引用它来读取值,这样做会尝试从 您的进程 中读取值。如果要读取存储在该地址的值,则必须改用ReadProcessMemory()。但是,mapping 到底应该关闭什么?为什么要取消引用 int_addr_ptr 以读取 mapping 键的字节?您不应该键入找到find 的每个副本的实际地址吗?在mapping 中存储find 的副本有什么意义?

更简单的vector&lt;void*&gt; 更有意义,因为调用者已经知道它正在搜索的find 值,因此无需在输出中重复。

作为一项额外的优化,我建议完全摆脱 byte_check 向量,因为它是不必要的内存分配。您使用它只是为了让迭代器与std::search() 一起使用。原始指针也是有效的迭代器。

话虽如此,请尝试更多类似的东西:

template<class T>
std::vector<void*> find_value(const T &find) {
    std::cout << "Searching for value\n";

    std::vector<void*> found;
    std::vector<uint8_t> mem_chunk;

    const uint8_t *find_begin, *find_end;
    size_t find_size;

    if constexpr (std::is_same_v<T, std::string>) {
        find_begin = reinterpret_cast<const uint8_t*>(find.c_str());
        find_size = find.size();
    }
    else {
        find_begin = reinterpret_cast<const uint8_t*>(&find);
        find_size = sizeof(find);
    }

    find_end = find_begin + find_size;

    uint8_t *addr = reinterpret_cast<uint8_t*>(0), *base;
    MEMORY_BASIC_INFORMATION info;

    while (VirtualQueryEx(m_proc, addr, &info, sizeof(info))) {
        base = static_cast<uint8_t*>(info.BaseAddress);

        if (info.State == MEM_COMMIT && (info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE)) {
            mem_chunk.reserve(info.RegionSize);

            size_t read;
            if (ReadProcessMemory(m_proc, info.BaseAddress, mem_chunk.data(), info.RegionSize, &read)) {
                auto start = mem_chunk.data(),
                     end = start + read,
                     pos = start;

                if (addr > base) {
                    pos += (addr - base);
                }

                while ((pos = std::search(pos, end, find_begin, find_end)) != end) {
                    found.push_back(base + (pos - start));
                    pos += find_size;
                }
            }
        }

        addr = base + info.RegionSize;
    }

    return found;
}

【讨论】:

  • 您提出了许多优点,但回答了您的一些设计问题。我选择了一张地图,因为我将在稍后阶段更新这些值。浏览地图并更新值,所以这只是初始地图。这个想法是映射键将是一个 int,我可以将其转换为十六进制,以显示内存地址,即。 (0x423a23a1) 或其他什么,我可以稍后进行翻译,但我认为那里是有道理的。最初的设计也是基于this
  • @TomasBerger 你不需要mapvectorvoid* 将为此工作。您可以按原样显示void*。如果您打算稍后使用WriteProcessMemory() 更改内存,您将需要void*s。
猜你喜欢
  • 2016-04-01
  • 2017-02-25
  • 1970-01-01
  • 2017-01-26
  • 2021-11-01
  • 2012-12-31
  • 1970-01-01
  • 2020-10-16
  • 2011-08-04
相关资源
最近更新 更多