I would like to override getdirentries (and others, like lstat) libc syscalls.
I can override -for example- lstat and chmod, but I can't override getdirentries (and amongst others fstatfs).
Example code is:
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <sys/_timespec.h>
#include <sys/stat.h>
#include <sys/mount.h>
#ifndef RTLD_NEXT
#define RTLD_NEXT ((void *) -1l)
#endif
int (*getdirentries_orig)(int fd, char *buf, int nbytes, long *basep);
int (*lstat_orig)(const char *path, struct stat *sb);
int (*fstatfs_orig)(int fd, struct statfs *buf);
int (*chmod_orig)(const char *path, mode_t mode);
#define HOOK(func) func##_##orig = dlsym(RTLD_NEXT,#func)
int getdirentries(int fd, char *buf, int nbytes, long *basep) {
HOOK(getdirentries);
printf("getdirentries\n");
return getdirentries_orig(fd, buf, nbytes, basep);
}
int lstat(const char *path, struct stat *sb) {
HOOK(lstat);
printf("lstat\n");
return (lstat_orig(path, sb));
}
int fstatfs(int fd, struct statfs *buf) {
HOOK(fstatfs);
printf("fstatfs\n");
return fstatfs_orig(fd, buf);
}
int chmod(const char *path, mode_t mode) {
HOOK(chmod);
printf("chmod\n");
return chmod_orig(path, mode);
}
I compile this on FreeBSD with:
cc -Wall -g -O2 -fPIC -shared -o preload.so preload.c
(on Linux, adding -ldl may be needed)
and use it with LD_PRELOAD=./preload.so bash.
If I then issue an ls -l, I get "lstat" printed multiple times, that's good.
But ls calls multiple getdirentries too, according to ktrace, and its override function does not get called. fstatfs also doesn't work.
How can I override getdirentries, fstatfs and possibly other syscalls, and why they aren't working in this case?
Thanks,
As it turns out, readdir() in libc/readdir.c (readdir is what ls calls and that should call getdirentries) calls _getdirentries, not getdirentries. If I override _getdirentries, it works. The same for fstatfs, so this is why my program did not work.
Related
If dlsym is available in dynamic linking setup, I can get access to the original impl pointers using dlsym with RTLD_NEXT and use them in my overrides, e.g. as follows:
// paste these in main.c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
int open(const char *path, int flags)
{
fprintf(stderr, "log_file_access_preload: open(\"%s\", %d)\n", path, flags);
typedef int (*orig_open_func_type)(const char *pathname, int flags);
orig_open_func_type orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open");
return orig_func(path, flags);
}
FILE* fopen(const char *path, const char *mode)
{
fprintf(stderr, "log_file_access_preload: fopen(\"%s\", \"%s\")\n", path, mode);
typedef FILE* (*orig_fopen_func_type)(const char *path, const char *mode);
orig_fopen_func_type orig_func = (orig_fopen_func_type)dlsym(RTLD_NEXT, "fopen");
return orig_func(path, mode);
}
Is there a way to do static linking in such a way that doesn't hide the original libc/POSIX symbols and so that I can use them in my overrides? Should I create my own copy of musl *.a files with renamed original symbols? Should it work? Is there another way?
Usecase: implement redirection of file read/access functions for a custom LaTeX program (compilation process is controlled by me, statically built with musl) to read files from ISO or TAR archive (that contains a prepared TeX Directory Structure) without extraction to disk
I'm exploring the getdents64 syscall. The resulting struct linux_dirent64 is not defined by the relevant headers. Both the related question and the example in man 2 getdirents64 are declaring their own structs. Although I'm aware of Linux syscall backwards compatibility, defining the struct locally like that looks like a hack. Is there another header I need to include that has this struct linux_dirent64 defined inside?
#define _GNU_SOURCE
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
struct linux_dirent64 {
ino64_t d_ino;
off64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[];
};
void test() {
char buf[1024];
const int procfs = open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
getdents64(procfs, buf, 1024);
printf("%lu\n", ((struct linux_dirent64 *) buf)->d_ino);
close(procfs);
}
Change struct linux_dirent64 to just struct dirent64. This works with glibc 2.36. It is not necessary to include another header as #include <dirent.h> provides it with #define _GNU_SOURCE. The original code would look like the following:
#define _GNU_SOURCE
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
void test() {
char buf[1024];
const int procfs = open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
getdents64(procfs, buf, 1024);
printf("%lu\n", ((struct dirent64 *) buf)->d_ino);
close(procfs);
}
I am trying to find somewhat elegant ways to mock and stub function calls to the standard C library functions.
While stubbing-off calls to C files of the project is easy by just linking other C files in the tests, stubbing the standard C functions is harder.
They are just there when linking.
Currently, my approach is to include the code-under-test from my test.cpp file, and placing defines like this:
#include <stdio.h>
#include <gtest/gtest.h>
#include "mymocks.h"
CMockFile MockFile;
#define open MockFile.open
#define close MockFile.close
#define read MockFile.read
#include "CodeUnderTestClass.cpp"
#undef open
#undef close
#undef read
// test-class here
This is cumbersome, and sometimes I run across code that uses 'open' as member names elsewhere or causes other collisions and issues with it. There are also cases of the code needing different defines and includes than the test-code.
So are there alternatives? Some link-time tricks or runtime tricks to override standard C functions? I thought about run-time hooking the functions but that might go too far as usually binary code is loaded read-only.
My unit-tests run only on Debian-Linux with gcc on amd64. So gcc, x64 or Linux specific tricks are also welcome.
I know that rewriting all the code-under-test to use an abstracted version of the C functions is an option, but that hint is not very useful for me.
Use library preloading to substitute system libraries with your own.
Consider following test program code, mytest.c:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
char buf[256];
int fd = open("file", O_RDONLY);
if (fd >= 0) {
printf("fd == %d\n", fd);
int r = read(fd, buf, sizeof(buf));
write(0, buf, r);
close(fd);
} else {
printf("can't open file\n");
}
return 0;
}
It will open a file called file from the current directory, print it's descriptor number (usually 3), read its content and then print it on the standard output (descriptor 0).
Now here is your test library code, mock.c:
#include <string.h>
#include <unistd.h>
int open(const char *pathname, int flags) {
return 100;
}
int close(int fd) {
return 0;
}
ssize_t read(int fd, void *buf, size_t count) {
strcpy(buf, "TEST!\n");
return 7;
}
Compile it to a shared library called mock.so:
$ gcc -shared -fpic -o mock.so mock.c
If you compiled mytest.c to the mytest binary, run it with following command:
$ LD_PRELOAD=./mock.so ./mytest
You should see the output:
fd == 100
TEST!
Functions defined in mock.c were preloaded and used as a first match during the dynamic linking process, hence executing your code, and not the code from the system libraries.
Update:
If you want to use "original" functions, you should extract them "by hand" from the proper shared library, using dlopen, dlmap and dlclose functions. Because I don't want to clutter previous example, here's the new one, the same as previous mock.c plus dynamic symbol loading stuff:
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <gnu/lib-names.h>
// this declares this function to run before main()
static void startup(void) __attribute__ ((constructor));
// this declares this function to run after main()
static void cleanup(void) __attribute__ ((destructor));
static void *sDlHandler = NULL;
ssize_t (*real_write)(int fd, const void *buf, size_t count) = NULL;
void startup(void) {
char *vError;
sDlHandler = dlopen(LIBC_SO, RTLD_LAZY);
if (sDlHandler == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
real_write = (ssize_t (*)(int, const void *, size_t))dlsym(sDlHandler, "write");
vError = dlerror();
if (vError != NULL) {
fprintf(stderr, "%s\n", vError);
exit(EXIT_FAILURE);
}
}
void cleanup(void) {
dlclose(sDlHandler);
}
int open(const char *pathname, int flags) {
return 100;
}
int close(int fd) {
return 0;
}
ssize_t read(int fd, void *buf, size_t count) {
strcpy(buf, "TEST!\n");
return 7;
}
ssize_t write(int fd, const void *buf, size_t count) {
if (fd == 0) {
real_write(fd, "mock: ", 6);
}
real_write(fd, buf, count);
return count;
}
Compile it with:
$ gcc -shared -fpic -o mock.so mock.c -ldl
Note the -ldl at the end of the command.
So: startup function will run before main (so you don't need to put any initialization code in your original program) and initialize real_write to be the original write function. cleanup function will run after main, so you don't need to add any "cleaning" code at the end of main function either.
All the rest works exactly the same as in the previous example, with the exception of newly implemented write function. For almost all the descriptors it will work as the original, and for file descriptor 0 it will write some extra data before the original content. In that case the output of the program will be:
$ LD_PRELOAD=./mock.so ./mytest
fd == 100
mock: TEST!
I think the problem is having one header file that has all items that my source files needs, but how do I fix this? Is it possible to use one or must I split them up with their own includes functionalities.
I not sure what wrong with my .h file that I'm including in all my source files. I read that the problem is each source file is compiling separately with this .h file and when linking all source files together, the compile finds there are multiple definitions even though I have a include guard.
How can I go about this? There's prototype.h that includes all my function prototypes, includes, extern variables, and structs. I have a few source files that all need some portion of this .h file.
My make file is as follows
TARGET = betty
obj-m := $(TARGET).o
betty-objs := main.o helpers.o syscall_overrides.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(RM) .*.cmd *.o *.ko -r .tmp*
And the error message (a portion of them) that it gives is:
/home/admin/Dropbox/COMP3000/FinalModule/helpers.o:(.bss+0x4): multiple definition of `clean_sys_mkdir'
/home/admin/Dropbox/COMP3000/FinalModule/main.o:(.bss+0x0): first defined here
/home/admin/Dropbox/COMP3000/FinalModule/helpers.o:(.bss+0x8): multiple definition of `clean_sys_execve'
/home/admin/Dropbox/COMP3000/FinalModule/main.o:(.bss+0x4): first defined here
/home/admin/Dropbox/COMP3000/FinalModule/helpers.o:(.bss+0xc): multiple definition of `clean_getdents64_syscall'
Code:
main.c
#define MODULE_NAME "Betty"
#include "prototypes.h"
extern unsigned long **syscall_table;
static int __init init(void)
{
...
}
static void __exit shutdown(void)
{
...
}
module_init(init);
module_exit(shutdown);
MODULE_LICENSE("..");
MODULE_AUTHOR("..");
MODULE_DESCRIPTION("..");
prototype.h
#ifndef PROTOTYPE_H
#define PROTOTYPE_H
#include <linux/module.h> // mandatory kernel module
#include <linux/kernel.h> // KERN_INFO
#include <linux/init.h> // __init and __exit
#include <asm/unistd.h> // __NR syscall nums
#include <linux/printk.h>
#include <linux/tty.h> // tty_struct
#include <linux/tty_driver.h>// write (already included by tty.h)
#include <linux/sched.h> // current
#include <linux/random.h> // get_random_bytes
#include <linux/syscalls.h> // from sys_execve asmlinkage
#include <linux/dirent.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/slab.h> // kmalloc()
#include <linux/time.h> // time
#include <linux/rtc.h> // rtc_time_to_tm
/*
Function prototypes for both original and custom syscalls that Betty uses
*/
asmlinkage long (*clean_sys_mkdir)(const char __user *pathname, umode_t mode);
asmlinkage long (*clean_sys_execve)(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp);
asmlinkage long (*clean_getdents64_syscall) (unsigned int fd,
struct linux_dirent64 __user * dirent,
unsigned int count);
static void find_process_id_to_hide(char* process_nm);
extern unsigned long **syscall_table;
#endif
helpers.c
#include "prototypes.h"
extern unsigned long **syscall_table; // extern variable
... just functions in this one defined in prototype.h
I have successfuly intercepted calls to read(),write(),open(),unlink(),rename(), creat() but somehow with exactly the same semantics intercepting stat() is not taking place. I have changed the execution environmnet using LD_PRELOAD.
Am I missing something?
The code is quite huge, which part of it will be most helpful to post so you can help?
Thanks.
Edit: I kept the interposed stat() wrapper simple to check if it works.
int stat(const char *path,struct stat *buff)
{
printf("client invoke: stat %s",path);
return 1;
}
Compile a function that calls stat(); see what reference(s) are generated (nm -g stat.o). Then you'll have a better idea of which function(s) to interpose. Hint: it probably isn't called stat().
If you are compiling with 64 bit file offsets, then stat() is either a macro or a redirected function declaration that resolves to stat64(), so you will have to interpose on that function too.
Well it was not very simple when running in linux. Gnu libc does some tricks. You need to intercept the __xstat and if you want to call the original save the call.
Here is how I got it to work
gcc -fPIC -shared -o stat.so stat.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static int (*old_xstat)(int ver, const char *path, struct stat *buf) = NULL;
static int (*old_xstat64)(int ver, const char *path, struct stat64 *buf) = NULL;
int __xstat(int ver, const char *path, struct stat *buf)
{
if ( old_xstat == NULL ) {
old_xstat = dlsym(RTLD_NEXT, "__xstat");
}
printf("xstat %s\n",path);
return old_xstat(ver,path, buf);
}
int __xstat64(int ver, const char *path, struct stat64 *buf)
{
if ( old_xstat64 == NULL ) {
old_xstat64 = dlsym(RTLD_NEXT, "__xstat64");
}
printf("xstat64 %s\n",path);
return old_xstat64(ver,path, buf);
}