大神论坛

找回密码
快速注册
查看: 99 | 回复: 0

[原创] Linux逆向分析Wine的PE内存与内存修改

主题

帖子

0

积分

初入江湖

UID
687
积分
0
精华
威望
0 点
违规
大神币
68 枚
注册时间
2023-10-14 10:56
发表于 2024-01-11 21:59
本帖最后由 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下传统防内存修改方法能不能防得住。


注:若转载请注明大神论坛来源(本贴地址)与作者信息。

返回顶部