模仿Cheat Engine内存搜索——(Sunday算法)
想要获取一个进程里面的某个数据,需要先知道这个数据的位置对于全局变量:偏移是固定的,可以通过“基址+偏移”直接定位
对于局部变量:位置是随机的,只能通过拦截或者搜索去定位
分析企业微信的时候,发现很多数据都不是全局变量,不能像个人微信那样直接跨进程读取。如果能够用代码模仿Cheat Engine进行内存搜索,就可以定位到局部变量,进而实现非注入读取。甚至可以做到兼容所有版本(比如登录二维码,直接内存扫描出来)。
Cheat Engine是开源的,下面是特征码搜索的源码:
```
DWORD AOBScan(HANDLE hProcess, const char* pattern, const char* mask, uint64_t start, uint64_t end, int inc, int protection,uint64_t * match_addr)
{
// 查询内存块信息,看下是不是可读可写可执行
VirtualQueryEx(hProcess, (void*)tmp, &rinfo, NULL);
if((rinfo.protection & protection) != 0) ...
// 读取内存,用暴力算法BF(Brute Force)算法进行字符匹配
ReadProcessMemory(hProcess, (void*)tmp2, MemoryBuff, 4096)
for (int i = 0; i < 4096; i += inc)
for (int k = 0; k < patternLength; k++)
if (!(mask == '?' || pattern == (MemoryBuff)))
}
```
可以看出Cheat Engine内存搜索的核心逻辑是
1、查看内存块信息:VirtualQueryEx
2、跨进程读取内存:ReadProcessMemory
3、通过“字符串模式匹配算法”进行比较定位
“字符串模式匹配算法”中比较好的算法是“Sunday算法”,代码如下:
```
int SundaySearch(const byte* target, int tLen, const byte* pattern, int pLen)
{
const int SHIFT_SIZE = 0x100;
byte shift = { 0 };
memset(shift, pLen + 1, SHIFT_SIZE);
for (int i = 0; i < pLen; i++) shift] = pLen - i;
for (int i = 0; i < tLen - pLen; i += shift])
{
for (int j = 0; j < pLen; j++)
{
if (target != pattern) break;
if (j == pLen - 1) return i;
}
}
return -1;
}
```
再结合VirtualQueryEx和ReadProcessMemory就可以模仿Cheat Engine 内存搜索,代码如下:
```
int ScanPattern(HANDLE hProcess, byte* pattern, int pLen)
{
const int MEM_SIZE = 0x1000;
byte mem = { 0 };
MEMORY_BASIC_INFORMATION mbi;
for (int curAddress = 0x0400000; curAddress < 0x3FFFFFFF; curAddress += mbi.RegionSize)
{
VirtualQueryEx(hProcess, (void*)curAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
if ((int)mbi.RegionSize <= 0) break;
if (mbi.State != MEM_COMMIT) continue;
//if (mbi.Protect != PAGE_READONLY && mbi.Protect != PAGE_READWRITE) continue;
for (int memIndex = 0; memIndex < (int)mbi.RegionSize / MEM_SIZE; memIndex++)
{
int beginAddress = curAddress + memIndex * MEM_SIZE;
ReadProcessMemory(hProcess, (void*)(beginAddress), mem, MEM_SIZE, 0);
int offset = SundaySearch(mem, MEM_SIZE, pattern, pLen);
if (offset >= 0) return beginAddress + offset;
}
}
return -1;
}
```
调用实例
```
int main()
{
DWORD dwPid = 0x28F4;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);
int address = 0x14EE4020;
byte* pattern = (byte*)&address;
int pLen = 4;
int result = ScanPattern(hProcess, pattern, pLen);
std::cout << std::hex<< result << "\n";
}
``` iamok 发表于 2021-7-29 10:36
楼主的代码好像不支持‘?’,ce的代码是支持的。
不支持,分享的只是最核心的东西,其他功能自行扩展一下
可以参考 :if (!(mask == '?' || pattern == (MemoryBuff))) 市面上的sunday算法有问题,有一定几率会崩,以前做过这样的东西
Sunday 算法好像有bug
http://www.vbgood.com/forum.php?mod=viewthread&tid=111193&fromuid=206406
(出处: VB爱好者乐园(VBGood))
666a,牛啊大佬 好东西啊,今天看见二个原创啦。
好啊,论坛高手如云。
感谢楼主分享。
虽然看不懂,但是不影响大佬牛 楼主的代码好像不支持‘?’,ce的代码是支持的。 学习了,谢谢啊 厉害了楼主,分析明确,学到了 真的牛,看见c,c++大佬只能膜拜 我也想做基於微信的自動化工具,可是既不敢又沒技術。