前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >目标文件函数隐藏初探

目标文件函数隐藏初探

作者头像
zqb_all
发布2019-12-27 10:43:59
1.1K0
发布2019-12-27 10:43:59
举报
文章被收录于专栏:QB杂货铺QB杂货铺

目标文件函数隐藏初探

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

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

将函数标记为static

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

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

objcopy修改符号表

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

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

代码语言:javascript
复制
objcopy --help
-G --keep-global-symbol <name>   Localize all symbols except <name>

于是使用如下命令,

代码语言:javascript
复制
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掉的符号。

代码语言:javascript
复制
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

代码语言:javascript
复制
cp mylib.o mylib_origin.o
strip -s -K api_1 -K api_2 mylib.o 

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

代码语言:javascript
复制
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时给外部使用的接口。

代码语言:javascript
复制
#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"); };

编译生产目标文件

代码语言:javascript
复制
gcc -o mylib.o -c mylib.c

查看符号表

代码语言:javascript
复制
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链接下试试

代码语言:javascript
复制
int main()
{
    api_1();
    api_2();
    inner_fun1();
    inner_fun2();
    return 0;
}

编译链接

代码语言:javascript
复制
gcc main.c mylib.o -o main

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

代码语言:javascript
复制
./main 

api 1
api 2
inner 1
inner 2

使用static的效果

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

代码语言:javascript
复制
#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"); };

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

代码语言:javascript
复制
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 了。

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

代码语言:javascript
复制
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

代码语言:javascript
复制
mv mylib.o mylib_origin.o
objcopy -G api_1 -G api_2  mylib_origin.o  mylib.o

修改前后

代码语言:javascript
复制
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

代码语言:javascript
复制
cp mylib.o mylib_origin.o
strip -N inner_fun1 -N inner_fun2 mylib.o

修改前后

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-10-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目标文件函数隐藏初探
    • 将函数标记为static
      • objcopy修改符号表
        • strip删减符号表
          • 例子
            • 使用static的效果
            • 使用objcopy的效果
            • 使用strip的效果
          • 结语
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档