Sometimes, LD_PRELOAD in HPUX and Solaris cannot take effect - c

I get some problem about LD_PRELOAD.
When I use LD_PRELOAD in HPUX and Solaris, I found that I cannot attach my open64/open/creat64/creat function in /usr/bin/touch, but my unlink can take effect in /usr/bin/rm, why?
I have do a simple test:
int open(int fd, int flag, mode_t mode)
{
return -1;
}
int open64(int fd, int flag, mode_t mode)
{
return -1;
}
int creat(int fd, mode_t mode)
{
return -1;
}
int creat64(int fd, mode_t mode)
{
return -1;
}
when i do this, i found : normally, i cannot open file, but touch can do it!
why!i was puzzled by this for long time.
who can help me.thx
at last, sorry for my poor English

i think your function signature is wrong. (int instead of char *)
on my system i see the following signature:
grep -w creat /usr/include/*
/usr/include/fcntl.h:#define creat64 creat
/usr/include/fcntl.h:extern int creat(const char *, mode_t);
grep -w open /usr/include/*
/usr/include/fcntl.h:#define open64 open
/usr/include/fcntl.h:extern int open(const char *, int, ...);

Related

arm64 bare-metal application build problem

I am using a arm cortex-a55 based MPU and use the toolchain gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar from 'developer.arm.com'. I just want to build a simple bare-metal application and will use freertos multi-task environment later.
In the Makefile:
set CC=/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc
c source compile options:
$CC -mcpu=cortex-a55 -Wall -fomit-frame-pointer -fno-stack-protector -ffunction-sections -fdata-sections
linking options:
$CC -Wl,--gc-sections -nostartfiles -T,link.lds -Wl,-Map,System.map
I write my own startup.S with _start entry point. It simply does data section relocation and bss clear, sp setup and bl to main().
int main()
{
stdout = fopen("uart", "w");
printf("Hello, world!\n");
while(1);
}
I also implement the platform depandant fuctions:
int _open(const char *pname, int flags){
uart_init();
return 1;
}
ssize_t _write(int fd, const void *buf, size_t count){
char *str = (char *)buf;
uart_print(str);
return (ssize_t)count;
}
void * _sbrk(ptrdiff_t incr)
{
static char *heap = NULL;
char *prev_heap;
if(NULL == heap)
heap = (char *)asm_get_heap_start();
prev_heap = heap;
heap += incr;
/* total 1KB heap area */
if((u64)heap > asm_get_heap_end())
{
errno = -ENOMEM;
return NULL;
}
return (void *)prev_heap;
}
and others like _read(), _lseek()...
After compile and link, download the raw binary to board and run. Nothing output. After debug I find the cpu hangs inside fopen(). If I replace fopen() with uart_init(), it will hang inside printf().
I am sure the uart_init() and uart_print() are OK. Because if I don't use fopen() and printf(), and use uart_init() and uart_print() directly, it works fine.
I am not sure if I miss some macro(s) needed for .c source file. There are lots of macros in the newlib/libc/include header files. Or something need to be done before 'printf()'.
Please advise, thanks.

Kernel Module to reboot linux

How can I EXPORT_SYMBOL in my dynamic module to reboot my linux system.
EXPORT_SYMBOL(register_restart_handler);
EXPORT_SYMBOL(unregister_restart_handler);
EXPORT_SYMBOL_GPL(kernel_restart);
how to use struct notifier_block and char *cmd in void kernel_restart(char *cmd) ?
I have Found the solution just add kernel_restart(NULL). No need to register handler.
int init_module(void)
{
mdelay(5000);
kernel_restart(NULL);
return 0;
}

Hooking fopen() function throws Segmentation fault

I'm trying to log access to a particular directory by hooking the fopen() function and using LD_PRELOAD.
My first question is: Is hooking fopen() enough to log operations that open a file?
My code throws Segmentation fault. In particular, the code is like this (ignoring error checks):
FILE* (my_fopen)(const char filename, const char* mode);
void* libc_handle;
void __attribute__ ((constructor)) init(void){
libc_handle = dlopen("libc.so.6", RTLD_LAZY);
*(void**)(&my_fopen) = dlsym(libc_handle,"fopen");
}
FILE* fopen(const char* filename, const char* mode){
printf("Hello world\n");
return my_fopen(filename, mode);
}
After compiling and specifying the new library in LD_PRELOAD, I ran
ls
and it throws Segmenation Fault. Any idea why that happened? I even tried to remove the printf(), but did not help.
You have some issues in your code, which are fixed in the sample below (I've also added the relevant headers and provided a main to give a complete program):
#include <stdio.h>
#include <dlfcn.h>
FILE* (*my_fopen)(const char *filename, const char* mode);
void* libc_handle;
void __attribute__ ((constructor)) init(void){
libc_handle = dlopen("libc.so.6", RTLD_LAZY);
my_fopen = dlsym(libc_handle,"fopen");
}
FILE* fopen(const char* filename, const char* mode){
printf("Hello, Pax\n");
return my_fopen(filename, mode);
}
int main (void) {
FILE *fout = fopen ("xyzzy.txt","w");
fclose (fout);
return 0;
}
The changes from what you provided are as follows:
The my_fopen function pointer should be exactly that, a pointer. I suspect you may have thought the FILE* made it so but that's not actually correct. To specify a ffunction pointer returning a FILE pointer, you need FILE * (*fn)(blah, blah).
Similarly, the first argument of that function must be a const char *, a pointer in other words. You had it as simply const char.
You don't actually need that convoluted expression for setting the my_fopen pointer (casting, taking address of, de-referencing). You can just use the much simpler my_fopen = .... In fact, I think the casting may be actually what's preventing gcc from reporting an error in this case since it's assuming, if you cast, that you know what you're doing.
You probably should also check the return value of dlopen. I haven't done that in this code but, if you don't find (or can't load) the library for some reason, the line after that will probably cause you grief.
When I compile and run this program on Red Hat Enterprise Linux Workstation release 6.4 (Santiago), I get the output of Hello, Pax and the file xyzzy.txt is created.
And, just as an aside, there are other functions which may be used to gain access to the file-system, things like open, opendir, freopen, creat, mkfifo (I think).
Depending on your needs, you may have some extra work to do.
One thing you may want to consider is that ls may not even use fopen. It can actually be built with just opendir/readdir and stat.
So, let's use a program that we know calls fopen. Enter the following program qqtest.c:
#include <stdio.h>
int main (void) {
FILE *fh = fopen ("xyzzy.txt", "w");
fclose (fh);
return 0;
}
and compile it with gcc -o qqtest qqtest.c, then run it. You should see no output but the file xyzzy.txt should be created. Once you've confirmed that, delete the xyzzy.txt file, then enter the following program qq.c:
#include <stdio.h>
#include <dlfcn.h>
FILE* (*my_fopen)(const char *filename, const char* mode);
void* libc_handle;
void __attribute__ ((constructor)) init(void){
libc_handle = dlopen("libc.so.6", RTLD_LAZY);
my_fopen = dlsym(libc_handle,"fopen");
}
FILE* fopen(const char* filename, const char* mode){
printf("Hello, Pax\n");
return my_fopen(filename, mode);
}
Compile this with gcc -shared -o qq.so qq.c -ldl and then run your qqtest program (changing the shared object path to your own directory of course):
LD_PRELOAD=/home/pax/qq.so ./qqtest
This time, you should see the Hello, Pax string output before the xyzzy.txt file is created, proof that it's calling your wrapper function, which in turn calls the original fopen.
Now, that's all very well but, even once you get this bit working, you have to intercept quite a few different calls to ensure you catch all changes.
That's going to take you quite a while to get done and, as Chris Stratton points out in a comment, the Linux kernel already has the capability to report file-system changes to you.
If your goal is to just track file-system changes rather than educate yourself on how it could be done, look into inotify to see how to do this without having to re-invent the wheel.

Magic in C with malloc, fork and open

I have such a funny problem I thought I'd share with you.
I cornered it down to the most little program I could :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int cmd_left(char *name)
{
pid_t pid;
int f_d;
if ((pid = fork()) == -1)
{
perror("");
exit(1);
}
f_d = open(name);
printf("%d\n", f_d);
close(f_d);
}
int main(int ac, char **av, char **env)
{
char **dummy_env;
if (ac < 2)
return (0);
dummy_env = malloc(10);
cmd_left(av[1]);
}
Basically, if I remove the malloc, opening works just fine.
You just have to compile and give the program a (valid) file to see the magic.
open(2) takes at least two parameters. Since you are passing it only one argument, you are invoking Undefined Behavior. In this case, open() is just using some garbage as second argument.
You need #include <fcntl.h> to get a declaration for open() in scope, which would then tell you that you are not calling it with enough arguments:
int open(const char *filename, int flags, ...);
(The optional argument - singular - is the permissions for the file (mode_t perms) if you have O_CREAT amongst the options in the flags argument.)
The call to malloc() scribbles over enough stack to remove the zeroes on it initially, which leaves the 'extra arguments' to open() in a state where they are not zero and you run into problems.
Undefined behaviour - which you're invoking - can lead to any weird result.
Make sure you compile with at least 'gcc -Wall' and I recommend 'gcc -Wmissing-prototypes -Wstrict-prototypes -Wall -Wextra'.
The header file for open is missing and open expects at least a second parameter.
If you fix that it should be OK.

Wrapper routine for write() with unistd.h included results in error

I am writing a wrapper routine for write() to override the original system function and within it i need to execute another program through execve(); for which I include the header file unistd.h. I get the error conflicting types for 'write' /usr/include/unistd.h:363:16: note: previous declaration of 'write'was here. I would be very gratefull if someone could help me out as I need to call another program from inside the wrapper and also send arguments to it from inside the wrapper routine.
The GNU linker has a --wrap <symbol> option which allows you to do this sort of thing.
If you link with --wrap write, references to write will redirect to __wrap_write (which you implement), and references to __real_write will redirect to the original write (so you can call it from within your wrapper implementation).
Here's a sophisticated test application using write() - I'm doing the compilation and linking steps separately because I'll want to use hello.o again in a minute:
$ cat hello.c
#include <unistd.h>
int main(void)
{
write(0, "Hello, world!\n", 14);
return 0;
}
$ gcc -Wall -c hello.c
$ gcc -o test1 hello.o
$ ./test1
Hello, world!
$
Here's an implementation of __wrap_write(), which calls __real_write(). (Note that we want a prototype for __real_write to match the original. I've added a matching prototype explicitly, but another possible option is to #define write __real_write before #include <unistd.h>.)
$ cat wrapper.c
#include <unistd.h>
extern ssize_t __real_write(int fd, const void *buf, size_t n);
ssize_t __wrap_write(int fd, const void *buf, size_t n)
{
__real_write(fd, "[wrapped] ", 10);
return __real_write(fd, buf, n);
}
$ gcc -Wall -c wrapper.c
$
Now, link the hello.o we made earlier with wrapper.o, passing the appropriate flags to the linker. (We can pass arbitrary options through gcc to the linker using the slightly odd -Wl,option syntax.)
$ gcc -o test2 -Wl,--wrap -Wl,write hello.o wrapper.o
$ ./test2
[wrapped] Hello, world!
$
An alternative to using the GNU liner --wrap symbol option as suggested by Matthew Slattery would be to use dlsym() to obtain the address of the execve() symbol at runtime in order to avoid the compile-time issues with including unistd.h.
I suggest reading Jay Conrod's blog post entitled Tutorial: Function Interposition in Linux for additional information on replacing calls to functions in dynamic libraries with calls to your own wrapper functions.
The following example provides a write() wrapper function that calls the original write() before calling execve() and does not include unistd.h. It is important to note that you cannot directly call the original write() from the wrapper because it will be interpreted as a recursive call to the wrapper itself.
Code:
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
size_t write(int fd, const void *buf, size_t count)
{
static size_t (*write_func)(int, const void *, size_t) = NULL;
static int (*execve_func)(const char *, char *const[], char *const[]) = NULL;
/* arguments for execve() */
char *path = "/bin/echo";
char *argv[] = { path, "hello world", NULL };
char *envp[] = { NULL };
if (!write_func)
{
/* get reference to original (libc provided) write */
write_func = (size_t(*)(int, const void *, size_t)) dlsym(RTLD_NEXT, "write");
}
if (!execve_func)
{
/* get reference to execve */
execve_func = (int(*)(const char *, char *const[], char *const[])) dlsym(RTLD_NEXT, "execve");
}
/* call original write() */
write_func(fd, buf, count);
/* call execve() */
return execve_func(path, argv, envp);
}
int main(int argc, char *argv[])
{
int filedes = 1;
char buf[] = "write() called\n";
size_t nbyte = sizeof buf / sizeof buf[0];
write(filedes, buf, nbyte);
return 0;
}
Output:
$ gcc -Wall -Werror -ldl test.c -o test
$ ./test
write() called
hello world
$
Note: This code is provided as an example of what is possible. I would recommend following Jonathan Leffler's advice on code segregation in constructing the final implementation.
It is an utterly bad idea to try wrapping write() and use POSIX functions. If you chose to work in standard C, then you could wrap write() because it is not a name reserved to the standard. However, once you start using POSIX functions - and execve() is a POSIX function - then you are running into conflicts; POSIX reserves the name write().
If you want to try, you may get away with it if you segregate the code carefully. You have your write() wrapper in one source file which does not include <unistd.h> or use any functions not defined in the C standard for the headers you do include. You have your code that does the execve() in a second file that does include <unistd.h>. And you link those parts together with appropriate function calls.
If you are lucky, it will work as intended. If you aren't lucky, all hell will break loose. And note that your luck status might change on different machines depending on factors outside your control such as o/s updates (bug fixes) or upgrades. It is a very fragile design decision to wrap write().
Just making an illustration for Muggen's attention call (therefore community wiki):
You want to redefine write and call write from inside your redefinition. Something like
void write(int a) {
/* code code code */
write(42); /* ??? what `write`?
??? recursive `write`?
??? the other `write`? */
/* code code code */
}
Better think better about it :)
If you segregate your code appropriately as suggested by Jonathan Leffler, you should be able to avoid compile-time issues related to including unistd.h. The following code is provided as an example of such segregation.
Note that you cannot interpose internal library function calls, since these are resolved before runtime. For instance, if some function in libc calls write(), it will never call your wrapper function.
Code:
exec.c
#include <unistd.h>
inline int execve_func(const char *path, char *const argv[], char *const envp[])
{
return execve(path, argv, envp);
}
test.c
#include <stdio.h>
extern int execve_func(const char *, char *const[], char *const[]);
size_t write(int fd, const void *buf, size_t count)
{
/* arguments for execve() */
char *path = "/bin/echo";
char *argv[] = { path, "hello world", NULL };
char *envp[] = { NULL };
return execve_func(path, argv, envp);
}
int main(int argc, char *argv[])
{
int filedes = 1;
char buf[] = "dummy";
size_t nbyte = sizeof buf / sizeof buf[0];
write(filedes, buf, nbyte);
return 0;
}
Output:
$ gcc -Wall -Werror test.c exec.c -o test
$ ./test
hello world
$

Resources