mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
fix: let Wine write hooks into code FEX has translated Wine 11's wineserver writes another process's memory with process_vm_writev, which the kernel refuses on a page that is not writable. FEX's WoW64 layer keeps the pages it has translated read-only to catch self-modifying code and re-protects them right after a program unprotects them, so WriteProcessMemory and NtWriteVirtualMemory into ntdll failed with a partial copy and Ubisoft games stopped at "Error at hooking API NtProtectVirtualMemory". Wine only falls back to /proc/<pid>/mem on ENOSYS or EPERM. libblsession now completes a single-buffer write that wineserver was refused with EFAULT through /proc/<pid>/mem, the way Wine wrote before Wine 11. Other programs and every other error keep the kernel's answer. The iovecs are read with process_vm_readv, so a bad pointer still returns EFAULT, and offsets are 64-bit in the i386 build.
56 lines
1.8 KiB
C
56 lines
1.8 KiB
C
#define _GNU_SOURCE
|
|
#define _FILE_OFFSET_BITS 64
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
|
|
typedef ssize_t (*vm_writev_fn)(pid_t, const struct iovec *, unsigned long, const struct iovec *, unsigned long,
|
|
unsigned long);
|
|
|
|
static int in_wineserver(void) {
|
|
static int known = -1;
|
|
if (known < 0) {
|
|
char exe[512];
|
|
ssize_t n = readlink("/proc/self/exe", exe, sizeof exe - 1);
|
|
exe[n > 0 ? n : 0] = 0;
|
|
const char *name = strrchr(exe, '/');
|
|
known = !strcmp(name ? name + 1 : exe, "wineserver");
|
|
}
|
|
return known;
|
|
}
|
|
|
|
ssize_t process_vm_writev(pid_t pid, const struct iovec *local, unsigned long liovcnt, const struct iovec *remote,
|
|
unsigned long riovcnt, unsigned long flags) {
|
|
static vm_writev_fn real;
|
|
if (!real) real = (vm_writev_fn)dlsym(RTLD_NEXT, "process_vm_writev");
|
|
if (!real) {
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
ssize_t result = real(pid, local, liovcnt, remote, riovcnt, flags);
|
|
if (result >= 0 || errno != EFAULT || liovcnt != 1 || riovcnt != 1 || flags || !in_wineserver()) return result;
|
|
struct iovec ends[2];
|
|
struct iovec into = {ends, sizeof ends};
|
|
struct iovec from[2] = {{(void *)local, sizeof *local}, {(void *)remote, sizeof *remote}};
|
|
if (process_vm_readv(getpid(), &into, 1, from, 2, 0) != (ssize_t)sizeof ends || ends[0].iov_len != ends[1].iov_len) {
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
char path[32];
|
|
snprintf(path, sizeof path, "/proc/%d/mem", (int)pid);
|
|
int fd = open(path, O_WRONLY | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
ssize_t written = pwrite(fd, ends[0].iov_base, ends[0].iov_len, (off_t)(uintptr_t)ends[1].iov_base);
|
|
close(fd);
|
|
if (written < 0) errno = EFAULT;
|
|
return written;
|
|
}
|