Skip to content

Instantly share code, notes, and snippets.

@jakobrs
Last active May 19, 2021 18:21
Show Gist options
  • Save jakobrs/cc311aaceffec348dfea6b69386659ee to your computer and use it in GitHub Desktop.
Save jakobrs/cc311aaceffec348dfea6b69386659ee to your computer and use it in GitHub Desktop.
// Compile with: gcc ./dlopen_from.c -masm=intel -shared -O3 -o libdlopen_from.so -ldl
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <signal.h>
#include <unistd.h>
#include <link.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
static void *alt_object;
static struct link_map *alt_linkmap;
static void *(*alt_dlopen_from_child)(int socket, const char *file, int mode, void *address);
static int client_socket;
static void *dlopen_from_parent(int socket, int mode);
void *dlopen_from_child(int socket, const char *file, int mode, void *address);
void *dlopen_from(const char *file, int mode, void *address);
static void *dlopen_from_post();
// Implementations
#define getaora() ((void **)(__builtin_frame_address(0) + 8))
static void *dlopen_from_parent(int socket, int mode) {
int l_length;
if (read(socket, &l_length, sizeof(int)) == 0) return NULL;
char l_name[l_length];
if (read(socket, l_name, l_length * sizeof(char)) == 0) return NULL;
close(socket);
return dlopen(l_name, mode);
}
__attribute__((noinline))
void *dlopen_from_child(int socket, const char *file, int mode, void *address) {
client_socket = socket;
size_t address_offset = (size_t)address % getpagesize();
if (mmap(address - address_offset,
address_offset + 0xd,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
0, 0) == MAP_FAILED) {
perror("mmap() failed");
raise(SIGKILL);
}
// I couldn't find a way to save `rax` from being overwritten in
// dlopen_from_post below, so as a workaround instead of running this:
// mov rdi, &dlopen_from_post
// call rdi
// I run this:
// push rax
// mov rdi, &dlopen_from_post
// jmp rdi
// So that rax is stored where the return address should be.
*((uint8_t *)(address + 0x0)) = 0x50; // push rax
*((uint16_t *)(address + 0x1)) = 0xbf48; // mov rdi, ...
*((void **)(address + 0x3)) = &dlopen_from_post;
*((uint16_t *)(address + 0xb)) = 0xe7ff; // jmp rdi
*getaora() = address;
return dlopen(file, mode);
}
// Like dlopen, but with an extra parameter specifying the "real" caller.
void *dlopen_from(const char *file, int mode, void *address) {
int switch_to_alt = 1;
Dl_info info;
if (alt_object == NULL) {
if (dladdr(&dlopen_from, &info) == 0) {
printf("dladdr() failed\n");
return NULL;
}
alt_object = dlmopen(LM_ID_NEWLM, info.dli_fname, RTLD_NOW);
if (alt_object == NULL) {
printf("Failed to dlmopen() libdlopen_from.so into new namespace\n");
return NULL;
}
alt_dlopen_from_child = dlsym(alt_object, "dlopen_from_child");
dladdr1(alt_dlopen_from_child, &info, (void **)&alt_linkmap, RTLD_DL_LINKMAP);
if (alt_linkmap == NULL) {
printf("Failed to get link_map structure for alt_object\n");
return NULL;
}
}
void *address_aligned = address - ((size_t)address % getpagesize());
struct link_map *address_linkmap;
dladdr1(address_aligned, &info, (void **)&address_linkmap, RTLD_DL_LINKMAP);
struct link_map *current_linkmap = alt_linkmap;
while (current_linkmap != NULL) {
if (current_linkmap == address_linkmap) {
switch_to_alt = 0;
}
current_linkmap = current_linkmap->l_next;
}
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) {
perror("Error creating socket pair");
return NULL;
}
int fork_result = fork();
if (fork_result == -1) {
printf("fork() failed");
return NULL;
} else if (fork_result > 0) {
close(sv[1]);
return dlopen_from_parent(sv[0], mode);
} else {
close(sv[0]);
if (switch_to_alt) {
return alt_dlopen_from_child(sv[1], file, mode, address);
} else {
return dlopen_from_child(sv[1], file, mode, address);
}
}
}
static void *dlopen_from_post() {
void *rax = __builtin_extract_return_addr(__builtin_return_address(0));
if (rax == NULL) raise(SIGKILL);
struct link_map *a;
if (dlinfo(rax, RTLD_DI_LINKMAP, &a)) {
perror("dlinfo failed");
raise(SIGKILL);
}
if (a == NULL) {
perror("dlinfo returned NULL");
raise(SIGKILL);
}
const char *l_name = a->l_name;
int l_length = strlen(l_name) + 1;
if (write(client_socket, &l_length, sizeof(int)) == -1) {
perror("write()ing l_length failed");
raise(SIGKILL);
} else if (write(client_socket, l_name, l_length * sizeof(char)) == -1) {
perror("write()ing l_name failed");
raise(SIGKILL);
}
close(client_socket);
dlclose(rax);
// We can't even return anyway
raise(SIGKILL);
__builtin_unreachable();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment