![](/img/trans.png)
[英]Using sys_write from an x86_64 function that takes only a char* argument?
[英]elf aarch64 golfed with sys_write
為了更好地理解ELF
格式和 ARM aarch64
,我嘗試創建沒有編譯器的 elf 二進制文件,只是用 bash 回顯字節。
可以在這里看到我的努力: http://www.github.com/glaudiston/elf
我已經成功地使用x64
的sys_write
和sys_exit
系統調用實現了一個完全工作的精靈。
但是對於aarch64
,它並沒有像我期望的那樣工作:
# cat make-elf.sh
#!/bin/bash
#
# depends on:
# - elf_fn.sh (github.com/glaudiston/elf)
# - base64 (gnu-coreutils)
#
. elf_fn.sh
instructions="";
instructions="${instructions}\nwrite $(echo -en "hello world\n" | base64 -w0)";
instructions="${instructions}\nexit 3";
write_elf elf "${instructions}";
它產生:
$ xxd elf
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0200 b700 0100 0000 7800 0100 0000 0000 ........x.......
00000020: 4000 0000 0000 0000 0000 0000 0000 0000 @...............
00000030: 0000 0000 4000 3800 0100 0000 0000 0000 ....@.8.........
00000040: 0100 0000 0500 0000 0000 0000 0000 0000 ................
00000050: 0000 0100 0000 0000 0000 0000 0000 0000 ................
00000060: 7800 0000 0000 0000 7800 0000 0000 0000 x.......x.......
00000070: 0000 0000 0000 0000 2000 80d2 010c 0058 ........ ......X
00000080: 8201 80d2 0808 80d2 0100 00d4 6000 80d2 ............`...
00000090: a80b 80d2 0100 00d4 6865 6c6c 6f20 776f ........hello wo
000000a0: 726c 640a
$ ./make-elf.sh 0 && ./elf; echo $?
3
$ cat elf | base64 -w0; echo
f0VMRgIBAQAAAAAAAAAAAAIAtwABAAAAeAABAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAeAAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAIACA0gEMAFiCAYDSCAiA0gEAANRgAIDSqAuA0gEAANRoZWxsbyB3b3JsZAo=
它返回預期的退出代碼,沒有非法異常,但sys_write
調用沒有打印任何內容。
隱藏所有ELF
開銷,我們有這個:
00000078: 2000 80d2 010c 0058 ......X
00000080: 8201 80d2 0808 80d2 0100 00d4 6000 80d2 ............`...
00000090: a80b 80d2 0100 00d4 6865 6c6c 6f20 776f ........hello wo
000000a0: 726c 640a rld.
exit 調用按預期工作,所以我也可以隱藏它:
00000078: 2000 80d2 010c 0058 ......X
00000080: 8201 80d2 0808 80d2 0100 00d4 ............
00000090: 6865 6c6c 6f20 776f hello wo
000000a0: 726c 640a rld.
所以我們有數據hello world.\n
從 position 98
開始。 我很困惑如何在這里進行sys_write
調用。 在x64
中,我可以設置下一個數據地址,在這種情況下應該是65688
(由PH_VADDR_V(65536) + ELF_HEADER_SIZE(64) + ELF_BODY_SIZE(32)
(沒有DATA_SECTION
)組成)”
對於 output fd
,我在r0
中將值 1 設置為2000 80d2
對於數據地址,我使用的是010c
,它是0c01
的little endian
表示,這些位: 00001100000 00001
最后 5 位是r1
寄存器,用於數據地址。
鑒於我只有 11 位在這里我使用了LDR
( 0058
) 但我也嘗試了MOV
(這里80d2
)。 沒有成功
我嘗試了從 0 到 2048 的任何值,它開始報告Illegal instruction
and exit code 132
。
我想也許aarch64
不允許我在x64
中使用的相同技巧來打印沒有標記數據部分的數據。 我會努力創建它,但這只是一個猜測,我真的很想了解為什么這不打印任何內容。
因此,您的字符串位於絕對地址0x10098
,您需要將此地址放入x1
寄存器中。
首先, LDR
不是你想要的。 顧名思義就是從memory加載(讀取)。你根本不想讓你的指令去訪問memory,它只是想把值0x10098
放到寄存器中。
MOV
更接近,它向寄存器寫入一個立即數,但問題是立即數限制為 16 位,而你需要 17。因為指令是 32 位,所以立即數只有這么多位。 我的猜測是你溢出了它並最終改變了操作碼位,所以你編碼了一個完全不同的指令。 (不要猜測編碼。查找它們。這會向您顯示 16 位限制。)
為了將任意立即值存入寄存器,預期的方法是使用一系列MOV/MOVK
指令一次寫入 16 位。 在這里你只需要其中兩個:
0: d2801301 mov x1, #0x98 // #152
4: f2a00021 movk x1, #0x1, lsl #16
雖然因為我們使用了一個額外的詞,字符串的地址也會發生變化,所以你必須相應地進行調整。
但是,特別是對於地址,AArch64 提供了相對於 pc 的地址生成指令ADR/ADRP
。 這些使您可以將立即值添加到程序計數器的當前值(即當前執行指令的地址)並將結果寫入寄存器。 作為獎勵,它們為立即數分配了更多位(盡管您將不再需要它們)。
這里我們可以使用ADR
。 它的操作碼在第 31 位為 0,在第 24-28 位為10000
。 目標寄存器是位 0-4,我們想要00001
。 立即數在第 29-30 位獲得低兩位,在 5-23 獲得高位。 ADR
指令將位於絕對地址0x1007c
,我們需要0x10098
,因此位移為0x1c = 0b11100
。 因此我們想要的編碼是
0 00 10000 0000000000000000111 00001 = 0x100000e1
一些一般提示:
首先嘗試使用匯編程序編寫代碼,這樣您就可以學習指令集並能夠專注於試驗指令的作用,而不是陷入它們的編碼方式。 如果您想稍后回來手動進行編碼,那很好,但是使用匯編程序,您還可以通過一種方式來檢查您的工作。
使用調試器單步執行您的程序。 那會向您表明您的LDR
給了您一個完全虛假的值,並且可能暗示它沒有按照您認為的那樣進行。
使用strace
查看您的程序進行了哪些系統調用。 那會告訴你(我想,我沒有測試) write
確實被調用但是地址錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.