有时候会遇到这样的需求,不希望命令行的某些参数被ps出来,比如命令行参数里可能存在一些用户名和密码之类的东西,在Linux下如果你想隐藏这些东西的话,你该如何操作?
如果一个程序的命令行是一个password之类的不便展示的字符串,如何不让ps打印出来呢?
ps是从/proc/$pid/cmdline里拿的命令行,而/proc/$pid/cmdline则是在内核空间解析用户程序的stack区域获取的数据,那么答案很简单,只需要覆盖掉这个区域即可,下面是个示例:
# include
intmain( intargc, char**argv) {charorig[ 16];
// 获取stack上的命令行strcpy(orig, argv[ 1]); // 获取命令行之后第一时间覆盖stack上的命令行strcpy(argv[ 1], "skinshoe"); getchar;}
如果应用程序不可修改代码重新编译,有没有什么统一的办法呢?当然有,用LD_PRELOAD很方便:
// inject.c# define_GNU_SOURCE # include
int(*_main) ( int, char* *, char* *); staticintpixie_main( intargc, char**argv, char**env) {chartmp[ 16]; strcpy(tmp, argv[ 1]); strncpy(argv[ 1], "pixie", strlen(argv[ 1])); argv[ 1] = tmp; return_main(argc, argv, env); }
int(*orig_start_main)( int(*main)( int, char**, char**), intargc, char**argv, void(*init) ( void), void(*fini) ( void), void(*_fini) ( void), void(*stack_end));
int__libc_start_main( int(*main)( int, char**, char**), intargc, char**argv, void(*init)( void), void(*fini)( void), void(*_fini)( void), void(*stack_end)) {orig_start_main = dlsym(RTLD_NEXT, "__libc_start_main"); _main = main;returnorig_start_main(pixie_main, argc, argv, init, fini, _fini, stack_end); }
编译之:
gcc-O2-fPIC-shared-olibinject.soinject.c-ldl
下面是一个“不能改的现有程序”:
// demo.c# include
intmain( intargc, char**argv) {printf( "%s\n", argv[ 1]); getchar;}
用LD_PRELOAD执行之:
LD_PRELOAD=./libinject.so ./demo 12345
此时,demo程序打印的依然是12345,然后ps看到的就是pixie了。
把LD_PRELOAD部署到路径里就好了。这是劫库的标准手法。
事事都是双刃剑,能做好事就能做坏事,用上面的把戏其实是可以任意修改任意程序的命令行的:
int(*_main) ( int, char* *, char* *); staticintpixie_main( intargc, char**argv, char**env ) {argv[ 1] = "pixie"; return_main(argc, argv, env); }
试试看:
root@zhaoya-VirtualBox:~ # LD_PRELOAD=./libinject.so ls -als: cannot access 'pixie': No such file ordirectory root@zhaoya-VirtualBox:~ # LD_PRELOAD=./libinject.so /bin/echo hellopixie