1 #include <TargetConditionals.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #if __has_include(<ptrauth.h>)
9 #include <ptrauth.h>
10 #endif
11
12 #include <sys/mman.h>
13 #include <sys/syslimits.h>
14
15 char *cmdname;
16
17 int
main(int argc,char * argv[])18 main(
19 int argc,
20 char *argv[])
21 {
22 uint32_t page_size;
23 void *page;
24 int ch;
25 int opt_interactive;
26
27 cmdname = argv[0];
28
29 opt_interactive = 0;
30 while ((ch = getopt(argc, argv, "i")) != -1) {
31 switch (ch) {
32 case 'i':
33 opt_interactive = 1;
34 break;
35 case '?':
36 default:
37 fprintf(stdout,
38 "Usage: %s [-i]\n"
39 "\t-i: interactive\n",
40 cmdname);
41 exit(1);
42 }
43 }
44
45 page_size = getpagesize();
46 page = mmap(NULL, page_size, PROT_READ | PROT_EXEC, MAP_ANON | MAP_SHARED, -1, 0);
47 if (!page) {
48 fprintf(stderr, "%s:%d mmap() error %d (%s)\n",
49 cmdname, __LINE__,
50 errno, strerror(errno));
51 exit(1);
52 }
53 if (opt_interactive) {
54 fprintf(stdout, "allocated page at %p\n",
55 page);
56 }
57
58 if (mprotect(page, page_size, PROT_READ | PROT_WRITE) != 0) {
59 fprintf(stderr, "%s:%d mprotect(RW) error %d (%s)\n",
60 cmdname, __LINE__,
61 errno, strerror(errno));
62 exit(1);
63 }
64
65 #if __arm64__
66 // arm64 chdir() syscall
67 char chdir_code[] = {
68 0x90, 0x01, 0x80, 0xd2, // movz x16, #0xc
69 0x01, 0x10, 0x00, 0xd4, // svc #0x80
70 0xc0, 0x03, 0x5f, 0xd6, // ret
71 };
72 #elif __x86_64__
73 // x86_64 chdir() syscall
74 char chdir_code[] = {
75 0xb8, 0x0c, 0x00, 0x00, 0x02, // movl $0x200000c, %eax
76 0x49, 0x89, 0xca, // movq %rcx, %r10
77 0x0f, 0x05, // syscall
78 0xc3, // retq
79 };
80 #elif __i386__
81 // i386 chdir() syscall
82 char chdir_code[] = {
83 0x90, // nop
84 0xc3, // retq
85 };
86 #endif
87 memcpy(page, chdir_code, sizeof chdir_code);
88
89 if (opt_interactive) {
90 fprintf(stdout,
91 "changed page protection to r/w and copied code at %p\n",
92 page);
93 fprintf(stdout, "pausing...\n");
94 fflush(stdout);
95 getchar();
96 }
97
98 if (mprotect(page, page_size, PROT_READ | PROT_EXEC) != 0) {
99 fprintf(stderr, "%s:%d mprotect(RX) error %d (%s)\n",
100 cmdname, __LINE__,
101 errno, strerror(errno));
102 exit(1);
103 }
104
105 if (opt_interactive) {
106 fprintf(stdout,
107 "changed page protection to r/x at %p\n",
108 page);
109 fprintf(stdout, "pausing...\n");
110 fflush(stdout);
111 getchar();
112 }
113
114 char origdir[PATH_MAX];
115 getcwd(origdir, sizeof(origdir) - 1);
116
117 chdir("/");
118 if (opt_interactive) {
119 fprintf(stdout, "cwd before = %s\n", getwd(NULL));
120 }
121
122 void (*mychdir)(char *) = page;
123 #if __has_feature(ptrauth_calls)
124 mychdir = ptrauth_sign_unauthenticated(mychdir, ptrauth_key_function_pointer, 0);
125 #endif
126 mychdir(getenv("HOME"));
127 if (opt_interactive) {
128 fprintf(stdout, "cwd after = %s\n", getwd(NULL));
129 fprintf(stdout, "pausing...\n");
130 fflush(stdout);
131 getchar();
132 }
133
134 fprintf(stdout, "%s: WARNING: unsigned code was executed\n",
135 cmdname);
136
137 /* fail: unsigned code was executed */
138 fprintf(stdout, "%s: FAIL\n", cmdname);
139 exit(1);
140 }
141