最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景簡(jiǎn)介

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

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

帶著這樣的問(wèn)題,我們寫(xiě)了四篇文章,這是其二。

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

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

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

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

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

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

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

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

eip2ecx:
 movl (%esp), %ecx
 ret

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

原理很簡(jiǎn)單,那就是調(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"

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

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

我們?cè)?.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ù)開(kāi)始位置
  • .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"

鏈接腳本簡(jiǎn)化

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

$ 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è)簡(jiǎn)化,直接用 .pushsection 和 .incbin 指令把 hello.bin 插入到 run-bin 即可,無(wú)需額外修改鏈接腳本:

$ 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)一步簡(jiǎ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é)

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

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

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

相關(guān)文章

最新評(píng)論

海盐县| 庆阳市| 阿拉善盟| 武胜县| 崇阳县| 韶山市| 富民县| 罗平县| 新田县| 西平县| 成武县| 辽中县| 溧水县| 罗江县| 冕宁县| 威远县| 海口市| 武义县| 泗阳县| 太仆寺旗| 工布江达县| 砚山县| 湛江市| 安陆市| 绥滨县| 巴彦淖尔市| 庄浪县| 梁山县| 临汾市| 儋州市| 裕民县| 祁阳县| 商南县| 峡江县| 星子县| 宜州市| 道真| 汉川市| 遵义县| 周口市| 屯昌县|