目录

C语言内存特征码搜索 模糊匹配(CE的AOBScan函数,暴力匹配)

目录

先声明一下,虽然代码出自cheat engine,但它不仅是用于游戏作弊,还广泛用于病毒查杀、反外挂等程序。

暴力匹配,应该没有更快更好的算法了,毕竟ceserver用的就是这个算法(

源码位于**/ceserver/native-api.h**,/ceserver/native-api.c

1
2
3
4
5
6
7
#ifndef NativeAPI_H_
#define NativeAPI_H_

#define MAX_HIT_COUNT  5000000
DWORD AOBScan(HANDLE hProcess, const char* pattern, const char* mask, uint64_t start, uint64_t end, int inc, int protection, uint64_t* match_addr);

#endif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "api.h"
#include "porthelp.h"
#include "ceserver.h"
#include "threads.h"
#include "symbols.h"
#include "context.h"
#include "native-api.h"

DWORD AOBScan(HANDLE hProcess, const char* pattern, const char* mask, uint64_t start, uint64_t end, int inc, int protection,uint64_t * match_addr) {

	RegionInfo rinfo;
	uint64_t tmp = start;
	uint64_t tmp2 = tmp;
	
	char* MemoryBuff = (char*)malloc(4096);
	int patternLength = (int)strlen(mask);
	
	int result_count = 0;
	
	while (tmp < end) {
		VirtualQueryEx(hProcess, (void*)tmp, &rinfo, NULL);
		if (rinfo.size == 0) {
			return -1;
		}
		if((rinfo.protection & protection) != 0)
		{
			tmp2 = tmp;
			while (tmp2 < tmp + rinfo.size)
			{
				if (!ReadProcessMemory(hProcess, (void*)tmp2, MemoryBuff, 4096)) {
					break;
				}
	
				for (int i = 0; i < 4096; i += inc)
				{
					for (int k = 0; k < patternLength; k++)
					{
	
						if (!(mask[k] == '?' || pattern[k] == (MemoryBuff[k])))
						{
							goto label;
	
						}
					}
					match_addr[result_count] = tmp2;
					result_count++;
					if (result_count >= MAX_HIT_COUNT)return result_count;
	
				label:
					tmp2 += inc; MemoryBuff += inc;
				}
				MemoryBuff -= 4096;
			}
		}
		tmp += rinfo.size;
	}

return result_count;
}

不知道你如何看待darkbyte的这段代码,我菜得狠,不敢乱评价,总之就是复制出来不能直接用,

于是我改了改,把关键内容提取出来了,我不是很擅长C语言,仅供参考…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
int AOBScan(void* addr, int len, const char* pattern, const char* mask, int pattern_len, int inc, long* match_addr, long base_addr) {
	char* MemoryBuff = (char*)addr;
	int result_count = 0;

	for (int i = 0; i < len; i += inc) {
		for (int k = 0; k < pattern_len; k++) {
			if ( mask[k] != 0xff && pattern[k] != MemoryBuff[i + k] ) {
				goto label;
			}
		}
		
		match_addr[result_count] = base_addr + i;
		result_count++;
		if (result_count >= MAX_HIT_COUNT){
			return result_count;
		}
		label:;
	}

	return result_count;
}

比较基础,但我还是大概解释一下:

参数一:指针,不解释了,(别问为啥不需要pid之类的…

参数二:扫描的那段内存的长度

参数三、四、五:特征码相关,例如特征码是aa bb cc ?? ?? aa bb

1
2
char pattern[7] = {0xaa, 0xbb, 0xcc, 0x随便写, 0x随便写, 0xaa, 0xbb};
char mask[7] = {0x随便写, 0x随便写, 0x随便写, 0xff, 0xff, 0x随便写, 0x随便写};

参数六:内存对齐,通常都是1(速度慢)、2、4、8,具体用哪个,因目标程序而异,也要看你特征码找的O不OK。

参数七:用来存储结果的数组

参数八:读外部进程需要用到,因为变量i是基于参数一 addr的偏移,而参数一是自身进程内存空间的地址…..(好像有点啰嗦了?懂的自然懂)

在我的红米10x (4g版百元机,我意思是 做个 性能参考)上简单测试了一下,运行内存约2GB的某64位程序,过滤好Ca内存([anon:libc_malloc]),内存对齐8字节,特征码40字节,12个字节是通配符,大概2~3s就能扫描出来全部结果。

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/db75225feabec8d8b64ee7d3c7165cd639554cbc-20220130111452739.png