为了更好地理解ELF
和ARM aarch64
,我尝试在没有编译器的情况下创建elf二进制文件,只使用bash回显字节。
威尔在这里可以看到我的努力:http://www.github.com/glaudiston/elf
我已经成功地实现了一个完全工作的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}";
它生成:
第一次
它返回预期的退出代码,没有非法异常,但是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
中设置值1用2000 80d2
对于数据地址,我使用010c
,即0c01
的little endian
表示,这些位:00001100000 00001
最后5位是r1
寄存器,用于数据地址。
假设我只有11位,这里我使用了LDR
(0058
),但我也尝试了MOV
(这里是80d2
)。没有成功
我尝试了从0到2048的任何值,在这些值开始报告Illegal instruction
和退出代码132
。
我想也许aarch64
不允许我在x64
中使用的相同的技巧打印没有标签数据节的数据。我将创建它,但这只是一个猜测,我真的想了解为什么这不是打印什么。
1条答案
按热度按时间rhfm7lfc1#
因此,您的字符串位于绝对地址
0x10098
,您需要将此地址写入x1
寄存器。首先,
LDR
并不是你想要的,顾名思义,它是从内存中load(read),你根本不想让你的指令访问内存,它只想把值0x10098
放到寄存器中。MOV
更接近,它将立即数写入寄存器,但问题是立即数被限制为16位,而您需要17位。因为指令是32位,所以立即数可用的位数就只有这么多。我猜您溢出了它,最终更改了操作码位。所以你编码了一个完全不同的指令。(不要猜测编码!查一下。这会告诉你16位的限制。)要将任意的立即数写入寄存器,我们需要一系列
MOV/MOVK
指令,一次写入16位,这里只需要两条指令:但是由于我们使用了一个额外的单词,字符串的地址也会发生变化,因此您必须进行相应的调整。
然而,特别是对于地址,AArch 64提供了pc相对地址生成指令
ADR/ADRP
。这些指令允许你将一个立即数加到程序计数器的当前值(即当前执行指令的地址),并将结果写入寄存器。作为一个额外的好处,它们为立即数分配了更多的位(尽管你不再需要它们)。这里我们可以使用
ADR
。它的操作码是位31的0,位24-28的10000
。目标寄存器是位0-4,我们需要00001
。立即数的低两位是位29-30。高位在5-23。ADR
指令将位于绝对地址0x1007c
,我们需要0x10098
,因此位移量为0x1c = 0b11100
。因此,我们需要的编码为一些一般提示:
LDR
给你一个完全虚假的值,可能是一个暗示,它没有做你认为它做的事情。strace
来查看你的程序进行了哪些系统调用。这将向你显示(我想,我没有测试)write
确实被调用了,但地址是错误的。