亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺析ELF轉(zhuǎn)二進(jìn)制允許把 Binary 文件加載到任意位置

 更新時(shí)間:2020年02月10日 11:24:20   投稿:mrr  
本文通過 eip + 偏移地址 實(shí)現(xiàn)了運(yùn)行時(shí)計(jì)算數(shù)據(jù)地址,不再需要把 Binary 文件裝載到固定的位置。本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧

背景簡介

有一天,某位同學(xué)在討論群聊起來:

除了直接把 C 語言程序編譯成 ELF 運(yùn)行以外,是否可以轉(zhuǎn)成二進(jìn)制,然后通過第三方程序加載到內(nèi)存后再運(yùn)行。

帶著這樣的問題,我們寫了四篇文章,這是其二。

上篇 介紹了如何把 ELF 文件轉(zhuǎn)成二進(jìn)制文件,并作為一個(gè)新的 Section 加入到另外一個(gè)程序中執(zhí)行。

這個(gè)代碼包括兩個(gè)段,一個(gè) text 段,一個(gè) data 段,默認(rèn)鏈接完以后,text 中是通過絕對(duì)地址訪問 data 的,ELF 轉(zhuǎn)成 Binary 后,這個(gè)地址也寫死在 ELF 中,如果要作為新的 Seciton 加入到另外一個(gè)程序,那么鏈接時(shí)必須確保 Binary 文件的加載地址跟之前的 ELF 加載地址一致,否則數(shù)據(jù)存放的位置就偏移了,訪問不到,所以上篇文章用了一個(gè)客制化的 ld script,在里頭把 Binary Seciton 的加載地址(運(yùn)行時(shí)地址)寫死的。

讓數(shù)據(jù)地址與加載地址無關(guān)

本篇來討論一個(gè)有意思的話題,那就是,是否可以把這個(gè)絕對(duì)地址給去掉,只要把這個(gè) Binary 插入到新程序的 Text 中,不關(guān)心加載地址,也能運(yùn)行?

想法是這樣:data 應(yīng)該跟 text 關(guān)聯(lián)起來,也就是說,用相對(duì) .text 的地址,因?yàn)?Binary 里頭的 .rodata 是跟在 .text 后面,在文件中的相對(duì)位置其實(shí)是固定的,是否可以在運(yùn)行時(shí)用一個(gè)偏移來訪問呢?也就是在運(yùn)行過程中,獲取到 .text 中的某個(gè)位置,然后通過距離來訪問這個(gè)數(shù)據(jù)?

在運(yùn)行時(shí)獲取 eip

由于加載地址是任意的,用 .text 中的符號(hào)也不行,因?yàn)樵阪溄訒r(shí)也一樣是寫死的(用動(dòng)態(tài)鏈接又把問題復(fù)雜度提升了),所以,唯一可能的辦法是 eip,即程序地址計(jì)數(shù)器。

但是 eip 是沒有辦法直接通過寄存器獲取的,得通過一定技巧來,下面這個(gè)函數(shù)就可以:

eip2ecx:
 movl (%esp), %ecx
 ret

這個(gè)函數(shù)能夠把 eip 放到 ecx 中。

原理很簡單,那就是調(diào)用它的 call 指令會(huì)把 next eip 放到 stack,并跳到 eip2ecx。所以 stack 頂部就是 eip。這里也可以直接用 pop %ecx 。

所以這條指令能夠拿到 .here 的地址,并且存放在 ecx 中:

call eip2ecx
.here:
 ...
 .section .rodata
.LC0:
 .string "Hello World\xa\x0"

通過 eip 與數(shù)據(jù)偏移計(jì)算數(shù)據(jù)地址

然后接下來,由于匯編器能夠算出 .here 離 .LC0(數(shù)據(jù)段起始位置): .LC0 - .here ,對(duì)匯編器而言,這個(gè)差值就是一個(gè)立即數(shù)。如果在 ecx 上加上(addl)這個(gè)差值,是不是就是數(shù)據(jù)在運(yùn)行時(shí)的位置?

我們在 .here 放上下面這條指令:

call eip2ecx
.here:
 addl $(.LC0 - .here), %ecx
 ...
 .section .rodata
.LC0:
 .string "Hello World\xa\x0"

同樣能夠拿到數(shù)據(jù)的地址,等同于:

movl $.LC0, %ecx    # ecx = $.LC0, the addr of string

下面幾個(gè)綜合一起回顧:

  • addl 這條指令的位置正好是運(yùn)行時(shí)的 next eip (call 指令的下一條)
  • .here 在匯編時(shí)確定,指向 next eip
  • .LC0 也是匯編時(shí)確定,指向數(shù)據(jù)開始位置
  • .LC0 - .here 剛好是 addl 這條指令跟數(shù)據(jù)段的距離/差值
  • call eip2ecx 返回以后,ecx 中存了 eip
  • addl 這條指令把 ecx 加上差值,剛好讓 ecx 指向了數(shù)據(jù)在內(nèi)存中的位置

完整代碼如下:

# hello.s
#
# as --32 -o hello.o hello.s
# ld -melf_i386 -o hello hello.o
# objcopy -O binary hello hello.bin
#

 .text
.global _start
_start:
 xorl %eax, %eax
 movb $4, %al     # eax = 4, sys_write(fd, addr, len)
 xorl %ebx, %ebx
 incl %ebx      # ebx = 1, standard output

 call eip2ecx
.here:
 addl $(.LC0 - .here), %ecx # ecx = $.LC0, the addr of string
         # equals to: movl $.LC0, %ecx

 xorl %edx, %edx
 movb $13, %dl     # edx = 13, the length of .string
 int $0x80
 xorl %eax, %eax
 movl %eax, %ebx    # ebx = 0
 incl %eax      # eax = 1, sys_exit
 int $0x80

eip2ecx:
 movl (%esp), %ecx
 ret

 .section .rodata
.LC0:
 .string "Hello World\xa\x0"

鏈接腳本簡化

這個(gè)生成的 hello.bin 鏈接到 run-bin,就不需要寫死加載地址了,隨便放,而且不需要調(diào)整 run-bin 本身的加載地址,所以 ld.script 的改動(dòng)可以非常簡單:

$ git diff ld.script ld.script.new
diff --git a/ld.script b/ld.script.new
index 91f8c5c..e14b586 100644
--- a/ld.script
+++ b/ld.script.new
@@ -60,6 +60,11 @@ SECTIONS
  /* .gnu.warning sections are handled specially by elf32.em. */
  *(.gnu.warning)
 }
+ .bin   :
+ {
+ bin_entry = .;
+ *(.bin)
+ }
 .fini   :
 {
  KEEP (*(SORT_NONE(.fini)))

直接用內(nèi)聯(lián)匯編嵌入二進(jìn)制文件

在這個(gè)基礎(chǔ)上,可以做一個(gè)簡化,直接用 .pushsection 和 .incbin 指令把 hello.bin 插入到 run-bin 即可,無需額外修改鏈接腳本:

$ cat run-bin.c
#include <stdio.h>

asm (".pushsection .text, \"ax\" \n"
  ".globl bin_entry \n"
  "bin_entry: \n"
  ".incbin \"./hello.bin\" \n"
  ".popsection"
);

extern void bin_entry(void);

int main(int argc, char *argv[])
{
 bin_entry();
 return 0;
}

這個(gè)內(nèi)聯(lián)匯編的效果跟上面的鏈接腳本完全等價(jià)。

把數(shù)據(jù)直接嵌入代碼中

進(jìn)一步簡化匯編代碼把 eip2ecx 函數(shù)去掉:

# hello.s
#
# as --32 -o hello.o hello.s
# ld -melf_i386 -o hello hello.o
# objcopy -O binary hello hello.bin
#

 .text
.global _start
_start:
 xorl %eax, %eax
 movb $4, %al     # eax = 4, sys_write(fd, addr, len)
 xorl %ebx, %ebx
 incl %ebx      # ebx = 1, standard output

 call eip2ecx
eip2ecx:
 pop %ecx
 addl $(.LC0 - eip2ecx), %ecx # ecx = $.LC0, the addr of string
         # equals to: movl $.LC0, %ecx

 xorl %edx, %edx
 movb $13, %dl     # edx = 13, the length of .string
 int $0x80
 xorl %eax, %eax
 movl %eax, %ebx    # ebx = 0
 incl %eax      # eax = 1, sys_exit
 int $0x80

.LC0:
 .string "Hello World\xa\x0"

再進(jìn)一步,直接把數(shù)據(jù)搬到 next eip 所在位置:

# hello.s
#
# as --32 -o hello.o hello.s
# ld -melf_i386 -o hello hello.o
# objcopy -O binary hello.o hello
#

 .text
.global _start
_start:
 xorl %eax, %eax
 movb $4, %al     # eax = 4, sys_write(fd, addr, len)
 xorl %ebx, %ebx
 incl %ebx      # ebx = 1, standard output
 call next      # push eip; jmp next
.LC0:
 .string "Hello World\xa\x0"
next:
 pop %ecx      # ecx = $.LC0, the addr of string
         # eip is just the addr of string, `call` helped us
 xorl %edx, %edx
 movb $13, %dl     # edx = 13, the length of .string
 int $0x80
 xorl %eax, %eax
 movl %eax, %ebx    # ebx = 0
 incl %eax      # eax = 1, sys_exit
 int $0x80

小結(jié)

本文通過 eip + 偏移地址 實(shí)現(xiàn)了運(yùn)行時(shí)計(jì)算數(shù)據(jù)地址,不再需要把 Binary 文件裝載到固定的位置。

另外,也討論到了如何用 .pushsection/.popsection 替代 ld script 來添加新的 Section,還討論了如何把數(shù)據(jù)直接嵌入到代碼中。

以上所述是小編給大家介紹的ELF轉(zhuǎn)二進(jìn)制允許把 Binary 文件加載到任意位置,希望對(duì)大家有所幫助!

相關(guān)文章

  • VScode配置匯編語言環(huán)境的實(shí)現(xiàn)步驟

    VScode配置匯編語言環(huán)境的實(shí)現(xiàn)步驟

    本文主要介紹了VScode配置匯編語言環(huán)境的實(shí)現(xiàn)步驟,文中通過圖文的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-03-03
  • 匯編中的數(shù)組分配和指針的實(shí)現(xiàn)代碼

    匯編中的數(shù)組分配和指針的實(shí)現(xiàn)代碼

    這篇文章主要介紹了匯編中的數(shù)組分配和指針的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • ARM體系下的GCC內(nèi)聯(lián)匯編教程詳解

    ARM體系下的GCC內(nèi)聯(lián)匯編教程詳解

    在操作系統(tǒng)級(jí)的編程中,有時(shí)候,C語言并不能完全的使用硬件的功能,這時(shí)候就需要嵌入一些匯編代碼來實(shí)現(xiàn)功能。這篇文章主要介紹了ARM體系下的GCC內(nèi)聯(lián)匯編,需要的朋友可以參考下
    2020-02-02
  • 匯編語言80x86系統(tǒng)通用數(shù)據(jù)傳送指令詳解

    匯編語言80x86系統(tǒng)通用數(shù)據(jù)傳送指令詳解

    這篇文章主要為大家介紹了匯編語言80x86系統(tǒng)通用的數(shù)據(jù)傳送指令詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • 匯編語言軟件延時(shí)1s的實(shí)現(xiàn)方法

    匯編語言軟件延時(shí)1s的實(shí)現(xiàn)方法

    這篇文章主要介紹了匯編語言軟件延時(shí)1s的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 詳解匯編語言MOV指令

    詳解匯編語言MOV指令

    在匯編語言中,MOV指令是數(shù)據(jù)傳送指令,也是最基本的編程指令,這篇文章主要介紹了匯編語言MOV指令,需要的朋友可以參考下
    2020-01-01
  • 匯編基礎(chǔ)程序編寫教程示例

    匯編基礎(chǔ)程序編寫教程示例

    這篇文章主要為大家介紹了匯編基礎(chǔ),程序編寫教程示例,文中附含詳細(xì)的圖文示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • os_object_release Crash 排查記錄分析

    os_object_release Crash 排查記錄分析

    這篇文章主要為大家介紹了os_object_release Crash 排查記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 匯編語言偽指令和匯編指令的區(qū)別

    匯編語言偽指令和匯編指令的區(qū)別

    指令是控制程序運(yùn)行時(shí)的機(jī)器代碼運(yùn)作的,是CPU執(zhí)行的依據(jù),編程、編譯、執(zhí)行都是有效的。偽指令不直接控制運(yùn)行時(shí)刻的機(jī)器,但是控制翻譯程序如何生成機(jī)器指令代碼,感興趣的朋友跟隨小編一起看看吧
    2020-01-01
  • 匯編跳轉(zhuǎn)指令使用總結(jié)

    匯編跳轉(zhuǎn)指令使用總結(jié)

    這篇文章主要介紹了匯編跳轉(zhuǎn)指令使用總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01

最新評(píng)論