I tried to use LD_PRELOAD to hook sprintf function , so I will print to file the result of buffer:
#define _GNU_SOURCE
#include <stdio.h>
#include<dlfcn.h>
int sprintf (char * src , const char * format , char* argp)
{
int (*original_func)(char*,const char * , char*);
original_func = dlsym(RTLD_NEXT,"sprintf");
int ret = (*original_func)(src ,format,argp);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
When I compile this code gcc -Wall -fPIC -shared -o my_lib.so test_ld.c -ldl
I got error
test_ld.c:5:5: error: conflicting types for ‘sprintf’
int sprintf (char * src , const char * format , char* argp)
^
In file included from test_ld.c:2:0:
/usr/include/stdio.h:364:12: note: previous declaration of ‘sprintf’ was here
extern int sprintf (char *__restrict __s,
How can I fix that?
The main problem you're having is that your prototype for sprintf doesn't match the official one. Your function has this signature:
int sprintf (char * src , const char * format , char* argp);
While the official one has:
int sprintf(char *str, const char *format, ...);
You'll need to change your function to have this signature. Once you do that, you'll need to use a va_list to get the variadic argument. Then you would use that to call vsprintf which takes an argument of this type instead of using dlsym to load sprintf.
#include <stdio.h>
#include <stdarg.h>
int sprintf (char * src , const char * format , ...)
{
va_list args;
va_start(args, format);
int ret = vsprintf(src, format, args);
va_end(args);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
Alex's first solution nicely addresses one problem: the conflicting declarations of sprintf (although there is no reason to not use the same signature as in stdio.h, see dbush's answer) . However, even then there remains one large elephant in the room: sprintf is a variadic function.
That means that, whenever the wrapped program calls sprintf with anything other than one char * third argument, your output may not be correct (and may even depend on your compiler's -O level)
Calling variadic functions from variadic functions (what is essentially what you're doing here) is a known problem. Any solution will be non-portable. With gcc, you can use __buitlin_apply and tap into gccs own private way of handling argument lists:
/* sprintf.c, compile with gcc -Wall -fPIC -shared -o sprintf.so sprintf.c -ldl
and use with LD_PRELOAD=./sprintf.so <program> */
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
# define ENOUGH 100 /* how many bytes of our call stack
to pass to the original function */
int sprintf (char *src) /* only needs the first argument */
{
void *original_func = dlsym(RTLD_NEXT,"sprintf");
void *arg = __builtin_apply_args();
void *ret = __builtin_apply((void *)original_func, arg, ENOUGH);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
__builtin_return(ret);
}
A few remarks:
In well-designed libraries variadic functions will have a non-variadic counterpart that uses one va_list argument instead of a variable number of arguments. In that case (and sprintf - vsprintf is such a case) you can use Alex's (portable) second solution with the va_* macros. If not, the solution with __builtin_apply() is the only possible, albeit gcc-specific, one.
See also: call printf using va_list
Possibly depending on the compiler version, when compiling main.c with the -O2 flag, main() will actually call __sprintf_chk() instead of sprintf() (regardless of -fno-builtin) and the wrapper won't work. To demonstrate the wrapper, compile main.c with -O0. Changing the main program to get the wrapper to work is the tail wagging the dog, of course. This shows the fragility of building wrappers: programs often don't call the library functions you expect. A ltrace <program> beforehand can save a lot of work....
You can rename the symbol in stdio, but then there's another problem, compilers like gcc use built-in implementations, unless you pass a flag like -fno-builtin, the compiler will generate the code inline in the executable, it won't link any library for functions like sprintf.
sprintf.c:
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
int sprintf (char * src , const char * format , char* argp)
{
int (*original_func)(char*,const char * , char*);
original_func = dlsym(RTLD_NEXT,"sprintf");
int ret = (*original_func)(src ,format,argp);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
main.c:
#include <stdio.h>
int main(int argc, char *argv[]) {
char buffer[80];
sprintf(buffer, "hello world");
puts(buffer);
return 0;
}
Makefile:
all: libsprintf.so main
main: main.c
gcc -Wall -O2 -fno-builtin -o main main.c
libsprintf.so: sprintf.c
gcc -Wall -O2 -shared -fPIC -fno-builtin -o libsprintf.so sprintf.c -ldl
.PHONY: clean
clean:
-rm -f main libsprintf.so
usage:
make
LD_PRELOAD=./libsprintf.so ./main
edit
sprintf.c with variadic implementation (it doesn't call sprintf):
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
#include <stdarg.h>
int sprintf (char * src , const char * fmt , ...)
{
va_list args;
va_start(args, fmt);
int ret = vsprintf(src, fmt, args);
va_end(args);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n", src);
fclose(output);
return ret;
}
Say I have a library I am making and I want to call a function rename or puts, how can I keep the original rename and puts from stdlib or stdio or whatever, and yet have my own function be puts?
#include <stdio.h>
alias puts original_puts;
void
puts(char *c) {
original_puts(c);
}
How can I accomplish something to this effect?
You can't alias library functions, but you can alias your own using preprocessor directives.
For example:
mylib.h:
#include <stdio.h>
void my_puts(char *c);
#define puts(arg) my_puts(arg)
mylib.c:
void my_puts(char *c)
{
(puts)(c);
}
Now, anytime someone calls puts, it substitutes a call to my_puts instead. Also, when you want to call the "real" function in your wrapper, you can put the function name in quotes. Because the macro that does the substitution is a function-like macro, the parenthesis prevent the substitution from happening.
If compiling with gcc or clang, you can wrap the symbol with -Wl,--wrap:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int __real_puts(const char *c);
int __wrap_puts(const char *c) {
__real_puts("Hello");
__real_puts(c);
return 0;
}
int main(int argc, char const *argv[]) {
puts("world");
}
$ gcc src.c -Wl,--wrap=puts && ./a.out
Hello
world
I wrote two little programs in C, crypt and decrypt.
I can call from the terminal:
./crypt some_argument it works.
But I want to pass to decrypt the output of crypt.
I already tried:
./decrypt $(./crypt hello) does not work
./crypt hello | ./decrypt does not work
Source code:
Crypt:
#include <stdio.h>
#include <stdlib.h>
#define CRYPT_KEY 5
char *crypto(char *str) {
char *tmp_adr = str;
while (*str != 0) {
*str += CRYPT_KEY;
str++;
}
return tmp_adr;
}
int main(int argc, char *argv[]) {
printf("Crypted message: %s\n", crypto(argv[1]));
}
Decypt is the same, but with a minus here:
*str -= CRYPT_KEY;
Someone know how to do that?
The output contains spaces. You need to quote it if you want everything in argv[1].
i.e. ./decrypt "$(./crypt some_argument)"
I was playing around with symbols and function pointers recently and noticed that though the following code runs fine:
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("%p\n",printf); // <--this line makes it work
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
This does not:
#include <stdio.h>
int main(int argc, const char * argv[]) {
// printf("%p\n",printf); // <-- commenting this out breaks it
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
(EXC_BAD_ACCESS)
How come dereferencing the exact same pointer causes issues when there is no reference to printf in the code? Even this works fine:
#include <stdio.h>
int main(int argc, const char * argv[]) {
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
void *_ = printf; // <-- because of this
Why is this?
On shared objects (.so) the symbols are really resolved only at the moment of first use. By default the linker sets the option -z lazy which tells:
When generating an executable or shared library, mark it to
tell the dynamic linker to defer function call resolution to
the point when the function is called (lazy binding), rather
than at load time. Lazy binding is the default.
You can change that behaviour by providing option -z now.
man ld for all gory details.
EDIT: Resolving a symbol is done with dynamic link API on POSIX systems. Functions dlsym(), dlopen(), dlclose() and dlerror() defined in <dlfcn.h>. This edition added so that you can search for these names.
Is there a way to enumerate environment variables and retrieve values using C?
Take a look at the environ global variable.
extern char **environ;
It might be defined in unistd.h (take a look at the environ (5) man page above).
Here's a little code demo I wrote:
#include <stdio.h>
extern char **environ;
int main()
{
for (char **env = environ; *env; ++env)
printf("%s\n", *env);
}
Here's how to use it:
matt#stanley:~/Desktop$ make enumenv CFLAGS=-std=c99
cc -std=c99 enumenv.c -o enumenv
matt#stanley:~/Desktop$ ./enumenv
ORBIT_SOCKETDIR=/tmp/orbit-matt
SSH_AGENT_PID=1474
TERM=xterm
SHELL=/bin/bash
... (so forth)
The environment information can be passed as an extra parameter to main. I don't know if it is compliant or not, but it definitely works (tested on Ubuntu). Just define the extra argument and its an array of char pointers terminated by a NULL pointer. The following will print out the lot.
#include <stdio>
int main(int argc, char *argv[], char *envp[])
{
int index = 0;
while (envp[index])
printf("%s\n", envp[index++];
}
There is a demo in the book "The Linux Programming Interface" at page 127.
Listing 6-3: Displaying the process environment
––––––––––––––––––––––––––––––––––––––––––––––––proc/display_env.c
#include "tlpi_hdr.h"
extern char **environ;
int
main(int argc, char *argv[])
{
char **ep;
for (ep = environ; *ep != NULL; ep++)
puts(*ep);
exit(EXIT_SUCCESS);
}