0

我将二进制文件转换为十六进制字符串,以在其中搜索用户提供的特定模式,就像防病毒软件处理其签名数据库一样。如果找到一个模式,那么它将返回 true。

我面临的一个困难是通配符和扫描速度慢。用户有数千种模式,每个模式最多 200 个字符,甚至更多。

例如,此模式用于验证文件是否在 C++ 下编译,而“?” 字符是一个通配符(可以匹配任何一个字符):

55 8B EC 53 8B 5D 08 56 8B 75 0C 85 F6 57 8B 7D 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 01

与那个相似的模式都堆叠在一个长度不等的文件中,所以我想你明白了。

这是我正在使用的代码(工作正常,但与其他在几秒钟内完成模式扫描的工具(如 ExeInfoPE 或 Die)相比非常慢)

        static bool compare(string[] mask, byte[] buffer, int position)
    {
        var i = 0; // index
        foreach (string x in mask) // loop through the mask
        {
            if (x.Contains("?")) // is current mask position a wildcard?
            {
            }// if so skip comparison
            else if (byte.Parse(x, System.Globalization.NumberStyles.HexNumber) == buffer[position + i]) // else try to compare
            {
            }// succeeded, move onto next byte
            else
                return false; // failed, pattern not found
            ++i; // increment the index.
        }
        return true; // pattern was found
    }

关于如何极大地提高速度,同时保持对通配符的支持以便我的工具可以在现实世界中使用的任何想法?

4

1 回答 1

0

如果您有多个要匹配的文件,您可以 - 应该将掩码预先转换为byte?[](或者更简单地说,如果有通配符,则为 true)。如果您有 1000 个文件,这项工作将无用地重复 1000 次。byte[]bool[] isWildcardbyte.Parse(x, System.Globalization.NumberStyles.HexNumber)

另一个问题...有多大buffer?我确实希望您只读取每个文件中最多 4kb 的数据(甚至更少……您可以预先检查所有掩码以查看哪个掩码最大)并且您没有读取整个文件.

第二件事,您可以尝试索引模式,至少对于模式的第一个字节(但请记住,您必须处理以 开头的模式的情况?)。

一些随机评论:

  • 目前尚不清楚您真正想要做什么。需要更多信息。您需要扫描多少个文件?1 个或 2 个或多个(10+、100+?)?

  • 您的模式从文件中的固定位置开始,或者至少始终存在于文件的某个位置(如 exe 文件的 MZ 签名,即文件的前两个字符),或者它们可以在任何地方?
    许多程序“作弊”:它们不会加载整个文件。他们装载小件。例如前 4kb 和最后 4kb。他们“知道”他们的模式必须存在。所以如果你加载整个文件,你肯定会变慢。

  • 您的模式的第一个字节,如何在统计上分布?例如……你有 1000 个模式,一个字节有 256 个可能的值。模式的第一个字节平均分配给所有可能的值?因此,平均有 4 种模式以每个可能的字节值开头(4 种模式以“A”开头,4 种以“B”开头,依此类推),或者存在更多的字节(如果例如,有 100 个以“A”开头的模式),因为在第一种情况下,可以按第一个字节索引您的模式并快速选择只有第一个字节的模式的小子集,在第二种情况下是第一个字节不足以选择要检查的模式子集。

一般来说,我会有两种结构:

Dictionary<byte, List<Pattern>> PatternsByFirstByte

List<Pattern> PatternsWithFirstCharacterWildcard

PatternsByFirstByte通过这种方式,对于每个字节,您必须从和所有模式中检查少量模式PatternsWithFirstCharacterWildcard。但是,如果第一个字节具有足够的判别力,则此方法有效。更高级的是创建一个完整的 trie 结构来保存/索引模式,和/或以不同的方式处理位置零 (?? ?? xx yy) 处的通配符。很明显 (?? ?? xx yy) 等价于 (xx yy) 起始位置 >= 2。然后您可以将模式xx yy放入字典中并在位置必须 >= 2 的位置添加注释,例如在Pattern班级本身中,那将是:

class Pattern
{
    public readonly byte?[] Bytes;
    public int MinimumStartingPosition;
}
于 2018-05-31T13:32:46.640 回答