专栏首页QB杂货铺目标文件函数隐藏初探

目标文件函数隐藏初探

目标文件函数隐藏初探

场景如下,需要以.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\n"); }
void inner_fun2() { printf("inner 2\n"); }
void api_1() { printf("api 1\n"); }
void api_2() { printf("api 2\n"); };

编译生产目标文件

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\n"); }
static void inner_fun2() { printf("inner 2\n"); }
void api_1() { printf("api 1\n"); }
void api_2() { printf("api 2\n"); };

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

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删除了符号表,也还是可以从二进制文件中分析到内外部函数名称的。所以如果想隐藏内部函数名称,以避免暴露内部逻辑,那就还需要使用一些其他的手段。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ubuntu14.04编译gnu global 6.6.3

    打算重新折腾下环境,看中了gtags ,可参考 Vim 8 中 C/C++ 符号索引:GTags 篇 ,先记录下编译过程

    zqb_all
  • vim的配置与使用

    经历了一次source insight 一言不合就崩溃之后,决定还是花点时间好好配置和学习以下vim

    zqb_all
  • 【swupdate文档 四】SWUpdate:使用默认解析器的语法和标记

    SWUpdate使用库“libconfig”作为镜像描述的默认解析器。 但是,可以扩展SWUpdate并添加一个自己的解析器, 以支持不同于libconfi...

    zqb_all
  • 爬虫之ssh证书警告错误

    ssh证书是美国网景公司发放的一个安全认证证书,有了这个证书即可证明网站是安全的,但是认证是需要收费的,

    小小咸鱼YwY
  • IIS4\\IIS5 CGI环境块伪造0day漏洞

    大约14年前发现一直到现在的0day 是IIS4\IIS5的漏洞,对应操作系统是winnt和win2000系统,微软不再支持这些软件,他们的策略想淘汰这些系统,...

    FB客服
  • IIS4\\IIS5 CGI环境块伪造0day漏洞

    大约14年前发现一直到现在的0day 是IIS4\IIS5的漏洞,对应操作系统是winnt和win2000系统,微软不再支持这些软件,他们的策略想淘汰这些系统,...

    安恒信息
  • python智能图片识别系统(图片切割、图片识别、区别标识)

    你好! python flask图片识别系统使用到的技术有:图片背景切割、图片格式转换(pdf转png)、图片模板匹配、图片区别标识。

    用户6334815
  • 2020护网期间公布漏洞总结-附部分漏洞Poc,Exp

    4.Apache DolphinScheduler远程代码执行漏洞(CVE-2020-11974),危害级别:危急,官方已发布补丁

    Gamma实验室
  • 前端基础-JavaScript构造函数

    JavaScript 语言使用构造函数作为对象的模板。 所谓 ”构造函数”,就是一个普通的函数,只不过我们专门用它来生成对象(new 构造函数),这样使用的函...

    cwl_java
  • C#基础 1(异同与区别及其特点)

    一.值类型与引用类型的主要区别   1.值类型分配在栈上,引用类型分配在堆上   2.值类型继承自ValueType,引用类型不会继承自ValueType   ...

    房上的猫

扫码关注云+社区

领取腾讯云代金券