I am implementing binary attestation from inside the kernel. I am reading the file using the kernel_read_from_file() function. The function definition is as follows:
int kernel_read_file_from_path(const char *path, void **buf, loff_t *size,
loff_t max_size, enum kernel_read_file_id id)
The function is storing the file content in buf. The code is working fine when I read files with .c or .h extension. But for ELF binaries:
Value stored in buf = ELF
What am I missing here? How can I read ELF binary from inside the kernel?
Here's the relevant code:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/file.h>
// #include "sha256.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert W. Oliver II");
MODULE_DESCRIPTION("A simple example Linux module.");
MODULE_VERSION("0.01");
static int __init lkm_example_init(void)
{
void *data;
loff_t size;
int ret;
char path1[50] = "/etc/bash.bashrc";
char path2[50] = "/bin/sh";
ret = kernel_read_file_from_path(path1, &data, &size, 0, READING_POLICY);
printk(KERN_INFO "Hello, World!\n");
printk(KERN_INFO "%lld\n", size);
printk(KERN_INFO "%s", (char*)data);
ret = kernel_read_file_from_path(path2, &data, &size, 0, READING_POLICY);
printk(KERN_INFO "%lld\n", size);
printk(KERN_INFO "%s", (char*)data);
// vfree(data);
return 0;
}
static void __exit lkm_example_exit(void)
{
printk(KERN_INFO "Goodbye, World!\n");
}
module_init(lkm_example_init);
module_exit(lkm_example_exit);
And here's the Makefile:
# Save file as read_elf.c
obj-m += read_elf.o
# This line tells makefile that the given object files are part of module
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
What am I missing here? How can I read ELF binary from inside the kernel?
You are missing the fact that an ELF file is not a text file, it's a binary file. However, you are trying to print it as a string (%s specifier in printk), which will only print the first few characters and stop at the first zero byte (\0) thinking it's the string terminator.
As it turns out, as #Tsyvarev notes in the comments above, ELF files always start with the bytes 7f 45 4c 46, which in ASCII are ELF (that first byte 7f is not printable). That's what you see in your buffer after reading.
If you take a look at size after reading you will indeed see that it's bigger than 4, meaning the file was correctly read. Though you might still want to check for errors and also make sure you read the entire file.
Related
I'm trying to compile a kernel module for Linux. I have the following files: testuio.c and Makefile. When I type make all I get the following errors:
$ make all
make -C /lib/modules/`uname -r`/build M=/srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
CC [M] /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o
In file included from /usr/include/unistd.h:25,
from /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.c:13:
/usr/include/features.h:424:12: fatal error: sys/cdefs.h: No such file or directory
# include <sys/cdefs.h>
^~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/scripts/Makefile.build:271: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:1665: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory] Error 2
make[1]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:179: sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
make: *** [Makefile:19: all] Error 2
This is correct, there is no such file under /usr/include/sys/. What I don't understand is why it would not find it under /usr/include/x86_64-linux-gnu/sys where there is such a file.
The following is part of gcc -xc -E -v - output:
#include <...> search starts here:
.
/usr/lib/gcc/x86_64-linux-gnu/8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
My Makefile contains:
# (1) consult https://www.kernel.org/doc/Documentation/kbuild/modules.txt to build a kbuild-compatible Makefile
# sections of special interest: 3.1 (shared makefile)
# (2) specifics about the Makefile under https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
# sections of special interest 3.7 (compilation flags)
ifneq ($(KERNELRELEASE),)
# kbuild part of the makefile (could pull this out into a file named Kbuild, this variant is more robust though)
obj-m += testuio.o
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
#ccflags-y = -I/usr/include/aarch64-linux-gnu -I/usr/include # see (2) section 3.7
else
# "normal" makefile
KDIR ?= /lib/modules/`uname -r`/build # ?= sets KDIR only if it has no value already
all:
make -C $(KDIR) M=$(PWD) modules
install:
make -C $(KDIR) M=$(PWD) modules_install
clean:
make -C $(KDIR) M=$(PWD) clean
endif
For reference my testuio.c contains:
// In order for a device to be compatible with this UIO platform device driver
// it needs to use "generic-uio" in its compatible property
// This is a kernel driver.
#include <linux/module.h> // included for module_* macros
#include <linux/device.h> // included for devm_kzalloc
#include <linux/mman.h> // included for mmap
// #include <linux/stat.h> // included for fstat (for being able to read out of files)
#include <linux/platform_device.h> // included for struct platform_device
#include <linux/uio_driver.h> // included for struct uio_info
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
#define EOPENUIOFD 1
#define EMEMMAP 2
#define UIO_SIZE_FILE "/sys/class/uio/uio0/maps/map0/size"
MODULE_LICENSE("");
MODULE_AUTHOR("Alexander Pastor");
MODULE_VERSION("0.0.1");
// MODULE_DEVICE_TABLE(???);
typedef struct testuio_dev {
struct uio_info* info;
} testuio_dev;
// TODO: change s.t. interupts can be handled correctly
// HINT: might not be necessary tho
static int testuio_irq_handler(int irq, struct uio_info* info)
{
// if (IRQ is not caused by my hardware)
if (true)
return IRQ_NONE;
/* Disable interrupt */
// Perform some register access to silence the IRQ line
return IRQ_HANDLED;
}
static int testuio_probe(struct platform_device* pdev)
{
struct testuio_dev* dev;
struct resource* res;
int irq;
// collect handles and information from platform device
// devm_kzalloc => managed (free upon detaching device from system) kernel zero-initialized memory allocation
// gfp flags => get free page flags
dev = devm_kzalloc(&pdev->dev, (sizeof(struct testuio_dev)), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
// basic uio info struct initialization (required!)
dev->info->name = "testuio";
dev->info->version = "0.0.1";
// memory region initialization
dev->info->mem[0].name = "dummy_mem";
dev->info->mem[0].addr = res->start;
dev->info->mem[0].size = resource_size(res);
dev->info->mem[0].memtype = UIO_MEM_PHYS;
// other memory types include:
// - UIO_MEM_LOGICAL allocated by kmalloc
// - UIO_MEM_VIRTUAL allocated by vmalloc
// interrupt initialization
dev->info->irq = irq;
dev->info->irq_flags = 0;
dev->info->handler = &testuio_irq_handler;
if(uio_register_device(&pdev->dev, info))
{
iounmap(dev->info->mem[0].internal_addr);
return -ENODEV;
} else {
return 0;
}
}
int main(int argc, char** argv)
{
int uio_fd; // UIO file descriptor
unsigned int uio_size; // memory size of UIO
FILE* size_fp; // pointer to the file containing memory size
void* base_address; // start address of mapped memory
uio_fd = open(/dev/uio, O_RDWR);
if(uio_fd == -1)
return EOPENUIOFD;
size_fp = fopen(UIO_SIZE_FILE, O_RDONLY);
fscanf(size_fp, "0x%08x", &uio_size); // 0x%08x expects unsigned int; %p expects void*
// Whenever the user space program reads or writes in the virtual address range
// it is accessing the device w/o the need for a system call. This improves performance.
base_address = mmap(NULL, // NULL => kernel chooses page-aligned address at which to create mapping
uio_size, // length of memory mapping
PROT_READ | PROT_WRITE, // flags: grant read + write access
MAP_SHARED_VALIDATE, // updates to mapping are visible to other processes
// (+validates given flags, available since Linux 4.15)
uio_fd,
0);
if (uio_fd == MAP_FAILED)
return EMEMMAP;
// --- BEGIN APPLICATION CODE
// ??? Is this even the right spot for the application code
// see also
// https://docplayer.net/37414164-Linux-user-space-device-drivers-john-linn-based-on-3-14-linux-kernel.html
// helpful: https://stackoverflow.com/questions/26259421/use-mmap-in-c-to-write-into-memory
// TODO: read in content from file as done in link above using fstat
int testvals[8] = {0xDEADBEEF, 7, 12, 13, 31, 42, -63, -65535}
memcpy(base_address, testvals, sizeof(testvals));
for(int i=0; i<8; i++)
{
printk(KERN_INFO "The value at address %p is %d",
base_address+i*sizeof(int),
(int)*(base_address+i*sizeof(int)));
}
//! interrupt stuff
// read() returns the number of interrupt events.
// It allows blocking and non-blocking modes with this being blocking mode
int pending = 0;
int reennable = 1;
read(uio_fd, (void*)&pending, sizeof(int));
//! device specific processing
// acking the interrupt in the device
write(uio_fd, (void*)&reenable, sizeof(int));
// --- END APPLICATON CODE
//! undo virtual address mapping
munmap(base_address, uio_size);
return 0;
}
You seem to be mixing up kernel and userspace stuff in your code. However all this is wrong:
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
As you're stitching some user-space include paths to your kernel flags.
And also all these:
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
don't belong in a kernel module.The main function also has to go away.
My assumption is you need a kernel module and a userspace application to test it. Don't mix the two of them together. Keep these things separate.
I have a strange segmentation fault that doesn't exist when everything is in 1 .c file, but does exist when I put part of the code in a dynamically linked library and link it to a test file. The complete code for the working 1 .c file code is at the bottom, the complete code for the error system with 2 .c and 1 .h file come first.
Here is the error system:
example.h:
#include <stdio.h>
#include <stdlib.h>
typedef struct MYARRAY {
int len;
void* items[];
} MYARRAY;
MYARRAY *collection;
void
mypush(void* p);
example.c:
#include "example.h"
void
mypush(void* p) {
printf("Here %lu\n", sizeof collection);
puts("FOO");
int len = collection->len++;
puts("BAR");
collection->items[len] = p;
}
example2.c:
This is essentially a test file:
#include "example.h"
void
test_print() {
puts("Here1");
mypush("foo");
puts("Here2");
}
int
main() {
collection = malloc(sizeof *collection + (sizeof collection->items[0] * 1000));
collection->len = 0;
puts("Start");
test_print();
puts("Done");
return 0;
}
Makefile:
I link example to example2 here, and run:
example:
#clang -I . -dynamiclib \
-undefined dynamic_lookup \
-o example.dylib example.c
#clang example2.c example.dylib -o example2.o
#./example2.o
.PHONY: example
The output is:
$ make example
Start
Here1
Here 8
FOO
make: *** [example] Segmentation fault: 11
But it should show the full output of:
$ make example
Start
Here1
Here 8
FOO
BAR
Here2
Done
The weird thing is everything works if it is this system:
example.c:
#include <stdio.h>
#include <stdlib.h>
typedef struct MYARRAY {
int len;
void* items[];
} MYARRAY;
MYARRAY *collection;
void
mypush(void* p) {
printf("Here %lu\n", sizeof collection);
puts("FOO");
int len = collection->len++;
puts("BAR");
collection->items[len] = p;
}
void
test_print() {
puts("Here1");
mypush("foo");
puts("Here");
}
int
main() {
collection = malloc(sizeof *collection + (sizeof collection->items[0] * 1000));
collection->len = 0;
puts("ASF");
test_print();
return 0;
}
Makefile:
example:
#clang -o example example.c
#./example
.PHONY: example
Wondering why it's creating a segmentation fault when it is linked like this, and what I am doing wrong.
I have checked otool and with DYLD_PRINT_LIBRARIES=YES and it shows it is importing the dynamically linked libraries, but for some reason it's segmentation faulting when linked but works fine when it isn't linked.
Your problem is this, in example.h:
MYARRAY *collection;
Since both main.c and example.c include this file, you end up defining collection twice, which results in undefined behavior. You need to make sure you define each object only once. The details are relatively unimportant since anything can happen with undefined behavior, but what's probably happening is that main.c is allocating memory for one object, but the one example.c is using is still NULL. As mentioned in the comments, since you define collection in main.c your linker is able to build the executable without needing to look for that symbol in the dynamic library, so you don't get a link time warning about it being defined there too, and obviously there'd be no cause for a warning at the time you compile the library.
It works for you when you put everything in one file because obviously then you're not defining anything twice, anymore. The error itself is nothing to do with the fact you're using a dynamic library, although that may have made it harder to detect.
It would be better to define this in example.c and provide a constructor function, there's no need for main() to be able to access it directly. But if you must do this, then define it in example.c and just declare an extern identifier in the header file to tell main.c that the object is defined somewhere else.
I'm a noob to linux kernel programming and thought I'd be able to find the answer for this (since it seems really simple) but haven't had any luck yet. I need to make a linux kernel module that prints the version number of the kernel. The assignment requires that implement a module which displays this kind of message when loaded:
"Hello Master. You are currently using Linux (version)", where (version) is the kernel version no.
How can I do this? I tried using uname (http://man7.org/linux/man-pages/man2/uname.2.html) but when I include sys/utsname.h, I get a fatal error upon compiling with my makefile
"Cannot open include file: 'sys/utsname.h': No such file or directory".
Here is my module
#undef __KERNEL__
#define __KERNEL__
#undef __MODULE__
#define __MODULE__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <sys/utsname.h>
struct utsname unameData;
static int __init config_init(void)
{
uname(&unameData);
printk(KERN_INFO "Version number is %s\n", unameData.version);
return 0;
}
static void __exit config_exit(void)
{
printk(KERN_INFO "config_exit executed with success\n");
return;
}
module_init(config_init);
module_exit(config_exit);
MODULE_LICENSE("GPL");
Makefile
obj-m := config.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
Firstly, you need to use the correct headers and also the correct function utsname(). Following code work well for me.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/utsname.h>
static int __init my_init(void)
{
printk(KERN_INFO "Kernel version %s\n", utsname()->version);
printk(KERN_INFO "Kernel release %s\n", utsname()->release);
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "exit module");
return;
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
dmesg output should looks something like this:
[ 1117.358451] Kernel version #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014
[ 1117.358457] Kernel release 3.13.0-37-generic
I'm trying to intercept the openat() system call on Linux using a custom shared library that I can load via LD_PRELOAD. An example intercept-openat.c has this content:
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <dlfcn.h>
int (*_original_openat)(int dirfd, const char *pathname, int flags, mode_t mode);
void init(void) __attribute__((constructor));
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
void init(void)
{
_original_openat = (int (*)(int, const char *, int, mode_t))
dlsym(RTLD_NEXT, "openat");
}
int openat(int dirfd, const char *pathname, int flags, mode_t mode)
{
fprintf(stderr, "intercepting openat()...\n");
return _original_openat(dirfd, pathname, flags, mode);
}
I compile it via gcc -fPIC -Wall -shared -o intercept-openat.so intercept-openat.c -ldl. Then, when I run this small example program:
int main(int argc, char *argv[])
{
int fd;
fd = openat(AT_FDCWD, "/home/feh/.vimrc", O_RDONLY);
if(fd == -1)
return -1;
close(fd);
return 0;
}
The openat() call is re-written via the library:
$ LD_PRELOAD=./intercept-openat.so ./openat
intercepting openat()...
However, the same does not happen with GNU tar, even though it uses the same system call:
$ strace -e openat tar cf /tmp/t.tgz .vimrc
openat(AT_FDCWD, ".vimrc", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC) = 4
$ LD_PRELOAD=./intercept-openat.so tar cf /tmp/t.tgz .vimrc
So the custom openat() from intercept-openat.so is not being called. Why is that?
It uses the same system call, but apparently it does not call that via the same C function. Alternatively, it could be that it does, but it's statically linked.
Either way, I think you've proved that it never dynamically links a function names "openat". If you still want to pursue this option, you might like to see if it links against a specific version of that function, but that's a long shot.
You can still intercept the system call by writing your program to use ptrace. This is the same interface used by strace and gdb. It will have a higher performance penalty though.
http://linux.die.net/man/2/ptrace
I'm trying to compile a binary file into a MACH_O object file so that it can be linked it into a dylib. The dylib is written in c/c++.
On linux the following command is used:
ld -r -b binary -o foo.o foo.bin
I have tried various option on OSX but to no avail:
ld -r foo.bin -o foo.o
gives:
ld: warning: -arch not specified
ld: warning: ignoring file foo.bin, file was built for unsupported file format which is not the architecture being linked (x86_64)
An empty .o file is created
ld -arch x86_64 -r foo.bin -o foo.o
ld: warning: ignoring file foo.bin, file was built for unsupported file format which is not the architecture being linked (x86_64)
Again and empty .o file is created. Checking the files with nm gives:
nm foo.o
nm: no name list
The binary file is actually, firmware that will be downloaded to an external device.
Thanks for looking
Here's the closest translation to the Linux linker command to perform binary embedding with the OSX linker:
touch stub.c
gcc -o stub.o -c stub.c
ld -r -o foo.o -sectcreate binary foo_bin foo.bin stub.o
foo.bin will be stored in segment binary, section foo_bin (both names are arbitrary but chosen to mimic GNU ld for ELF on Linux) of the foo.o object.
stub is necessary because ld refuses to create just a custom segment/section. You don't need it if you link directly with a real code object.
To get data back from the section, use getsectbyname (struct is defined in mach-o/loader.h):
#include <mach-o/getsect.h>
const struct section_64 *sect = getsectbyname("binary", "foo_bin");
char *buffer = calloc(1, sect->size+1);
memcpy(buffer, sect->addr, sect->size); // whatever
or getsectdata:
#include <mach-o/getsect.h>
size_t size;
char *data = getsectdata("binary", "foo_bin", &size);
char *buffer = calloc(1, size+1);
memcpy(buffer, data, size); // whatever
(I used it to store text data, hence the stringification via calloc zeroing of size+1 plus blob copying)
Warning: Since 10.7, ASLR got stronger and messes badly with getsect* functions, resulting in segfaults. set disable-aslr off in GDB before running to reproduce EXC_BAD_ACCESS (SIGSEGV) in debug conditions. People had to jump through inordinate hoops to find the real address and get this working again.
A simple workaround is to get the offset and size, open the binary and read the data straight from disk. Here is a working example:
// main.c, build with gcc -o main main.c foo.o
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <mach-o/getsect.h>
int main() {
// finding the filename of the running binary is left as an exercise to the reader
char *filename = "main";
const struct section_64 *sect = getsectbyname("binary", "foo_bin");
if (sect == NULL) {
exit(1);
}
char *buffer = calloc(1, sect->size+1);
int fd = open(filename, O_RDONLY);
if (fd < 0) {
exit(1);
}
lseek(fd, sect->offset, SEEK_SET);
if (read(fd, buffer, sect->size) != sect->size) {
close(fd);
exit(1);
}
printf("%s", buffer);
}