想象一下,你有一些代码,编译它,并删除所有符号。如果你在它上运行gdb,在任意点停止它,并回溯它,你仍然会得到有意义的信息
int baz() {
sleep(10);
return 0;
}
int bar() {
return baz();
}
int foo() {
return bar();
}
int main() {
return foo();
}
$ gcc -fomit-frame-pointer test.c
$ strip -x a.out
$ gdb ./a.out
#0 0x0000003a4989a470 in __nanosleep_nocancel () from /lib64/libc.so.6
#1 0x0000003a4989a2c4 in sleep () from /lib64/libc.so.6
#2 0x00000000004004b7 in baz ()
#3 0x00000000004004cf in bar ()
#4 0x00000000004004e2 in foo ()
#5 0x00000000004004f5 in main ()
假设您想阻止用户从baz到main查看(或混淆)。可能吗?欢迎硬核方法。
答案 0 :(得分:4)
如果您希望函数能够返回,则无法阻止堆栈跟踪。
如果不需要返回,则覆盖返回地址和帧指针将阻止堆栈跟踪
int bar() {
int nptrs; void * buffer[100];
nptrs = backtrace(buffer, 100);
backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO);
// this is machine and compiler specific
// the following works for most i386 compilant compilers that create frame pointers
void ** stack_base_pointer;
__asm__ ("mov %%rbp, %0" : "=g" (stack_base_pointer));
// we will save the original values
void * return_address = stack_base_pointer[1];
void * frame_pointer = stack_base_pointer[0];
// overwrite them
stack_base_pointer[1] = NULL;
stack_base_pointer[0] = NULL;
// this function can not return now
printf("return_address = %p\nframe pointer = %p\n", return_address, frame_pointer);
puts("Removed return address and frame pointer");
nptrs = backtrace(buffer, 100);
backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO);
// we need to restore the return address in order to be able to return
stack_base_pointer[1] = return_address;
return 0;
}
int foo() {
puts("calling bar");
bar();
puts("successfully returned from bar");
return 0;
}
int main() {
foo();
return 0;
}
但请注意,这将特定于您正在编译的编译器和目标系统。
在使用Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
:
$ ./a.out
calling bar
0 a.out 0x000000010e23cd7f bar + 31
1 a.out 0x000000010e23ce48 foo + 26
2 a.out 0x000000010e23ce69 main + 14
3 libdyld.dylib 0x00007fff905a35fd start + 1
return_address = 0x10e23ce48
frame pointer = 0x7fff519c3b90
Removed return address and frame pointer
0 a.out 0x000000010e23cdff bar + 159
successfully returned from bar
答案 1 :(得分:2)
正如M. Waltz在评论中已经说过的那样。
从条形手册页:
-x --discard-all Remove non-global symbols.
产生的回溯:__ nanosleep_nocancel,__sleep,baz,bar,foo,main
-s
--strip-all
Remove all symbols.
产生的回溯:__ nanosleep_nocancel,_ _ snle,?? (),?? (),?? (),?? ()
你真的想使用-s,而不是-x。 -x仅删除非全局符号,对于C(例如),这意味着所有非静态函数仍将在符号表中具有条目,并且在回溯中可见(您可以使用readelf -a a.out进行验证)。
如果你还想隐藏回溯的地址,其中一些将通过使用更高级别的优化来完成,因为函数被内联等等。对于“真正的硬核”,你可以把编译器和ABI搞得一团糟,所以通用工具无法读取,但这可能太过分了。
答案 2 :(得分:1)
假设您想阻止用户从baz到main查看(或混淆)。可能吗?欢迎硬核方法。
在Linux上似乎可以隐藏你的一些功能。使用共享库并移动除main之外的所有函数。隐藏您不想被人看到的功能:
>more mylib.c
__attribute__ ((visibility ("hidden"))) int baz() {
sleep(10);
return 0;
}
__attribute__ ((visibility ("hidden"))) int bar() {
return baz();
}
int foo() {
return bar();
}
Build&条:
gcc -fomit-frame-pointer -fno-inline -fpic -shared mylib.c -o libmylib.so
strip -x libmylib.so
并运行:
>gdb -q a.out
Reading symbols from /home/a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/a.out
^C
Program received signal SIGINT, Interrupt.
0x0000003c412ab900 in __nanosleep_nocancel () from /lib64/libc.so.6
(gdb) bt
#0 0x0000003c412ab900 in __nanosleep_nocancel () from /lib64/libc.so.6
#1 0x0000003c412ab790 in sleep () from /lib64/libc.so.6
#2 0x00007ffff7dfc537 in ?? () from libmylib.so
#3 0x00007ffff7dfc54f in ?? () from libmylib.so
#4 0x00007ffff7dfc562 in foo () from libmylib.so
#5 0x00000000004005ba in main ()
(gdb)