第一课我们浅解安卓ROOT手机如何修改游戏内存
在当前流行的FPS,MOBA游戏中,我们几乎都能看到游戏外挂的身影,在FPS游戏中,可见变态功能层出不穷,例如加速,锁血 遁地,飞天,路飞,无后座,范围伤害等…,然而在MOBA游戏中,最常见的只有透视和自瞄。
切入正题,如何操作内存?在安卓中,我们可直接操作/proc/${pid}/mem
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
简单解释下这个函数
fd:要读取数据的文件描述符
buf:数据缓存区指针,存放读取出来的数据
count:读取数据的字节数
offset:读取的起始地址的偏移量,读取地址=文件开始+offset。注意,执行后,文件偏移指针不变
还有另一个函数:pread64
很多人不知道pread64和pread的区别,其实pread64是用64位定位方式,用于对大文件的支持,与pread不同的是,pread64的偏移量参数为off64_t,而不是off_t
读写游戏内存最重要的是获取游戏进程的PID,什么是PID?PID就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序,但是在android系统中一般不会把已经kill掉的进程ID重新分配给新的进程,新产生进程的进程号,一般比产生之前所有的进程号都要大。
那么问题来了,如何获取游戏pid?
第一种方式:
遍历/proc/${pid}/cmdline
cmdline文件储存的是当前进程的启动名(包名)
直接贴代码:
pid_t GetProcessID(const char *process_name)
{
int id;
pid_t pid = -1;
DIR *dir;
FILE *fp;
char filename[32];
char cmdline[256];
struct dirent *entry;
if (process_name == NULL)
{
return -1;
}
dir = opendir("/proc");
if (dir == NULL)
{
return -1;
}
while ((entry = readdir(dir)) != NULL)
{
id = atoi(entry->d_name);
if (id != 0)
{
sprintf(filename, "/proc/%d/cmdline", id);
fp = fopen(filename, "r");
if (fp)
{
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
if (strcmp(process_name, cmdline) == 0)
{
pid = id;
break;
}
}
}
}
closedir(dir);
return pid;
}
第二种方式:使用shell命令的pidof
char *shell(const char *command)
{
FILE *fp = NULL;
char line[256] = { };
char *result = (char *)malloc(2048);
memset(result, 0, sizeof(result));
fp = popen(command, "r");
while (fgets(line, sizeof(line), fp) != NULL)
{
strncat(result, line, strlen(line));
}
pclose(fp);
return result;
}
pid_t GetProcessID(char *process_name)
{
char cmd[256] = {0};
sprintf(cmd,"su -c pidof %s",process_name);
char *id = shell(cmd);
return strlen(id) == 0 ? -1 : atoi(id);
}
如何读内存:
char filename[256] = {0};
sprintf(filename,"/proc/%d/mem",pid);//pid为获取到的游戏pid
int fd = open(filename,O_RDWR | O_SYNC);//以可读写(O_RDWR)且同步(O_SYNC)的方式打开文件
int buf = 0;
pread(fd,&buf,sizeof(buf),内存地址);
printf("value:%d\n",buf);
学会了读取内存,那如何写入内存呢?
这时候需要用到另一个函数:pwrite
pwrite的参数和pread一样,我就不讲解了
char filename[256] = {0};
sprintf(filename,"/proc/%d/mem",pid);//pid为获取到的游戏pid
int fd = open(filename,O_RDWR | O_SYNC);//以可读写(O_RDWR)且同步(O_SYNC)的方式打开文件
int wbuf = 666;
pwrite(fd,&wbuf,sizeof(wbuf),内存地址);//向指定地址写入指定值(修改地址的值)
根据上两段代码,我们可以发现一个知识点:内存地址
这个内存地址如何获取?
在proc文件系统中,有一个内存段映射文件:/proc/${pid}/maps
打开这个文件,我们可以得到以下结构:
f1ed1000-f1ed9000 r--p 00000000 fc:02 3092 /system/lib/libsensor.so
f1ed9000-f1edf000 r-xp 00008000 fc:02 3092 /system/lib/libsensor.so
f1edf000-f1ee0000 rw-p 0000e000 fc:02 3092 /system/lib/libsensor.so
f1ee0000-f1ee2000 r--p 0000f000 fc:02 3092 /system/lib/libsensor.so
f1ee2000-f1ee3000 rw-p 00000000 00:00 0 [anon:.bss]
关于maps文件各列解释,可以参考
https://blog.csdn.net/lijzheng/article/details/23618365
可以看出,第一列既为我们需要的游戏内存地址。
现在问题来了,像gg修改器,我们如何在内存里搜索一个值呢?
这时候又涉及一个知识点:值所在的内存范围
为了方便,我们可以使用GG修改器的内存范围
struct Memory
{
int A = 32;
int As = 524288;
int B = 131072;
int Xa = 16384;
int Xs = 32768;
int Ca = 4;
int Cb = 16;
int Cd = 8;
int Ch = 1;
int J = 65536;
int Jh = 2;
int O = -2080896;
int Ps = 262144;
int S = 64;
int V = 1048576;
} MemRange;
int getMemRange(char *str)
{
if (strlen(str) == 0)
return MemRange.A;
if (strstr(str, "/dev/ashmem/") != NULL)
return MemRange.As;
if (strstr(str, "/system/fonts/") != NULL)
return MemRange.B;
if (strstr(str, "/data/app/") != NULL)
return MemRange.Xa;
if (strstr(str, "/system/framework/") != NULL)
return MemRange.Xs;
if (strcmp(str, "[anon:libc_malloc]") == 0)
return MemRange.Ca;
if (strstr(str, ":bss") != NULL)
return MemRange.Cb;
if (strstr(str, "/data/data/") != NULL)
return MemRange.Cd;
if (strstr(str, "[anon:dalvik") != NULL)
return MemRange.J;
if (strcmp(str, "[stack]") == 0)
return MemRange.S;
if (strcmp(str, "/dev/kgsl-3d0") == 0)
return MemRange.V;
return MemRange.O;
}
如何搜索值:
通过遍历内存段映射地址,判断值是否为搜索的值
例如f1ed1000-f1ed9000,我们需要遍历f1ed1000到f1ed9000的所有地址的值来判断
直接上代码:
long start = 0xf1ed1000;
long end = 0xf1ed9000;
int value = 1;//我们要搜索的值
char filename[256] = {0};
sprintf(filename,"/proc/%d/mem",pid);
int fd = open(filename,O_RDWR | O_SYNC);
long size = end - start;
int count = 0;//计数
void *buf = calloc(1, size);
pread64(fd,buf,size,start);
int block_size = sizeof(int);
for(int i = 0;i<size;i+=block_size)
{
int search_value = *(int*)(buf + i);
if(search_value == value)
{
//搜索到数值
printf("index:%d address:%lx value:%d\n",i,start + i,search_value);
count++;
}
}
printf("搜索到:%d个数值\n",count);
现在搜索数值会了,修改数值不就迎刃而解了吗?
今天的教程结束,有不懂的可以私信问我。手打代码不易,看完加个关注,持续更新游戏作弊教程