assembly elf aarch64使用sys_write进行了高尔夫练习

soat7uwm  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(141)

为了更好地理解ELF和ARM aarch64,我尝试在没有编译器的情况下创建elf二进制文件,只使用bash回显字节。
威尔在这里可以看到我的努力:http://www.github.com/glaudiston/elf
我已经成功地实现了一个完全工作的elf与sys_writesys_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,即0c01little endian表示,这些位:00001100000 00001最后5位是r1寄存器,用于数据地址。
假设我只有11位,这里我使用了LDR0058),但我也尝试了MOV(这里是80d2)。没有成功
我尝试了从0到2048的任何值,在这些值开始报告Illegal instruction和退出代码132
我想也许aarch64不允许我在x64中使用的相同的技巧打印没有标签数据节的数据。我将创建它,但这只是一个猜测,我真的想了解为什么这不是打印什么。

rhfm7lfc

rhfm7lfc1#

因此,您的字符串位于绝对地址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

但是由于我们使用了一个额外的单词,字符串的地址也会发生变化,因此您必须进行相应的调整。
然而,特别是对于地址,AArch 64提供了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确实被调用了,但地址是错误的。

相关问题