为了更好地理解ELF格式和ARM aarch64,我尝试在没有编译器的情况下创建精灵二进制文件,只需将字节与bash相对应。
威尔可以在这里看到我的努力:http://www.github.com/glaudiston/elf
我已经成功地实现了一个完全工作的精灵与sys_write和sys_exit系统为x64。
但是对于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.退出呼叫正在按预期工作,所以我也可以隐藏它:
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从98位置开始。对于如何在这里进行sys_write调用,我感到非常困惑。在x64中,我可以设置为下一个数据地址,在本例中应该是65688(由PH_VADDR_V(65536) + ELF_HEADER_SIZE(64) + ELF_BODY_SIZE(32) (没有DATA_SECTION)组成)
对于输出fd,我在r0中用2000 80d2设置值1
对于数据地址,我使用的是010c,即0c01的little endian表示--这个位:00001100000 00001最后5位是r1寄存器,用于数据地址。
考虑到这里只有11位,我使用了LDR (0058),但是我也尝试过MOV (这里是80d2)。没有成功
我尝试过从0到2048的任何值,其中它开始报告Illegal instruction和退出代码132。
我认为aarch64可能不允许我在x64中使用的方法在没有标签数据部分的情况下打印数据。我将致力于创建它,但这只是一个猜测,我真的想了解为什么这个没有打印任何东西。
发布于 2022-11-22 14:55:33
因此,您的字符串位于绝对地址0x10098,您需要将该地址输入x1寄存器。
首先,LDR不是你想要的。顾名思义,它是来自内存的load (read)。您根本不希望指令访问内存,它只是想将值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确实会被调用,但是地址是错误的。https://stackoverflow.com/questions/74532163
复制相似问题