大话 elf 格式! -- 共享elf 格式

魔棒生成三个文件的命令还copy 到这里!

$ echo 1234 |xxd -r -ps > 1.bin
$ objcopy -I binary -O elf64-x86-64 -B i386 1.bin 1.o
$ gcc -shared -o 1.so 1.o

shared object elf 格式研究.

共享对象是可以加载到内存中文件(so文件), 与可执行文件没什么差别.,其特点是更加突出了导入,导出的函数及变量.
编译的时候需要加入-fPIC 选项.

这个8bytes二进制文件1.bin一下暴涨到7828个字节.
共享对象又往这个结构中添加了什么内容?
让电脑执行程序为什么会添加这么多东西?
我们有用的8个byte 的信息被淹没在这8K 的数据中, 好在我们的数据特殊,找找还能看得到.
123456789abcdef0

把8K 的数据往上贴太占版面了, 阅读也不方便,所以此处忽略, 但研究方法还是类似.

$ objdump -s 1.so

有一个直观概念.
最好导出到文件, 用vim 加上标记来看.

$ readelf -S 1.so
There are 26 section headers, starting at offset 0x1140:
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.build-i NOTE             0000000000000190  00000190
       0000000000000024  0000000000000000   A       0     0     4

.... 26 个section.

第一个section, 文件偏移190,
我们知道, 文件头64byte, 那0x40-0x190内容是什么呢? 哦! 明白了,是程序头.
用readelf -l 1.so 可以看到. 6个segment,每个segment header entity size 56 bytes
先来段小插曲, 6个segment 实际可简化为2个加载段. 一个可读可执行段0-6bc,
一个可读写,e00,238size, 中间缝隙6bc-e00 用0填充, 为对齐的需要.

第一个加载段分析

第一个加载段包含了很多节区,如下.
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .eh_frame
它们的共性是只读, 占据section 的前半部.

读取note

$ readelf -n 1.so, 可导出.note.gnu.build-id

读取动态符号

$ readelf --dyn-sym 1.so
Symbol table '.dynsym' contains 15 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000570     0 SECTION LOCAL  DEFAULT    9 
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
     7: 0000000000000008     0 NOTYPE  GLOBAL DEFAULT  ABS _binary_1_bin_size
     8: 0000000000201038     0 NOTYPE  GLOBAL DEFAULT   20 _edata
     9: 0000000000201040     0 NOTYPE  GLOBAL DEFAULT   21 _end
    10: 0000000000201038     0 NOTYPE  GLOBAL DEFAULT   20 _binary_1_bin_end
    11: 0000000000201038     0 NOTYPE  GLOBAL DEFAULT   21 __bss_start
    12: 0000000000000570     0 FUNC    GLOBAL DEFAULT    9 _init
    13: 00000000000006a8     0 FUNC    GLOBAL DEFAULT   12 _fini
    14: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT   20 _binary_1_bin_start

包含了15个动态符号, 看来要与外部模块连接了.
除了提供bin 文件数据3个符号外, 还提供_init, _fini 全局函数功能,
_init(570) 在.init 节, _fini(6a8) 在.fini 节.
代码入口(5c0), 是.text起始位置,对应于一个函数deregister_tm_clones,
但这是个局部符号, strip 后就没有了. 代码部分见后分析.
并增加了_edata,_end __bss_start全局符号.

读取 .gnu.version, .gnu.version_r 信息,

这些是与ABI 相关的信息.

$ readelf -V 1.so
Version symbols section '.gnu.version' contains 15 entries:
 Addr: 0000000000000440  Offset: 0x000440  Link: 3 (.dynsym)
  000:   0 (*local*)       0 (*local*)       0 (*local*)       0 (*local*)    
  004:   0 (*local*)       0 (*local*)       2 (GLIBC_2.2.5)   1 (*global*)   
  008:   1 (*global*)      1 (*global*)      1 (*global*)      1 (*global*)   
  00c:   1 (*global*)      1 (*global*)      1 (*global*)   

Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x0000000000000460  Offset: 0x000460  Link: 4 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.2.5  Flags: none  Version: 2

读取1.so 的重定位信息.

共享对象的重定位信息是用来在模块间建立连接的, 是模块间的螺丝与螺母!

重定位的结构定义,带addend型.
typedef struct
{
  Elf64_Addr    r_offset;       /* Address */
  Elf64_Xword   r_info;         /* Relocation type and symbol index */
  Elf64_Sxword  r_addend;       /* Addend */
} Elf64_Rela;

offset 说明修改那里,
info 说明用那个符号,怎样修改,
addend是调整信息.

读取重定位内容

$ readelf -r 1.so
Relocation section '.rela.dyn' at offset 0x480 contains 8 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200e00  000000000008 R_X86_64_RELATIVE                    670
000000200e08  000000000008 R_X86_64_RELATIVE                    630
000000201028  000000000008 R_X86_64_RELATIVE                    201028
000000200fd8  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200fe0  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200fe8  000400000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200ff0  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8  000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0
这个so依赖于libc, 引入外部数据5个.外部数据地址在.got区中定义, 初始值是0, 由loader 填充正确值.

还有3个R_X86_64_RELATIVE, 绝对值630,670,201028, 这是无名符号,对应于.init_array, .fini_array .data 地址, 需要rebase 重定位
填充的内容:
670 是 <frame_dummy>入口
630 是<__do_global_dtors_aux>的入口
    这2个符号都是LOCAL, 会被strip 掉, 因而会成为无名入口.
201028 是 .data 的地址,代码会跳转或者引用到该地址. 具体要看反汇编!
Relocation section '.rela.plt' at offset 0x540 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000201018  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000201020  000600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0

引入外部函数2条R_X86_64_JUMP_SLOT,
外部函数地址在.got.plt 中,初始值一般是延迟加载对应的函数地址.

对照

$ objdump -R 1.so
1.so:     file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000200e00 R_X86_64_RELATIVE  *ABS*+0x0000000000000670
0000000000200e08 R_X86_64_RELATIVE  *ABS*+0x0000000000000630
0000000000201028 R_X86_64_RELATIVE  *ABS*+0x0000000000201028
0000000000200fd8 R_X86_64_GLOB_DAT  _ITM_deregisterTMCloneTable
0000000000200fe0 R_X86_64_GLOB_DAT  __gmon_start__
0000000000200fe8 R_X86_64_GLOB_DAT  _Jv_RegisterClasses
0000000000200ff0 R_X86_64_GLOB_DAT  _ITM_registerTMCloneTable
0000000000200ff8 R_X86_64_GLOB_DAT  __cxa_finalize
0000000000201018 R_X86_64_JUMP_SLOT  __gmon_start__
0000000000201020 R_X86_64_JUMP_SLOT  __cxa_finalize

反编译代码节

$objdump -d 1.so 反汇编观察,
反编译了.init, .plt, .text, .fini 节,
.init, .fini节是为全局初始化,反初始化而构造的,
.fini 目前为空,
.init 判别是否调用gmon_start, 它的判断依据是.got 中的一项 200fe0 待分析!!

.plt (过程连接表)是调用外部函数的轻巧代码,向.got.plt表地址进行跳转.
201008 储存有本模块module_ID, 201010是dynamic_resolved_name, 依次解析它的外部调用地址.
那么是谁会调用这两个外部函数? 又是谁会访问5个外部数据? 还有谁访问了这3个需要rebase 的本地指针.
这个就是反汇编了.

.text 反汇编代码阅读很过瘾, 让人云里雾里!!! 框架,
拿外部是数据做判断,调用外部的函数.非我所控.
代码就不贴了,可以结合IDA 观察!

.eh_frame 是异常处理框架节,目前是空,就不用关心了.

第二个加载段分析

第二个加载段可读写属性, 重定位目的地址就在这了. 包含如下节区.
01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss

.init_array, .fini_array, 这里有见定义,它们的地址加载时有rebase重定位, 系统框架会使用该地址.
;org 200E00h
.init_array:0000000000200E00 __frame_dummy_init_array_entry dq offset frame_dummy
;org 200E08h
.fini_array:0000000000200E08 __do_global_dtors_aux_fini_array_entry dq offset __do_global_dtors_aux
但未见使用.

.jcr 为空(与java 有关?),

.dynamic 是动态连接的表头

$ readelf -d 1.so
Dynamic section at offset 0xe18 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6] 依赖libc 库
 0x000000000000000c (INIT)               0x570          INIT 代码入口570
 0x000000000000000d (FINI)               0x6a8          FINI 代码入口6a8
 0x0000000000000019 (INIT_ARRAY)         0x200e00       array 是什么意思.
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200e08
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x1b8          符号HASH 表地址
 0x0000000000000005 (STRTAB)             0x368          字符串表地址
 0x0000000000000006 (SYMTAB)             0x200          符号表地址
 0x000000000000000a (STRSZ)              215 (bytes)    字符串表大小
 0x000000000000000b (SYMENT)             24 (bytes)     符号项大学
 0x0000000000000003 (PLTGOT)             0x201000       .got.plt 地址
 0x0000000000000002 (PLTRELSZ)           48 (bytes)     .rela.plt 的大小
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x540A         .rela.plt 的地址
 0x0000000000000007 (RELA)               0x480          .rela.dyn 的地址
 0x0000000000000008 (RELASZ)             192 (bytes)    .rela.dyn 的大小
 0x0000000000000009 (RELAENT)            24 (bytes)     .rel 项大小
 0x000000006ffffffe (VERNEED)            0x460          VERSION need 地址
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x440          VERSION Symbol 地址
 0x000000006ffffff9 (RELACOUNT)          3              不知道什么意思, 3个重定位? 哪三个? ABS 吗? 不懂!
 0x0000000000000000 (NULL)               0x0

.got 是全局偏移表.

.rel.dyn 将会把修改的全局变量地址放到这个表中.

.got.plt 是外部函数重定位表

.rel.plt 将会把修改的全局函数地址放到这个表中.

.data 是数据节,

但前面多了几个数据. 是一个rebase 地址.

.bss

框架会使用, 多看反编译代码.

.comment 节:

$ readelf -p .comment 1.so // 按字符串输出节内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值