zoukankan      html  css  js  c++  java
  • 目标文件函数隐藏初探

    目标文件函数隐藏初探

    场景如下,需要以.o形式(静态库形式),发布一个库,给其他代码集成。生成库mylib.o之后,使用nm查看,可以查看到很多函数符号。但其实这个库跟外界,应该是只通过一组指定的函数接口进行交互,其他的函数不应该暴露给外界,更不应该供外界直接调用。

    为此,可以进行一些处理。

    将函数标记为static

    一种可行的方式是,将内部使用的函数,源码中标记为static。

    但这么修改之后,库本身的其他源文件,也无法使用该函数了,因为c语言中的static是将函数的作用域限定在了函数所在的源文件。

    objcopy修改符号表

    生成库之后,可使用工具链中的 objcopy 工具,修改符号表,将内部函数都修改为本地函数,这样外部代码无法直接链接到这些函数,只能使用指定的函数。

    查看帮助可知,objcopy 支持将除 -G 参数指定的符号外,其他符号全部修改成本地符号。

    objcopy --help
    -G --keep-global-symbol <name>   Localize all symbols except <name>
    

    于是使用如下命令,

    mv mylib.o mylib_origin.o
    objcopy -G api_1 -G api_2  mylib_origin.o  mylib.o
    

    strip删减符号表

    生成库之后,可使用工具链中的 strip 工具,裁剪符号表,将不打算给外界使用的函数,直接从符号表中删除。

    查看帮助可知,strip可用 -s 参数删除所有符号,使用 -K 参数指定要保留的符号,使用 -N 指定要strip掉的符号。

    strip --help
    -s --strip-all                   Remove all symbol and relocation information
    -N --strip-symbol=<name>         Do not copy symbol <name>
    -K --keep-symbol=<name>          Do not strip symbol <name>
         --keep-file-symbols           Do not strip file symbol(s)
    
    

    于是使用如下命令,可删除所有符号,只保留api_1和api_2

    cp mylib.o mylib_origin.o
    strip -s -K api_1 -K api_2 mylib.o 
    

    使用如下命令,则是只删除inner_fun1和inner_fun2

    cp mylib.o mylib_origin.o
    strip -N inner_fun1 -N inner_fun2 mylib.o
    

    例子

    假设库mylib.c 中有四个函数,inner_fun1,inner_fun2是内部使用的函数,api_1,api_2时给外部使用的接口。

    #include <stdio.h>
    
    void inner_fun1() { printf("inner 1
    "); }
    void inner_fun2() { printf("inner 2
    "); }
    void api_1() { printf("api 1
    "); }
    void api_2() { printf("api 2
    "); };
    

    编译生产目标文件

    gcc -o mylib.o -c mylib.c
    

    查看符号表

    nm mylib.o
    
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000000 T inner_fun1
    0000000000000013 T inner_fun2
                     U puts
    

    四个函数都可以看到,且都是 T ,即可被外部链接。

    注:对于每一个符号来说,其类型如果是小写的,则表明该符号是 local 的;大写则表明该符号是 global(external) 的。

    写个main.c链接下试试

    int main()
    {
    	api_1();
    	api_2();
    	inner_fun1();
    	inner_fun2();
    	return 0;
    }
    

    编译链接

    gcc main.c mylib.o -o main
    

    执行main,可以看到成功调用了api,也成功调用了inner的函数。

    ./main 
    
    api 1
    api 2
    inner 1
    inner 2
    

    使用static的效果

    那么先试试 static 定义,将mylib.c中的inner函数加上static

    #include <stdio.h>
    
    static void inner_fun1() { printf("inner 1
    "); }
    static void inner_fun2() { printf("inner 2
    "); }
    void api_1() { printf("api 1
    "); }
    void api_2() { printf("api 2
    "); };
    

    重新生成库,再查看符号表

    nm mylib.o
    
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000000 t inner_fun1
    0000000000000013 t inner_fun2
                     U puts
    

    可以看到,inner_fun1和inner_fun2的标记,已经不是 T,而是 t 了。

    此时,外部函数尝试链接使用,会报错

    gcc main.c mylib.o -o main
    
    /tmp/cccUN3aL.o:在函数‘main’中:
    main.c:(.text+0x1e):对‘inner_fun1’未定义的引用
    main.c:(.text+0x28):对‘inner_fun2’未定义的引用
    collect2: error: ld returned 1 exit status
    
    

    使用objcopy的效果

    不修改源文件,直接使用objcopy修改mylib.o

    mv mylib.o mylib_origin.o
    objcopy -G api_1 -G api_2  mylib_origin.o  mylib.o
    

    修改前后

    nm mylib_origin.o mylib.o
    
    mylib_origin.o:
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000000 T inner_fun1
    0000000000000013 T inner_fun2
                     U puts
                     
    mylib.o:
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000000 t inner_fun1
    0000000000000013 t inner_fun2
                     U puts
    
    

    此时,main.c 就只能使用api_1, api_2,无法使用inner_fun1, inner_fun2了。因为inner_fun1, inner_fun2是内部符号了。

    使用strip的效果

    不修改源文件,直接使用strip修改mylib.o

    cp mylib.o mylib_origin.o
    strip -N inner_fun1 -N inner_fun2 mylib.o
    

    修改前后

    nm mylib_origin.o mylib.o 
    
    mylib_origin.o:
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000000 T inner_fun1
    0000000000000013 T inner_fun2
                     U puts
    
    mylib.o:
    0000000000000026 T api_1
    0000000000000039 T api_2
                     U _GLOBAL_OFFSET_TABLE_
                     U puts
    

    此时,main.c 就只能使用api_1, api_2,无法使用inner_fun1, inner_fun2了。因为inner_fun1, inner_fun2不存在符号表中了。

    结语

    本文主要介绍了,static标记函数,objcopy和strip三种方式,避免库内部函数被外部程序使用。但即使strip删除了符号表,也还是可以从二进制文件中分析到内外部函数名称的。所以如果想隐藏内部函数名称,以避免暴露内部逻辑,那就还需要使用一些其他的手段。

  • 相关阅读:
    纯手写F3飞控的直升机固件(2.直升机倾斜盘混控了解)
    STM32输出PWM
    使用多个交叉编译器
    内核编译报错
    mdm9607平台2.2版本 编译指令
    linux 应用编程APIS
    linux 内核API总结
    Do away with the notion of hardsect_size
    大端 小端和网络字节序说明
    TI tlv320aic3104 codec调试之路径控制
  • 原文地址:https://www.cnblogs.com/zqb-all/p/9822108.html
Copyright © 2011-2022 走看看