added a PoC to show different ways to hook into the loading process of ELF binaries on Linux
This commit is contained in:
parent
f5e4ac0244
commit
b3b3825526
8 changed files with 274 additions and 0 deletions
87
c/linux_loader_poc/Makefile
Normal file
87
c/linux_loader_poc/Makefile
Normal file
|
@ -0,0 +1,87 @@
|
|||
## Proof-of-concept to show different methods to load executables in the Linux kernel
|
||||
#
|
||||
# @copyright 2016 Steffen Vogel
|
||||
# @license http://www.gnu.org/licenses/gpl.txt GNU Public License
|
||||
# @author Steffen Vogel <post@steffenvogel.de>
|
||||
# @link http://www.steffenvogel.de
|
||||
#########################################################################################
|
||||
|
||||
TARGETS = demo-interpreter demo-binfmt_misc proxy proxy-static
|
||||
|
||||
# We need to know absolute paths to our interpreters / loaders
|
||||
MKDIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
PROXY = $(MKDIR)/proxy
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -g -std=c11 -fPIC
|
||||
LDFLAGS =
|
||||
|
||||
# We are patching the OS/ABI field of the ELF header
|
||||
ELF_OSABI_OFFSET = 7
|
||||
ELF_OSABI = "\\xa1"
|
||||
|
||||
# We are registering a new binary format in the kernel
|
||||
BINFMT_MISC_PATH = /proc/sys/fs/binfmt_misc
|
||||
BINFMT_MISC_NAME = hermit
|
||||
BINFMT_MISC_FILE = $(BINFMT_MISC_PATH)/$(BINFMT_MISC_NAME)
|
||||
|
||||
.PHONY: all clean binfmt_misc
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
binfmt_misc: $(BINFMT_MISC_PATH)/$(BINFMT_MISC_NAME)
|
||||
|
||||
# Register a new binary format within the kernel binfmt subsystem
|
||||
# binfmt_misc is a kernel module which allows us to register new formats
|
||||
# based on magic numbers and filename extension matching.
|
||||
# Loading is then performed by custom interpreters in the userspace.
|
||||
$(BINFMT_MISC_FILE):
|
||||
# Mount binfmt_misc pseudo FS ##########################################################
|
||||
test -d $(BINFMT_MISC_PATH) || mount binfmt_misc -t binfmt_misc $(BINFMT_MISC_PATH)
|
||||
|
||||
# Remove old entry #####################################################################
|
||||
test -f $(BINFMT_MISC_FILE) && echo -1 > $(BINFMT_MISC_FILE)
|
||||
|
||||
# Register new format ##################################################################
|
||||
echo ":$(BINFMT_MISC_NAME):M:$(ELF_OSABI_OFFSET):$(ELF_OSABI)::$(PROXY):" > $(BINFMT_MISC_PATH)/register
|
||||
|
||||
# Test for success and show result #####################################################
|
||||
test -f $(BINFMT_MISC_FILE) && cat $(BINFMT_MISC_FILE)
|
||||
|
||||
# We have to versions of our demo application:
|
||||
#
|
||||
# 1. We use a custom dynamic linker (instead of the usual dynamic linker: ld-linux.so)
|
||||
# For some reason our interpreter must be statically linked or we get a segfault.
|
||||
demo-interpreter: demo.o
|
||||
# Link demo_interpreter ################################################################
|
||||
$(CC) $(LDFLAGS) -Wl,-dynamic-linker,$(PROXY)-static -o $@ $?
|
||||
|
||||
# Verify ###############################################################################
|
||||
readelf -l $@ | grep -A 2 INTERP
|
||||
|
||||
# 2. We register a new binary format within the Linux kernel
|
||||
# And patch our binary in a way it get's recognized by the new format
|
||||
# binfmt_misc succeeds with starting our dynamically linked interpreter "proxy" :-)
|
||||
demo-binfmt_misc: demo.o $(BINFMT_MISC_FILE)
|
||||
# Link demo_binfmt_misc ################################################################
|
||||
$(CC) $(LDFLAGS) -o $@ $<
|
||||
|
||||
# Patch OS/ABI field in ELF header to match the binfmt_misc format #####################
|
||||
printf $(ELF_OSABI) | dd of=$@ bs=1 seek=$(ELF_OSABI_OFFSET) count=1 conv=notrunc
|
||||
|
||||
# Verify ###############################################################################
|
||||
readelf -h $@ | grep "OS/ABI"
|
||||
|
||||
# This is the loader / proxy which is executed by the binfmt_misc subsystem or the 1st stage loader "loader"
|
||||
proxy: proxy.o
|
||||
# Link proxy ###########################################################################
|
||||
$(CC) $(LDFLAGS) -o $@ $?
|
||||
|
||||
proxy-static: proxy.o
|
||||
# Link proxy ###########################################################################
|
||||
$(CC) $(LDFLAGS) -static -o $@ $?
|
||||
|
||||
clean:
|
||||
rm -rf $(TARGETS)
|
||||
rm -rf *.o
|
||||
make -C kernel clean
|
8
c/linux_loader_poc/README.md
Normal file
8
c/linux_loader_poc/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Custom ELF interpreter under Linux
|
||||
|
||||
This is proof-of-concept shows how to use a custom
|
||||
|
||||
|
||||
#### Credits
|
||||
|
||||
Steffen Vogel <post@steffenvogel.de>
|
16
c/linux_loader_poc/demo.c
Normal file
16
c/linux_loader_poc/demo.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/** Proof-of-concept to show different methods to load executables in the Linux kernel
|
||||
*
|
||||
* @copyright 2016 Steffen Vogel
|
||||
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @link http://www.steffenvogel.de
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello world\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/custom_loader_poc/kernel/binfmt_hermit.ko
|
||||
/custom_loader_poc/kernel/binfmt_hermit.o
|
18
c/linux_loader_poc/kernel/Makefile
Normal file
18
c/linux_loader_poc/kernel/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
MODULE = binfmt_hermit
|
||||
|
||||
obj-m += $(MODULE).o
|
||||
|
||||
.PHONY: all clean install reload
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
|
||||
install: all
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules_install
|
||||
|
||||
reload: install
|
||||
modprobe -r $(MODULE)
|
||||
modprobe $(MODULE)
|
0
c/linux_loader_poc/kernel/Module.symvers
Normal file
0
c/linux_loader_poc/kernel/Module.symvers
Normal file
125
c/linux_loader_poc/kernel/binfmt_hermit.c
Normal file
125
c/linux_loader_poc/kernel/binfmt_hermit.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/** Proof-of-concept to show different methods to load executables in the Linux kernel
|
||||
*
|
||||
* @copyright 2016 Steffen Vogel
|
||||
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @link http://www.steffenvogel.de
|
||||
*/
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
#include <linux/init.h> /* Needed for the macros */
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
static int load_binary(struct linux_binprm *bprm)
|
||||
{
|
||||
int retval;
|
||||
const char *i_name, *i_arg;
|
||||
char interp[BINPRM_BUF_SIZE];
|
||||
struct file *file;
|
||||
struct elfhdr *hdr = (struct elfhdr *) bprm->buf;
|
||||
|
||||
/* Check if this is an ELF file */
|
||||
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0)
|
||||
return -ENOEXEC;
|
||||
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)
|
||||
return -ENOEXEC;
|
||||
if (!elf_check_arch(hdr))
|
||||
return -ENOEXEC;
|
||||
if (!bprm->file->f_op->mmap)
|
||||
return -ENOEXEC;
|
||||
|
||||
printk(KERN_INFO "Got ELF file in binfmt_hermit\n");
|
||||
|
||||
if (hdr->e_ident[EI_OSABI] != 0xa1)
|
||||
return -ENOEXEC;
|
||||
|
||||
/* Hardcoded for now */
|
||||
i_name = "/custom_loader_poc/proxy";
|
||||
i_arg = NULL;
|
||||
|
||||
strcpy (interp, i_name);
|
||||
|
||||
printk(KERN_INFO "It's a hermit one! Start the interpreter\n");
|
||||
|
||||
/*
|
||||
* OK, we've parsed out the interpreter name and
|
||||
* (optional) argument.
|
||||
* Splice in (1) the interpreter's name for argv[0]
|
||||
* (2) (optional) argument to interpreter
|
||||
* (3) filename of shell script (replace argv[0])
|
||||
*
|
||||
* This is done in reverse order, because of how the
|
||||
* user environment and arguments are stored.
|
||||
*/
|
||||
retval = remove_arg_zero(bprm);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = copy_strings_kernel(1, &bprm->interp, bprm);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
bprm->argc++;
|
||||
|
||||
retval = copy_strings_kernel(1, &i_name, bprm);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
bprm->argc++;
|
||||
|
||||
retval = bprm_change_interp(interp, bprm);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* OK, now restart the process with the interpreter's dentry.
|
||||
*/
|
||||
file = open_exec(interp);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
bprm->file = file;
|
||||
retval = prepare_binprm(bprm);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return search_binary_handler(bprm);
|
||||
}
|
||||
|
||||
static struct linux_binfmt script_format = {
|
||||
.module = THIS_MODULE,
|
||||
.load_binary = load_binary,
|
||||
};
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
printk(KERN_INFO "Loaded binary format for HermitCore\n");
|
||||
|
||||
register_binfmt(&script_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
printk(KERN_INFO "Un-loaded binary format for HermitCore\n");
|
||||
|
||||
unregister_binfmt(&script_format);
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Steffen Vogel <steffen.vogel@rwth-aachen.de>");
|
18
c/linux_loader_poc/proxy.c
Normal file
18
c/linux_loader_poc/proxy.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/** Proof-of-concept to show different methods to load executables in the Linux kernel
|
||||
*
|
||||
* @copyright 2016 Steffen Vogel
|
||||
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @link http://www.steffenvogel.de
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("This is the dynamically-linked proxy: %s\n", argv[0]);
|
||||
printf(" Running now /usr/bin/objdump -dS %s\n\n", argv[1]);
|
||||
|
||||
execl("/usr/bin/objdump", "objdump", "-dS", argv[0], NULL);
|
||||
}
|
Loading…
Add table
Reference in a new issue