iOS ARM64 系统调用

loc*_*cct 0 assembly 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");
Run Code Online (Sandbox Code Playgroud)

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

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"
                 );
Run Code Online (Sandbox Code Playgroud)

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

有很多关于 ARM 汇编的在线文章,一些使用svc 0x80和一些使用svc 0等,因此可能会有一些变化。我尝试了各种方法,但无法使这两个代码片段起作用。

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

编辑:这是我编写 C 函数系统调用时 Xcode 在其程序集视图中显示的内容 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
Run Code Online (Sandbox Code Playgroud)

我不确定为什么会发出此代码。

Kam*_*l.S 6

这应该能让你继续前进。正如 @Siguza 提到的,您必须使用x16,而不是x8系统调用号。

#import <sys/syscall.h>
char testStringGlobal[] = "helloWorld from global variable\n";
int main(int argc, char * argv[]) {
    char testStringOnStack[] = "helloWorld from stack variable\n";
#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 \t\n"
                 //set file descriptor to STD_OUT 1 so it appears in Xcode debug output
                 "mov x0, #1 \t\n"
                 //hardcoded length
                 "mov x2, #32 \t\n"
                 //syscall write is 4
                 "mov x16, #0x4 \t\n"
                 //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 \t\n" //notice the underscore preceding variable name by convention
                 //add the offset of the testStringGlobal variable
                 "add x1,x1,_testStringGlobal@PAGEOFF \t\n"
                 //set file descriptor to STD_OUT 1 so it appears in Xcode debug output
                 "mov x0, #1 \t\n"
                 //hardcoded length
                 "mov x2, #32 \t\n"
                 //syscall write is 4
                 "mov x16, #0x4 \t\n"
                 //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]));
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

对于 @PeterCordes 的精彩评论,是的,有一个系统调用编号定义标头<sys/syscall.h>,我将其包含在上面的变体 1 中的代码片段^中。但重要的是要提到它是由 Apple 定义的,如下所示:

#ifdef __APPLE_API_PRIVATE
#define SYS_syscall        0
#define SYS_exit           1
#define SYS_fork           2
#define SYS_read           3
#define SYS_write          4
Run Code Online (Sandbox Code Playgroud)

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

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


Sig*_*uza 5

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

据我所知,arm64 的 XNU 系统调用 ABI 是完全私有的,如有更改,恕不另行通知,因此它没有遵循已发布的标准,但您可以通过获取 XNU 源代码的副本(如tarballs,或者如果您愿意,可以在线查看),grephandle_svc函数,然后只需按照代码进行操作。
我不会详细说明您到底在哪里找到哪些位,但最终结果是:

  • 传递给的立即数svc被忽略,但标准库使用svc 0x80.
  • x16 保存系统调用号
  • x0通过x8最多保留 9 个参数*
  • 堆栈上没有参数
  • x0x1保存最多 2 个返回值(例如在 的情况下fork
  • 进位位用于报告错误,在这种情况下x0保存错误代码

* 这仅在x16 = 0具有 8 个参数的间接系统调用 ( )的情况下使用。
* XNU 源代码中的评论也提到了x9,但似乎写那篇文章的工程师应该纠正一对一的错误。

然后是可用的实际系统调用号:

  • “UNIX 系统调用”的规范来源是bsd/kern/syscalls.masterXNU 源代码树中的文件。在最新的 iOS 13 测试版中,这些系统调用号0最多可达大约540
  • “Mach 系统调用”的规范来源是osfmk/kern/syscall_sw.cXNU 源代码树中的文件。这些系统调用是用和之间的负数调用的(例如,将是)。-10-100-28task_self_trap
  • 无关的最后一点,两次系统调用mach_absolute_time,并mach_continuous_time可以与系统调用号调用-3-4分别。
  • 通过platform_syscallsyscall number可以进行一些低级操作0x80000000