1 year ago
#342583
Software-security-is-not-free
How to execute aarch64 generated machine code in c
I saw and tried some samples of executing machine code in C for Linux. It works well. When I tried for aarch64, it always fails. Here is what I tried. C file:
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <limits.h> /* for PAGESIZE */
#ifndef PAGESIZE
#define PAGESIZE 4096
#endif
const static unsigned char code[] = {
0x40, 0x05, 0x80, 0x52, // mov w0, #0x2a
//0xd6, 0x5f, 0x03, 0xc0 // ret instruction
0x40, 0x97, 0x3b, 0xd4 // brk #0xdcba
};
int main(int argc, char **argv) {
int codelen = (int) sizeof(code);
// in order to manipulate memory protection, we must work with
// whole pages allocated directly from the operating system.
static size_t pagesize=0;
if (!pagesize) {
pagesize = sysconf(_SC_PAGESIZE);
if (pagesize == (size_t)-1) {
printf("getpagesize fail\n");
return 1;
} else {
printf("--success getting the pagesize=%ld\n", pagesize);
}
}
// allocate at least enough space for the code + 1 byte
// (so that there will be at least one INT3 - see below),
// rounded up to a multiple of the system page size.
size_t rounded_codesize = ((codelen + 1 + pagesize - 1)/ pagesize) * pagesize;
void *executable_area = mmap(0, rounded_codesize,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
if (!executable_area) {
printf("mmap fail\n");
return 1;
} else {
printf("--success mapping the rounded_codesize=%ld of %p\n", rounded_codesize, executable_area);
}
// at this point, executable_area points to memory that is writable but
// *not* executable. load the code into it.
memcpy(executable_area, code, codelen);
// fill the space at the end with INT3 instructions, to guarantee
// a prompt crash if the generated code runs off the end.
// must change this if generating code for non-x86.
memset(executable_area + codelen, 0xCC, rounded_codesize - codelen);
// make executable_area actually executable (and unwritable)
if (mprotect(executable_area, rounded_codesize, PROT_READ|PROT_EXEC))
{
printf("mprotect fail\n");
return 1;
} else {
printf("--success in calling mprotect with read|exec\n");
}
// now we can call it. passing arguments / receiving return values
// is left as an exercise (consult libffi source code for clues).
int ret = ((int (*)(void)) executable_area)();
printf("get this done. returned: %d", ret);
munmap(executable_area, rounded_codesize);
return 0;
}
Here is what I tried to compile/run the above code.
aarch64-linux-gnu-gcc test.c -g -O0 -o test.out -DGNU_SOURCE
file test.out
test.out: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=209123aff57cef69be8037e5616a3b71ceb6ad38, for GNU/Linux 3.7.0, with debug_info, not stripped
qemu-aarch64-static -L /usr/aarch64-linux-gnu/ ./test.out
--success getting the pagesize=4096
--success mapping the rounded_codesize=4096 of 0x40019ca000
--success in calling mprotect with read|exec
qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
make: *** [Makefile:21: aarch64_test1] Trace/breakpoint trap
It fails in calling the above machine code "int ret = ((int (*)(void)) executable_area)();". I am just wondering if there is anything wrong in my trying with aarch compiling toolchain.
arm64
machine-code
generated
0 Answers
Your Answer