源码下载地址:百度网盘,提取码:cat1 。
本文来讲解 ELF 里面的 符号段 symtab
,也可以叫 符号表,因为相对于 段表 来说,symtab
他是一个段。但是对于 各个符号项 来说,symtab
他是一个表。所以把他叫成 是段 或者是表都可以。
首先讲一下什么是符号,其实 符号 有时候也叫 token,或者 ID,是编译系统在做 词法分析的时候 生成的。一个函数,一个变量名,都是 符号,例如 main() 函数,sum 函数,array 数组,这些都是符号。还是以之前的 main 项目做讲解。用 xelfviewer
来分析,如下图:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-1-1024x359.png)
如上图所示,第 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 就行,如下:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-2-1024x483.png)
从上图可以看到,有 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 个段,如下图:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-3-1024x405.png)
从上图可以看到, array
符号所在的段是 .data
,这里讲一个扩展知识,全局变量跟静态变量一般都放在这个 .data
段里面,未初始化的会放到 .bss
段。
然后 array
符号 的 st_value
字段的值是 0x201010,这个是重点,如果我们想逆向修改 array 数组的内容,必须通过 st_value
找到这个数组在main文件中的存储位置。但是 这个 0x201010 肯定不是偏移位,整个 main 文件都没这么大,那 0x201010 是什么呢?
首先 array 符号的内容,是在 .data
段里面的。.data
里面可以有多个全局变量的内容。注意看 .data
段的 sh_addr
跟 sh_offset
字段。sh_addr
是 0x201000,sh_offset
是 0x1000,sh_offset
很明显就是 main 文件的偏移位。第一个全局变量的内容就在 main 文件的 第 0x1000 字节。所以 array 符号的位置 就是 st_value
减去 sh_addr
,就是array 符号相对于 入口的偏移位,也就是 0x10,所以 array 的内容在 main 文件的 0x1010 的位置。如下图:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-4.png)
从上图可以看到, array
数组的 元素,7,5,1 全都在这里。
这里讲个扩展知识,如果一个 符号不是数组,而是单个 int
,那 st_value
就是他实际的值,不是偏移位。
现在已经找到 array 的位置了,现在来做一个小小的逆向操作,先来执行一下 main 文件,命令如下:
./main
echo $?
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-5.png)
可以看到,main 运行之后,返回值是 13,现在 用 sublime 修改 main 的二进制文件,修改如下:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-6.png)
如上图所示,我把 5 改成了 8,在运行一次,效果如下:
![](https://www.xianwaizhiyin.net/wp-content/uploads/2022/04/byxt-2-7.png)
逆向成功。
这里声明一下,本文讲的是比较简单的逆向操作,通常逆向一个软件,都是看不到源代码,甚至有些软件,会把符号表删掉不让你分析。所以很多时候,逆向只能不断猜某个函数是干什么的,某个变量在哪里,不断缩小范围来猜。
但是删除符号表发布,他也有不方便的地方,出debug了,他也不好调试分析问题在哪里。
相关阅读:
2,《Linux 系统中编译、链接的基石 - ELF 文件:扒开它的层层外衣,从字节码的粒度来探索》
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1,QQ:2338195090。