前言
市面上的PC端读写内存的教程太多了,而Android端的内存读写教程则非常少,故此研究一番.
app:植物大战僵尸单机版
部分代码参考ce-server和网上各种资料.
环境
1.雷电模拟器3(3.120)
2.Android Studio (3.6)
3.ndk (22.1.7171670)
ce搜索
模拟器连接ce-server
在ce官网下载ce-server
https://www.cheatengine.org/downloads.php
将压缩包里的ceserver_x86传到模拟器上
adb push ceserver_x86 /data/local/tmp
更改权限并启动
adb shell
cd /data/local/tmp
chmod 777 ceserver_x86
./ceserver_x86
端口转发
adb forward tcp:52736 tcp:52736
ce附加里选择网络
ce搜索阳光
先搜初始阳光150,捡起阳光再次搜索,很容易搜索到阳光地址
特征码搜索阳光地址
我尝试了许多次,按照传统的找指针方法始终没用找到阳光的指针,只能选择用特征码搜索,-_-.
ce浏览相关内存区域,可以找到一组特征码
96 00 00 00 04 00 00 00
结合maps文件可知(cat /proc/xxxx/maps)
阳光地址分布在
9d400000-9d401000
9d401000-a1402000
间,且模块无名称.
改写这些特征地址游戏并无崩溃.
则可将所有的特征地址都当作阳光地址处理.(我偷懒了)
外部读写内存
读内存
百度得知,读内存大致有3种方法
- open /proc/xx/mem 文件,并使用lseek64和read读取对应地址的内存
(参考ce源码 Cheat Engine\ceserver\api.c ReadProcessMemory)
- ptrace读内存
(参考ce源码 Cheat Engine\ceserver\api.c ReadProcessMemory)
- syscall读内存
写内存
我这里采用mem方式读取内存,ptrace写入内存
部分代码参考ce-server
https://github.com/cheat-engine/cheat-engine/tree/master/Cheat%20Engine/ceserver
实验过程
代码就不一一展开了,大致过程如下,大家可以去github查看代码及注释:
1.根据包名获取待读取进程pid.
2.根据pid获取maps文件并筛选出符合条件的内存区域.
3.读取待读取内存到本地内存.
4.特征码搜索出对应的内存地址.
5.修改对应的内存地址.
int main(int argc, char *argv[])
{
auto pid = find_pid("com.popcap.pvz");//获取pid
if (pid == 0)
{
printf("Can't find pid\n");
return -1;
}
else
{
printf("%d\n", pid);
}
PModuleListEntry ml = get_process_map(pid);//读取进程的模块信息,PModuleListEntry是一个链表
while (ml->next_ptr != nullptr)//循环所有的模块
{
if (!ml->moduleName[0]) //判断是否有模块名,注意,这个例子是没有模块名的.
{
int size = ml->moduleSize;
unsigned char *target = (unsigned char *)malloc(size);
unsigned char *target_ = target;
int r = ReadProcessMemory(pid, (void *)ml->baseAddress, target, size);//读取内存
if (r == 0)
{
goto lable; //unreadable
}
while (true) //循环搜索所有匹配到的特征地址
{
int offset = AOBScan(target, size, pattern, sizeof(pattern));
if (offset != -1)
{
target += offset + sizeof(pattern);
size -= offset + sizeof(pattern);
unsigned long long offset_ = target - target_ - sizeof(pattern);//这里是拿到偏移
unsigned long long address = ml->baseAddress + offset_;//搜索到的目标进程的内存地址
// printf("offset_:%llx\n", offset_);
printf("%llx\n", address);
unsigned int data[] = {0x12345678};
WriteProcessMemory(pid, (void *)address, (void *)data, sizeof(data)/sizeof(unsigned int));//写内存
continue;
}
break;
}
free(target_);
}
lable:
ml = (PModuleListEntry)ml->next_ptr;
}
return 0;
}
最后效果如图:
写在最后
本文仅仅是在模拟器上做了测试,对于其他几种读写内存方式也没有尝试,希望对小伙伴们有所帮助.
也推荐一个b站爱分享的up主:赶码人
https://space.bilibili.com/669962565?spm_id_from=333.788.b_765f7570696e666f.2
github地址:
https://github.com/PShocker/Android_ndk_mem
文中所用app:
链接:https://pan.baidu.com/s/11slA0I_OAHpgVltIv3n1Pw?pwd=724z
提取码:724z