From f63a673a8b09caaea63c85d81aa7555baad696d5 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Thu, 28 Sep 2023 12:44:49 -0700 Subject: [PATCH] Prepare to emulate memfd_create() on 32-bit Android (ARM and i386) modified: stub/src/include/linux.h new file: stub/src/upxfd_android.c new file: stub/src/upxfd_create.c --- src/stub/src/include/linux.h | 1 + src/stub/src/upxfd_android.c | 104 +++++++++++++++++++++++++++++++++++ src/stub/src/upxfd_create.c | 52 ++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/stub/src/upxfd_android.c create mode 100644 src/stub/src/upxfd_create.c diff --git a/src/stub/src/include/linux.h b/src/stub/src/include/linux.h index 932272b7..76bf149d 100644 --- a/src/stub/src/include/linux.h +++ b/src/stub/src/include/linux.h @@ -554,6 +554,7 @@ int mprotect(void const *, size_t, int); int open(char const *, unsigned, unsigned); int openat(int fd, char const *, unsigned, unsigned); ssize_t read(int, void *, size_t); +int unlink(char const *); ssize_t write(int, void const *, size_t); #endif /*}*/ diff --git a/src/stub/src/upxfd_android.c b/src/stub/src/upxfd_android.c new file mode 100644 index 00000000..95a5a4df --- /dev/null +++ b/src/stub/src/upxfd_android.c @@ -0,0 +1,104 @@ +/* upxfd_android.c -- workaround memfd_create for 32-bit Android + + This file is part of the UPX executable compressor. + + Copyright (C) 2023 John F. Reiser + All Rights Reserved. + */ + +#if defined(__i386__) //}{ +#define addr_string(string) ({ \ + char const *str; \ + asm("call 0f; .asciz \"" string "\"; 0: pop %0" \ +/*out*/ : "=r"(str) ); \ + str; \ +}) +#elif defined(__arm__) //}{ +#define addr_string(string) ({ \ + char const *str; \ + asm("bl 0f; .string \"" string "\"; .balign 4; 0: mov %0,lr" \ +/*out*/ : "=r"(str) \ +/* in*/ : \ +/*und*/ : "lr"); \ + str; \ +}) +#else //}{ +# error; // only: __arm__ or __i386__ (for ARM emulator) +#endif //} + +// Upper half of ASCII (chars 0100-0177) are all legal in a Linux filename. +// So the shortest code is " return 0100 + (077 & x); " +// But the 5 chars which follow 'Z' ("[\\]^_") or 'z' ("{|}~\x7F") +// look ugly, so substitute digits. +static unsigned sixbit(unsigned x) +{ + unsigned y = 037 & x; // "upper case" + x &= 077; + if (033 <= y) { // last 5 chars in each 32 are ugly + if (040 & x) // "lower case" + x += '4' - 'z'; // "56789" follows 'z'; + else + x += '/' - 'Z'; // "01234" follows 'Z'; + } + x += 0100; // upper half ASCII: "@ABC"... + return x; +} + +#define S_IRWXU 00700 /* rwx------ User Read-Write-eXecute */ +extern void *alloca(unsigned size); +#include "include/linux.h" // syscalls; i386 inlines via "int 0x80" +extern void *mempcpy(void *dst, void const *src, unsigned len); + +// Early 32-bit Android lacks memfd_create. +// Try /data/data/$APP_NAME/cache/upxAAA +// where APP_NAME is discovered as basename(argv[0]) +// and argv[0] is guessed from /proc/self/cmdline. +// Also 32-bit Android has inconsistent __NR_ftruncate, +// so use direct write() +// +#define BUFLEN 0x2000 /* 4KiB for PATH_MAX; 8KiB for zeroing */ +#define ENOSPC 28 /* no space left on device */ + +int upxfd_android(unsigned len) // returns fd with (.st_size = len); else -ENOSPC +{ + char *buf = alloca(BUFLEN); + char *p = mempcpy(&buf[0], addr_string("/data/data/"), 11); // '/' sentinel at end + int fd = open(addr_string("/proc/self/cmdline"), O_RDONLY, 0); + int rlen = read(fd, p, BUFLEN); + if (rlen < 0) { + return -1; + } + while (*p) { // advance to end of argv[0] + if (' '==*p) break; + ++p; + } + { + char *app_end = p; + while ('/' != *--p) ; // last component of argv[0] + p = mempcpy(&buf[10], p, app_end - p); + } + p = mempcpy(p, addr_string("/cache/upx"), 10); + pid_t pid = getpid(); + p[0] = sixbit(pid >> 0*6); + p[1] = sixbit(pid >> 1*6); + p[2] = sixbit(pid >> 2*6); + p[3]='\0'; + + fd = open(&buf[0], O_CREAT|O_EXCL|O_RDWR, S_IRWXU); + if (fd < 0) { + return -1; + } + unlink(&buf[0]); + + extern void *memset(void *dst, int c, unsigned); + memset(buf, 0, BUFLEN); + while (0 < len) { + int x = (len < BUFLEN) ? len : BUFLEN; + if (x != write(fd, buf, BUFLEN)) { + return -ENOSPC; + } + len -= x; + } + lseek(fd, 0, SEEK_SET); + return fd; +} diff --git a/src/stub/src/upxfd_create.c b/src/stub/src/upxfd_create.c new file mode 100644 index 00000000..e7429eca --- /dev/null +++ b/src/stub/src/upxfd_create.c @@ -0,0 +1,52 @@ +/* upxfd_create.c -- workaround for 32-bit Android + + This file is part of the UPX executable compressor. + + Copyright (C) 2023 John F. Reiser + All Rights Reserved. + */ + +// Like memfd_create, but early 32-bit Android lacks. +// Also 32-bit Android has inconsistent __NR_ftruncate, +// so use direct write(). +extern void *alloca(unsigned long size); +#include "include/linux.h" // syscalls; i386 inlines via "int 0x80" + +// 1. Try memfd_create +// 2. If Android or emulator, try /data/data/$APP_NAME/cache/upxAAA +// where APP_NAME is discovered as basename($(< /proc/self/cmdline)) +// If Android, then write zeroes to desired length, then lseek back to offset 0. +// (Except make_hatch() just writes the actual data, and closes the file.) +// If not Android then ftruncate() to desired length. +// Return (mapped_addr | (1+ fd)) +// +extern int upxfd_android(int len); + +void *upxfd_create(void *addr, unsigned len) +{ + char str_upx[] = "upx"; + int fd = memfd_create(str_upx, 0); +#if defined(__arm__) || defined(__i386__) //{ workaround: android & emulators + if (fd < 0) { + fd = upxfd_android(len); // also emulates ftruncate(fd, len) + } +#else //}{ + if (0 > ftruncate(fd, len)) { + return (void *)-1ul; + } +#endif //} + addr = mmap(addr, len, PROT_READ|PROT_WRITE, + (addr ? MAP_FIXED : 0)|MAP_SHARED, fd, 0); + if (PAGE_MASK <= (unsigned long)addr) { + return addr; + } + return (void *)((long)addr + (1+ (unsigned)fd)); +} + +#if 0 //{ test +int main(int argc, char *argv[]) +{ + upxfd_create(0, 100); + return 0; +} +#endif //}