iOS ARM64系统调用

iOS ARM64系统调用,ios,assembly,arm64,Ios,Assembly,Arm64,我正在学习更多关于外壳代码和在iOS设备上使用arm64进行系统调用的知识。我正在测试的设备是iphone6s 我从这个链接得到了系统调用列表 我了解到x8用于从这里输入arm64的系统调用号 我认为用于为arm64传递参数的各种寄存器应该与arm相同,所以我引用了这个链接 我在Xcode中编写了内联汇编,下面是一些代码片段 //exit syscall __asm__ volatile("mov x8, #1"); __asm__ volatile("mov x0, #0"); __asm__

我正在学习更多关于外壳代码和在iOS设备上使用arm64进行系统调用的知识。我正在测试的设备是iphone6s

我从这个链接得到了系统调用列表

我了解到x8用于从这里输入arm64的系统调用号

我认为用于为arm64传递参数的各种寄存器应该与arm相同,所以我引用了这个链接

我在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等,因此可能会有一些变化。我尝试了各种方法,但无法使这两个代码段正常工作

有人能提供一些指导吗

编辑: 这就是我编写C函数syscall int return_value=syscall1,0时Xcode在其汇编视图中显示的内容


我不知道为什么会发出这段代码。

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

据我所知,用于arm64的XNU系统调用ABI是完全私有的,可能会在不通知的情况下进行更改,因此没有发布它所遵循的标准,但是您可以通过获取XNU源代码的副本,或者如果您愿意的话,获取handle_svc函数的grep,然后只需遵循代码即可大致了解它的工作原理。 我不打算详细说明你到底在哪里找到了哪些比特,但最终结果是:

传递给svc的立即数被忽略,但标准库使用svc 0x80。 x16保存系统调用号 x0到x8最多可容纳9个参数* 堆栈上没有参数 x0和x1最多可保存2个返回值,例如,在拨叉的情况下 进位用于报告错误,在这种情况下x0保存错误代码 *这仅用于具有8个参数的间接系统调用x16=0的情况。 *XNU源代码中的评论也提到了x9,但似乎编写该代码的工程师应该对其中的一个错误进行反思

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

UNIX系统调用的规范源是XNU源代码树中的文件bsd/kern/syscalls.master。在最新的iOS13测试版中,这些系统调用号从0到大约540。 Mach系统调用的规范源是XNU源代码树中的文件osfmk/kern/syscall_sw.c。这些系统调用是用-10到-100之间的负数调用的,例如-28将是task_self_trap。 与最后一点无关,两个系统调用mach_absolute_time和mach_continuous_time可以分别用系统调用号-3和-4调用。 一些低级操作可通过系统调用号为0x8000000的平台_syscall获得。
这会让你走的。正如@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]));
    }
}
我还没有听说过因直接通过svc 0x80使用系统调用而导致iOS应用程序AppStore被拒绝的案例,尽管它绝对不是公共API


至于建议的=@ccc by@PeterCordes,即syscall在出错时设置的进位标志,作为输出约束,在最新的XCode11 beta/LLVM 8.0.0版本中不受支持,即使对于x86也是如此,对于ARM肯定不受支持。

为什么这是标记的外壳代码?您是否计划使用编译器+内联asm来生成漏洞利用负载,而不是在asm中手工编写?如果您通过优化编译了第二个代码段,那么写入缓冲区[]中的存储可能会被优化为死区,因为您不使用内存缓冲区或虚拟内存源输入。寄存器中的指针并不意味着指向内存的指针也是asm语句的输入或输出。此外,还可以使用register char*buf asmx1使编译器为r约束选择x1。这使您可以将asm语句简化为仅包含系统调用指令,而不包含mov指令。但是你必须记住指定一个x0输出。我认为用于为arm64传入参数的各种寄存器应该与arm相同——这听起来像是一个非常危险的假设。您是否尝试单步进入libc write system call wrapper函数查看它的功能?Hi@PeterCordes我知道它们不一样,但我没有找到一个特定于iOS ARM64的链接,但此链接似乎表明x0-x5用于ARM64系统调用。我不太明白你对第二段的建议。所以,若我只是想用write将一些东西打印到控制台屏幕上,你们能举一个简单的例子来说明我是如何做到的吗?甚至我的退出系统调用代码段也不起作用。这些代码片段不会在Xcode中导致任何错误,只是它们似乎没有像我预期的那样执行。我对iOS没有任何具体的了解,否则我会发布一个答案。我建议使用调试器单步进入libc w
rite函数以查看现有库代码如何进行系统调用。或者,如果可以找到write wrapper函数,只需反汇编C库。我假设iOS与普通Unix类似,它有一个libc.so或一些包含系统调用包装函数的东西,您可以从C.Hi@Siguza调用。我尝试了您的方法,它起了作用,但我注意到了一些东西。一些系统调用可以按预期执行getpid、getppid等,但其他系统调用不打开/写入文件、创建文件夹等。我想这与Unix文件权限有关,对吗?当我运行getlogin系统调用时,它返回了mobile,所以我猜mobile只有受限的权限。感谢帮助UNIX文件权限发挥作用,是的,mobile是非特权用户,但如果您是在非越狱设备上从普通应用程序运行此应用程序,那么它的作用远不止这些。具体来说,AppleMobileFileIntegrity和Sandbox kexts通过XNU的MACF框架钩住数百个函数,可以禁止基于多种标准的操作。除此之外,它还用于将每个应用程序限制在自己的容器中。尝试运行chdir[NSHomeDirectory stringByAppendingPathComponent:@Documents].UTF8String,然后在这些open/mkdir调用之前,它们应该会成功。您需要GNU C Extended asm在修改的寄存器上声明碰撞。另外,您不能假设寄存器值在两个asm语句之间存在。使用asm volatilesvc 0x80:=r retval:r callnum,…:记忆力对于每个系统调用,使用register long x0 asmx0 local register vars使用适当的输入约束来告诉编译器输入和输出的位置。@PeterCordes一如既往地感谢您的反馈。我现在试图纠正第一个错误。如果寄存器输入值是硬编码的,我还需要r部件吗?对于retval也是一样,因为我只关心系统调用的副作用,所以如果我不需要输出,我还需要标记它们吗?对于第一个系统调用来说,这几乎是正确的。您仍然缺少一个告诉编译器您读取了输入寄存器指向的内存的内存碰撞器。通过对x0使用输入和/或输出约束,可以提高效率。。2和x16,而不是将mov指令放入模板中。mov x1,%0特别无用,因为编译器已经需要在其他寄存器中生成指针。这就是寄存器char*x1 asmx1;是for。您通常需要一个=r输出,并为x0指定一个寄存器变量,这样您就可以在需要时检查返回值,而不只是在x0上声明一个clobber。有关使用寄存器asm局部变量使r和=r约束选择特定寄存器的32位ARM示例,请参阅。@PeterCordes您在syscall定义的标题上是正确的,更新了答案
#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]));
    }
}
#ifdef __APPLE_API_PRIVATE
#define SYS_syscall        0
#define SYS_exit           1
#define SYS_fork           2
#define SYS_read           3
#define SYS_write          4