《编译系统-自底向上研究方法》ELF符号段 - 弦外之音

/ 0评 / 0

源码下载地址:百度网盘,提取码:cat1 。

本文来讲解 ELF 里面的 符号段 symtab,也可以叫 符号表,因为相对于 段表 来说,symtab 他是一个段。但是对于 各个符号项 来说,symtab 他是一个表。所以把他叫成 是段 或者是表都可以。



首先讲一下什么是符号,其实 符号 有时候也叫 token,或者 ID,是编译系统在做 词法分析的时候 生成的。一个函数,一个变量名,都是 符号,例如 main() 函数,sum 函数,array 数组,这些都是符号。还是以之前的 main 项目做讲解。用 xelfviewer 来分析,如下图:

如上图所示,第 25 个段就是符号表,上图有几个重点,符号表是从 main 的第 0x1048 字节开始解析的,还有一个 sh_entsize 重点字段是 0x18 字节。符号表里面是有多个符号的,每个符号占 0x18 字节。符号其实也是有结构体定义的,代码如下,在 /usr/include/elf.h 文件里面:

typedef struct
{
  Elf64_Word    st_name;        /* Symbol name (string tbl index) */
  unsigned char st_info;        /* Symbol type and binding */
  unsigned char st_other;       /* Symbol visibility */
  Elf64_Section st_shndx;       /* Section index */
  Elf64_Addr    st_value;       /* Symbol value */
  Elf64_Xword   st_size;        /* Symbol size */
} Elf64_Sym;

Elf64_Sym 这个结构体就是 0x18 字节。现在就来看看 符号表里面都有哪些符号,点击 xelfviewer 软件左边 的 Symbol table 就行,如下:

从上图可以看到,有 array, sum 这些符号。上面这个表格就是用 Elf64_Sym 结构体解析处理的,所有字段都能对得上。本文只挑一些重点的字段来讲。

1,st_name,这个是名字,但是存的不是字符串,而是偏移值,这里偏移值不是从 shstrtab 段找了,而是从 strtab 段找。main 里面有两个字符串表。

2,st_size,这个字段代表符号有多大,array 是一个 int 数组,有3个元素,所以一共12字节,也就是 0xc。如果符号是一个函数,那这个 st_size 就是函数内的全部指令加一起一共有多大。例如 sum 函数是 0x45 字节大小。

3,st_shndx,看到 带 ndx 的东西,就要下意识想到,这是一个下标值,也就是符号所在的段。

4,st_value,符号的值。

现在就用 array 符号来讲解一下 st_shndx 字段的具体含义,首先 array 符号 的 st_shndx 是 0x16 (注意16进制的),也就是第 22 个段,如下图:

从上图可以看到, array 符号所在的段是 .data,这里讲一个扩展知识,全局变量跟静态变量一般都放在这个 .data 段里面,未初始化的会放到 .bss 段。

然后 array 符号 的 st_value 字段的值是 0x201010,这个是重点,如果我们想逆向修改 array 数组的内容,必须通过 st_value 找到这个数组在main文件中的存储位置。但是 这个 0x201010 肯定不是偏移位,整个 main 文件都没这么大,那 0x201010 是什么呢?

首先 array 符号的内容,是在 .data 段里面的。.data 里面可以有多个全局变量的内容。注意看 .data 段的 sh_addrsh_offset 字段。sh_addr 是 0x201000,sh_offset 是 0x1000,sh_offset 很明显就是 main 文件的偏移位。第一个全局变量的内容就在 main 文件的 第 0x1000 字节。所以 array 符号的位置 就是 st_value 减去 sh_addr ,就是array 符号相对于 入口的偏移位,也就是 0x10,所以 array 的内容在 main 文件的 0x1010 的位置。如下图:

从上图可以看到, array 数组的 元素,7,5,1 全都在这里。

这里讲个扩展知识,如果一个 符号不是数组,而是单个 int ,那 st_value 就是他实际的值,不是偏移位。


现在已经找到 array 的位置了,现在来做一个小小的逆向操作,先来执行一下 main 文件,命令如下:

./main
echo $?

可以看到,main 运行之后,返回值是 13,现在 用 sublime 修改 main 的二进制文件,修改如下:

如上图所示,我把 5 改成了 8,在运行一次,效果如下:

逆向成功。


这里声明一下,本文讲的是比较简单的逆向操作,通常逆向一个软件,都是看不到源代码,甚至有些软件,会把符号表删掉不让你分析。所以很多时候,逆向只能不断猜某个函数是干什么的,某个变量在哪里,不断缩小范围来猜。

但是删除符号表发布,他也有不方便的地方,出debug了,他也不好调试分析问题在哪里。


相关阅读:

1,《GCC 链接过程中的【重定位】过程分析》

2,《Linux 系统中编译、链接的基石 - ELF 文件:扒开它的层层外衣,从字节码的粒度来探索》


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1,QQ:2338195090。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注