大神论坛

找回密码
快速注册
查看: 138 | 回复: 0

[原创] 逆向分析之基于SSE2搜索内存特征码(支持模糊匹配)附源码

主题

帖子

0

积分

初入江湖

UID
692
积分
0
精华
威望
0 点
违规
大神币
68 枚
注册时间
2023-10-14 10:58
发表于 2023-11-08 22:36
本帖最后由 唐三没藏 于 2023-11-08 22:36 编辑

前言:在学习自写劫持补丁时,遇到搜索内存特征码,于是就有了这代码!!!

支持通配符??,支持一码多匹配,支持x86 x64
效果:

代码:

//  核心算法来自大神论坛www.dslt.tech
// 其余由wtujoxk整理,支持通配符??,支持一码多匹配,支持x86 x64
// 如使用请保留出处

#include <windows.h>
#include <intrin.h>
#include <psapi.h>
#include <string>
#include <vector>

/* SSE2PatternFind()特征码搜索
1) module 需要搜索的模块 HMODULE hModule = GetModuleHandle(L"模块名称");
2) pattern 搜索特征码,支持通配符?? 格式为:std::string pattern = "55 ?? 77 88 ?? AA BB";
3) searchNum 搜索个数,0为搜索整个模块,默认为0
4) deviation 特征码地址离目标地址的偏移距离,上负下正,默认不偏移
return返回值,找到的地址
调用 std::vector<ULONGLONG> retList = SSE2PatternFind(hModule, pattern, 10);
*/
std::vector<ULONGLONG> SSE2PatternFind(HMODULE module, std::string pattern, ULONGLONG searchNum = 0, ULONGLONG deviation = 0)
{
std::vector<ULONGLONG> retList;
// "12 34 ?? 78 ?? BC"
// 去除所有空格
if (!pattern.empty())
{
int index = 0;
while ((index = pattern.find(' ', index)) >= 0)
{
pattern.erase(index, 1);
}
index = 0;
while (true)
{
// 删掉头部通配符
index = pattern.find("??", index);
if (index == 0) {
pattern.erase(index, 2);
}
else {
break;
}
}
}
// 特征码长度不能为单数
if (pattern.length() % 2 != 0) return retList;
// 特征码长度
int len = pattern.length() / 2;
std::string SigMask = "";
std::string finalPattern = "";

// 将特征码转换为目标格式,并将mask做相应的匹配
for (int i = 0; i < len; i++) {
std::string tempStr = pattern.substr(i * 2, 2);
if (tempStr == "??") {
SigMask += "?";
finalPattern += char(0xFF);
}
else {
SigMask += "x";
finalPattern += char(strtoul(tempStr.c_str(), 0, 16));
}
}

// 必须初始化,否则报错
MODULEINFO moduleInfo = { 0 };
if (GetModuleInformation(GetCurrentProcess(), module, &moduleInfo, sizeof(moduleInfo)) == 0)
return retList;
ULONGLONG VirtualAddress = (ULONGLONG)moduleInfo.lpBaseOfDll;
ULONGLONG VirtualLength = moduleInfo.SizeOfImage;

// 常规变量
PUCHAR MaxAddress = (PUCHAR)(VirtualAddress + VirtualLength);
PUCHAR BaseAddress;
PUCHAR CurrAddress;
PUCHAR CurrPattern;
PCHAR CurrMask;
BOOLEAN CurrEqual;
register UCHAR CurrUChar;

// SSE 加速相关变量
__m128i SigHead = _mm_set1_epi8((CHAR)finalPattern[0]);
__m128i CurHead, CurComp;
ULONG MskComp, IdxComp;
ULONGLONG i, j, nCount = 0;

//
// 第一层遍历使用 SSE 将逐字节加速为逐 16 字节每次(最终加速 12 倍获益主要来源与此)
//
// 第二层子串匹配不能使用 SSE 加速,原因有四
// 1. SSE 虽为单指令多数据,但单个指令 CPU 周期比常规指令要高
//
// 2. 从概率上来说,子串匹配时第一个字节命中失败与 SSE 一次性对比 16 个字节命中失败在概率上几乎相等
//
// 3. 根据实验采用 SSE 优化第二层子串匹配将显著降低最终查找速度
//
// 4. 理论上,即使 SSE 单条指令与常规指令具有同样的CPU周期,最高也只能加速 16 倍
//
for (i = 0; i <= VirtualLength - 16; i += 16)
{
CurHead = _mm_loadu_si128((__m128i*)(VirtualAddress + i));
CurComp = _mm_cmpeq_epi8(SigHead, CurHead);
MskComp = _mm_movemask_epi8(CurComp);

BaseAddress = (PUCHAR)(VirtualAddress + i);
j = 0;
while (_BitScanForward(&IdxComp, MskComp))
{
CurrAddress = BaseAddress + j + IdxComp;
CurrPattern = (PUCHAR)finalPattern.c_str();
CurrMask = (PCHAR)SigMask.c_str();
for (; CurrAddress <= MaxAddress; CurrAddress++, CurrPattern++, CurrMask++)
{
// 因为是暴力搜索整个系统的物理内存,而本函数自身的堆栈区当然也属于整个物理内存的一部分
// 因此为了避免匹配到参数 SigPattern 本身,对其做了相应过滤操作,如不需要可以自行简化 2 行
CurrUChar = *CurrPattern;
// *CurrPattern = CurrUChar + 0x1;
CurrEqual = (*CurrAddress == CurrUChar);
// *CurrPattern = CurrUChar;

if (!CurrEqual) { if (*CurrMask == 'x') break; }
if (*CurrMask == 0)
{
retList.push_back((ULONGLONG)(BaseAddress + j + IdxComp + deviation));
if (++nCount >= searchNum && searchNum != 0) return retList;
break;
}
}

++IdxComp;
MskComp = MskComp >> IdxComp;
j += IdxComp;
}
}
return retList;
}

调用代码:

#include <iostream>
#include <windows.h>
#include <shlwapi.h>
#include <Psapi.h>
#include "SSE2PatternFind.h"


using namespace std;

int main()
{
HMODULE hModule = GetModuleHandle(L"ntdll.dll");
string pattern = "78 ?? 48 8D ?? ?? 50";

clock_t begin = clock();
vector<ULONGLONG> retList = SSE2PatternFind(hModule, pattern);
cout << "SSE搜索用时:" << clock() - begin << " ms" << endl;
for (int i = 0; i < retList.size(); i++)
cout << "特征码:"+ pattern + " 第" << i + 1 << "个--->结果: " << (void*)retList[i] << endl;

system("pause");
return 0;
}



注:此代码搜索内存不连续和映射不完整的模块会抛锚 ,有能力的自己完善!


注:若转载请注明大神论坛来源(本贴地址)与作者信息。


返回顶部