2021-10-02 15:17:04 +00:00
|
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
|
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
|
|
|
|
│ │
|
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
|
│ │
|
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
|
|
|
|
|
|
|
|
/**
|
2022-05-22 11:51:02 +00:00
|
|
|
|
* @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD
|
2022-05-21 14:52:58 +00:00
|
|
|
|
*
|
|
|
|
|
* We recommend using the normal APE design, where binaries assimilate
|
|
|
|
|
* themselves once by self-modifying the first 64 bytes. If that can't
|
|
|
|
|
* meet your requirements then we provide an excellent alternative.
|
2021-10-02 15:17:04 +00:00
|
|
|
|
*
|
|
|
|
|
* m=tiny
|
|
|
|
|
* make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com
|
2022-05-22 11:51:02 +00:00
|
|
|
|
* o/$m/ape/ape.elf o/$m/examples/printargs.com
|
2022-05-21 14:52:58 +00:00
|
|
|
|
*
|
|
|
|
|
* This is an embeddable Actually Portable Executable interpreter. The
|
|
|
|
|
* `ape/ape.S` bootloader embeds this binary inside each binary that's
|
|
|
|
|
* linked using `$(APE_NO_MODIFY_SELF)` so it is an automated seamless
|
|
|
|
|
* process. the shell script at the top of the .COM files will copy it
|
2022-06-27 22:00:51 +00:00
|
|
|
|
* to `${TMPDIR:-${HOME:-.}}/.ape` and call execve(). It's a zero copy
|
2022-05-21 14:52:58 +00:00
|
|
|
|
* operation in praxis since this payload uses mmap() to load the rest
|
|
|
|
|
* of your executable the same way the kernel does, based on ELF phdrs
|
|
|
|
|
* which are located in accordance with the first sh printf statement.
|
|
|
|
|
*
|
|
|
|
|
* APE executables will look for this program on the system path first
|
|
|
|
|
* so your APE loader may be installed to your system as follows:
|
|
|
|
|
*
|
|
|
|
|
* m=tiny
|
2022-05-22 11:51:02 +00:00
|
|
|
|
* make -j8 MODE=$m o/$m/ape/ape.elf
|
|
|
|
|
* sudo cp o/$m/ape/ape.elf /usr/bin/ape
|
|
|
|
|
*
|
|
|
|
|
* For Mac OS X systems you should install the `ape.macho` executable:
|
|
|
|
|
*
|
|
|
|
|
* sudo cp o/$m/ape/ape.macho /usr/bin/ape
|
2022-05-21 14:52:58 +00:00
|
|
|
|
*
|
|
|
|
|
* Your APE loader may be used as a shebang interpreter by doing this:
|
|
|
|
|
*
|
|
|
|
|
* #!/usr/bin/ape python.com
|
|
|
|
|
* # -*- python -*-
|
|
|
|
|
* print("hello world")
|
|
|
|
|
*
|
|
|
|
|
* However you won't need to do that, if your APE Loader is registered
|
|
|
|
|
* as a binfmt_misc interpreter. You can do that as follows with root:
|
|
|
|
|
*
|
2022-05-22 11:51:02 +00:00
|
|
|
|
* sudo cp -f o/$m/ape/ape.elf /usr/bin
|
2022-05-21 14:52:58 +00:00
|
|
|
|
* f=/proc/sys/fs/binfmt_misc/register
|
|
|
|
|
* sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f"
|
|
|
|
|
*
|
|
|
|
|
* If the register file doesn't exist on your Linux machine then you'd
|
|
|
|
|
* load it using the following commands:
|
|
|
|
|
*
|
|
|
|
|
* sudo modprobe binfmt_misc
|
|
|
|
|
* sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
|
|
|
|
|
*
|
|
|
|
|
* You should now experience a performance boost, and you can also now
|
|
|
|
|
* use the regular shebang form:
|
|
|
|
|
*
|
|
|
|
|
* #!/usr/bin/python.com
|
|
|
|
|
* # -*- python -*-
|
|
|
|
|
* print("hello world")
|
2021-10-02 15:17:04 +00:00
|
|
|
|
*
|
|
|
|
|
* @note this can probably be used as a binfmt_misc interpreter
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
#define LINUX 1
|
|
|
|
|
#define XNU 8
|
|
|
|
|
#define OPENBSD 16
|
|
|
|
|
#define FREEBSD 32
|
|
|
|
|
#define NETBSD 64
|
2021-10-02 15:17:04 +00:00
|
|
|
|
|
2022-05-22 11:51:02 +00:00
|
|
|
|
#define SupportsLinux() (SUPPORT_VECTOR & LINUX)
|
|
|
|
|
#define SupportsXnu() (SUPPORT_VECTOR & XNU)
|
|
|
|
|
#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD)
|
|
|
|
|
#define SupportsOpenbsd() (SUPPORT_VECTOR & OPENBSD)
|
|
|
|
|
#define SupportsNetbsd() (SUPPORT_VECTOR & NETBSD)
|
|
|
|
|
|
|
|
|
|
#define IsLinux() (SupportsLinux() && os == LINUX)
|
|
|
|
|
#define IsXnu() (SupportsXnu() && os == XNU)
|
|
|
|
|
#define IsFreebsd() (SupportsFreebsd() && os == FREEBSD)
|
|
|
|
|
#define IsOpenbsd() (SupportsOpenbsd() && os == OPENBSD)
|
|
|
|
|
#define IsNetbsd() (SupportsNetbsd() && os == NETBSD)
|
|
|
|
|
|
2022-08-15 03:16:44 +00:00
|
|
|
|
#define O_RDONLY 0
|
|
|
|
|
#define PROT_READ 1
|
|
|
|
|
#define PROT_WRITE 2
|
|
|
|
|
#define PROT_EXEC 4
|
|
|
|
|
#define MAP_SHARED 1
|
|
|
|
|
#define MAP_PRIVATE 2
|
|
|
|
|
#define MAP_FIXED 16
|
|
|
|
|
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
|
|
|
|
|
#define AT_EXECFN_LINUX 31
|
|
|
|
|
#define AT_EXECFN_NETBSD 2014
|
2023-07-01 12:10:12 +00:00
|
|
|
|
#define ELFCLASS32 1
|
2022-08-15 03:16:44 +00:00
|
|
|
|
#define ELFDATA2LSB 1
|
|
|
|
|
#define EM_NEXGEN32E 62
|
|
|
|
|
#define ET_EXEC 2
|
|
|
|
|
#define PT_LOAD 1
|
|
|
|
|
#define PT_DYNAMIC 2
|
|
|
|
|
#define EI_CLASS 4
|
|
|
|
|
#define EI_DATA 5
|
|
|
|
|
#define PF_X 1
|
|
|
|
|
#define PF_W 2
|
|
|
|
|
#define PF_R 4
|
|
|
|
|
#define X_OK 1
|
|
|
|
|
#define XCR0_SSE 2
|
|
|
|
|
#define XCR0_AVX 4
|
|
|
|
|
#define PR_SET_MM 35
|
|
|
|
|
#define PR_SET_MM_EXE_FILE 13
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
|
|
|
|
|
#define Roundup(X, K) (((X) + (K)-1) & -(K))
|
|
|
|
|
#define Rounddown(X, K) ((X) & -(K))
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
#define Read32(S) \
|
|
|
|
|
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
|
|
|
|
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
|
|
|
|
|
|
|
|
|
#define Read64(S) \
|
|
|
|
|
((unsigned long)(255 & (S)[7]) << 070 | \
|
|
|
|
|
(unsigned long)(255 & (S)[6]) << 060 | \
|
|
|
|
|
(unsigned long)(255 & (S)[5]) << 050 | \
|
|
|
|
|
(unsigned long)(255 & (S)[4]) << 040 | \
|
|
|
|
|
(unsigned long)(255 & (S)[3]) << 030 | \
|
|
|
|
|
(unsigned long)(255 & (S)[2]) << 020 | \
|
|
|
|
|
(unsigned long)(255 & (S)[1]) << 010 | \
|
|
|
|
|
(unsigned long)(255 & (S)[0]) << 000)
|
|
|
|
|
|
|
|
|
|
struct ElfEhdr {
|
|
|
|
|
unsigned char e_ident[16];
|
|
|
|
|
unsigned short e_type;
|
|
|
|
|
unsigned short e_machine;
|
|
|
|
|
unsigned e_version;
|
|
|
|
|
unsigned long e_entry;
|
|
|
|
|
unsigned long e_phoff;
|
|
|
|
|
unsigned long e_shoff;
|
|
|
|
|
unsigned e_flags;
|
|
|
|
|
unsigned short e_ehsize;
|
|
|
|
|
unsigned short e_phentsize;
|
|
|
|
|
unsigned short e_phnum;
|
|
|
|
|
unsigned short e_shentsize;
|
|
|
|
|
unsigned short e_shnum;
|
|
|
|
|
unsigned short e_shstrndx;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ElfPhdr {
|
|
|
|
|
unsigned p_type;
|
|
|
|
|
unsigned p_flags;
|
|
|
|
|
unsigned long p_offset;
|
|
|
|
|
unsigned long p_vaddr;
|
|
|
|
|
unsigned long p_paddr;
|
|
|
|
|
unsigned long p_filesz;
|
|
|
|
|
unsigned long p_memsz;
|
|
|
|
|
unsigned long p_align;
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
union ElfEhdrBuf {
|
|
|
|
|
struct ElfEhdr ehdr;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
};
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
union ElfPhdrBuf {
|
|
|
|
|
struct ElfPhdr phdr;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PathSearcher {
|
|
|
|
|
int os;
|
|
|
|
|
const char *name;
|
|
|
|
|
const char *syspath;
|
|
|
|
|
unsigned long namelen;
|
|
|
|
|
char path[1024];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ApeLoader {
|
|
|
|
|
union ElfEhdrBuf ehdr;
|
|
|
|
|
union ElfPhdrBuf phdr;
|
|
|
|
|
struct PathSearcher ps;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
long SystemCall(long arg1, //
|
|
|
|
|
long arg2, //
|
|
|
|
|
long arg3, //
|
|
|
|
|
long arg4, //
|
|
|
|
|
long arg5, //
|
|
|
|
|
long arg6, //
|
|
|
|
|
long arg7, //
|
|
|
|
|
long magi);
|
2023-05-19 02:05:08 +00:00
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static int ToLower(int c) {
|
|
|
|
|
return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned long StrLen(const char *s) {
|
|
|
|
|
unsigned long n = 0;
|
|
|
|
|
while (*s++) ++n;
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
|
|
|
|
for (; n; --n, ++s) {
|
|
|
|
|
if ((*s & 255) == c) {
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static void *MemMove(void *a, const void *b, unsigned long n) {
|
|
|
|
|
char *d = a;
|
|
|
|
|
unsigned long i;
|
|
|
|
|
const char *s = b;
|
|
|
|
|
if (d > s) {
|
|
|
|
|
for (i = n; i--;) {
|
|
|
|
|
d[i] = s[i];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
|
d[i] = s[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static char *GetEnv(char **p, const char *s) {
|
|
|
|
|
unsigned long i, j;
|
|
|
|
|
if (p) {
|
|
|
|
|
for (i = 0; p[i]; ++i) {
|
|
|
|
|
for (j = 0;; ++j) {
|
|
|
|
|
if (!s[j]) {
|
|
|
|
|
if (p[i][j] == '=') {
|
|
|
|
|
return p[i] + j + 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (s[j] != p[i][j]) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *Utoa(char p[21], unsigned long x) {
|
|
|
|
|
char t;
|
|
|
|
|
unsigned long i, a, b;
|
|
|
|
|
i = 0;
|
|
|
|
|
do {
|
|
|
|
|
p[i++] = x % 10 + '0';
|
|
|
|
|
x = x / 10;
|
|
|
|
|
} while (x > 0);
|
|
|
|
|
p[i] = '\0';
|
|
|
|
|
if (i) {
|
|
|
|
|
for (a = 0, b = i - 1; a < b; ++a, --b) {
|
|
|
|
|
t = p[a];
|
|
|
|
|
p[a] = p[b];
|
|
|
|
|
p[b] = t;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p + i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *Itoa(char p[21], long x) {
|
|
|
|
|
if (x < 0) *p++ = '-', x = -(unsigned long)x;
|
|
|
|
|
return Utoa(p, x);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
|
|
|
|
|
SystemCall(rc, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
(IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0));
|
|
|
|
|
__builtin_unreachable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Close(int fd, int os) {
|
|
|
|
|
return SystemCall(fd, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
(IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static long Pread(int fd, void *data, unsigned long size, long off, int os) {
|
|
|
|
|
long magi;
|
2022-05-22 11:51:02 +00:00
|
|
|
|
if (IsLinux()) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
magi = 0x011;
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else if (IsXnu()) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
magi = 0x2000099;
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else if (IsFreebsd()) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
magi = 0x1db;
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else if (IsOpenbsd()) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
magi = 0x0a9; // OpenBSD v7.3+
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else if (IsNetbsd()) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
magi = 0x0ad;
|
2022-05-21 14:52:58 +00:00
|
|
|
|
} else {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
__builtin_unreachable();
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
return SystemCall(fd, (long)data, size, off, off, 0, 0, magi);
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static long Write(int fd, const void *data, unsigned long size, int os) {
|
|
|
|
|
return SystemCall(fd, (long)data, size, 0, 0, 0, 0,
|
|
|
|
|
(IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0));
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static int Execve(const char *prog, char **argv, char **envp, int os) {
|
|
|
|
|
return SystemCall((long)prog, (long)argv, (long)envp, 0, 0, 0, 0,
|
|
|
|
|
59 | (IsXnu() ? 0x2000000 : 0));
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 11:51:02 +00:00
|
|
|
|
static int Access(const char *path, int mode, int os) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
return SystemCall((long)path, mode, 0, 0, 0, 0, 0,
|
|
|
|
|
(IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0));
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static int Msyscall(long p, unsigned long n, int os) {
|
|
|
|
|
if (IsOpenbsd()) {
|
|
|
|
|
return SystemCall(p, n, 0, 0, 0, 0, 0, 37);
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
return 0;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 11:51:02 +00:00
|
|
|
|
static int Open(const char *path, int flags, int mode, int os) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
return SystemCall((long)path, flags, mode, 0, 0, 0, 0,
|
|
|
|
|
(IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0));
|
2022-08-15 03:16:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static long Mmap(void *addr, unsigned long size, int prot, int flags, int fd,
|
|
|
|
|
long off, int os) {
|
|
|
|
|
long magi;
|
|
|
|
|
if (IsLinux()) {
|
|
|
|
|
magi = 9;
|
|
|
|
|
} else if (IsXnu()) {
|
|
|
|
|
magi = 0x2000000 | 197;
|
|
|
|
|
} else if (IsFreebsd()) {
|
|
|
|
|
magi = 477;
|
|
|
|
|
} else if (IsOpenbsd()) {
|
|
|
|
|
magi = 49; // OpenBSD v7.3+
|
|
|
|
|
} else if (IsNetbsd()) {
|
|
|
|
|
magi = 197;
|
|
|
|
|
} else {
|
|
|
|
|
__builtin_unreachable();
|
|
|
|
|
}
|
|
|
|
|
return SystemCall((long)addr, size, prot, flags, fd, off, off, magi);
|
2022-08-15 03:16:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static long Print(int os, int fd, const char *s, ...) {
|
|
|
|
|
int c;
|
|
|
|
|
unsigned n;
|
|
|
|
|
char b[512];
|
|
|
|
|
__builtin_va_list va;
|
|
|
|
|
__builtin_va_start(va, s);
|
|
|
|
|
for (n = 0; s; s = __builtin_va_arg(va, const char *)) {
|
|
|
|
|
while ((c = *s++)) {
|
|
|
|
|
if (n < sizeof(b)) {
|
|
|
|
|
b[n++] = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
__builtin_va_end(va);
|
|
|
|
|
return Write(fd, b, n, os);
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static void Perror(int os, const char *thing, long rc, const char *reason) {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
char ibuf[21];
|
2023-07-01 12:10:12 +00:00
|
|
|
|
ibuf[0] = 0;
|
|
|
|
|
if (rc) Itoa(ibuf, -rc);
|
|
|
|
|
Print(os, 2, "ape error: ", thing, ": ", reason,
|
|
|
|
|
rc ? " failed w/ errno " : "", ibuf, "\n", 0l);
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
|
|
|
|
|
const char *s) {
|
|
|
|
|
Perror(os, c, rc, s);
|
|
|
|
|
Exit(127, os);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int StrCmp(const char *l, const char *r) {
|
|
|
|
|
unsigned long i = 0;
|
|
|
|
|
while (l[i] == r[i] && r[i]) ++i;
|
|
|
|
|
return (l[i] & 255) - (r[i] & 255);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static void *MemSet(void *a, int c, unsigned long n) {
|
|
|
|
|
char *d = a;
|
|
|
|
|
unsigned long i;
|
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
|
d[i] = c;
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
|
|
|
|
|
unsigned long i, m;
|
|
|
|
|
if (n >= (m = StrLen(s))) {
|
|
|
|
|
for (i = n - m; i < n; ++i) {
|
|
|
|
|
if (ToLower(p[i]) != *s++) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
return 1;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static char IsComPath(struct PathSearcher *ps) {
|
|
|
|
|
return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") ||
|
|
|
|
|
EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") ||
|
|
|
|
|
EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg");
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static char AccessCommand(struct PathSearcher *ps, const char *suffix,
|
|
|
|
|
unsigned long pathlen) {
|
|
|
|
|
unsigned long suffixlen;
|
|
|
|
|
suffixlen = StrLen(suffix);
|
|
|
|
|
if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0;
|
|
|
|
|
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
|
2023-07-01 12:10:12 +00:00
|
|
|
|
MemMove(ps->path + pathlen, ps->name, ps->namelen);
|
|
|
|
|
MemMove(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
|
2022-05-21 14:52:58 +00:00
|
|
|
|
return !Access(ps->path, X_OK, ps->os);
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
|
|
|
|
const char *p;
|
|
|
|
|
unsigned long i;
|
|
|
|
|
for (p = ps->syspath;;) {
|
|
|
|
|
for (i = 0; p[i] && p[i] != ':'; ++i) {
|
|
|
|
|
if (i < sizeof(ps->path)) {
|
|
|
|
|
ps->path[i] = p[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (AccessCommand(ps, suffix, i)) {
|
|
|
|
|
return 1;
|
|
|
|
|
} else if (p[i] == ':') {
|
|
|
|
|
p += i + 1;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
|
|
|
|
if (MemChr(ps->name, '/', ps->namelen) ||
|
|
|
|
|
MemChr(ps->name, '\\', ps->namelen)) {
|
|
|
|
|
ps->path[0] = 0;
|
|
|
|
|
return AccessCommand(ps, suffix, 0);
|
|
|
|
|
} else {
|
|
|
|
|
if (AccessCommand(ps, suffix, 0)) return 1;
|
|
|
|
|
}
|
|
|
|
|
return SearchPath(ps, suffix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *Commandv(struct PathSearcher *ps, int os, const char *name,
|
|
|
|
|
const char *syspath) {
|
|
|
|
|
ps->os = os;
|
|
|
|
|
ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin";
|
|
|
|
|
if (!(ps->namelen = StrLen((ps->name = name)))) return 0;
|
|
|
|
|
if (ps->namelen + 1 > sizeof(ps->path)) return 0;
|
|
|
|
|
if (FindCommand(ps, "") || (!IsComPath(ps) && FindCommand(ps, ".com"))) {
|
|
|
|
|
return ps->path;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
2023-07-01 12:10:12 +00:00
|
|
|
|
long *sp, struct ElfEhdr *e,
|
|
|
|
|
struct ElfPhdr *p) {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
long rc;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
int prot, flags;
|
|
|
|
|
long code, codesize;
|
2023-07-01 12:10:12 +00:00
|
|
|
|
unsigned long a, b, i;
|
|
|
|
|
|
2021-10-02 15:17:04 +00:00
|
|
|
|
code = 0;
|
|
|
|
|
codesize = 0;
|
2023-07-01 12:10:12 +00:00
|
|
|
|
for (i = e->e_phnum; i--;) {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
if (p[i].p_type == PT_DYNAMIC) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
Pexit(os, exe, 0, "not a static executable");
|
|
|
|
|
}
|
|
|
|
|
if (p[i].p_type != PT_LOAD) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!p[i].p_memsz) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (p[i].p_vaddr & 4095) {
|
|
|
|
|
Pexit(os, exe, 0, "APE phdr addr must be 4096-aligned");
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
if (p[i].p_offset & 4095) {
|
|
|
|
|
Pexit(os, exe, 0, "APE phdr offset must be 4096-aligned");
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
prot = 0;
|
2021-10-08 15:11:51 +00:00
|
|
|
|
flags = MAP_FIXED | MAP_PRIVATE;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
if (p[i].p_flags & PF_R) {
|
|
|
|
|
prot |= PROT_READ;
|
|
|
|
|
}
|
|
|
|
|
if (p[i].p_flags & PF_W) {
|
|
|
|
|
prot |= PROT_WRITE;
|
|
|
|
|
}
|
|
|
|
|
if (p[i].p_flags & PF_X) {
|
|
|
|
|
prot |= PROT_EXEC;
|
|
|
|
|
code = p[i].p_vaddr;
|
|
|
|
|
codesize = p[i].p_filesz;
|
|
|
|
|
}
|
|
|
|
|
if (p[i].p_filesz) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
if ((rc = Mmap((void *)p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
2022-05-21 14:52:58 +00:00
|
|
|
|
p[i].p_offset, os)) < 0) {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
Pexit(os, exe, rc, "image mmap");
|
|
|
|
|
}
|
|
|
|
|
if ((a = Min(-p[i].p_filesz & 4095, p[i].p_memsz - p[i].p_filesz))) {
|
|
|
|
|
MemSet((void *)(p[i].p_vaddr + p[i].p_filesz), 0, a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((b = Roundup(p[i].p_memsz, 4096)) >
|
|
|
|
|
(a = Roundup(p[i].p_filesz, 4096))) {
|
|
|
|
|
if ((rc = Mmap((void *)p[i].p_vaddr + a, b - a, prot,
|
|
|
|
|
flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
|
|
|
|
|
Pexit(os, exe, rc, "bss mmap");
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
if (!code) {
|
|
|
|
|
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
|
|
|
|
|
}
|
2022-08-15 03:16:44 +00:00
|
|
|
|
|
2022-05-21 14:52:58 +00:00
|
|
|
|
Close(fd, os);
|
2023-07-01 12:10:12 +00:00
|
|
|
|
Msyscall(code, codesize, os);
|
2022-05-22 11:51:02 +00:00
|
|
|
|
|
|
|
|
|
// we clear all the general registers we can to have some wiggle room
|
|
|
|
|
// to extend the behavior of this loader in the future. we don't need
|
|
|
|
|
// to clear the xmm registers since the ape loader should be compiled
|
|
|
|
|
// with the -mgeneral-regs-only flag.
|
2022-05-21 14:52:58 +00:00
|
|
|
|
asm volatile("xor\t%%eax,%%eax\n\t"
|
2023-07-01 12:10:12 +00:00
|
|
|
|
"xor\t%%r8d,%%r8d\n\t"
|
2022-05-21 14:52:58 +00:00
|
|
|
|
"xor\t%%r9d,%%r9d\n\t"
|
|
|
|
|
"xor\t%%r10d,%%r10d\n\t"
|
|
|
|
|
"xor\t%%r11d,%%r11d\n\t"
|
2022-07-20 21:01:15 +00:00
|
|
|
|
"xor\t%%ebx,%%ebx\n\t" // netbsd doesnt't clear this
|
|
|
|
|
"xor\t%%r12d,%%r12d\n\t" // netbsd doesnt't clear this
|
|
|
|
|
"xor\t%%r13d,%%r13d\n\t" // netbsd doesnt't clear this
|
|
|
|
|
"xor\t%%r14d,%%r14d\n\t" // netbsd doesnt't clear this
|
|
|
|
|
"xor\t%%r15d,%%r15d\n\t" // netbsd doesnt't clear this
|
2022-05-21 14:52:58 +00:00
|
|
|
|
"mov\t%%rdx,%%rsp\n\t"
|
|
|
|
|
"xor\t%%edx,%%edx\n\t"
|
|
|
|
|
"push\t%%rsi\n\t"
|
|
|
|
|
"xor\t%%esi,%%esi\n\t"
|
|
|
|
|
"xor\t%%ebp,%%ebp\n\t"
|
|
|
|
|
"ret"
|
2021-10-02 15:17:04 +00:00
|
|
|
|
: /* no outputs */
|
2023-07-01 12:10:12 +00:00
|
|
|
|
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os)
|
2021-10-02 15:17:04 +00:00
|
|
|
|
: "memory");
|
2022-05-21 14:52:58 +00:00
|
|
|
|
__builtin_unreachable();
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
|
|
|
|
|
int os) {
|
|
|
|
|
unsigned size;
|
|
|
|
|
if (Read32(M->ehdr.buf) == Read32("\177ELF") && //
|
|
|
|
|
M->ehdr.ehdr.e_type == ET_EXEC && //
|
|
|
|
|
M->ehdr.ehdr.e_machine == EM_NEXGEN32E && //
|
|
|
|
|
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 && //
|
|
|
|
|
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) && //
|
|
|
|
|
(size = (unsigned)M->ehdr.ehdr.e_phnum * M->ehdr.ehdr.e_phentsize) <=
|
|
|
|
|
sizeof(M->phdr.buf) &&
|
|
|
|
|
Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os) == size) {
|
|
|
|
|
Spawn(os, exe, fd, sp, &M->ehdr.ehdr, &M->phdr.phdr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
2022-05-22 11:51:02 +00:00
|
|
|
|
int rc;
|
2023-07-01 12:10:12 +00:00
|
|
|
|
unsigned i, n;
|
|
|
|
|
int c, fd, os, argc;
|
|
|
|
|
struct ApeLoader *M;
|
|
|
|
|
long *auxv, *ap, *ew;
|
|
|
|
|
char *p, *exe, *prog, **argv, **envp;
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
|
|
|
|
// detect freebsd
|
2023-07-01 12:10:12 +00:00
|
|
|
|
if (SupportsXnu() && dl == XNU) {
|
2022-06-27 23:28:59 +00:00
|
|
|
|
os = XNU;
|
2022-05-22 11:51:02 +00:00
|
|
|
|
} else if (SupportsFreebsd() && di) {
|
2021-10-02 15:17:04 +00:00
|
|
|
|
os = FREEBSD;
|
|
|
|
|
sp = (long *)di;
|
2022-05-21 14:52:58 +00:00
|
|
|
|
} else {
|
2022-05-22 11:51:02 +00:00
|
|
|
|
os = 0;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
|
|
|
|
// extract arguments
|
2021-10-02 15:17:04 +00:00
|
|
|
|
argc = *sp;
|
|
|
|
|
argv = (char **)(sp + 1);
|
2022-05-21 14:52:58 +00:00
|
|
|
|
envp = (char **)(sp + 1 + argc + 1);
|
|
|
|
|
auxv = (long *)(sp + 1 + argc + 1);
|
2021-10-02 15:17:04 +00:00
|
|
|
|
for (;;) {
|
|
|
|
|
if (!*auxv++) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// detect openbsd
|
|
|
|
|
if (SupportsOpenbsd() && !os && !auxv[0]) {
|
|
|
|
|
os = OPENBSD;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// detect netbsd and find end of words
|
|
|
|
|
for (ap = auxv; ap[0]; ap += 2) {
|
|
|
|
|
if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
|
|
|
|
|
os = NETBSD;
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
}
|
|
|
|
|
ew = ap + 1;
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// allocate loader memory
|
|
|
|
|
n = sizeof(*M) / sizeof(long);
|
|
|
|
|
MemMove(sp - n, sp, (char *)ew - (char *)sp);
|
|
|
|
|
sp -= n, argv -= n, envp -= n, auxv -= n;
|
|
|
|
|
M = (struct ApeLoader *)(ew - n);
|
2022-05-22 11:51:02 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// default operating system
|
|
|
|
|
if (!os) {
|
|
|
|
|
os = LINUX;
|
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// we can load via shell, shebang, or binfmt_misc
|
|
|
|
|
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
|
|
|
|
// if the first argument is a hyphen then we give the user the
|
|
|
|
|
// power to change argv[0] or omit it entirely. most operating
|
|
|
|
|
// systems don't permit the omission of argv[0] but we do, b/c
|
|
|
|
|
// it's specified by ANSI X3.159-1988.
|
|
|
|
|
prog = (char *)sp[3];
|
|
|
|
|
argc = sp[3] = sp[0] - 3;
|
|
|
|
|
argv = (char **)((sp += 3) + 1);
|
|
|
|
|
} else if (argc < 2) {
|
|
|
|
|
Print(os, 2,
|
|
|
|
|
"usage: ape PROG [ARGV1,ARGV2,...]\n"
|
|
|
|
|
" ape - PROG [ARGV0,ARGV1,...]\n"
|
|
|
|
|
"αcτµαlly pδrταblε εxεcµταblε loader v1.1\n"
|
|
|
|
|
"copyright 2022 justine alexandra roberts tunney\n"
|
|
|
|
|
"https://justine.lol/ape.html\n",
|
|
|
|
|
0l);
|
|
|
|
|
Exit(1, os);
|
|
|
|
|
} else {
|
|
|
|
|
prog = (char *)sp[2];
|
|
|
|
|
argc = sp[1] = sp[0] - 1;
|
|
|
|
|
argv = (char **)((sp += 1) + 1);
|
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// resolve path of executable and read its first page
|
|
|
|
|
if (!(exe = Commandv(&M->ps, os, prog, GetEnv(envp, "PATH")))) {
|
|
|
|
|
Pexit(os, prog, 0, "not found (maybe chmod +x)");
|
|
|
|
|
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
|
|
|
|
Pexit(os, exe, fd, "open");
|
|
|
|
|
} else if ((rc = Pread(fd, M->ehdr.buf, sizeof(M->ehdr.buf), 0, os)) < 0) {
|
|
|
|
|
Pexit(os, exe, rc, "read");
|
|
|
|
|
} else if (rc != sizeof(M->ehdr.buf)) {
|
|
|
|
|
Pexit(os, exe, 0, "too small");
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
2022-05-21 14:52:58 +00:00
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// change argv[0] to resolved path if it's ambiguous
|
|
|
|
|
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
|
|
|
|
|
argv[0] = exe;
|
2022-05-21 14:52:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 12:10:12 +00:00
|
|
|
|
// ape intended behavior
|
|
|
|
|
// 1. if file is a native executable, try to run it natively
|
|
|
|
|
// 2. if ape, will scan shell script for elf printf statements
|
|
|
|
|
// 3. shell script may have multiple lines producing elf headers
|
|
|
|
|
// 4. all elf printf lines must exist in the first 4096 bytes of file
|
|
|
|
|
// 5. elf program headers may appear anywhere in the binary
|
|
|
|
|
if ((IsXnu() && Read32(M->ehdr.buf) == 0xFEEDFACE + 1) ||
|
|
|
|
|
(!IsXnu() && Read32(M->ehdr.buf) == Read32("\177ELF"))) {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
Close(fd, os);
|
|
|
|
|
Execve(exe, argv, envp, os);
|
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
if (Read64(M->ehdr.buf) == Read64("MZqFpD='") ||
|
|
|
|
|
Read64(M->ehdr.buf) == Read64("jartsr='")) {
|
|
|
|
|
for (p = M->ehdr.buf; p < M->ehdr.buf + sizeof(M->ehdr.buf); ++p) {
|
|
|
|
|
if (Read64(p) != Read64("printf '")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0, p += 8;
|
|
|
|
|
p + 3 < M->ehdr.buf + sizeof(M->ehdr.buf) && (c = *p++) != '\'';) {
|
|
|
|
|
if (c == '\\') {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
if ('0' <= *p && *p <= '7') {
|
2023-07-01 12:10:12 +00:00
|
|
|
|
c = *p++ - '0';
|
2021-10-02 15:17:04 +00:00
|
|
|
|
if ('0' <= *p && *p <= '7') {
|
2022-05-21 14:52:58 +00:00
|
|
|
|
c *= 8;
|
|
|
|
|
c += *p++ - '0';
|
2023-07-01 12:10:12 +00:00
|
|
|
|
if ('0' <= *p && *p <= '7') {
|
|
|
|
|
c *= 8;
|
|
|
|
|
c += *p++ - '0';
|
|
|
|
|
}
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
M->ehdr.buf[i++] = c;
|
|
|
|
|
}
|
|
|
|
|
if (i >= sizeof(M->ehdr.ehdr)) {
|
|
|
|
|
TryElf(M, exe, fd, sp, os);
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-01 12:10:12 +00:00
|
|
|
|
TryElf(M, exe, fd, sp, os);
|
|
|
|
|
Pexit(os, exe, 0, "Not an acceptable APE/ELF executable for x86-64");
|
2021-10-02 15:17:04 +00:00
|
|
|
|
}
|