在WIN上的游戏玩家,大多对游戏修改器比较熟悉,玩家可以利用游戏修改器来修改游戏里的人物的经验值或金钱等。那么WIN上的游戏修改器是怎么做的那?关于WIN上的游戏修改器的制作方法在网上有很多技术资料,我还清楚的记得,在《程序员》杂志的最早的几刊中对此有比较详细的介绍,那个时候我还在上大学^_^,这里不免为《程序员》杂志做一个广告,这个杂志是我上学时候最爱读的,现在依然如此,每一期都不会错过,从里面我学到了很多东西,在很大程度上丰富了自己的知识面,在这里我要感谢这个杂志,也衷心的希望《程序员》杂志越办越好!
哈哈,不免有些跑题了,我们言归正传,在WIN上有专门的API来对另一个进程的地址空间进行扫描,当然,通过什么样的扫描方式,来定位、猜测游戏中相关数据我就不说了,重要的是这个API提供给我们一个手段来操作另一个进程地址空间里的数据,正常来说,每一个进程都有自己独立的地址空间,这个进程是无法直接访问另一个进程地址空间的数据的。
在UNIX上其实我们也有类似的函数来进行这样的操作,pread、pwrite这些函数允许我们对另一个进程地址空间进行读写,很多调试工具其实就是利用这些接口来完成对另一个程序进行调试的,但是使用这2个函数,要有先决条件,就是要先使用ptrace函数catch住目标进程,然后才能调用pread或pwrite,既然已经catch住目标进程了,其实目标进程当前就停止运行了,那么有没有办法不catch目标进程,我们就能修改另一个进程地址空间的数据那?答案当然是有^_^,我们可以利用进程文件系统proc来轻松完成这件事情。
UNIX上的进程文件系统其实是一个虚拟的文件系统,你使用命令cd /proc,你发现这里面对应每一个进程号都有相应的一个文件夹,这个文件夹里面的东西就有能够帮我们完成修改另一个进程地址空间所需的东东^_^。另外在目录/usr/proc/bin目录里面包含了很多进程文件系统命令,例如:pstack,该命令可以查看另一个进程的堆栈信息,其实该命令的实现就是通过进程文件系统来实现的。
这里我们假设被修改地址空间数据的进程的进程号为3548,那么/proc/3548文件夹里面的文件as就是我们所需要的,这个文件是该进程地址空间里的数据的完整映射,并且我们可以通过标准的读写函数read、write等对这个as文件进行操作,从而达到修改3548进程地址空间里数据的目的,下面我列出了一段简单的代码来描述这个过程:
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <string.h>
int main(void)
{
int procfd;
int except_value = 0;
int old_value = 0;
int new_value = 0;
unsigned long address= 0;
char cmd_string[50];
char proc_id[50];
memset(cmd_string, 0, sizeof(cmd_string));
memset(proc_id, 0 ,sizeof(proc_id));
system("clear");
printf("\t\t.............欢迎使用内存数据修改工具........\n");
printf("\n");
printf("\n");
printf("请输入要修改的进程的进程id号.....\n");
scanf("%s",proc_id);
printf("输入要修改的数据内存地址(输入的数据是十六进制格式,例如:ed80249
8)\n");
scanf("%x",&address);
printf("输入期望的数据\n");
scanf("%d",&except_value);
sprintf(cmd_string, "%s%s%s","/proc/",proc_id,"/as");
procfd = open(cmd_string ,O_RDWR);//打开文件/proc//as,获得文件句柄
if(procfd < 0){
printf("打开进程文件系统失败,错误码为%d\n", errno);
exit(1);
}
lseek(procfd,address,SEEK_SET);
read(procfd, &old_value, 4);
printf("该内存地址上的数据为%d\n", old_value);
lseek(procfd,address,SEEK_SET);
write(procfd, &except_value, 4);
lseek(procfd,address,SEEK_SET);
read(procfd, &new_value, 4);
printf("修改后该内存地址上的数据为%d\n", new_value);
}
代码很简单、清晰,大家可以根据这个思路写出更复杂更好玩的东西,欢迎大家就这个东西和我做进一步交流!