iOS ARM64 系统调用



我正在学习更多关于shellcode的信息,并在iOS设备上的arm64中进行系统调用。我正在测试的设备是iPhone 6S。

我从此链接(https://github.com/radare/radare2/blob/master/libr/include/sflib/darwin-arm-64/ios-syscalls.txt)获得了系统调用列表。

我了解到 x8 用于从这里放置 arm64 的系统调用号码 (http://arm.ninja/2016/03/07/decoding-syscalls-in-arm64/)。

我认为用于传入 arm64 参数的各种寄存器应该与 arm 相同,所以我参考了这个链接 (https://w3challs.com/syscalls/?arch=arm_strong),取自 https://azeria-labs.com/writing-arm-shellcode/。

我用 Xcode 编写了内联程序集,这里有一些片段

//exit syscall
__asm__ volatile("mov x8, #1");
__asm__ volatile("mov x0, #0");
__asm__ volatile("svc 0x80");

但是,当我跳过这些代码时,应用程序不会终止。

char write_buffer[]="console_text";
int write_buffer_size = sizeof(write_buffer);
__asm__ volatile("mov x8,#4;"     //arm64 uses x8 for syscall number
"mov x0,#1;"     //1 for stdout file descriptor
"mov x1,%0;"    //the buffer to display
"mov x2,%1;"    //buffer size
"svc 0x80;"
:
:"r"(write_buffer),"r"(write_buffer_size)
:"x0","x1","x2","x8"
);

如果此系统调用有效,它应该在 Xcode 的控制台输出屏幕中打印出一些文本。但是,不会打印任何内容。

有许多关于ARM组装的在线文章,有些使用svc 0x80,有些使用svc 0等,因此可以有一些变体。我尝试了各种方法,但我无法让两个代码片段工作。

有人可以提供一些指导吗?

编辑: 这就是 Xcode 在编写 C 函数系统调用时在其程序集视图中显示的内容int return_value=syscall(1,0);

mov x1, sp
mov x30, #0
str x30, [x1]
orr w8, wzr, #0x1
stur    x0, [x29, #-32]         ; 8-byte Folded Spill
mov x0, x8
bl  _syscall

我不确定为什么发出这段代码。

用于系统调用的寄存器是任意的,你选择的资源对于 XNU 来说肯定是错误的。

据我所知,用于 arm64 的 XNU 系统调用 ABI 是私有的,如有更改,恕不另行通知,因此没有发布它遵循的标准,但您可以通过查看 XNU 源代码(在线查看或下载压缩包)了解它的工作原理,特别是handle_svc函数.
我不打算详细介绍您究竟在哪里找到哪些位, 但最终结果是:

  • 传递给svc的即时传递将被忽略,但标准库使用svc 0x80(请参阅此处和此处)。
  • x16保存系统呼叫号码
  • x0x8最多可容纳 9 个参数*
  • 堆栈上没有参数
  • x0x1最多可容纳 2 个返回值(例如,在fork的情况下)
  • 进位用于报告错误,在这种情况下,x0保存错误代码

*这仅用于具有 8 个参数的间接系统调用 (x16 = 0) 的情况.
* XNU 源代码中的注释也提到了x9,但似乎编写该代码的工程师应该复习

一下错误。然后是可用的实际系统呼叫号码:

  • "UNIX 系统调用"的规范源是 XNU 源代码树中bsd/kern/syscalls.master的文件。在最新的iOS 13测试版中,系统呼叫号码从0到大约540
  • "Mach 系统调用"的规范源是 XNU 源代码树中osfmk/kern/syscall_sw.c的文件。这些系统调用以-10-100之间的数调用(例如-28将是task_self_trap)。
  • 与最后一点无关,两个系统调用mach_absolute_timemach_continuous_time可以分别使用-3-4的系统调用编号调用。
  • 一些低级操作可通过系统调用号码0x80000000platform_syscall获得。

这应该让你开始。如@Siguza所述,您必须使用x16,而不是x8作为系统呼叫号码。

#import <sys/syscall.h>
char testStringGlobal[] = "helloWorld from global variablen";
int main(int argc, char * argv[]) {
char testStringOnStack[] = "helloWorld from stack variablen";
#if TARGET_CPU_ARM64
//VARIANT 1 suggested by @PeterCordes
//an an input it's a file descriptor set to STD_OUT 1 so the syscall write output appears in Xcode debug output
//as an output this will be used for returning syscall return value;
register long x0 asm("x0") = 1;
//as an input string to write
//as an output this will be used for returning syscall return value higher half (in this particular case 0)
register char *x1 asm("x1") = testStringOnStack;
//string length
register long x2 asm("x2") = strlen(testStringOnStack);
//syscall write is 4
register long x16 asm("x16") = SYS_write; //syscall write definition - see my footnote below
//full variant using stack local variables for register x0,x1,x2,x16 input
//syscall result collected in x0 & x1 using "semi" intrinsic assembler
asm volatile(//all args prepared, make the syscall
"svc #0x80"
:"=r"(x0),"=r"(x1) //mark x0 & x1 as syscall outputs
:"r"(x0), "r"(x1), "r"(x2), "r"(x16): //mark the inputs
//inform the compiler we read the memory
"memory",
//inform the compiler we clobber carry flag (during the syscall itself)
"cc");
//VARIANT 2
//syscall write for globals variable using "semi" intrinsic assembler
//args hardcoded
//output of syscall is ignored
asm volatile(//prepare x1 with the help of x8 register
"mov x1, %0 tn"
//set file descriptor to STD_OUT 1 so it appears in Xcode debug output
"mov x0, #1 tn"
//hardcoded length
"mov x2, #32 tn"
//syscall write is 4
"mov x16, #0x4 tn"
//all args prepared, make the syscall
"svc #0x80"
::"r"(testStringGlobal):
//clobbered registers list
"x1","x0","x2","x16",
//inform the compiler we read the memory
"memory",
//inform the compiler we clobber carry flag (during the syscall itself)
"cc");
//VARIANT 3 - only applicable to global variables using "page" address
//which is  PC-relative addressing to load addresses at a fixed offset from the current location (PIC code).
//syscall write for global variable using "semi" intrinsic assembler
asm volatile(//set x1 on proper PAGE
"adrp x1,_testStringGlobal@PAGE tn" //notice the underscore preceding variable name by convention
//add the offset of the testStringGlobal variable
"add x1,x1,_testStringGlobal@PAGEOFF tn"
//set file descriptor to STD_OUT 1 so it appears in Xcode debug output
"mov x0, #1 tn"
//hardcoded length
"mov x2, #32 tn"
//syscall write is 4
"mov x16, #0x4 tn"
//all args prepared, make the syscall
"svc #0x80"
:::
//clobbered registers list
"x1","x0","x2","x16",
//inform the compiler we read the memory
"memory",
//inform the compiler we clobber carry flag (during the syscall itself)
"cc");
#endif
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

编辑

为了@PeterCordes出色的评论,是的,有一个系统调用号码定义标头<sys/syscall.h>我将其包含在变体 1 的上述代码片段^中。但重要的是要提到它内部是由苹果定义的

#ifdef __APPLE_API_PRIVATE
#define SYS_syscall        0
#define SYS_exit           1
#define SYS_fork           2
#define SYS_read           3
#define SYS_write          4

我还没有听说过iOS应用程序AppStore因直接通过svc 0x80使用系统调用而被拒绝的案例,但它绝对不是公共API。

至于@PeterCordes建议的"=@ccc",即携带标志(由系统调用在错误时设置)作为输出约束,即使对于x86,最新的XCode11 beta/LLVM 8.0.0也不支持该约束,绝对不适用于ARM。

相关内容

  • 没有找到相关文章

最新更新