I'm trying to patch the entry point of an ELF file directly via the e_entry field:
Elf64_Ehdr *ehdr = NULL;
Elf64_Phdr *phdr = NULL;
Elf64_Shdr *shdr = NULL;
if (argc < 2)
{
printf("Usage: %s <executable>\n", argv[0]);
exit(EXIT_SUCCESS);
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
if (fstat(fd, &st) < 0)
{
perror("fstat");
exit(EXIT_FAILURE);
}
/* map whole executable into memory */
mapped_file = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_file < 0)
{
perror("mmap");
exit(EXIT_FAILURE);
}
// check for an ELF file
check_elf(mapped_file, argv);
ehdr = (Elf64_Ehdr *) mapped_file;
phdr = (Elf64_Phdr *) &mapped_file[ehdr->e_phoff];
shdr = (Elf64_Shdr *) &mapped_file[ehdr->e_shoff];
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
if (ehdr->e_type != ET_EXEC)
{
fprintf(stderr, "%s is not an ELF executable.\n", argv[1]);
exit(EXIT_FAILURE);
}
printf("Program entry point: %08x\n", ehdr->e_entry);
int text_found = 0;
uint64_t test_addr;
uint64_t text_end;
size_t test_len = strlen(shellcode);
int text_idx;
for (i = 0; i < ehdr->e_phnum; ++i)
{
if (text_found)
{
phdr[i].p_offset += PAGE_SIZE;
continue;
}
if (phdr[i].p_type == PT_LOAD && phdr[i].p_flags == ( PF_R | PF_X))
{
test_addr = phdr[i].p_vaddr + phdr[i].p_filesz;
text_end = phdr[i].p_vaddr + phdr[i].p_filesz;
printf("TEXT SEGMENT ends at 0x%x\n", text_end);
puts("Changing entry point...");
ehdr->e_entry = (Elf64_Addr *) test_addr;
memmove(test_addr, shellcode, test_len);
phdr[i].p_filesz += test_len;
phdr[i].p_memsz += test_len;
text_found++;
}
}
//patch sections
for (i = 0; i < ehdr->e_shnum; ++i)
{
if (shdr->sh_offset >= test_addr)
shdr->sh_offset += PAGE_SIZE;
else
if (shdr->sh_size + shdr->sh_addr == test_addr)
shdr->sh_size += test_len;
}
ehdr->e_shoff += PAGE_SIZE;
close(fd);
}
The shellcode in this case is just a bunch of NOPs with an int3 instruction at the end.
I made sure to adjust the segments and sections that come after this new code, but the problem is that as soon as I patch the entry point the program crashes, why is that?
changing:
memmove(test_addr, shellcode, test_len);
to:
memmove(mapped_file + phdr[i].p_offset + phdr[i].p_filesz, shellcode, test_len);
Seems to fix your problem. test_addr is a virtual address belong to the file you have mapped; you cannot use that directly as a pointer. The bits you want to muck with are the file map address, p_offset and p_filesz.
I suspect that you haven't enable write-access to program's header. You can do this via something like
const uintptr_t page_size = 4096;
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
ehdr->e_entry = test_addr;
Related
I'm writing a Linux KVM hypervisor for x86 16-bit guests running in real mode. When doing interrupt calls (int ... instruction), I've encountered the KVM_INTERNAL_ERROR_SIMUL_EX error on Linux kernel 3.13.0. The same code is running fine on Linux kernel 3.16.0. Am I missing something? Is there a workaround I can add to my code to make it work with Linux kernel 3.13.0 (and possibly earlier)?
The test guest calls int 0x18 ... int 0x4f, all of which is handled in the hypervisor (C code after KVM_RUN has returned). When it's working correctly, all of the interrupt calls work. On Linux kernel 3.13.0, int 0x21 starts failing (and then int 0x22, int 0x23 and int 0x24 would also fail).
I was trying to write the shortest example C code to demonstrate the problem, here it is:
/* Based on: https://gist.github.com/zserge/d68683f17c68709818f8baab0ded2d15
* Based on: https://gist.githubusercontent.com/zserge/d68683f17c68709818f8baab0ded2d15/raw/b79033254b092ec9121bb891938b27dd128030d7/kvm-host-simple.c
*
* Compile: gcc -ansi -pedantic -s -O2 -W -Wall -o kvm16 kvm16.c && ./kvm16
*
* Expected correct output (e.g. on Linux 3.16.0 compiled for i386 (i686)):
*
* ...
* info: int 0x4f iret to: ...
* info: success, exiting
*
* Failure output (e.g. on Linux 3.13.0 compiled for amd64 (x86_64)):
*
* info: int 0x20 iret to: cs=0x0070 ip=0x0013
* fatal: KVM internal error suberror=2
*
* // Encounter unexpected simultaneous exceptions.
* #define KVM_INTERNAL_ERROR_SIMUL_EX 2
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#define INT_NUM 0x21 /* Also works for INT_NUM == 0x20. */
int main(int argc, char *argv[]) {
int kvm_fd, vm_fd, vcpu_fd;
void *mem;
struct kvm_userspace_memory_region region;
struct kvm_run *run;
struct kvm_regs regs;
struct kvm_sregs sregs;
(void)argc; (void)argv;
if ((kvm_fd = open("/dev/kvm", O_RDWR)) < 0) {
perror("failed to open /dev/kvm");
return 1;
}
if ((vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0)) < 0) {
perror("failed to create vm");
return 1;
}
if ((mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)) == NULL) {
perror("mmap");
return 1;
}
memset(®ion, 0, sizeof(region));
region.slot = 0;
region.guest_phys_addr = 0;
region.memory_size = 0x1000;
region.userspace_addr = (uintptr_t)mem;
if (ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, ®ion) < 0) {
perror("KVM_SET_USER_MEMORY_REGION");
return 1;
}
{ /* 8086 real mode machine code. */
char *p = (char*)mem + 0x700;
unsigned int_num;
for (int_num = 0; int_num < 0x100; ++int_num) {
*(unsigned short*)((char*)mem + int_num * 4) = int_num; /* Interrupt vector INT_NUM offset := INT_NUM. */
*(unsigned short*)((char*)mem + int_num * 4 + 2) = 0x54; /* Interrupt vector INT_NUM segment := 0x54. */
}
*p++ = (char)0xf4; /* hlt. */
for (int_num = 0x18; int_num < 0x50; ++int_num) {
*p++ = (char)0xcd; /* int int_num. */
*p++ = (char)int_num;
}
*p++ = (char)0xf4;
}
memset((char*)mem + 0x540, '\xf4', 0x100); /* 256 times hlt. Interrupt vectors point here. */
if ((vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0)) < 0) {
perror("KVM_CREATE_VCPU");
return 1;
}
{
int kvm_run_mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
if (kvm_run_mmap_size < 0) {
perror("KVM_GET_VCPU_MMAP_SIZE");
return 1;
}
run = (struct kvm_run *)mmap(
NULL, kvm_run_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpu_fd, 0);
if (run == NULL) {
perror("mmap kvm_run");
return 1;
}
}
memset(®s, '\0', sizeof(regs));
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
{
int fd = open("kvm16.sregs", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
if (write(fd, &sregs, sizeof(sregs)) != sizeof(sregs)) {
perror("write");
return 1;
}
if (close(fd) != 0) {
perror("close");
return 1;
}
}
sregs.cs.base = (sregs.cs.selector = 0x70) << 4;
sregs.ds.base = (sregs.ds.selector = sregs.cs.selector) << 4;
sregs.es.base = (sregs.es.selector = sregs.cs.selector) << 4;
sregs.ss.base = (sregs.ss.selector = sregs.cs.selector) << 4;
if (ioctl(vcpu_fd, KVM_GET_REGS, ®s) < 0) {
perror("KVM_GET_REGS");
return 1;
}
regs.rflags = 1 << 1; /* Reserved bit in EFLAGS. Even needed after KVM_GET_REGS. */
regs.rip = 0;
regs.rsp = 0x1000 - 0x700;
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, ®s) < 0) {
perror("KVM_SET_REGS");
return 1;
}
for (;;) {
int ret = ioctl(vcpu_fd, KVM_RUN, 0);
unsigned short cs, ip;
if (ret < 0) {
perror("KVM_RUN");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_REGS, ®s) < 0) {
perror("KVM_GET_REGS");
return 1;
}
cs = sregs.cs.selector;
ip = regs.rip;
if (run->exit_reason == KVM_EXIT_HLT) {
fprintf(stderr, "info: hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
if (cs == 0x70) {
if (ip != 0 + 1) {
fprintf(stderr, "info: success, exiting\n");
return 0; /* EXIT_SUCCESS after the second `hlt' in the code. */
}
} else if (cs == 0x54) { /* Simulate iret. */
const char *csip_ptr = (const char*)mem + ((unsigned short)sregs.ss.selector << 4) + (unsigned short)regs.rsp;
const unsigned short int_ip = ((const unsigned short*)csip_ptr)[0];
const unsigned short int_cs = ((const unsigned short*)csip_ptr)[1];
const unsigned short int_flags = ((const unsigned short*)csip_ptr)[2];
fprintf(stderr, "info: int 0x%02x iret to: cs=0x%04x ip=0x%04x\n", ip - 1, int_cs, int_ip);
sregs.cs.base = (sregs.cs.selector = int_cs) << 4;
regs.rip = int_ip;
if (int_flags & (1 << 9)) regs.rflags |= (1 << 9); /* Set IF back to 1 if it was 1. */
regs.rsp += 6; /* pop ip, pop cs, popfw . */
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, ®s) < 0) {
perror("KVM_SET_REGS");
return 1;
}
} else {
fprintf(stderr, "fatal: unexpected hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
return 5;
}
} else if (run->exit_reason == KVM_EXIT_INTERNAL_ERROR) {
fprintf(stderr, "fatal: KVM internal error suberror=%d\n", (unsigned)run->internal.suberror);
return 4;
} else {
fprintf(stderr, "fatal: unexpected KVM exit: exit_reason=%d cs=0x%04x ip=0x%04x\n", run->exit_reason, cs, ip);
return 2;
}
}
}
I am trying to read a file by the parent process, send the content to two child processes and child processes write content to a shared memory segment. However, at each run, I get different outputs and I cannot figure out why.
I use two named pipes and after writing to shared memory, the parent process opens the shared memory and reads the content of each memory.
The first child writes to memory as it gets but the second child converts to hexadecimal.
Here is my main function; assume that stringtohex() works correctly.
int main()
{
pid_t pid_1, pid_2;
int fd, sd; // pipes
const int SIZE = 4096;
const char infile[] = "in.txt";
FILE *tp; // file pointers
pid_1 = fork();
if (pid_1 < 0)
{
fprintf(stderr, "Fork 1 failed");
return(1);
}
if (pid_1 > 0)
{
pid_2 = fork();
if (pid_2 < 0)
{
fprintf(stderr, "Fork 2 failed");
return(1);
}
if (pid_2 > 0) // parent
{
tp = fopen(infile, "r");
if (tp == 0)
{
fprintf(stderr, "Failed to open %s for reading", infile);
return(1);
}
mknod(PIPE_NAME_1, S_IFIFO | 0666, 0);
mknod(PIPE_NAME_2, S_IFIFO | 0666, 0);
fd = open(PIPE_NAME_1, O_WRONLY);
sd = open(PIPE_NAME_2, O_WRONLY);
if (fd > 0 && sd > 0)
{
char line[300];
while (fgets(line, sizeof(line), tp))
{
int len = strlen(line);
int num_1 = write(fd, line, len);
int num_2 = write(sd, line, len);
if (num_1 != len || num_2 != len)
perror("write");
else
printf("Ch 1: wrote %d bytes\n", num_1);
printf("Ch 2: wrote %d bytes\n", num_2);
}
close(fd);
close(sd);
}
fclose(tp);
wait(NULL);
int shm_fd = shm_open("Ch_1", O_RDONLY, 0666);
if (shm_fd == -1)
{
fprintf(stderr, "Failed: Shared Memory 1");
exit(-1);
}
int shm_sd = shm_open("Ch_2", O_RDONLY, 0666);
if (shm_sd == -1)
{
fprintf(stderr, "Failed: Shared Memory 2");
exit(-1);
}
void *ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED)
{
printf("Map failed 1\n");
return -1;
}
void *ptr2 = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_sd, 0);
if (ptr2 == MAP_FAILED)
{
printf("Map failed 2\n");
return -1;
}
fprintf(stdout, "Normal input: \n%s\n", ptr);
fprintf(stderr, "Hexadecimal: \n%s\n", ptr2);
}
else // second child
{
// open shared memory segment
int shm_child_2 = shm_open("Ch_2", O_CREAT | O_RDWR, 0666);
// configure the size of the shared memory segment
ftruncate(shm_child_2, SIZE);
// map the pointer to the segment
void *ptr_child_2 = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_child_2, 0);
if (ptr_child_2 == MAP_FAILED)
{
printf("Map failed 3\n");
return -1;
}
// configure named pipe
mknod(PIPE_NAME_2, S_IFIFO | 0666, 0);
sd = open(PIPE_NAME_2, O_RDONLY);
if (sd > 0) // reading from pipe
{
int num;
char s[300];
while ((num = read(sd, s, sizeof(s))) > 0)
{
// convert to hexadecimal
char hex[strlen(s) * 2 + 1];
stringtohex(s, hex);
// write into segment
sprintf(ptr_child_2, "%s", hex);
ptr_child_2 += strlen(s) * 2;
}
close(sd);
}
exit(0);
}
}
else // first child
{
// create shared memory segment
int shm_child_1 = shm_open("Ch_1", O_CREAT | O_RDWR, 0666);
// configure the size of the shared memory segment
ftruncate(shm_child_1, SIZE);
// map the pointer to the segment
void *ptr_child_1 = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_child_1, 0);
if (ptr_child_1 == MAP_FAILED)
{
printf("Map failed 4\n");
return -1;
}
// configure named pipe
mknod(PIPE_NAME_1, S_IFIFO | 0666, 0);
fd = open(PIPE_NAME_1, O_RDONLY);
if (fd > 0) // reading from pipe
{
int num;
char s[300];
while ((num = read(fd, s, strlen(s))) > 0)
{
// write into segment
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += strlen(s);
}
close(fd);
}
exit(0);
}
return 0;
}
For example input file has:
Harun
sasmaz
59900
1234
Aaaa
Results are:
Normal input:
HARUN
sasmaz
59900
1234
Aaaa4
Hexadecimal:
484152554E0A7361736D617A0A35393930300A313233340A41616161
or
Normal input:
HARUN
sasmaz
asmaz59900
1234
Aaaa
Hexadecimal:
484152554E0A7361736D617A0A35393930300A0A313233340A41616161FF03
Hello I am trying to back up a vector by mmap.
However, I have tried msync then munmap but it doesn't work. After I write to the (char *) then munmap the file, the file has no content. The mmap file is also created with flag MAP_SHARED. Would really appreciate it if anyone can help.
//update file descriptor
if ((fd = open(filename.c_str(), O_RDWR | S_IRWXU)) < 0) { //| O_CREAT
printf("ERROR opening file %s for writing", filename.c_str());
exit(1);
}
//lseek create a file large enough
off_t i = lseek(fd, frontier_size * URL_MAX_SIZE, SEEK_SET);
if (i != frontier_size * URL_MAX_SIZE) {
cout << "failed to seek";
}
//reposition and write 3 bytes to the file else will failed to read
char buff[3] = "ta";
ssize_t kk = lseek(fd, 0, SEEK_SET);
if (kk < 0) {
cout << "failed to reposition";
}
ssize_t temp_write = write(fd, (void *)& buff, 2);
if (temp_write < 0) {
cout << "failed to write";
cout << temp_write;
}
//reposition to begining
ssize_t k = lseek(fd, 0, SEEK_SET);
if (k < 0) {
cout << "failed to reposition";
}
char * map = (char *)mmap(0, frontier_size * URL_MAX_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
printf("failed mmap");
exit(1);
}
mmap_frontier = map;
//write to frontier
for (int i = 0; i < frontier.size(); ++i) {
strcpy(mmap_frontier, frontier[i].c_str());
mmap_frontier += URL_MAX_SIZE;
}
mmap_frontier -= frontier.size() * URL_MAX_SIZE;
ssize_t k = lseek(fd, 0, SEEK_SET);
if (k < 0) {
cout << "failed to reposition";
}
int sync = msync((void *)0, frontier.size() * URL_MAX_SIZE, MS_ASYNC);
if (sync < 0 ) {
cout << "failed to sync";
}
int unmap = munmap((void *)0, frontier.size() * URL_MAX_SIZE);
if (unmap < 0) {
cout << "failed to unmap";
}
There are quite a few problems with your code, and with the question:
S_IRWXU is the 3rd argument to open(), not a flag for the 2nd parameter.
mmap() won't work correctly if the file is too small. You can use ftruncte() to set the file size correctly. You tried to seek past the total size of the mapping and write a couple of bytes ("ta"), but before doing that you issued the seek lseek(fd, 0, SEEK_SET) which means the file size was set to 3 rather than mapping_size+3.
You're not backing the vector with an mmapped file, the vector has nothing to do with it, the vector uses its own memory that isn't related in any way to this mapping (please edit your question...).
You called msync() with the address (void *)0, so the actual address which needs to be synced, map, is not being synced.
Likewise, you called munmap() with the address (void *)0, so the actual address which needs to be unmapped is not being unmapped.
You called msync() with MS_ASYNC, which means there's no guarantee that the sync happens before you read the file's contents.
Here's what's working for me (error handling omitted for brevity):
unsigned frontier_size = 2;
const unsigned URL_MAX_SIZE = 100;
int fd = open("data", O_RDWR);
loff_t size = frontier_size * URL_MAX_SIZE;
ftruncate(fd, size);
char *map = (char *)mmap(0, size, PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(map, "hello there");
msync(map, size, MS_SYNC);
munmap(map, size);
close(fd);
I'm having problems with writing to shared memory segment. Here's the code:
EDIT: after I removed that == (mistake), now I'm getting Bus Error (Core Dumped), here's the edited code:
// Struct for data from shared memory
typedef struct {
pthread_mutex_t shared_mutex;
int last_used_job_id;
} shared1;
static void *job_generator(void *param)
{
int J = *((int *) param);
shared1 *shd;
int shm;
int job_id;
// Open shared memory, don't create it if doesn't exist
shm = shm_open("/lab5", O_RDWR, 00600);
// Check
if (shm == -1) {
// Open shared memory, create it if doesn't exist (O_CREAT)
shm = shm_open(q_name, O_RDWR | O_CREAT, 00600);
// Map space for struct
shd = mmap(NULL, sizeof(shared1), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if (shd == (void *) -1) {
perror ( "mmap" );
exit(1);
}
// Initialize mutex
if (pthread_mutex_init(&(shd->shared_mutex), NULL) != 0)
{
printf("Mutex initialization failed!\n");
exit(1);
}
}
else
{
// Map space for struct
shd = mmap(NULL, sizeof(shared1), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if (shd == (void *) -1) {
perror ( "mmap" );
exit(1);
}
}
// Lock mutex
pthread_mutex_lock(&(shd->shared_mutex));
job_id = shd->last_used_job_id + 1;
shd->last_used_job_id = job_id + J;
printf("a: %d\n", shd->last_used_job_id);
return NULL;
}
it's caused by any of the instructions which are using shd, so any of these:
// Lock mutex
pthread_mutex_lock(&(shd->shared_mutex));
job_id = shd->last_used_job_id + 1;
shd->last_used_job_id = job_id + J;
printf("a: %d\n", shd->last_used_job_id);
I think this is where your problem lies:
shd == mmap(NULL, sizeof(shared1), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
You're comparing shd to the return value of mmap with '=='. I think you meant to use a single '=' which would assign the return value to shd.
I am trying to implement the caesar cipher with mmap. I think the cipher works so fine but the problem is, the mmap. The idea of it is to update the file, if there was a change.
So far it isn't working. I can just read the memory mapped file and print it. But if I make any modificaiton, I get a segmentation fault. Unfortunately, I couldn't solve the problem myself. So, I would appreciate it, if you could help me with it.
Here is the code.
int main (int argc, char *argv[]) {
if(argc != 5)
fprintf(stdeer, "usage: ./cipher (encrypt|decrypt) <file name> (casar| vigenere) <key>\n");
// (encrypt / decrypt) can be found in argv[1]
// filename in argv[2]
// encryption method in argv[3]
// key in argv[4]
int fd = open(argv[2], O_RDWR, S_IWRITE | S_IREAD);
if (fd < 0)
hanle_error("open");
off_t len = lseek(fd, 0, SEEK_END);
if (len == (off_t)-1)
handle_error("lseek");
unsigned char* data = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Add PROT_WRITE
if (data == MAP_FAILED)
handle_error("mmap");
char c = *argv[4];
int key = 0;
if(strcmp(argv[3], "caesar") == 0) {
key = c - 48;
if(strcmp(argv[1], "decrpyt") == 0)
key = -key;
int num = 0;
for(int size_t i = 0; i < (size_t)len; i++) {
if(data[i] >= 97 && data[i] <= 122) {
num = data[i];
num +=key;
if(num > 'z') {
num -= 26;
data[i] = num + '0';
} else if (num < 'a') {
num += 26;
data[i] = num + '0';
} else {
data[i] = num + '0';
}
} else {
continue;
}
}
}
return 0;
}
A possible input can be anything e.g.
SsWd asdas
qwmkfd aw.
The algorithm above should just modify the lower case letters and leave the rest as it is.
I hope someone might be able to help me.
Also, I only implemented the caesar cipher.
EDIT: The seg fault is gone after I added PROT_WRITE. But know I get weird question marks for the modified lower case letters. Does anyone know why?
If you want to write to the file too, then
unsigned char* data = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);
should be
unsigned char* data = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
That may not be the only issue, but would explain the seg fault.
The reason is self explanatory!