我有以下代码,假设调用main函数两次。
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <limits.h>
#include <link.h>
#include <stdio.h>
#include <time.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
#include <stdlib.h>
#include <sys/mman.h>
extern char** environ;
static int (*main_orig)(
int,
char**,
char**
);
static int (*__libc_start_main_orig)(
int (*)(int, char**, char**),
int,
char**,
int (*)(int, char**, char**),
void (*)(void),
void (*)(void),
void*
);
static int (*init_orig)(
int,
char**,
char**
);
static void (*fini_orig)(void);
static void (*rtld_fini_orig)(void);
static void* stack_end_orig;
static char* libs[] = {
"libpthread-2.22.so",
NULL
};
static void my_dlopen(char* lib_name)
{
int ret;
struct timespec before;
struct timespec after;
long long dl_time;
void* handle = NULL;
(void) clock_gettime(CLOCK_MONOTONIC, &before);
handle = dlopen(lib_name, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
if (handle == NULL) {
fprintf(stdout, "%-35s :: dlopen failed with %s :: ", lib_name, dlerror());
}
else {
fprintf(stdout, "%-35s :: dlopen [OK] :: ", lib_name);
ret = dlclose(handle);
if (ret != 0) {
fprintf(stdout, "dlclose failed with %s :: ", dlerror());
}
else {
fprintf(stdout, "dlclose [OK] :: ");
}
}
(void) clock_gettime(CLOCK_MONOTONIC, &after);
dl_time = ((long long) (after.tv_sec - before.tv_sec)) * 1000000000ll +
((long long) (after.tv_nsec - before.tv_nsec));
fprintf(stdout, "%12lld us\n", dl_time / 1000ll);
}
static int my_main(int argc, char** argv, char** envp)
{
int i;
int ret;
fprintf(stdout, "--- Before main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);
for (i = 0; argv[i] != NULL; i++) {
fprintf(stdout, "argv[%2d] = %s\n", i, argv[i]);
}
for (i = 0; envp[i] != NULL; i++) {
fprintf(stdout, "envp[%2d] = %s\n", i, envp[i]);
}
ret = main_orig(argc, argv, envp);
fprintf(stdout, "--- After main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);
return ret;
}
int main_hook(int argc, char** argv, char** envp)
{
int i;
int ret;
struct timespec before;
struct timespec after;
long long dl_time;
void* dummy_buffer = NULL;
const size_t dummy_size = 256 * 1024 * 1024;
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
if (ret == -1) {
fprintf(stdout, "mlockall failed with errno = %d\n", errno);
}
else {
fprintf(stdout, "mlockall [OK]\n");
}
ret = mallopt(M_TRIM_THRESHOLD, -1);
if (ret == 1) {
fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [OK]\n");
}
else {
fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [!!]\n");
}
ret = mallopt(M_MMAP_MAX, 0);
if (ret == 1) {
fprintf(stdout, "mallopt(M_MMAP_MAX, 0) [OK]\n");
}
else {
fprintf(stdout, "mallopt(M_MMAP_MAX, 0) [!!]\n");
}
dummy_buffer = malloc(dummy_size);
if (dummy_buffer != NULL) {
fprintf(stdout, "dummy_buffer = %p :: %zu\n", dummy_buffer, dummy_size);
memset(dummy_buffer, 0x00, dummy_size);
free(dummy_buffer);
}
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
(void) clock_gettime(CLOCK_MONOTONIC, &before);
for (i = 0; libs[i] != NULL; i++) {
my_dlopen(libs[i]);
}
(void) clock_gettime(CLOCK_MONOTONIC, &after);
dl_time = ((long long) (after.tv_sec - before.tv_sec)) * 1000000000ll +
((long long) (after.tv_nsec - before.tv_nsec));
fprintf(stdout, "****************************************************\n");
fprintf(stdout, "Total dlopen time = %lld ms\n", dl_time / 1000000ll);
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
ret = 0;
ret += my_main(argc, argv, envp);
ret += my_main(argc, argv, envp);
return ret;
}
int __libc_start_main( int (*main)(int, char**, char**),
int argc,
char** argv,
int (*init)(int, char**, char**),
void (*fini)(void),
void (*rtld_fini)(void),
void* stack_end)
{
int i;
int ret;
for (i = 0; environ[i] != NULL; i++) {
char* substr = strstr(environ[i], "LD_PRELOAD=");
if (substr != NULL) {
fprintf(stdout, "%s found and replaced with ", environ[i]);
memset(&environ[i][0], 'x', strlen(environ[i]));
}
fprintf(stdout, "%s\n", environ[i]);
}
__libc_start_main_orig = dlsym(RTLD_NEXT, "__libc_start_main");
main_orig = main;
init_orig = init;
fini_orig = fini;
rtld_fini_orig = rtld_fini;
stack_end_orig = stack_end;
ret = __libc_start_main_orig(main_hook, argc, argv, init, fini, rtld_fini, stack_end);
return ret;
}
此代码编译为共享库,包含:
$CC -ggdb -fPIC -shared replace_main.c -o replace_main -ldl -lrt -pthread.
我像这样使用so文件:
LD_PRELOAD=/var/log/replace_main ls -l /
这给了我:
LD_PRELOAD=/var/log/replace_main found and replaced with xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TERM=xterm-256color
SHELL=/bin/sh
SSH_CLIENT=xxxxxxxxx
OLDPWD=/var/log
SSH_TTY=/dev/pts/0
USER=root
LD_LIBRARY_PATH=xxxxxxxxx
MAIL=/var/mail/root
PATH=xxxxxxxxx
LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
PWD=/var/log
EDITOR=vi
TZ=UTC
PS1=\u@\h:\w\$
SHLVL=1
HOME=/root
LOGNAME=root
SSH_CONNECTION=xxxxxxxxx
_=/bin/ls
****************************************************
mlockall [OK]
mallopt(M_TRIM_THRESHOLD, -1) [OK]
mallopt(M_MMAP_MAX, 0) [OK]
dummy_buffer = 0x5a4020 :: 268435456
****************************************************
libpthread-2.22.so :: dlopen [OK] :: dlclose [OK] :: 596 us
****************************************************
Total dlopen time = 0 ms
****************************************************
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u@\h:\w\$
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
total 19
drwxr-xr-x 2 root root 6176 Jul 17 2018 bin
drwxr-xr-x 2 root root 3 Jul 17 2018 boot
-rw-rw-r-- 1 sirpa tracing 2447 Jul 17 2018 cxp9025851_3.xml
drwxr-xr-x 8 root root 8300 Apr 24 11:48 dev
drwxr-xr-x 38 root root 1584 Jul 17 2018 etc
drwxr-xr-x 3 root root 28 Jul 17 2018 home
drwxr-xr-x 8 root root 5263 Jul 17 2018 lib
-rwxr-xr-x 1 root root 509 Apr 25 2018 linuxrc.sh
drwxr-xr-x 2 root root 3 Apr 22 2018 media
drwxr-xr-x 2 root root 3 Apr 22 2018 mnt
drwxr-xr-x 8 root root 115 Jul 17 2018 opt
dr-xr-xr-x 759 root root 0 Jan 1 1970 proc
drwxrwxrwx 28 sirpa users 4096 Apr 23 17:51 rcs
drwxr-xr-x 2 root root 3 Apr 22 2018 root
drwxrwxrwt 12 root root 540 Apr 24 11:49 run
drwxr-xr-x 2 root root 2868 Jul 17 2018 sbin
drwxr-xr-x 53 root root 12288 Apr 24 10:39 software
dr-xr-xr-x 12 root root 0 Apr 24 11:48 sys
drwxrwxrwt 10 root root 740 Apr 24 12:49 tmp
drwxr-xr-x 10 root root 150 Apr 22 2018 usr
drwxr-xr-x 13 root root 186 Jul 17 2018 var
--- After main --- 25870 :: 3265 :: 0x12068
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u@\h:\w\$
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
bin boot cxp9025851_3.xml dev etc home lib linuxrc.sh media mnt opt proc rcs root run sbin software sys tmp usr var
--- After main --- 25870 :: 3265 :: 0x12068
root@du1:/var/log#
正如您所看到的那样,ls的main被调用两次,但第二次似乎忽略了-l参数。原因是什么?
PS:我在内核4.1的ARM Linux变种上运行代码
谢谢,
原因是什么?
选项解析库具有内部状态,您不会重置该状态。
来自man 3 getopt
:
extern int optind, ...
The variable optind is the index of the next element to be processed in argv.
The system initializes this value to 1. The caller can reset it to 1 to restart
scanning of the same argv, or when scanning a new argument vector.
由于你没有将optind
中的/bin/ls
重置为1
,当真正的main
称为getopt
时,它会立即获得“不再选择”的答案。
这可以用一个简单的程序来证明:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int rc;
while ((rc = getopt(argc, argv, "abc")) != -1) {
printf("getopt: '%c'\n", rc);
}
return 0;
}
gcc main.c -o main
LD_PRELOAD=./replace_main.so ./main -ab
...
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a
请注意,第二次调用时缺少getopt
行(正如人们所期望的那样)。
如果我在optind = 1;
中的while
循环之前添加main.c
,那么它会按照您的预期开始工作:
--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca
--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca