1. 程式人生 > >ELF檔案動態重定向

ELF檔案動態重定向

本文分析ELF檔案在執行時動態重定向的實現。

以下面的兩個程式為例:

$ cat mlib.c
int boo()
{
    return 0;
}


int foo()
{
    void *fn = boo;
    boo();
    return 0;
}

$ cat ./main.c
int boo();


int main()
{
    void *fn = boo;$ cat ./main.c
int boo();


int main()
{
    void *fn = boo;
    boo();
}
    boo();
}

$ gcc -o libmlib.so -shared -fPIC ./mlib.c
$ gcc -o main -lmlib -L. ./main.c

main函式中引用的函式boo定義在shared library中。

loader如何實現對shared library中的函式呼叫進行重定向

初始時,main函式中對boo的呼叫實際call的是[email protected],地址為0x400560,位置處於section .plt中(參見表一)。


(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400660 <+0>:     push   %rbp
   0x0000000000400661 <+1>:     mov    %rsp,%rbp
=> 0x0000000000400664 <+4>:     sub    $0x10,%rsp
   0x0000000000400668 <+8>:     movq   $0x400560,-0x8(%rbp)
   0x0000000000400670 <+16>:    mov    $0x0,%eax
   0x0000000000400675 <+21>:    callq  0x400560 <
[email protected]
>  <---- call boo()
   0x000000000040067a <+26>:    leaveq
   0x000000000040067b <+27>:    retq
End of assembler dump.


[email protected]進行反彙編,可以看出第一條jmp語句跳到了下一行0x400566。0x2被push到了堆疊。0x2應該代表.rela.plt中的第三項(0為第一項),該資訊將被傳遞給loader。


(gdb) disassemble 0x400560
Dump of assembler code for function [email protected]
:
   0x0000000000400560 <+0>:     jmpq   *0x2004f2(%rip)        # 0x600a58 <[email protected]>
   0x0000000000400566 <+6>:     pushq  $0x2
   0x000000000040056b <+11>:    jmpq   0x400530


(gdb) x /xg 0x600a58
0x600a58 <[email protected]>: 0x0000000000400566


push操作後,jmp到了0x400530,接著又把0x600a38中的資料push到了堆疊。注意0x600a38是.got.plt 中的第二個entry(每個entry的size是8)。最後又jmp到了0x00007ffff7def2d0(地址0x600a40中的資料)。


(gdb) x /5i 0x400530
   0x400530:    pushq  0x200502(%rip)        # 0x600a38
   0x400536:    jmpq   *0x200504(%rip)        # 0x600a40
   0x40053c:    nopl   0x0(%rax)
   0x400540 <[email protected]>:    jmpq   *0x200502(%rip)        # 0x600a48 <[email protected]>
   0x400546 <[email protected]+6>:  pushq  $0x0


(gdb) x /xg 0x600a38
0x600a38:       0x00007ffff7ffe190
(gdb) x /xg 0x600a40
0x600a40:       0x00007ffff7def2d0


從shared library map中可以看出,0x00007ffff7def2d0對應到/lib64/ld-linux-x86-64.so.2中。loader將利用之前push的兩個引數0x2和0x00007ffff7ffe190對[email protected](即0x600a58)中的資料進行更新,修改成函式boo的實際地址。


(gdb) info sharedlibrary
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddbba0  0x00007ffff7df4baa  Yes (*)     /lib64/ld-linux-x86-64.so.2
                                        No          linux-vdso.so.1
0x00007ffff7bda5b0  0x00007ffff7bda6c8  Yes (*)     ./libmlib.so
0x00007ffff784c470  0x00007ffff7978aa0  Yes (*)     /usr/lib/libc.so.6
(*): Shared library is missing debugging information.


將程式執行到斷點0x000000000040067a,檢查0x600a58的數值,可以看出已經變成了函式boo的地址。


(gdb) x /xg 0x600a58
0x600a58 <[email protected]>: 0x00007ffff7bda698
(gdb) p boo

$1 = {<text variable, no debug info>} 0x7ffff7bda698 <boo>

loader如何實現對shared library中變數的重定向

在main函式中對boo的地址進行了引用。可以看出boo的地址就是0x400560,即<[email protected]>,而非boo的真正地址。後面可以看出,即使在libmlib.so中對boo的引用同樣也指向0x400560。××這樣做的目的應當是確保在main和library當中對同一函式的地址引用是相等的××。


   0x0000000000400668 <+8>:     movq   $0x400560,-0x8(%rbp)


在shared library中的foo函式中對boo也做了引用,可以看出boo的地址被用0x200978中的資料間接代替了,而且0x200978中的數值是0,顯然不對。


gdb ./libmlib.so
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000000006a3 <+0>:     push   %rbp
   0x00000000000006a4 <+1>:     mov    %rsp,%rbp
   0x00000000000006a7 <+4>:     sub    $0x10,%rsp
   0x00000000000006ab <+8>:     mov    0x2002c6(%rip),%rax        # 0x200978
   0x00000000000006b2 <+15>:    mov    %rax,-0x8(%rbp)
   0x00000000000006b6 <+19>:    mov    $0x0,%eax
   0x00000000000006bb <+24>:    callq  0x590 <[email protected]>
   0x00000000000006c0 <+29>:    mov    $0x0,%eax
   0x00000000000006c5 <+34>:    leaveq
   0x00000000000006c6 <+35>:    retq
(gdb) x /xg 0x200978
0x200978:       0x0000000000000000


這是因為在.rela.dyn中有一個boo的重定向項。當libmlib.so被load進main中時,loader將會根據相應symbol的地址修正所有R_X86_64_GLOB_DAT重定向項的值。這些重定向項所指向的需要修改的地址都位於section .got中(參見表三)。


$ readelf -r ./libmlib.so


Relocation section '.rela.dyn' at offset 0x430 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200780  000000000008 R_X86_64_RELATIVE                    660
000000200788  000000000008 R_X86_64_RELATIVE                    620
0000002009b8  000000000008 R_X86_64_RELATIVE                    2009b8
000000200958  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200960  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200968  000400000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200970  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200978  000a00000006 R_X86_64_GLOB_DAT 0000000000000698 boo + 0
000000200980  000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0


Relocation section '.rela.plt' at offset 0x508 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
0000002009a0  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
0000002009a8  000a00000007 R_X86_64_JUMP_SLO 0000000000000698 boo + 0
0000002009b0  000600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0


下面可以看出,在load之後foo中所引用的boo的地址更新成了0x400560,即<[email protected]>。


gdb ./main


(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00007ffff7bda6a3 <+0>:     push   %rbp
   0x00007ffff7bda6a4 <+1>:     mov    %rsp,%rbp
   0x00007ffff7bda6a7 <+4>:     sub    $0x10,%rsp
   0x00007ffff7bda6ab <+8>:     mov    0x2002c6(%rip),%rax        # 0x7ffff7dda978
   0x00007ffff7bda6b2 <+15>:    mov    %rax,-0x8(%rbp)
   0x00007ffff7bda6b6 <+19>:    mov    $0x0,%eax
   0x00007ffff7bda6bb <+24>:    callq  0x7ffff7bda590 <[email protected]>
   0x00007ffff7bda6c0 <+29>:    mov    $0x0,%eax
   0x00007ffff7bda6c5 <+34>:    leaveq
   0x00007ffff7bda6c6 <+35>:    retq
End of assembler dump.
(gdb) x /xg 0x7ffff7dda978

0x7ffff7dda978: 0x0000000000400560

與重定向相關的section

.plt

重定向的入口程式碼。對於每一個需要重定向的函式,都會類似下面的程式碼。其中push的數值應該代表.rela.plt中的對應entry。
(gdb) disassemble 0x400560
Dump of assembler code for function [email protected]:
   0x0000000000400560 <+0>:     jmpq   *0x2004f2(%rip)        # 0x600a58 <[email protected]>
   0x0000000000400566 <+6>:     pushq  $0x2
   0x000000000040056b <+11>:    jmpq   0x400530

.got

R_X86_64_GLOB_DAT重定向項所指向的需要修改的地址。用於重定向對於變數的引用。

.got.plt

每一項佔8個位元組。
第一項的數值是0x0000000000600848,即dynamic section的起始地址。
第二項的數值是0x00007ffff7ffe190,據說代表link_map的起始地址?loader用它進行relocation.
第三項的數值是0x00007ffff7def2d0,是loader的_dl_runtime_resolve函式起始地址。


(gdb) disassemble 0x00007ffff7def2d0
Dump of assembler code for function _dl_runtime_resolve:


之後的每一項對應一個函式的重定向地址。初始時,如[email protected]所示,該值指向下一條彙編指令。重定向完成後,該值指向真正的函式地址。


(gdb) x /8xg 0x600a30
0x600a30:       0x0000000000600848      0x00007ffff7ffe190
0x600a40:       0x00007ffff7def2d0      0x00007ffff784e920

附表

表一,main的section
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040021c  0000021c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040023c  0000023c
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400260  00000260
       000000000000003c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002a0  000002a0
       0000000000000120  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000004003c0  000003c0
       00000000000000b5  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400476  00000476
       0000000000000018  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400490  00000490
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004004b0  000004b0
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004004c8  000004c8
       0000000000000048  0000000000000018   A       5    12     8
  [11] .init             PROGBITS         0000000000400510  00000510
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400530  00000530
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400570  00000570
       0000000000000184  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004006f4  000006f4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400700  00000700
       0000000000000004  0000000000000004  AM       0     0     4
  [16] .eh_frame_hdr     PROGBITS         0000000000400704  00000704
       0000000000000034  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400738  00000738
       00000000000000f4  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600830  00000830
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600838  00000838
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600840  00000840
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600848  00000848
       00000000000001e0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600a28  00000a28
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000600a30  00000a30
       0000000000000030  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000600a60  00000a60
       0000000000000010  0000000000000000  WA       0     0     8
  [25] .bss              NOBITS           0000000000600a70  00000a70
       0000000000000008  0000000000000000  WA       0     0     4
  [26] .comment          PROGBITS         0000000000000000  00000a70
       000000000000004e  0000000000000001  MS       0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  00000abe
       0000000000000108  0000000000000000           0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00001348
       0000000000000648  0000000000000018          29    47     8
  [29] .strtab           STRTAB           0000000000000000  00001990
       000000000000022f  0000000000000000           0     0     1

表二,main的重定向資訊
注意.rela.plt中的第三行boo的重定向資訊,其中offset是000000600a58,剛好在section .got.plt中。
$ readelf -r ./main


Relocation section '.rela.dyn' at offset 0x4b0 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600a28  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0


Relocation section '.rela.plt' at offset 0x4c8 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600a48  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
000000600a50  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000600a58  000800000007 R_X86_64_JUMP_SLO 0000000000400560 boo + 0

表三,libmlib.so的section(部分)
$ readelf -S ./libmlib.so
There are 27 section headers, starting at offset 0xad8:


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
  [ 2] .gnu.hash         GNU_HASH         00000000000001b8  000001b8
       0000000000000040  0000000000000000   A       3     0     8
  ..........
  [19] .got              PROGBITS         0000000000200958  00000958
       0000000000000030  0000000000000008  WA       0     0     8





相關推薦

ELF檔案動態定向

本文分析ELF檔案在執行時動態重定向的實現。 以下面的兩個程式為例: $ cat mlib.c int boo() { return 0; } int foo() { void *fn = boo; boo(); return 0; }

apache(.htaccess檔案)路由定向配置步驟

1.在專案根目錄資料夾下面新建.htaccess檔案(apache重寫); .htaccess檔案內容如下: <IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f Rewri

url下載檔案定向+cookie設定)

很多時候,下載檔案時會重定向,並且要求攜帶cookie才允許下載,這種情況下,如果讓下載支援重定向,並且設定cookie呢?下面的程式碼可以供大家參考。 public static voi

過濾驅動 檔案訪問定向方法

  在pre callback 中,使用IoReplaceFileObjectName 修改 Data->Iopb->TargetFileObject 檔案路徑, 然後:                 Data->IoStatus.Status = ST

Linux:基礎IO(檔案描述符分配規則)(定向)(inode)(軟硬連結)(動態庫靜態庫)

目錄 檔案描述符的分配規則 重定向原理 FILE 總結 理解檔案系統 inode是什麼 inode內容 硬連結 軟連結 軟硬連結區別: 動態庫和靜態庫 如何生成自己的動態庫和靜態庫 如何連結一個庫生成可執行程式 檔案描述符的分配規則 最

Python檔案&IO處理技巧(1): 讀寫、定向、間隔符、路徑、存在性與檔案列表

1. 文字資料的讀寫 open() & write() : rt模式的重新整理模式 當我們需要讀寫各種不同編碼的文字資料(如ASCII,UTF-8或UTF-16編碼等), 可以直接使用帶rt模式的open()內建函式。如果需要將文字內容寫入到一個檔案中,就要使用帶有 w

IDA動態遠端除錯elf檔案的最簡單設定(姿勢)

0x01: 將IDA安裝目錄下的dbgsrv資料夾整個都拷貝到linux下(我都拷貝到了ubuntu桌面) 0x02: 將所要除錯的檔案直接放在dbgsrv目錄下 0x03: IDA設定如下: 0x04: 這裡根據你的elf檔案型別

ELF檔案載入與動態連結(二)

GOT應該儲存的是puts函式的絕對虛地址,這裡為什麼儲存的卻是[email protected]的第二條指令呢? 原來“直譯器”將動態庫載入記憶體後,並沒有直接將函式地址更新到GOT表中,而是在函式第一次被呼叫時,才會進行函式地址的重定位,這樣做的好處是可以加快程式載入速度,尤其對大型程式來說。有

ELF檔案載入與動態連結(一)

關於ELF檔案的詳細介紹,推薦閱讀: ELF檔案格式分析 —— 滕啟明。ELF檔案由ELF頭部、程式頭部表、節區頭部表以及節區4部分組成。   通過objdump工具和readelf工具,可以觀察ELF檔案詳細資訊。 ELF檔案載入過程分析 從編譯、連結和執行的角度,應用程

轉載:C#關閉檔案定向,實現操作System32資料夾

我們已經知道:     ①:本機模式64位程式執行在純模式下,並且訪問鍵和儲存在以下注冊表子鍵中的值:HKEY_LOCAL_MACHINE \ Software     ②:32位程式執行在WOW64模式下,並且訪問鍵和值儲存在以下注冊表子項中:HKEY_LOCAL_MACHINE \ So

[譯]如何將docker日誌定向到單個檔案

原文來源: how-to-redirect-docker-logs-to-a-single-file 問題: 我想把某一個docker的log全部匯出到一個檔案裡進行分析,我該怎麼做? 其實不用那樣操作,Docker預設把所有的日誌存在一個log檔案裡面。你可以通過以下命令檢視docker對應log的地址:

SpringMVC篇:轉發與定向、圖片檔案上傳、Json(jackson)

注意: 專案:war 和 專案:war  exploded 兩者並不同,idea  執行 專案:war  exploded   <dependency> <groupId>org.

定向標準輸入到檔案

#!/bin/bash echo '' > redis.txt for((i=1;i<=3;i++)) do echo 'get te

SprinMVC轉發、定向、收集date資料、自定義轉換器、檔案上傳、json資料轉換

1.專案模組圖 2.完成SpringMVC的基本搭建 pom.xml(下載jar包的檔案) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.

Linux基礎-檔案管理和定向

Linux基礎-檔案管理和重定向 2018/7/27 15:00:07 1. 每一個分割槽都是一個獨立存在的檔案系統 2. 目錄:路徑對映符  3. 檔案 檔案有兩種資料 元資料:描述資料的資料屬性 metadata 資料:data FHS:

tee---將資料定向檔案

tee命令用於將資料重定向到檔案,另一方面還可以提供一份重定向資料的副本作為後續命令的stdin。簡單的說就是把資料重定向到給定檔案和螢幕上。 存在快取機制,每1024個位元組將輸出一次。若從管道接收輸入資料,應該是緩衝區滿,才將資料轉存到指定的檔案中。若檔案內容不到1024個位元組,則接收完

動態連結中資料的定向例子

程式的大致框架: 主程式: #include <stdio.h> extern void hello(void); extern int a; int main(void) { printf("in main.c a=%d\n", a); printf("i

Nginx下訪問原始碼.html檔案定向到相對目錄下.php檔案

                                                        請求報錯404故障 故障描述: LNMP環境下發布商城原始碼,通過web請求訪問.html檔案報404錯誤請求檔案不存在,在原始碼裡只存在.php檔案 例子圖

動態sql 和定向

<select id="findByTiaoJian" parameterType="cn.zzsxt.furniture.vo.TiaojianVo" resultType="cn.zzsxt.furniture.vo.MoneyUserVo"> SELEC

【C language】測試自動程式碼生成過程的定向檔案

測試自動程式碼生成過程的重定向 #include <stdio.h> #include <string.h> #include <stdbool.h> #include <stdio.h> #include <stdar