吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 772|回复: 6
收起左侧

[C&C++ 原创] 对SSE2模式匹配算法SSE2PatternFind的一点改造优化

[复制链接]
haogl 发表于 2024-9-4 08:18
本帖最后由 haogl 于 2024-9-4 10:35 编辑


     为对某软件的指定模块打特征码补丁,最近研究学习了各种搜索算法(Sunday、Shift_And、BNDM等),对比发现,内存特征码搜索还是下面两位大佬基于SSE2指令集的搜索算法速度最快。
于是认真学习了两位大佬的文章,根据自己的理解做了一点点改造和优化,分享给大家,欢迎拍砖!!!


学习参考文章如下,感谢大佬们分享的好文!
https://bbs.kanxue.com/thread-209946.htm
https://www.52pojie.cn/thread-1854232-1-1.html

主要改进:
①通过掩码的方式使其支持 前、中、后通配符 及半字节;
②通过SSE2指令集找到特征码字节序列中的第一个不为'??'的元素后,后续的字节只比较不是'??'的特征字节,优化比较字节数。

通过测试,根据不同的特征码型式(特征码长度不同、通配符数量不同等),在我的环境中,比优化前(上面文章中代码)搜索速度提升5%~15%左右。


本人不是计算机专业,也不从事相关工作,纯属业余爱好,代码难免错漏,更谈不上优雅,望各位大佬批评指正!

20240904------判断特征码全都是'??'时,返回FALSE;修改结束搜索的条件。



[C++] 纯文本查看 复制代码
/****************************************************************************************
 * @brief SSE2PatternFindEx-使用SSE2加速暴力搜索内存特征码(支持模糊匹配)-By.Haogl优化-20240903
 * @Param retList 用于存储搜索到的特征码对应的内存地址
 * @param searchStartAddr 需要搜索的起始内存地址
 * @param searchSize 要搜索的内存块的大小,可上负下正(以字节为单位)
 * @param myPattern 搜索特征码,支持通配符??、?,如:"?? 5? 77 ?? 88 ?? ?A ??"
 * @param offsetSize 特征码地址离目标地址的偏移距离,上负下正,0不偏移
 * @param searchNum 搜索个数,0为搜索整个模块,默认为0
 * @Return 成功返回TRUE,失败返回FALSE
 * 
第一层遍历使用 SSE 将逐字节加速为逐 16 字节每次(最终加速 12 倍获益主要来源与此)
第二层子串匹配不能使用 SSE 加速,原因有四
        1. SSE 虽为单指令多数据,但单个指令 CPU 周期比常规指令要高
        2. 从概率上来说,子串匹配时第一个字节命中失败与 SSE 一次性对比 16 个字节命中失败在概率上几乎相等
        3. 根据实验采用 SSE 优化第二层子串匹配将显著降低最终查找速度
        4. 理论上,即使 SSE 单条指令与常规指令具有同样的CPU周期,最高也只能加速 16 倍
*/

// 特征码初始化,将传入的特征码字符串myPattern格式转化为目标特征码字节序列
BOOL InitPattern(const std::string& myPattern, std::vector<UCHAR>& vecPtn, std::vector<UCHAR>& vecMsk, std::vector<ULONG>& vecIdx)
{
        std::string patternText = myPattern;
        if (patternText.empty()) { return FALSE; }
        patternText.erase(std::remove(patternText.begin(), patternText.end(), ' '), patternText.end());  // 去除所有空格

        for (char ch : patternText) {
                if (ch != '?' && !((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))) { return FALSE; }
        }

        if (patternText.length() % 2 != 0) { return FALSE; }
        ULONG len = patternText.length() / 2;

        for (ULONG i = 0; i < len; i++)
        {
                std::string tmpS = patternText.substr(i * 2, 2); 
                if ("??" != tmpS) // 不是"??"的特征字符
                {
                        if ('?' == tmpS.at(0)) // 左半字节为'?'
                        {
                                tmpS.at(0) = 'F';
                                vecMsk.push_back(UCHAR(0xFF) << 4);
                        }
                        else if ('?' == tmpS.at(1)) // 右半字节为'?'
                        {
                                tmpS.at(1) = 'F';
                                vecMsk.push_back(UCHAR(0xFF) >> 4);
                        }
                        else
                        {
                                vecMsk.push_back(UCHAR(0x00));
                        }
                        vecIdx.push_back(i);
                }
                if ("??" == tmpS) // 为"??"的特征字符
                {
                        tmpS.at(0) = 'F';
                        tmpS.at(1) = 'F';
                        vecMsk.push_back(UCHAR(0xFF));
                }
                vecPtn.push_back(strtoul(tmpS.c_str(), nullptr, 16));
        }
        if (0 == vecIdx.size()) { return FALSE; }
        return TRUE;
}

// 使用SSE2加速暴力搜索内存特征码(支持模糊匹配,如:"?? 5? 77 ?? 88 ?? ?A ??")
BOOL SSE2PatternFindEx(std::vector<ULONGLONG>& retList, const ULONGLONG searchStartAddr,
        const LONGLONG searchSize, const std::string& myPattern, const LONGLONG offsetSize, const ULONGLONG searchNum)
{
        if (0 == searchStartAddr || 0 == searchSize) { return FALSE; }

        ULONGLONG realStartAddr = searchStartAddr;
        if ((searchSize < 0) && (searchStartAddr > std::abs(searchSize))) //searchSize可上负下正(以字节为单位)
        {
                realStartAddr = searchStartAddr - std::abs(searchSize);
        }

        std::vector<UCHAR> vecPtn;  // 存储转换后的特征码字节序列
        std::vector<UCHAR> vecMsk;  // 存储特征字符串中'??'、'?'的掩码('??'和'?'用1替代,非'?'为0)
        std::vector<ULONG> vecIdx;  // 存储不是'??'的特征码字节在原始特征码字节序列(传入的有'??'的特征码)中的索引下标

        if ( !InitPattern(myPattern, vecPtn, vecMsk, vecIdx) ) { return FALSE; }

        // 常规变量
        PUCHAR maxAddress = (PUCHAR)(realStartAddr + std::abs(searchSize));
        PUCHAR baseAddress;
        PUCHAR currAddress;
        PUCHAR currPattern = &vecPtn[0]; // 特征码字节序列的首地址
        UCHAR currEqual;
        register UCHAR currPtnCh;

        // SSE加速相关变量
        __m128i ptnHead = _mm_set1_epi8(vecPtn.at(vecIdx.at(0)));   
        __m128i headMsk = _mm_set1_epi8(vecMsk.at(vecIdx.at(0)));
        __m128i curHead, curComp;
        ULONG bitComp, idxComp;
        ULONG j;

        for (ULONGLONG i = vecIdx.at(0); i <= searchSize - 16; i += 16)
        {
                baseAddress = (PUCHAR)(realStartAddr + i);
                curHead = _mm_loadu_si128((__m128i*)(baseAddress));
                curHead = _mm_or_si128(curHead, headMsk);        // 将对应特征码中'?'位置的二进制位替换为1
                curComp = _mm_cmpeq_epi8(ptnHead, curHead);
                bitComp = _mm_movemask_epi8(curComp);

                j = 0;
                while (_BitScanForward(&idxComp, bitComp))
                {
                        currAddress = baseAddress + j + idxComp - vecIdx.at(0);

                        for ( ULONG iCh = 1; iCh < vecIdx.size() && (size_t)currAddress <= (size_t)maxAddress - vecPtn.size(); iCh++ )
                        {
                                currPtnCh = currPattern[vecIdx.at(iCh)];
                                // currPattern[vecIdx.at(iCh)] = currPtnCh + 0x1;  // 要排除本函数自身的特征码字节序列,可以打开这两行
                                // currEqual为0时表示两个字节相同(包含半字节'?'的判断)
                                currEqual = ( ( currAddress[vecIdx.at(iCh)] | vecMsk.at(vecIdx.at(iCh)) ) ^ currPtnCh );
                                // currPattern[vecIdx.at(iCh)] = currPtnCh;
                                
                                if ( currEqual ) { break; } // currEqual不为0时两个字节不相同

                                if ( iCh + 1 == vecIdx.size() ) // 找到特征码字节序列
                                {
                                        retList.push_back((ULONGLONG)(currAddress + offsetSize));
                                        if ( searchNum != 0 && retList.size() >= searchNum ) { return TRUE; }
                                        break;
                                }
                        }
                        ++idxComp;
                        bitComp = bitComp >> idxComp;
                        j += idxComp;
                }
        }
        return TRUE;
}

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
wushaominkk + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wolaikaoyan 发表于 2024-9-4 11:48
太牛了,学习中
666888tzq 发表于 2024-9-4 12:49
Elaineliu 发表于 2024-9-4 14:22
本帖最后由 Elaineliu 于 2024-9-4 16:17 编辑

你应该把代码放到github上

https://www-igm.univ-mlv.fr/~lecroq/string/
精确字符串匹配算法 36个,不知道你用的是哪个
guogss 发表于 2024-9-4 14:26
有没有使用例子啊?来一个调用实例啊
AuroraVerses 发表于 2024-9-5 00:14
收藏了,学习学习
guogss 发表于 2024-9-7 00:05
这个适用于什么vs??还有就是需要包含哪些h文件啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-28 07:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表