ELF动态解析符号过程

上一篇 / 下一篇  2008-01-30 15:03:12 / 个人分类:Linux下C语言

★★ 前言Linux宝库Q}b.{9h.L3D0pU p

本篇文章以linux为平台为例,演示ELF动态解析符号的过程。
t(re'D!_.O0不正之处,还请斧正。Linux宝库[S1`]Z

Linux宝库c0i*K%{ ujT.r

通常,ELF解析符号方式称为lazy MODE装载的。这种装载技术是ELF平台上
#Fod'krM0默认的方式。在不同的体系平台在实现这种机制也是不同的。但是i386和SPARC
8P5Yiz#o7OSk9V0在大部分上是相同的。Linux宝库t3|DA%B(HK A

Linux宝库'Kag5vo `)yE!o1T C

动态连接器(rtld)提供符号的动态连接,装载共享objects和解析标号的引用。
&pe+S mt zP R0通常是ld.so,它可以是一个共享object也可以是个可执行的文件。Linux宝库f"k*nM!B^

v}A;zjy4j`T|0
-lfx"{k8]cXx0★★ 符号表(symbol table)Linux宝库"{9`&|(Lt

Linux宝库.Oj FHr|*C^LV

每个object要想使它对其他的ELF文件可用,就要用到符号表(symbol table)中
2G5l Hr;r`0symbol entry.事实上,一个symbol entry 是个symbol结构,它描述了这个Linux宝库U^8e7OMR
symbol的名字和该symbol的value.symbol name被编码作为dynamic stringLinux宝库1]S9j'})g#XPv[
table的索引(index). The value of a symbol是在ELF OBJECT文件内该Linux宝库 |ux3PX.MT B
symbol的地址。该地址通常需要被重新定位(加上该object装载到内存的基地址
1y%RmlQk0^)~0(base load address)). 从而构成该symbol在内存中的绝对地址。
Q,B4W A&I0一个符号表入口有如下的格式:
gD+M5V#R'c0 typedef struct
a}dC`1?Mw/F+u7X0{
k9_+x\-O1z0  Elf32_Word    st_name;   /* Symbol name (string tbl index) */Linux宝库*x+i4y6y!kt2pj
  Elf32_Addr    st_value;  /* Symbol value */Linux宝库OW l(hzu)N$W
  Elf32_Word    st_size;   /* Symbol size */
I'q!c+U A7k0  unsigned char st_info;   /* Symbol type and binding */Linux宝库z-h1?%DYS!`a(lj%~
  unsigned char st_other;  /* No defined meaning, 0 */
+}%p*o:{T2Y0  Elf32_Section st_shndx;  /* Section index */Linux宝库VQSk*W k
} Elf32_Sym;

Gv%[4D8U0B0Linux宝库WLn4Ae

可执行文件他们知道运行时刻他们的地址,所以他们内部的引用符号在编译时候就已
(v$H#Pc-]:_@Q6UWt0经被重定位了。

W(u5X/zFrV0Linux宝库GAMxOO }"|)C-@


+Cs;wg_Z%w0★★ GOT(global offset table)

%@PW[qS0

!o)hU0R'L!E;f0GOT是一个数组,存在ELF image的数据段中,他们是一些指向objects的指针(通常
v_-x$I UR n0是数据objects).动态连接器将重新修改那些编译时还没有确定下来地址的符号的
:X0?3A ]}W'U0GOT入口。所以说GOT在i386动态连接中扮演着重要的角色。

q tY8a"Z5\0Linux宝库:y4N-}*fF9\(IIB'P


Gz1g@#wy-\{ N0★★ PLT(procedure linkage table)

I&UC\]i ^.Y0

)~Wvp:Wi#I}0PLT是一个这样的结构,它的entries包含了一些代码片段用来传输控制到外部的过程。
V(k4JO:b ~0在i386体系下,PLT和他的代码片段entries有如下格式:Linux宝库 E*{o9Vr {5d%C

Linux宝库HR#f(A{g2M3}

PLT0:Linux宝库k ?7{ l:?
 push GOT[1] ; word of identifying information
_ Y4Bul0 jmp GOT[2] ; pointer to rtld function nop
RU^x(O|{0 ...
.v x1T @H1U6`:i0PLTn:
A"HA'n8t;nmQJ7]5q)T:`9?0 jmp GOT[x + n] ; GOT offset of symbol addressLinux宝库)E5z.eHnpn
 push n ; relocation offset of symbolLinux宝库#EzF rR mgP
 jmp PLT0 ; call the rtldLinux宝库Wt/X"b+r!h3g-C
PLTn + 1Linux宝库"N8`@!B ?#kQ
 jmp GOT[x +n +1]; GOT offset of symbol address
S.U.@VTPvp0 push n +1 ; relocation offset of symbolLinux宝库JS7RG#Xn0o
 jmp PLT0 ; call the rtldLinux宝库"K,ac'[/U/?,n-bK

%w qdR+zqO k0当传输控制到一个外部的函数时,它传输执行到PLT 中跟该symbol相关的那个entryLinux宝库cM Xh ~
(是在编译时候连接器安装的)。在PLT entry中第一条指令将jump到一个存储在GOTLinux宝库 [`$gy-v(rEp
中的一个指针地址;假如符号还没有被解析,该GOT中存放着的是该PLT entry中的Linux宝库-QR2R-K!z;U
下一条指令地址。该指令push一个在重定位表中的偏移量到stack,然后下一条指令
6DvW8{hC$^.y-E0传输控制到PLT[0]入口。该PLT[0]包含了调用RTLD解析符号的函数代码。该
1Ny|p Y)p?GT0解析符号函数地址由程序装载器已经插入到GOT[2]中了。

#QZ]j.p3Y{k0

;X(\;g1EV.hD1A0e z"E0动态连接器将展开stack并且获取需要解析符号在重定位表地址信息。重定位入口、Linux宝库#m*d{aH H
符号表和字符串表共同决定着PLT entry引用的那个符号和在进程内存中符号应该
-hSMu0bpA'h3J0存放的地址。假如可能的话,该符号将被解析出来,它的地址将被存放在被该
Rl/R NB [:s Ll0PLT entry使用的GOT entry中。下一次该符号被请求时,与之对应的GOT已经包
hgn-~#F` S+|$F0含了该符号的地址了。所以,所有后来的调用将直接通过GOT传输控制。动态连接器
u xJr%e0只解析第一次被二进制文件所引用的符号;这种引用方式就是我们上面所说的Linux宝库4V yO3H:g DRQ9_VO#y
lazy MODE。

u+N6B:k A1O?0Linux宝库L.mghwQQ1mp

Linux宝库$H4S/Ep T
★★ 哈希表和链(hash table and chain)Linux宝库 K*P9{?5Q u8h2QB N6Jo

Linux宝库D1ty NIc{ L

除了符号表(symbol table),GOT(global offset table),PLT(procedure
1gpA;e(R;X+o@1~!_0linkage table),字符串表(string table),ELF objects还可以包含一个Linux宝库9M"yQ]5r$?Q
hash table和chain(用来使动态连接器解析符号更加容易)。hash table和chain
hH'^O'h`0d0通常被用来迅速判定在符号表中哪个entry可能符合所请求的符号名。hash table(总Linux宝库K&F+z q%\\-p\ A.}
是伴随着chain的)被作为整型数组存放。在hash表中,一半位置是留给那些buckets的,
N{Tl\z%}0另一半是留给在chain中的元素(element)的. hash table直接反映了symbol table
PyN,?$uK B-E0的元素数目和他们的次序。

|s*F ^l;WGi0

ZZcSU/u T6H&V0动态连接器结构提供了所有动态连接的执行是以透明方式访问动态连接器.
2MWInt`L*j]e'C0然而,明确访问也是可用的。动态连接(装载共享objects和解析符号),Linux宝库8S7o9b0b2v0kb8Mc
可以通过直接访问RTLD的那些函数来完成:dlopen() , dlsym() andLinux宝库4aP:MUW3^ M
dlclose() .这些函数被包含在动态连接器本身中。为了访问那些函数,
Tf;l%W}6w \iQ.p0连接时需要把动态连接函数库(libdl)连接进去。该库包含了一些stub函数
6G;WIs.kL?fzE0允许编译时候连接器解析那些函数的引用;然而那些stub函数只简单的返回0。Linux宝库dZw4g Y/YRh^
因为事实上函数驻留在动态连接器中,假如从静态连接的ELF文件中调用Linux宝库#{%t;D?N L#sIE
那些函数,共享object的装载将会失败。Linux宝库gE?8I Pp j

Linux宝库_}#T?8j(l)Y'B

对于执行动态连接器所必须的是:hash table,hash table元素的数目,Linux宝库2h6c&rF+Z
chain,dynamic string table和dynamic symbol talbe。满足了Linux宝库lHP%Ii?N"}
这些条件,下面算法适用任何symbol的地址计算:Linux宝库7r4y4iT8d

Linux宝库zX/k6@Gt:W

1. hn = elf_hash(sym_name) % nbuckets;Linux宝库XG.{ \_2x'W1p
2. for (ndx = hash[ hn ]; ndx; ndx = chain[ ndx ]) {
_-H-H:V{ ]VZ03. symbol = sym_tab + ndx;
*dK0FT\S2?04. if (strcmp(sym_name, str_tab + symbol->st_name) == 0)Linux宝库1CiL~9Z
5. return (load_addr + symbol->st_value); }Linux宝库.UV%R/pIu$e'B

Linux宝库,D/fS)ZzW X

hash号是elf_hash()的返回值,在ELF规范的第4部分有定义,以hash table中元素
1a}*R4V'\/[0个数取模。该号被用来做hash table的下表索引,求得hash值,找出与之匹配的符号Linux宝库0ZPt:e,Sd5mX R
名的chain的索引(line 3)。使用该索引,符号从符号表中获得(line 3).比较获得
I4WW]D2@ hz4X5ih0的符号名和请求的符号名是否相同(line 5).使用这个算法,就可以简单解析任何符号了。Linux宝库9| ['`$jj

$u~-Y2Z.x SN'x$t0Linux宝库j2l6b e O1kfngW
★★ 演示

?(Q#?yK*r.U7@0ri0Linux宝库J7x%V6z4Y

#include <stdio.h>
w-z:M*pn F g u"Z0int main(int argc, char *argv[])
4J.A:E%B'M7FU LI0{Linux宝库t.n\#IeT4q` f kTx
 printf("Hello, world\n");Linux宝库0O~ rV[0l?
 return 0;
-YK2V(n@tAJ0}

$_$RK@;B.@0Linux宝库+Pe[1_ _zj~+@ a

Linux宝库 ~:v$O3DM7K/J
Relocation section '.rel.plt' at offset 0x278 contains 4 entries:Linux宝库 zzTx4rT Pg#Lo
  Offset    Info  Type            Symbol's Value  Symbol's Name
/i:X&Fs^(C0  0804947c  00107 R_386_JUMP_SLOT       080482d8  __register_frame_infoLinux宝库!\r(`goX7k;b
  08049480  00207 R_386_JUMP_SLOT       080482e8  __deregister_frame_info
m5Qw:C]vf/}Q+d"@0  08049484  00307 R_386_JUMP_SLOT       080482f8  __libc_start_main
d7I2X$`iD#at vXXh0  08049488  00407 R_386_JUMP_SLOT       08048308  printf
c"Ji:}G$L0只有R_386_JUMP_SLOT的才会出现在GOT中Linux宝库0OX+o(?L6f.W }Aa2JN

MNi8nvK)x0Symbol table '.dynsym' contains 7 entries:Linux宝库M5Dy Ra^y
  Num:    Value  Size Type    Bind   Ot  Ndx NameLinux宝库5[6T.s6]@9jA
    0:        0     0 NOTYPE  LOCAL   0  UNDLinux宝库 _3F(H8j E|
    1:  80482d8   116 FUNC    WEAK    0  UND__register_frame_info@GLIBC_2.0(2)
d)n&C QC$s3S0    2:  80482e8   162 FUNC    WEAK    0  UND__deregister_frame_info@GLIBC_2.0(
$K Oy!j5oZ"O02)
ry,L:^ Q:[v^ DfG*`0    3:  80482f8   261 FUNC    GLOBAL  0  UND__libc_start_main@GLIBC_2.0(2)
OEmK^ ~+}#O HAY0    4:  8048308    41 FUNC    GLOBAL  0  UNDprintf@GLIBC_2.0(2)
l"I"qo k0    5:  804843c     4 OBJECT  GLOBAL  0   14 _IO_stdin_used
c)U9AE+F8f0    6:        0     0 NOTYPE  WEAK    0  UND __gmon_start__

V4JsRcoeu8io0

#sb(o k"p$[K0
/PD5v Is:m0[alert7@redhat]$ gcc -o test test.cLinux宝库hPS,ucb
[alert7@redhat]$ ./test
?dB6`&dV Z8q0Hello, worldLinux宝库do z7Uo,fRR0E
[alert7@redhat]$ objdump -x testLinux宝库\CPL:U0`m
...
Wd.s |@'D0Dynamic Section:
&m _1nZFA+C:h'W0  NEEDED      libc.so.6Linux宝库:F/L$f'~ Bpvr
  INIT        0x8048298Linux宝库O*WJ|4Q([!A;~
  FINI        0x804841cLinux宝库.@-V4jj5z?
  HASH        0x8048128
FHC`{6I0  STRTAB      0x80481c8
O|:?)tZ-|;aE0  SYMTAB      0x8048158
iVf?%C ft0  STRSZ       0x70Linux宝库i,[z;J!d*Nh"[/}%P
  SYMENT      0x10Linux宝库9P \Br+J$b v
  DEBUG       0x0Linux宝库#OGug"fwi
  PLTGOT      0x8049470
(Ze7sihO0  PLTRELSZ    0x20Linux宝库{8f?8Qc W+`x
  PLTREL      0x11Linux宝库 E+C;dg]K6d,qN0S
  JMPREL      0x8048278Linux宝库D9vI(@#yK/C?9a
  REL         0x8048270
_1x)Vnj:{)z0  RELSZ       0x8
:i F6p7oc0  RELENT      0x8
bE^]+Co0  VERNEED     0x8048250
+Mywnt6Nyf0  VERNEEDNUM  0x1Linux宝库*Q/l~bbvdh2q
  VERSYM      0x8048242Linux宝库1M d:b2Xs@
...
%n+[n7_D0  7 .rel.got      00000008  08048270  08048270  00000270  2**2
e x9~H-N Dt-l z0                  CONTENTS, ALLOC, LOAD, READONLY, DATALinux宝库C8P `:S*\6P
  8 .rel.plt      00000020  08048278  08048278  00000278  2**2Linux宝库(VG D,Mk
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
)ZK1@)S Km0  9 .init         0000002f  08048298  08048298  00000298  2**2Linux宝库#w0dh$UR8_u
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
s*i&e8YVZ a/rF0 10 .plt          00000050  080482c8  080482c8  000002c8  2**2
7_;JI"KB;N)qm0                  CONTENTS, ALLOC, LOAD, READONLY, CODE
#Q{mFm2pm-{2f0 11 .text         000000fc  08048320  08048320  00000320  2**4Linux宝库Hu A/^.r#}-eb$jv
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
(\:{U#Qu f0 12 .fini         0000001a  0804841c  0804841c  0000041c  2**2
S1p xE?$lud0                  CONTENTS, ALLOC, LOAD, READONLY, CODELinux宝库'p7k#kz]'A
 13 .rodata       00000016  08048438  08048438  00000438  2**2Linux宝库5jw eQe
                  CONTENTS, ALLOC, LOAD, READONLY, DATALinux宝库2_ o_$m}:KYX
 14 .data         0000000c  08049450  08049450  00000450  2**2
w~0G U yV0                  CONTENTS, ALLOC, LOAD, DATALinux宝库 p9\0Vh }\(Br
 15 .eh_frame     00000004  0804945c  0804945c  0000045c  2**2
}1\*RB`d!S#}0                  CONTENTS, ALLOC, LOAD, DATA
,QpX"Q@ c0 16 .ctors        00000008  08049460  08049460  00000460  2**2
Cu LVKyuDrZ0                  CONTENTS, ALLOC, LOAD, DATA
;L(U1{1CN)rVn0 17 .dtors        00000008  08049468  08049468  00000468  2**2Linux宝库4C.v9Vlo3f^)B$z9s
                  CONTENTS, ALLOC, LOAD, DATA
r Wzv8j0 18 .got          00000020  08049470  08049470  00000470  2**2
3_,qo \'K8e.h0                  CONTENTS, ALLOC, LOAD, DATALinux宝库s s*A-o4MM
 19 .dynamic      000000a0  08049490  08049490  00000490  2**2Linux宝库7~ w/@j,D(Q-j-}3S8Ek
                  CONTENTS, ALLOC, LOAD, DATALinux宝库Y.M#y&z1e1~
...
Y}3Q+S/g2k-ZS)A0[alert7@redhat]$ gdb -q test
%Xg_ i9Q*{ y0(gdb) disass main
8|%v s0HZ6}q F0Dump of assembler code for function main:Linux宝库"gFC%muA
0x80483d0 <main>:       push   %ebpLinux宝库v#uUQ o.sS
0x80483d1 <main+1>:     mov    %esp,%ebpLinux宝库d;\|/eB e4Tce
0x80483d3 <main+3>:     push   $0x8048440Linux宝库.} t2L/K4C n
0x80483d8 <main+8>:     call   0x8048308 <printf>Linux宝库6QPq&\J.Pu
0x80483dd <main+13>:    add    $0x4,%esp
gt _/VQ4AT D*P5I00x80483e0 <main+16>:    xor    %eax,%eaxLinux宝库@F&f4az'\.Ae%U K
0x80483e2 <main+18>:    jmp    0x80483e4 <main+20>Linux宝库#g? z Kl!^&`W
0x80483e4 <main+20>:    leave
5z}l1Bew$B-Ad00x80483e5 <main+21>:    retLinux宝库 t ~ IsNn
...Linux宝库'O/j'u@:R6h!wa"n
0x80483ef <main+31>:    nopLinux宝库}Q.juiti
End of assembler dump.
N:J.`#if[ XE;Fg:F0(gdb) b * 0x80483d8
9g{W6Y @1[rR0Breakpoint 1 at 0x80483d8
2ZF6TBr8e o0(gdb) rLinux宝库U Vx*wq9h0]
Starting program: /home/alert7/test

QY5Ap,l#z0

Y:| Hw L'`-M Q6B6k;v0Breakpoint 1, 0x80483d8 in main ()
&xkllJ7tN0(gdb) disass 0x8048308    ① ⑴Linux宝库n w(rFSMr
Dump of assembler code for function printf:Linux宝库5uM^)`Q P0Iq
/****************************************/ //PLT4:Linux宝库 aB J)e+h$@0T
0x8048308 <printf>:     jmp    *0x8049488       //jmp GOT[6]Linux宝库'FTX7m%g\g8s4i$o
      //此时,GOT[6]中存在的是0x804830e
*rC@ I3WYJ00x804830e <printf+6>:   push   $0x18  //$0x18为printf重定位入口在JMPREL section中的偏移量
e2^jR-oe'|k00x8048313 <printf+11>:  jmp    0x80482c8 <_init+48> //jmp PLT0
$h2X%O I+M%M;N!d0      //PLT0处存放着调用RTLD函数的指令
XMI4h(u1TT;_0      //当函数返回时候,把GOT[6]修改为真正的Linux宝库C:n@P'|
      //printf函数地址,然后直接跳到printf函数Linux宝库;wN }NT{
      //执行。
G1i8V-b%x)K8V'N d/l0Xg0该部分为PLT的一部分Linux宝库4i[(x:Qb.e P$z
/****************************************/Linux宝库ES_9ab6a6cZ
End of assembler dump.
6R*[;X2|#M^0(gdb) x 0x8049488    
J4~PD-[:p+k$u00x8049488 <_GLOBAL_OFFSET_TABLE_+24>:   0x0804830e
J!wg b ]:tql0080482c8 <.plt>:    ②  //PLT0:
,T\| pC,JQO0 80482c8:       ff 35 74 94 04 08       pushl  0x8049474 //pushl GOT[1]地址Linux宝库!HW!z2a!IVd
        //GOT[1]是一个鉴别信息,是link_map类型的一个指针

0c W%u"C*{*W(O0Linux宝库4f4SW#}$T^ jze{

 80482ce:       ff 25 78 94 04 08       jmp    *0x8049478 //JMP GOT[2]
| QE3?!_Q0        //跳到动态连接器解析函数执行
7t(gYy's2G })I0 80482d4:       00 00                   add    %al,(%eax)
jS_1I,\0 80482d6:       00 00                   add    %al,(%eax)

Hk&HP5Z,Hb'c u0

#L(_H7|9M}'r1LhJ0 80482d8:       ff 25 7c 94 04 08       jmp    *0x804947c //PLT1:Linux宝库"C8C0u W RN
 80482de:       68 00 00 00 00          push   $0x0Linux宝库h V4V'@{&I
 80482e3:       e9 e0 ff ff ff          jmp    80482c8 <_init+0x30>Linux宝库 FQ Lp%U!t

1loCA(NQk0 80482e8:       ff 25 80 94 04 08       jmp    *0x8049480 //PLT2:
] V(XrH$O+W0 80482ee:       68 08 00 00 00          push   $0x8
2l,W}^"s&Z2I;q0 80482f3:       e9 d0 ff ff ff          jmp    80482c8 <_init+0x30>

G ak/Bj0

/sZ%EMY1tZd#F6u\:C0 80482f8:       ff 25 84 94 04 08       jmp    *0x8049484 //PLT3:
t.W*i*w&a#V Dc_0 80482fe:       68 10 00 00 00          push   $0x10Linux宝库0[{Q5fJ P Ol0E7U
 8048303:       e9 c0 ff ff ff          jmp    80482c8 <_init+0x30>

`0EVmo0

b~~(`1Xs4H%VUC0 8048308:       ff 25 88 94 04 08       jmp    *0x8049488 //PLT4:Linux宝库4_2JJf*N`vr+V
 804830e:       68 18 00 00 00          push   $0x18
r AB{_ d5P@ u0 8048313:       e9 b0 ff ff ff          jmp    80482c8 <_init+0x30>Linux宝库U\-j_c)K(]

N&PI#?+o1o}0(gdb) b * 0x80482c8
.D&[H`0R1uM0Breakpoint 2 at 0x80482c8
1e.g0u+o9{0(gdb) c
.R,a*i@K6OX w3o0R L0Continuing.

X|qt8\'gG0

F(B9e^*K+^0Breakpoint 2, 0x80482c8 in _init ()
2pcE PZ f T'E.C"z-gy0(gdb) x/8x 0x8049470
6?w1AA0~7d)cD00x8049470 <_GLOBAL_OFFSET_TABLE_>:      0x08049490      0x40013ed0      0x4000a960      0x400fa550
n3j*@9c7OQq00x8049480 <_GLOBAL_OFFSET_TABLE_+16>:   0x080482ee      0x400328cc      0x0804830e      0x00000000Linux宝库o1v'MP n N
(gdb) x/50x 0x40013ed0 ( * link_map类型)Linux宝库(X} T#{3F)n
0x40013ed0:     0x00000000      0x40010c27      0x08049490      0x400143e0
3K2oO5K},A![00x40013ee0:     0x00000000      0x40014100      0x00000000      0x08049490
dF,VHbg"S;j00x40013ef0:     0x080494e0      0x080494d8      0x080494a8      0x080494b0
q-M L!M G }00x40013f00:     0x080494b8      0x00000000      0x00000000      0x00000000
vY:vj] |S"t00x40013f10:     0x080494c0      0x080494c8      0x08049498      0x080494a0Linux宝库3\'n+V9C:e!l
0x40013f20:     0x00000000      0x00000000      0x00000000      0x080494f8
%Y?'W8d![G4v'f0d8|00x40013f30:     0x08049500      0x08049508      0x080494e8      0x080494d0
Z$c`%u(`00x40013f40:     0x00000000      0x080494f0      0x00000000      0x00000000
,nJg2X/Wr4D^00x40013f50:     0x00000000      0x00000000      0x00000000      0x00000000Linux宝库 V7}-[ L T(O(k
0x40013f60:     0x00000000      0x00000000      0x00000000      0x00000000Linux宝库J#N"`/r5s(r x;v2S
(gdb) disass 0x4000a960    ③
b,VHZ2w*{c0Dump of assembler code for function _dl_runtime_resolve:
'yAQ~Y u-q00x4000a960 <_dl_runtime_resolve>:       push   %eaxLinux宝库$H&S2h;mM f
0x4000a961 <_dl_runtime_resolve+1>:     push   %ecx
*@$^rZ4q$| oT~00x4000a962 <_dl_runtime_resolve+2>:     push   %edx
j&g E w8TW;n5t I/R$W00x4000a963 <_dl_runtime_resolve+3>:     mov    0x10(%esp,1),%edx
4i~$o7{Tj w a Y00x4000a967 <_dl_runtime_resolve+7>:     mov    0xc(%esp,1),%eaxLinux宝库)hP-m I~2ix?^
0x4000a96b <_dl_runtime_resolve+11>:    call   0x4000a740 <fixup>Linux宝库e sW${a
     //调用真正的解析函数fixup(),修正GOT[6],使它指向真正的printf函数地址Linux宝库II(LP1j5C
0x4000a970 <_dl_runtime_resolve+16>:    pop    %edx
%A5g S!E-npt00x4000a971 <_dl_runtime_resolve+17>:    pop    %ecx
0G&J-f$L&C00x4000a972 <_dl_runtime_resolve+18>:    xchg   %eax,(%esp,1)
4GA1hKZ`~)pd00x4000a975 <_dl_runtime_resolve+21>:    ret    $0x8 //跳到printf函数地址执行Linux宝库 n L6|?6K)MTFp)v'O
0x4000a978 <_dl_runtime_resolve+24>:    nopLinux宝库1vOyR)W x
0x4000a979 <_dl_runtime_resolve+25>:    lea    0x0(%esi,1),%esi
j sQ^?0End of assembler dump.Linux宝库(kw7bL(X,igp
(gdb) b * 0x4000a972Linux宝库 M@1rFE
Breakpoint 4 at 0x4000a972: file dl-runtime.c, line 182.Linux宝库f8k Z:R_$j7k
(gdb) c
ls9j4`:K;a4B4|^%nc0Continuing.

S2?(Wmp*Px"eS0

;V7M4eP+h5S?0Breakpoint 4, 0x4000a972 in _dl_runtime_resolve () at dl-runtime.c:182Linux宝库t(IW'{!w f
182     in dl-runtime.cLinux宝库 |z T*G+{8T#v
(gdb) i reg $eax $esp
aL:s-|+T6?$J8K0eax            0x4006804c       1074167884
)O6AF%c;P)t[0esp            0xbffffb64       -1073743004Linux宝库 yz/TN7h7T#Dis
(gdb) b *0x4000a975
5}H"}%N8[Snl0Breakpoint 5 at 0x4000a975: file dl-runtime.c, line 182.Linux宝库9N4Ou"?0Z/f-j'b5r
(gdb) cLinux宝库T#Fx#k:cOV9b
Continuing.

:F(s2p7Jb$sS(`4a N0Linux宝库FWA t `"~

Breakpoint 5, 0x4000a975 in _dl_runtime_resolve () at dl-runtime.c:182Linux宝库O:Z~T*k
182     in dl-runtime.c
i6H JQvH|+OUe0(gdb) siLinux宝库1vD'w$uo
printf (format=0x1 <Address 0x1 out of bounds>) at printf.c:26
n{`/I B:}oy [v026      printf.c: No such file or directory.Linux宝库a It3IP6yW:^
(gdb) disass     ④ ⑵
eZ,ej:Q M9M0Dump of assembler code for function printf:Linux宝库aqnI%_
0x4006804c <printf>:    push   %ebpLinux宝库u8E Mqhp$U
0x4006804d <printf+1>:  mov    %esp,%ebp
]"L%KKR t00x4006804f <printf+3>:  push   %ebx
*fS.R I$MX:\Z00x40068050 <printf+4>:  call   0x40068055 <printf+9>Linux宝库&M;a/_e | B
0x40068055 <printf+9>:  pop    %ebx
dpF,]6kNh00x40068056 <printf+10>: add    $0xa2197,%ebx
!Dd7tENy2]6F&hd00x4006805c <printf+16>: lea    0xc(%ebp),%eax
+IH.TQz.y.l1q:O00x4006805f <printf+19>: push   %eax
/Ub-~2j+h9p+_ s00x40068060 <printf+20>: pushl  0x8(%ebp)
{Wx2zvn00x40068063 <printf+23>: mov    0x81c(%ebx),%eaxLinux宝库 {(w*~0\+M6B0Q7g!b,@ _
0x40068069 <printf+29>: pushl  (%eax)Linux宝库%f2d hW oh b|
0x4006806b <printf+31>: call   0x400325b4Linux宝库L)uw5r(Lt
0x40068070 <printf+36>: mov    0xfffffffc(%ebp),%ebx
|.E?U:tFM00x40068073 <printf+39>: leaveLinux宝库D"ZH7|} ?x;j5w0r
0x40068074 <printf+40>: ret
:P[$gd1u \If#W1R^0End of assembler dump.
"Njn D*_Mi h ~G0(gdb) x/8x 0x8049470Linux宝库,UM/H)SFx
0x8049470 <_GLOBAL_OFFSET_TABLE_>:      0x08049490      0x40013ed0      0x4000a960      0x400fa550
t;M$Q+y5k/mR00x8049480 <_GLOBAL_OFFSET_TABLE_+16>:   0x080482ee      0x400328cc      0x4006804c      0x00000000Linux宝库A!t0@&_t ja

Z/VW.VQe0GOT[6]已经被修正为0x4006804c了Linux宝库%o9{2q/ea

;P{5Qb3M0第一次调用printf()的时候需要经过①->②->③->④
[R5Y:\3[0以后调用printf()的时候就不需要这么复杂了,只要经过⑴->⑵就可以了

x$q/z'g-p;],][0

.O7N r*x;e0我们来看看到底是如何修正GOT[6]的,也是就说如何找到要修正的地址的
c nE4`&Z0(以前我在这点理解上发生了一些比较大的误解,误导各位的地方还请包涵:) )

e#J?9i3Bd f Pt~0Linux宝库yC2r9TM {,g3X5k

1:
+OV.Ga:mt?Tq0进入PLT4的时候 push   $0x18 ,该$0x18为printf重定位入口在JMPREL section中的偏移量
]3Ewg-^02:
4SW)M@"jmp0printf重定位地址为JMPREL+$0x18  /* Elf32_Rel * reloc = JMPREL + reloc_offset; */Linux宝库QL5HcjJ}*d/b
(gdb) x/8x 0x8048278+0x18
&b%V7o\ JR00x8048290:  0x08049488      0x00000407      0x53e58955      0x000000e8Linux宝库ti BT)\0YS
0x80482a0 <_init+8>:    0xc3815b00      0x000011cf      0x001cbb83      0x74000000
e-m5b"p{x+\0typedef struct {Linux宝库k"XV Q!z
      Elf32_Addr r_offset;
~e)M+Ia8F%[0      Elf32_Word r_info;Linux宝库f8|[.w*~uv*P
  } Elf32_Rel;Linux宝库N1tC)k*y!c mw
也就是说printf重定位printf_retloc.r_offset=0x08049488;
:H3Py/IV5u7p yQ0    printf_retloc.r_info=0x00000407;
-mu1Y$F `K8f8k0再看看0x08049488是什么地方Linux宝库%C/Sfpv$?.kI8G
(gdb) x 0x08049488Linux宝库%d PZ2w+M,h#Ok
0x8049488 <_GLOBAL_OFFSET_TABLE_+24>:   0x4006804cLinux宝库a+e-cJ/`&vJ
也就是GOT[6]Linux宝库XD/q Rm^^M
3:
'a/I ra ePL0void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
SHE@$J0对一个可执行文件 或一个共享目标而言,rel_addr就等于reloc->r_offset
ir#ps4W0所以rel_addr=0x08049488=GOT[6];Linux宝库5a)e8s'l5zf#p[
4:Linux宝库#JD8D(NVO_
*reloc_addr = value;
-A8pz-xY0修正了rel_addr也就是GOT[6]
s7}Qjo;Q8X0A-\0至于value是如何计算的,请参考下面的源代码
m x_0i-z8HdUr0 Linux宝库/D6g.b,A(v+I7C Lc$i
同时r_info又关联着一个符号Linux宝库v`(X4kuay
Elf32_Sym * sym = &SYMTAB[ ELF32_R_SYM (reloc->r_info) ];Linux宝库-h&},\ m@E H
sym=0x8048158+0x00000407;Linux宝库 _H7HT:~\,B1J
  typedef struct {Linux宝库,v{3E6P*u S
      Elf32_Word st_name;
J+O T\#dE:z6}E0      Elf32_Addr st_value;Linux宝库,m1Q)@A.U?"i
      Elf32_Word st_size;
/Jj&k0h%axY)\1tz q0      unsigned char st_info;Linux宝库1|MOYE8];?]xb
      unsigned char st_other;Linux宝库xw,A|m(ZF/bW+L*M
      Elf32_Half st_shndx;
|({!hD!p4q`6e0  } Elf32_Sym;Linux宝库g^El3`ip J
(gdb) x/10x 0x8048158+0x00000407Linux宝库P"x*X&A] I_Q
0x804855f:      0x00003a00      0x00008000      0x00000000      0x00006900Linux宝库;s5M} MkG|
0x804856f:      0x00008000      0x00000000      0x00008300      0x00008000Linux宝库a)p9{6k2N)f7C
0x804857f:      0x00000000      0x0000b700

A1BDr ] I } ~0

{K%tJI)s [?0link_map结构说明如下:
B2E1yEG0/* Structure describing a loaded shared object.  The `l_next' and `l_prev'Linux宝库 Q!T]H_"z8~t$AR
   members form. a chain of all the shared objects loaded at startup.

MAG:f H0

s6x)B+q SY2k)nr0   These data structures exist in space used by the run-time dynamic linker;
Z;JC c8sW;M!@-X#J0   modifying them may have disastrous results.

Im4K0d/s;c0Linux宝库!yO a`*K"Fk\

   This data structure might change in future, if necessary.  User-levelLinux宝库!g?2n&C^X8@0e e
   programs must avoid defining objects of this type.  */Linux宝库Drrw3b7W#D*O

Linux宝库] UN1y(?,p'Z

Linux宝库WV E:K3U
★★ glibc中动态解析符号的源代码(glibc 2.1.3的实现)Linux宝库MYh5FRkjq

5o L VTL1Cy^fh0 .textLinux宝库2E"HD7{ ui)k'W
 .globl _dl_runtime_resolve
$Hll jz&@4du`0 .type _dl_runtime_resolve, @function
ERE:U9Zum iX fS0 .align 16Linux宝库)z'y |e~6Tq
_dl_runtime_resolve:Linux宝库 VSX`~,F
 pushl %eax  # Preserve registers otherwise clobbered.
2B z4j)? ~ |c0 pushl %ecxLinux宝库XU5?%` }A,~
 pushl %edxLinux宝库)e o#O4I|3FC
 movl 16(%esp), %edx # Copy args pushed by PLT in register.  NoteLinux宝库W2N ?+I+Z4n!d
 movl 12(%esp), %eax # that `fixup' takes its parameters in regs.Linux宝库4E ?UK3o
 call fixup  # Call resolver.
'G |q5u7RC0 popl %edx  # Get register content back.Linux宝库\'u~'zA
 popl %ecxLinux宝库,M hm%n C`!hq"o
 xchgl %eax, (%esp) # Get %eax contents end store function address.
vU"eT|VCz0 ret $8   # Jump to function address.

g:k!qkz2l0

!Q4^;M5|8Sq8P0static ElfW(Addr) __attribute__ ((unused))Linux宝库z#Z,xmG5U"y
fixup (
g.PiL`:\0# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
(o9d){m%FI4_g0        ELF_MACHINE_RUNTIME_FIXUP_ARGS,Linux宝库qA Wo`m&Is)K
# endif
a8\"f B P+W0       struct link_map *l, ElfW(Word) reloc_offset)
/cx/N"nF3N;V0{Linux宝库.T Pe:c"T zT
  const ElfW(Sym) *const symtabLinux宝库qe3~,[.q(Z
    = (const void *) l->l_info[DT_SYMTAB]->d_un.d_ptr;
T4b^SS)ZCP0  const char *strtab = (const void *) l->l_info[DT_STRTAB]->d_un.d_ptr;Linux宝库;V](z1wB

Linux宝库;U%G V b@

  const PLTREL *const reloc  /*计算函数重定位人口*/
4Bw V;m5z R0    = (const void *) (l->l_info[DT_JMPREL]->d_un.d_ptr + reloc_offset);Linux宝库]2B l~o\
                      /*l->l_info[DT_JMPREL]->d_un.d_ptr 为JMPREL section的地址*/Linux宝库2T E ["x8~[0|

c/E ~7P?'E"F0  const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];/*计算函数symtab入口*/Linux宝库'x^%l!M,y3?
  void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);/*重定向符号的绝对地址*/
o1U n}e:W M3t@E0    
y%h0gPs+B;G0  ElfW(Addr) value;Linux宝库.G1a0P&J&zX%]2t&PO u`

q,X*T$PI0  /* The use of `alloca' here looks ridiculous but it helps.  The goal is
&C V&~'O c/HM },^%V0     to prevent the function from being inlined and thus optimized out.
*_;z0a{%k U-jl0     There is no official way to do this so we use this trick.  gcc never
,z/sK vr2@0     inlines functions which use `alloca'.  */
;{w'`CAObl0  alloca (sizeof (int));

I:eI7IuD |0

"tS&p%} j-\'Q;d0  /* Sanity check that we're really looking at a PLT relocation.  */Linux宝库N$q$|yt*q+AWB
  assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);/*健壮性检查*/Linux宝库T n-^ e2oC6C[

Linux宝库@)Rq]$YqS4r1e

   /* Look up the target symbol.  */
}N [1q]P0  switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)Linux宝库HN#q5Llo&j4y:C3O wj
    {Linux宝库 b1o+DIu"P
    default:Linux宝库E6y.ca LB${Z
      {Linux宝库0h(g)j$w'O$T#S
 const ElfW(Half) *vernum =Linux宝库E;NN/pn2Y:vvp
   (const void *) l->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr;Linux宝库n%ucD@iM
 ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)];
f0f7I+u&s ]?0 const struct r_found_version *version = &l->l_versions[ndx];

!W3O~V+m0

p]g1wXhD0 if (version->hash != 0)
+T1L2^&Ef;l#\0   {
)[(U[ F%q1j/iz\0     value = _dl_lookup_versioned_symbol(strtab + sym->st_name,
O QmGf0P K})^0      &sym, l->l_scope, l->l_name,Linux宝库(H2C^p|
      version, ELF_MACHINE_JMP_SLOT);
?$H"hk1p+D)fm3W ^0     break;Linux宝库2l3r8N#om9B
   }Linux宝库]:}%m'W\.TyLU
      }Linux宝库 ~,oes&B*`p
    case 0:Linux宝库l:b1gIo1Jve
      value = _dl_lookup_symbol (strtab + sym->st_name, &sym, l->l_scope,Linux宝库te$Eo:\`T
     l->l_name, ELF_MACHINE_JMP_SLOT);Linux宝库] Wfd{a\i8p'Z
    }Linux宝库 AfJTTr U|
   /*此时value为object装载的基地址*/
]7bES;Zy]K0  /* Currently value contains the base load address of the object
M5lBsB0l8I)`#^+{0     that defines sym.  Now add in the symbol offset.  */

c1eOyYJ\7^5SP0Linux宝库bA:GV)Q

  value = (sym ? value + sym->st_value : 0);/*函数的绝对地址*/Linux宝库#X$cn8U!C8p {EbT

YL0V\ \)@+\0  /* And now perhaps the relocation addend.  */
@W%u5t&?;?M-\HO B0  value = elf_machine_plt_value (l, reloc, value);/*可能还需要一下重定位*/Linux宝库_tF!hnT1]d!swv

wB;P&RS*M0o0  /* Finally, fix up the plt itself.  */Linux宝库1AzU[;~F8y
  elf_machine_fixup_plt (l, reloc, rel_addr, value);/*修正rel_addr,一般来说是GOT[N]*/Linux宝库_eC!V p2f G

Linux宝库%wL E1U2w^QK

  return value;
N6^:Y2M Tdj0}

2j/v o'F&T0Linux宝库.LEg!G~#k

Linux宝库Ts1btKgmb
static inline Elf32_Addr
3~wlXP0^,t0elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,Linux宝库$a5v |OE_g(d
         Elf32_Addr value)
-g @ GOaH._*|$h0{
0vS&zv1Xr;vG0  return value + reloc->r_addend;
].HyYO0}

(wAp4L7Yx:uCuN t0Linux宝库(o&u'MuCE%A


6K_f_ `+?s0/* Fixup a PLT entry to bounce directly to the function at VALUE.  */Linux宝库KoY%j$H(OP
static inline void
I(PW f I_U6p0elf_machine_fixup_plt (struct link_map *map, const Elf32_Rel *reloc,Linux宝库%t LJ;TE h a m
         Elf32_Addr *reloc_addr, Elf32_Addr value)
&M3Ze5yT@g!k)S0{
f.cKer;J0  *reloc_addr = value;
aB ?o*k? D6m0}Linux宝库V6q0Zd4H

Linux宝库 pZuTQ

Linux宝库%y!N#QS Mul;s/f
参考资料:Linux宝库6p]i&e3LU_ lhn

t$[$Z0Yl D V01.glibc 2.1.3 srcLinux宝库yW-I o8UEd+[
2.<<ELF文件格式>>Linux宝库h Rr1@.k-]{ g
3.<<Cheating the ELF Subversive Dynamic Linking to Libraries>> write by the grugqLinux宝库xV*x(_x)}1s
4.Linux动态链接技术
b u9o ^/se g0 http://www.linuxforum.net/forum/showflat.php?Cat=&Board=Kstudy&Number=102793&page=1&view=collapsed&sb=5&o=31&part=
M3ZF6X*I6E05.p58-0x04  by Nergal <nergal@owl.openwall.com>Linux宝库 }kI@mf4M M
  << The advanced return-into-lib(c) exploits >>Linux宝库`4px6z7z;^Q#W.[
Linux宝库 ]"a` F(K7H%[ic4|


相关阅读:

TAG: 符号 ELF 动态 解析

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

数据统计

  • 访问量: 16109
  • 日志数: 31
  • 建立时间: 2007-10-21
  • 更新时间: 2008-02-19

RSS订阅

Open Toolbar