I am trying to use sscanf()
from preload.so which is generated from preload.c.
To check my sscanf()
from preload.so is called, I added extra print statement: printf("test\n");
Is there something I am missing?
Files content mentioned below:
//preload.c
#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
//foo.c
#include <stdio.h>
int main(void)
{
int i;
sscanf("42", "%d", &i);
printf("%d\n", i);
return 0;
}
I am doing below steps:
# gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
# export LD_PRELOAD=$PWD/preload.so
# gcc foo.c -o foo
test
test
test
test
test
test
test
test
test
test
test
O/p I am getting:
# echo $LD_PRELOAD
/AMIT/sscanf_override/preload.so
# ./foo
42
# LD_PRELOAD=$PWD/preload.so ./foo
42
The expected output is:
$ gcc foo.c -o foo
$ LD_PRELOAD=$PWD/preload.so ./foo
test
42
Even ldd output is pointing to preload.so as below, still while execution its giving preference to sscanf()
of system and not the one from preload.so
root@***sscanf_override]# ldd foo
linux-vdso.so.1 (0x00007fff4a5e0000)
/AMIT/sscanf_override/preload.so (0x00007f1cf270a000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1cf2345000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f1cf2141000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1cf290c000)
[root@***sscanf_override]#
Tweak here is, if I remove -D_GNU_SOURCE=1
,it works, gcc -fPIC -shared preload.c -o preload.so -ldl
But I can not proceed ahead without defining GNU_SOURCE as its must for future use .
As suggested by @Rachid K, it worked when I redefined my preload.c as below:
#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
__attribute__((force_align_arg_pointer)) int __isoc99_sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
CodePudding user response:
Have a look at readelf -s foo
.
I think it is likely that there is no call to sscanf
in your executable. Assuming you are using GLIBC as libc, I suspect a call to __isoc99_sscanf
. This is a redirection which the library makes, apparently because its original sscanf
variant uses extensions conflicting with the standard, see this question.
If you look then at readelf -s preload.so
as well, it will probably show a definition for sscanf
.
The redirection happens via macro in stdio.h
which you include in both, but I suspect _GNU_SOURCE=1
disables the redirection, so that even though stdio.h
is included in preload.c
, it is not replacing sscanf
with __isoc99_sscanf
there.
In the compilation of foo
, you probably do not use -D_GNU_SOURCE=1
and therefore you are getting a mismatch between the symbol names.
Symbol interposition with LD_PRELOAD
is always a bit tricky. In addition to issues such as above, there are also many situations in which compilers will optimize out or transform standard library calls. For example printf
-> puts
if the format string doesn't use any formatting.
CodePudding user response:
sscanf()
may be a macro referencing an internal function. Have a look at <stdio.h>. For example, on my system, I have:
extern int __isoc99_sscanf (const char *__restrict __s,
const char *__restrict __format, ...) __THROW;
# define fscanf __isoc99_fscanf
# define scanf __isoc99_scanf
# define sscanf __isoc99_sscanf
Hence, sscanf()
is actually a macro referencing __isoc99_sscanf()
. So, if you redefine the latter, you get what you expect.
#include <stdarg.h>
#include <stdio.h>
//__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
int __isoc99_sscanf(const char *str, const char *format, ...)
{
int ret;
va_list ap;
va_start(ap, format);
printf("test\n");
ret = vsscanf(str, format, ap);
va_end(ap);
return ret;
}
After rebuild, you get:
$ gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
$ LD_PRELOAD=`pwd`/preload.so ./foo
test
42