游戏作弊-内存读写原理(一)

第一课我们浅解安卓ROOT手机如何修改游戏内存

在当前流行的FPS,MOBA游戏中,我们几乎都能看到游戏外挂的身影,在FPS游戏中,可见变态功能层出不穷,例如加速,锁血 遁地,飞天,路飞,无后座,范围伤害等…,然而在MOBA游戏中,最常见的只有透视和自瞄。

切入正题,如何操作内存?在安卓中,我们可直接操作/proc/${pid}/mem

使用C语言pread函数

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);

现在搜索数值会了,修改数值不就迎刃而解了吗?

今天的教程结束,有不懂的可以私信问我。手打代码不易,看完加个关注,持续更新游戏作弊教程

你可能感兴趣的:(笔记)