想要获取一个进程里面的某个数据,需要先知道这个数据的位置
对于全局变量:偏移是固定的,可以通过“基址+偏移”直接定位
对于局部变量:位置是随机的,只能通过拦截或者搜索去定位
分析企业微信的时候,发现很多数据都不是全局变量,不能像个人微信那样直接跨进程读取。如果能够用代码模仿Cheat Engine进行内存搜索,就可以定位到局部变量,进而实现非注入读取。甚至可以做到兼容所有版本(比如登录二维码,直接内存扫描出来)。
Cheat Engine是开源的,下面是特征码搜索的源码:
可以看出Cheat Engine内存搜索的核心逻辑是
1、查看内存块信息:VirtualQueryEx
2、跨进程读取内存:ReadProcessMemory
3、通过“字符串模式匹配算法”进行比较定位
“字符串模式匹配算法”中比较好的算法是“Sunday算法”,代码如下:
再结合VirtualQueryEx和ReadProcessMemory就可以模仿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[k]
=
=
'?'
|| pattern[k]
=
=
(MemoryBuff[k])))
}
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[k]
=
=
'?'
|| pattern[k]
=
=
(MemoryBuff[k])))
}
int
SundaySearch(const byte
*
target,
int
tLen, const byte
*
pattern,
int
pLen)
{
const
int
SHIFT_SIZE
=
0x100
;
byte shift[SHIFT_SIZE]
=
{
0
};
memset(shift, pLen
+
1
, SHIFT_SIZE);
for
(
int
i
=
0
; i < pLen; i
+
+
) shift[pattern[i]]
=
pLen
-
i;
for
(
int
i
=
0
; i < tLen
-
pLen; i
+
=
shift[target[i
+
pLen]])
{
for
(
int
j
=
0
; j < pLen; j
+
+
)
{
if
(target[i
+
j] !
=
pattern[j])
break
;
if
(j
=
=
pLen
-
1
)
return
i;
}
}
return
-
1
;
}
int
SundaySearch(const byte
*
target,
int
tLen, const byte
*
pattern,
int
pLen)
{
const
int
SHIFT_SIZE
=
0x100
;
byte shift[SHIFT_SIZE]
=
{
0
};
memset(shift, pLen
+
1
, SHIFT_SIZE);
for
(
int
i
=
0
; i < pLen; i
+
+
) shift[pattern[i]]
=
pLen
-
i;
for
(
int
i
=
0
; i < tLen
-
pLen; i
+
=
shift[target[i
+
pLen]])
{
for
(
int
j
=
0
; j < pLen; j
+
+
)
{
if
(target[i
+
j] !
=
pattern[j])
break
;
if
(j
=
=
pLen
-
1
)
return
i;
}
}
return
-
1
;
}
int
ScanPattern(HANDLE hProcess, byte
*
pattern,
int
pLen)
{
const
int
MEM_SIZE
=
0x1000
;
byte mem[MEM_SIZE]
=
{
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
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)