C++ 在内存中搜索匹配模式的二进制序列

问题描述

我确定这个问题已经在某个地方被问过并解决了,但实际上我没有找到任何适合我的东西。

我正在尝试编写一个 DLL,它需要在应用程序的 .text 部分中查找某些内容。我想我可以找到包含我想要的内容的 .text 部分,所以我认为,我应该能够获得一个包含整个 .text 部分 (?)

的字节数组 (unsigned char *?)

现在,由于到处都有指针,我想我需要使用正则表达式来忽略某些可能会根据应用程序版本而改变的字节。

那么找到我的模式的最佳方法是什么? 这种模式的一个例子: “48 8d ?? ?? ?? ?? ?? e8”

解决方法

使用正则表达式是不必要的工作。您想要的是一种指定带有“匹配任何内容”标记的字节模式的方法。

很遗憾,您不能使用任何 unsigned char 值,因为所有可用值(从 0x00xff)都是可能的匹配项。一种解决方案可能是为此使用 uint16_t 而不是 uint8_t 并使用高于 0xff 的任何值来具有特殊含义。对于您的示例,假设您为此选择了 0xffff

const uint16_t pattern[] = { 0x48,0x8d,0xffff,0xe8 };

您现在必须逐个字节地进行匹配(您不能再直接使用 memcmpfind 之类的东西):

#define pattern_match_anything  0xffff

// Checks if the text[start_index:text_size] region matches pattern.
bool check_at_index(const uint8_t *text,size_t text_size,size_t start_index,const uint16_t *pattern)
{
    size_t pattern_index = 0;
    
    // Some bounds checks omitted for simplicity.
    for (size_t i = start_index; i < text_size; i++)
    {
        if (pattern[pattern_index] == pattern_match_anything)
        {
            pattern_index++;
            continue;
        }
        
        if (text[i] != pattern[pattern_index]) return false;

        pattern_index++;
    }
    
    return true;
}
,

您当然可以为此使用正则表达式,尽管这可能不是最快的方法,但这取决于您的需要。你只是想找个位置吗?是否要提取内存区域的副本?或者只是比赛内部的一些内存位置?

这是一个 std::regex 选项:

char const* find_chunk(char const* const beg,char const* const end)
{
    static std::regex const re{R"(\x48\x8D....\xE8)",std::regex_constants::optimize};

    std::cmatch m;
    if(!std::regex_search(beg,end,m,re))
        return end;

    return m[0].first;
}

一种可能更快的方法是单独手动检查图案部分,如下所示:

char const* find_chunk(char const* const beg,char const* const end)
{
    static char const match[] = {'\x48','\x8D'};

    auto found = std::search(beg,std::begin(match),std::end(match));

    if(std::distance(found,end) > 6 && found[6] == '\xE8')
        return found;

    return end;
}

正如评论中提到的,另一种方法是像这样使用 std::optional<char>

char const* find_chunk(char const* const beg,char const* const end)
{
    static std::optional<char> const m[] = {{'\x48'},{'\x8D'},{},{'\xE8'}};

    return std::search(beg,std::begin(m),std::end(m),[](auto a,auto b){
        if(!b.has_value())
            return true;
        return a == b.value();
    });
}