提升搜索速度的建议
对代码做了一些优化,目前测试提高了近50倍(另外使用了缓存以及缩短了搜索范围),贴一下,作为参考:
/// <summary>
/// 内存过滤规则,例如:(state == MEM_COMMIT && (protect == PAGE_EXECUTE || protect == PAGE_EXECUTE_READ || protect == PAGE_EXECUTE_READ || protect == PAGE_READWRITE || protect == PAGE_READONLY))
/// </summary>
/// <param name="state"></param>
/// <param name="protect"></param>
/// <returns>返回值1:该内存页是否需要扫描;返回值2:是否使用内部默认扫描函数,true表示采用默认</returns>
public delegate (bool, bool) CustomMemoryFilter(uint state, uint protect);
/// <summary>
/// 自定义的字节序列数据搜索函数
/// </summary>
/// <param name="data"></param>
/// <param name="bytesToFind"></param>
/// <returns>相对偏移,负数表示未搜索到,否则是匹配的相对偏移量</returns>
public delegate int CustomSearcher(byte[] data, byte[] bytesToFind);
/// <summary>
/// 在进程所有内存空间搜索字节数组
/// </summary>
/// <param name="handle">进程句柄</param>
/// <param name="searchBytes">待搜索的字节数组</param>
/// <param name="customMemoryFilter">过滤内页面属性的条件</param>
/// <param name="customSearch">自定义搜索函数</param>
/// <returns></returns>
public static List<long> SearchProcessAllMemory(IntPtr handle, byte[] searchBytes, CustomMemoryFilter? customMemoryFilter, CustomSearcher? customSearch) {
List<long> addrList = new();
IntPtr minAddress = IntPtr.Zero;
IntPtr maxAddress = IntPtr.MaxValue;
int pos = 0;
bool shouldScan = false;
bool useDefaultScan = true;
while (minAddress < maxAddress) {
MEMORY_BASIC_INFORMATION64 memInfo;
int result = VirtualQueryEx(handle, minAddress, out memInfo, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION64)));
if (result == 0) { break; }
if (customMemoryFilter != null) {
(shouldScan, useDefaultScan) = customMemoryFilter(memInfo.State, memInfo.Protect);
} else {
shouldScan = (memInfo.State == MEM_COMMIT && (memInfo.Protect == PAGE_EXECUTE || memInfo.Protect == PAGE_EXECUTE_READ || memInfo.Protect == PAGE_EXECUTE_READ || memInfo.Protect == PAGE_READWRITE || memInfo.Protect == PAGE_READONLY));
}
if (shouldScan) {
byte[] buffer = new byte[(long)memInfo.RegionSize];
if (ReadProcessMemory(handle, memInfo.BaseAddress, buffer, buffer.Length, out _)) {
pos = -1;
if (useDefaultScan) {
pos = SearchBytes(buffer, searchBytes);
} else if (customSearch != null) {
pos = customSearch(buffer, searchBytes);
}
if (pos >= 0) { addrList.Add(memInfo.BaseAddress + pos); }
}
}
minAddress = memInfo.BaseAddress + (nint)memInfo.RegionSize;
}
return addrList;
}
/// <summary>
/// 在内存数据中查找字节序列,注意:搜索方法不回溯且每次递进8字节
/// </summary>
/// <param name="data"></param>
/// <param name="bytesToFind"></param>
/// <returns></returns>
private static int SearchBytes(byte[] data, byte[] bytesToFind) {
for (int i = 0; i < data.Length - bytesToFind.Length; i += sizeof(long)) {
for (int j = 0; j < bytesToFind.Length;) {
if (data[i + j] != bytesToFind[j]) {
break;
} else {
j++;
if (j == bytesToFind.Length) {
return i;
}
}
}
}
return -1;
}
外部使用时:
// 在进程的所有地址空间里搜索。讨巧:该字符串特征为0x10对齐,且内存页面属性为可读写,可以提高搜索速度。
byte[] searchBytes = Encoding.UTF8.GetBytes("-----BEGIN PUBLIC KEY-----");
var listAddr = NativeAPIHelper.SearchProcessAllMemory(handle, searchBytes,
(state, protect) => ((state == NativeAPI.MEM_COMMIT && protect == NativeAPI.PAGE_READWRITE), false),
(data, search) => HexPatternMatcherKMP.SearchBytes(data, search, 0x10)
);
// 对所有地址在指定某块内里搜索
List<int> listTargetAddr = new();
foreach (var address in listAddr) {
matchedOffset = HexPatternMatcherKMP.SearchNumber(session.CacheBuffer, address);
if (matchedOffset >= 0) { listTargetAddr.Add(matchedOffset); }
}
// 取较大的那个,该地址只比手机型号地址大0x28
matchedOffset = listTargetAddr.Max();
这位师傅厉害,回头我学习下看看合并进来!感谢!
@SuxueCode https://github.com/xaoyaoo/PyWxDump 这个里面 还有一个把手机型号作为搜索特征的方法。 综合起来,相当于有三个:
- 搜索注册ID
- 搜索手机型号
- 搜索公钥头信息(虽然慢,但是思路挺好,至少也是一个有效特征,而且有点意思)
测试下来前两个最快,基本上在1ms以内就完成了。
123点方法现在已经全部失效了 我软件内也写有基于用户名和公钥头的方法,2和3出现的时间差不多,但是3的效果比较好,最后还是移植了3过来 根据之前的体验,公钥头的速度C#跑起来其实并不慢,主要还是搜索内存这块实现起来比较麻烦一些,毕竟对底层不熟悉
123点方法现在已经全部失效了 我软件内也写有基于用户名和公钥头的方法,2和3出现的时间差不多,但是3的效果比较好,最后还是移植了3过来 根据之前的体验,公钥头的速度C#跑起来其实并不慢,主要还是搜索内存这块实现起来比较麻烦一些,毕竟对底层不熟悉
亲测3.9.9.43版本三个方法都有效。
囧,跑了一下还真是,之前有一段时间接到批量反馈说不行了 但是1/2点的成功率实际环境下,没有3高 因为用户名基于地址去获取本质上还是要维护地址,2的话实际环境下,机型干扰可能会比较大 公钥头这个是目前观察到成功率最高的算法了
囧,跑了一下还真是,之前有一段时间接到批量反馈说不行了 但是1/2点的成功率实际环境下,没有3高 因为用户名基于地址去获取本质上还是要维护地址,2的话实际环境下,机型干扰可能会比较大 公钥头这个是目前观察到成功率最高的算法了
不用刻意去维护,减少搜索时间的技巧是缩小内存范围,可以找一些大概的特征去限定下,速度就上来了。