本帖最后由 mengxiansheng98 于 2024-01-11 21:59 编辑
众~~~所周知,Wine是个好东西,Linux系统可以用它跑Windows程序。它通过实现一系列的Windows API,将Windows PE (可执行和可载入文件格式)转换为Linux操作系统可以理解的格式。让Windows应用在Linux上找到家的感觉。
再众~~~所周知,Linux下有个有趣的特性——“一切皆文件”。这意味着能在/proc/{pid}/maps里一窥进程的内存分布,还能在/proc/{pid}/mem里随心所欲的“画饼”。理论成立,开始实践。
实践只是为了验证前面的猜想,当然得挑个软柿子捏,于是就找到了一个快20年前的老游戏:“XXX大作战”。 操作第一步,探个究竟,Wine是否真把PE内存镜像本本原原地摆放好了。
通过命令ps -e | grep -i MONE,锁定目标进程PID为17551。 接着,cat /proc/17551/maps | grep exe揭秘了内存布局,果然跟Windows如出一辙,PE文件映射从0x00400000起点开始。 这时就能来点wirte和read操作,悄悄摸进/proc/{pid}/mem修改内存,居然比Windows还要简单。
首先是捕捉游戏进程的PID,一段轮子代码搞定。 int findPid(string processName) { FILE *fp; char path[1035]; fp = popen("ps -ef", "r"); if (fp == nullptr) { printf("Failed to run command\n"); exit(1); } while (fgets(path, sizeof(path) - 1, fp) != nullptr) { if (::strstr(path, processName.c_str()) != nullptr) { ::strtok(path, " "); auto token = ::strtok(nullptr, " "); return stoi(token); } } pclose(fp); return -1; }
接下来,打开对应的mem文件,妙哉,这文件描述符就相当于是Windows的句柄。 int openMem(int pid) { char mem_filename[1024]; sprintf(mem_filename, "/proc/%d/mem", pid); return open(mem_filename, O_RDWR); }
后续两个模板函数分别操控读写内存。 template<class T> T readMem(long address, int mem_file) { T a; if (lseek(mem_file, address, SEEK_SET) == -1) { ::printf("lseek error %s \n", strerror(errno)); } read(mem_file, &a, sizeof(T)); return a; } template<class T> void writeMem(long address, T value, int mem_file) { if (lseek(mem_file, address, SEEK_SET) == -1) { ::printf("lseek error %s \n", strerror(errno)); } write(mem_file, &value, sizeof(T)); }
该有的都有了,最后把它们组合起来,验证一下功能。 #include <string> #include <cstring> #include <fcntl.h> #include <atomic> const unsigned int num_current_offset = 0x268c; // 当前收集的数量 const unsigned int kind_current_offset = 0x2690; // 当前收集的种类 const unsigned int isShot_current_offset = 0x2680; // 是否可以释放 using namespace std; int findPid(string processName) { FILE *fp; char path[1035]; fp = popen("ps -ef", "r"); if (fp == nullptr) { printf("Failed to run command\n"); exit(1); } while (fgets(path, sizeof(path) - 1, fp) != nullptr) { if (::strstr(path, processName.c_str()) != nullptr) { ::strtok(path, " "); auto token = ::strtok(nullptr, " "); return stoi(token); } } pclose(fp); return -1; } int openMem(int pid) { char mem_filename[1024]; sprintf(mem_filename, "/proc/%d/mem", pid); return open(mem_filename, O_RDWR); } template<class T> T readMem(long address, int mem_file) { T a; if (lseek(mem_file, address, SEEK_SET) == -1) { ::printf("lseek error %s \n", strerror(errno)); } read(mem_file, &a, sizeof(T)); return a; } template<class T> void writeMem(long address, T value, int mem_file) { if (lseek(mem_file, address, SEEK_SET) == -1) { ::printf("lseek error %s \n", strerror(errno)); } write(mem_file, &value, sizeof(T)); } int main() { int pid = findPid("MONE~OUQ.EXE"); if (pid == -1) return -1; int mem_file = openMem(pid); long address = 0x484a3c; auto class_address = readMem<unsigned int>(address, mem_file); while (true) { writeMem<int>(class_address + num_current_offset, 2, mem_file); writeMem<unsigned char>(class_address + kind_current_offset, 21, mem_file); writeMem<unsigned char>(class_address + isShot_current_offset, 1, mem_file); sleep(1); } return 0; }
ps:因为不是用windows api OpenProcess、ReadProcessMemory、WriteProcessMemory,不知道windows下传统防内存修改方法能不能防得住。
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|