├── aarch64_pac.py └── ios12_kernel_cache_helper.py /aarch64_pac.py: -------------------------------------------------------------------------------- 1 | # AArch64 8.3-A Pointer Authentication extension 2 | # Enhanced and Python version of xerub C++ plugin available there: https://github.com/xerub/idastuff/blob/master/arm64/aarch64_pac 3 | 4 | # Copyright (c) 2018 Eloi Benoist-Vanderbeken - Synacktiv 5 | # Copyright (c) 2018 xerub 6 | 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License version 9 | # 2 as published by the Free Software Foundation. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | import idaapi 20 | import ida_hexrays 21 | 22 | PAC_NONE = 0 23 | PAC_PACIASP = PAC_NONE + 1 24 | PAC_PACIBSP = PAC_PACIASP + 1 25 | PAC_AUTIASP = PAC_PACIBSP + 1 26 | PAC_AUTIBSP = PAC_AUTIASP + 1 27 | PAC_PACIAZ = PAC_AUTIBSP + 1 28 | PAC_PACIBZ = PAC_PACIAZ + 1 29 | PAC_AUTIAZ = PAC_PACIBZ + 1 30 | PAC_AUTIBZ = PAC_AUTIAZ + 1 31 | PAC_PACIA1716 = PAC_AUTIBZ + 1 32 | PAC_PACIB1716 = PAC_PACIA1716 + 1 33 | PAC_AUTIA1716 = PAC_PACIB1716 + 1 34 | PAC_AUTIB1716 = PAC_AUTIA1716 + 1 35 | PAC_PACIA = PAC_AUTIB1716 + 1 36 | PAC_PACIB = PAC_PACIA + 1 37 | PAC_PACDA = PAC_PACIB + 1 38 | PAC_PACDB = PAC_PACDA + 1 39 | PAC_AUTIA = PAC_PACDB + 1 40 | PAC_AUTIB = PAC_AUTIA + 1 41 | PAC_AUTDA = PAC_AUTIB + 1 42 | PAC_AUTDB = PAC_AUTDA + 1 43 | PAC_PACIZA = PAC_AUTDB + 1 44 | PAC_PACIZB = PAC_PACIZA + 1 45 | PAC_PACDZA = PAC_PACIZB + 1 46 | PAC_PACDZB = PAC_PACDZA + 1 47 | PAC_AUTIZA = PAC_PACDZB + 1 48 | PAC_AUTIZB = PAC_AUTIZA + 1 49 | PAC_AUTDZA = PAC_AUTIZB + 1 50 | PAC_AUTDZB = PAC_AUTDZA + 1 51 | PAC_PACGA = PAC_AUTDZB + 1 52 | PAC_XPACLRI = PAC_PACGA + 1 53 | PAC_XPACI = PAC_XPACLRI + 1 54 | PAC_XPACD = PAC_XPACI + 1 55 | PAC_RETAA = PAC_XPACD + 1 56 | PAC_RETAB = PAC_RETAA + 1 57 | PAC_BRAA = PAC_RETAB + 1 58 | PAC_BRAB = PAC_BRAA + 1 59 | PAC_BRAAZ = PAC_BRAB + 1 60 | PAC_BRABZ = PAC_BRAAZ + 1 61 | PAC_BLRAA = PAC_BRABZ + 1 62 | PAC_BLRAB = PAC_BLRAA + 1 63 | PAC_BLRAAZ = PAC_BLRAB + 1 64 | PAC_BLRABZ = PAC_BLRAAZ + 1 65 | PAC_ERETAA = PAC_BLRABZ + 1 66 | PAC_ERETAB = PAC_ERETAA + 1 67 | PAC_LDRAA = PAC_ERETAB + 1 68 | PAC_LDRAB = PAC_LDRAA + 1 69 | 70 | OP_NAMES = { 71 | PAC_PACIASP: "PACIASP", 72 | PAC_PACIBSP: "PACIBSP", 73 | PAC_AUTIASP: "AUTIASP", 74 | PAC_AUTIBSP: "AUTIBSP", 75 | PAC_PACIAZ: "PACIAZ", 76 | PAC_PACIBZ: "PACIBZ", 77 | PAC_AUTIAZ: "AUTIAZ", 78 | PAC_AUTIBZ: "AUTIBZ", 79 | PAC_PACIA1716: "PACIA1716", 80 | PAC_PACIB1716: "PACIB1716", 81 | PAC_AUTIA1716: "AUTIA1716", 82 | PAC_AUTIB1716: "AUTIB1716", 83 | PAC_PACIA: "PACIA", 84 | PAC_PACIB: "PACIB", 85 | PAC_PACDA: "PACDA", 86 | PAC_PACDB: "PACDB", 87 | PAC_AUTIA: "AUTIA", 88 | PAC_AUTIB: "AUTIB", 89 | PAC_AUTDA: "AUTDA", 90 | PAC_AUTDB: "AUTDB", 91 | PAC_PACIZA: "PACIZA", 92 | PAC_PACIZB: "PACIZB", 93 | PAC_PACDZA: "PACDZA", 94 | PAC_PACDZB: "PACDZB", 95 | PAC_AUTIZA: "AUTIZA", 96 | PAC_AUTIZB: "AUTIZB", 97 | PAC_AUTDZA: "AUTDZA", 98 | PAC_AUTDZB: "AUTDZB", 99 | PAC_PACGA: "PACGA", 100 | PAC_XPACLRI: "XPACLRI", 101 | PAC_XPACI: "XPACI", 102 | PAC_XPACD: "XPACD", 103 | PAC_RETAA: "RETAA", 104 | PAC_RETAB: "RETAB", 105 | PAC_BRAA: "BRAA", 106 | PAC_BRAB: "BRAB", 107 | PAC_BRAAZ: "BRAAZ", 108 | PAC_BRABZ: "BRABZ", 109 | PAC_BLRAA: "BLRAA", 110 | PAC_BLRAB: "BLRAB", 111 | PAC_BLRAAZ: "BLRAAZ", 112 | PAC_BLRABZ: "BLRABZ", 113 | PAC_ERETAA: "ERETAA", 114 | PAC_ERETAB: "ERETAB", 115 | PAC_LDRAA: "LDRAA", 116 | PAC_LDRAB: "LDRAB" 117 | } 118 | 119 | def decode_PAC(d, insn): 120 | if (d & 0xffffc000) == 0xdac10000: 121 | m = (d >> 10) & 7 122 | Z = (d >> 13) & 1 123 | Xn = (d >> 5) & 0x1F 124 | Xd = d & 0x1F 125 | if Z == 0: 126 | insn.itype = idaapi.ARM_hlt 127 | insn.segpref = 14 128 | insn.Op1.type = idaapi.o_reg 129 | insn.Op1.reg = Xd + 129 130 | insn.Op1.dtype = idaapi.dt_qword 131 | insn.Op3.type = idaapi.o_reg 132 | insn.Op3.reg = Xd + 129 133 | insn.Op3.dtype = idaapi.dt_qword 134 | insn.Op3.flags = 0 135 | insn.Op2.type = idaapi.o_reg 136 | insn.Op2.reg = Xn + 129 137 | insn.Op2.dtype = idaapi.dt_qword 138 | insn.Op2.flags = idaapi.OF_SHOW 139 | insn.insnpref = PAC_PACIA + m 140 | insn.size = 4 141 | return True 142 | elif Xn == 31: 143 | insn.itype = idaapi.ARM_hlt 144 | insn.segpref = 14 145 | insn.Op1.type = idaapi.o_reg 146 | insn.Op1.reg = Xd + 129 147 | insn.Op1.dtype = idaapi.dt_qword 148 | insn.Op3.type = idaapi.o_reg 149 | insn.Op3.reg = Xd + 129 150 | insn.Op3.dtype = idaapi.dt_qword 151 | insn.Op3.flags = 0 152 | insn.insnpref = PAC_PACIZA + m 153 | insn.size = 4 154 | return True 155 | if (d & 0xfffffd1f) == 0xd503211f: 156 | m = (d >> 6) & 3 157 | CRm = (d >> 9) & 1 158 | op2 = (d >> 5) & 1 159 | if CRm == 0: 160 | insn.itype = idaapi.ARM_hlt 161 | insn.segpref = 14 162 | insn.Op1.type = idaapi.o_void 163 | insn.insnpref = PAC_PACIA1716 + m 164 | elif op2: 165 | insn.itype = idaapi.ARM_hlt 166 | insn.segpref = 14 167 | insn.Op1.type = idaapi.o_void 168 | insn.insnpref = PAC_PACIASP + m 169 | else: 170 | insn.itype = idaapi.ARM_hlt 171 | insn.segpref = 14 172 | insn.Op1.type = idaapi.o_void 173 | insn.insnpref = PAC_PACIAZ + m 174 | insn.size = 4 175 | return True 176 | if (d & 0xffe0fc00) == 0x9ac03000: 177 | Xm = (d >> 16) & 0x1F 178 | Xn = (d >> 5) & 0x1F 179 | Xd = d & 0x1F 180 | insn.itype = idaapi.ARM_hlt 181 | insn.segpref = 14 182 | insn.Op1.type = idaapi.o_reg 183 | insn.Op1.reg = Xd + 129 184 | insn.Op1.dtype = idaapi.dt_qword 185 | insn.Op2.type = idaapi.o_reg 186 | insn.Op2.reg = Xn + 129 187 | insn.Op2.dtype = idaapi.dt_qword 188 | insn.Op3.type = idaapi.o_reg 189 | insn.Op3.reg = Xm + 129 190 | insn.Op3.dtype = idaapi.dt_qword 191 | insn.insnpref = PAC_PACGA 192 | insn.size = 4 193 | return True 194 | if (d & 0xfffffbe0) == 0xdac143e0: 195 | D = (d >> 10) & 1 196 | Xd = d & 0x1F 197 | insn.itype = idaapi.ARM_hlt 198 | insn.segpref = 14 199 | insn.Op1.type = idaapi.o_reg 200 | insn.Op1.reg = Xd + 129 201 | insn.Op1.dtype = idaapi.dt_qword 202 | insn.Op3.type = idaapi.o_reg 203 | insn.Op3.reg = Xd + 129 204 | insn.Op3.dtype = idaapi.dt_qword 205 | insn.Op3.flags = 0 206 | insn.insnpref = PAC_XPACI + D 207 | insn.size = 4 208 | return True 209 | if d == 0xd50320ff: 210 | insn.itype = idaapi.ARM_hlt 211 | insn.segpref = 14 212 | insn.Op1.type = idaapi.o_void 213 | insn.insnpref = PAC_XPACLRI 214 | insn.size = 4 215 | return True 216 | if (d & 0xfffffbff) == 0xd65f0bff: 217 | M = (d >> 10) & 1 218 | insn.insnpref = PAC_RETAA + M 219 | insn.itype = idaapi.ARM_ret 220 | insn.segpref = 14 221 | insn.Op1.type = idaapi.o_reg 222 | insn.Op1.reg = 30 + 129 223 | insn.Op1.dtype = idaapi.dt_qword 224 | insn.Op1.flags = 0 225 | insn.size = 4 226 | return True 227 | if (d & 0xfedff800) == 0xd61f0800: 228 | is_blr = (d >> 19) & 4 229 | Z = (d >> 24) & 1 230 | M = (d >> 10) & 1 231 | Xn = (d >> 5) & 0x1F 232 | Xm = d & 0x1F 233 | if Z == 0 and Xm == 31: 234 | insn.itype = idaapi.ARM_blr if is_blr else idaapi.ARM_br 235 | insn.segpref = 14 236 | insn.Op1.type = idaapi.o_reg 237 | insn.Op1.reg = Xn + 129 238 | insn.Op1.dtype = idaapi.dt_qword 239 | insn.insnpref = PAC_BRAAZ + M + is_blr 240 | insn.size = 4 241 | return True 242 | elif Z: 243 | insn.itype = idaapi.ARM_blr if is_blr else idaapi.ARM_br 244 | insn.segpref = 14 245 | insn.Op1.type = idaapi.o_reg 246 | insn.Op1.reg = Xn + 129 247 | insn.Op1.dtype = idaapi.dt_qword 248 | insn.Op2.type = idaapi.o_reg 249 | insn.Op2.reg = Xm + 129 250 | insn.Op2.dtype = idaapi.dt_qword 251 | insn.Op2.flags = idaapi.OF_SHOW 252 | insn.insnpref = PAC_BRAA + M + is_blr 253 | insn.size = 4 254 | return True 255 | if (d & 0xfffffbff) == 0xd69f0bff: 256 | M = (d >> 10) & 1 257 | insn.insnpref = PAC_ERETAA + M 258 | insn.itype = idaapi.ARM_eret 259 | insn.segpref = 14 260 | insn.size = 4 261 | return True 262 | if (d & 0xff200400) == 0xf8200400: 263 | M = (d >> 23) & 1 264 | imm10 = ((d & 0x400000) << 9) | ((d & 0x1ff000) << 10) 265 | offset = imm10 >> 19 266 | W = (d >> 11) & 1 267 | Xn = (d >> 5) & 0x1F 268 | Xt = d & 0x1F 269 | insn.itype = idaapi.ARM_ldr 270 | insn.segpref = 14 271 | insn.Op1.type = idaapi.o_reg 272 | insn.Op1.reg = Xt + 129 273 | insn.Op1.dtype = idaapi.dt_qword 274 | insn.Op2.type = idaapi.o_displ 275 | insn.Op2.reg = Xn + 129 276 | insn.Op2.dtype = idaapi.dt_qword 277 | insn.Op2.addr = offset 278 | if W != 0: 279 | insn.auxpref = 0x20 280 | insn.insnpref = PAC_LDRAA + M 281 | insn.size = 4 282 | return True 283 | return False 284 | 285 | class Aarch64PACHook(idaapi.IDP_Hooks): 286 | CUSTOM_INSTRUCTIONS = {idaapi.ARM_hlt, idaapi.ARM_ret, idaapi.ARM_blr, idaapi.ARM_br, idaapi.ARM_eret,idaapi.ARM_ldr} 287 | INDENT = 16 288 | def ev_ana_insn(self, outctx): 289 | return outctx.size if decode_PAC(idaapi.get_dword(outctx.ea), outctx) else 0 290 | 291 | def ev_emu_insn(self, insn): 292 | if insn.itype != idaapi.ARM_brk: 293 | return False 294 | return True 295 | 296 | def ev_out_mnem(self, outctx): 297 | if outctx.insn.itype in self.CUSTOM_INSTRUCTIONS: 298 | mnem = OP_NAMES.get(ord(outctx.insn.insnpref), None) 299 | if mnem is not None: 300 | outctx.out_custom_mnem(mnem, self.INDENT) 301 | return 1 302 | return 0 303 | 304 | class Aarch64PACPlugin(idaapi.plugin_t): 305 | flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE 306 | comment = "ARM v8.3-A Pointer Authentication extension" 307 | wanted_hotkey = "" 308 | help = "Runs transparently" 309 | wanted_name = "Aarch64 PAC" 310 | hook = None 311 | enabled = 1 312 | 313 | def init(self): 314 | if idaapi.ph_get_id() != idaapi.PLFM_ARM or idaapi.BADADDR <= 0xFFFFFFFF: 315 | return idaapi.PLUGIN_SKIP 316 | print "%s init"%self.comment 317 | self.hook = Aarch64PACHook() 318 | self.hook.hook() 319 | return idaapi.PLUGIN_KEEP 320 | 321 | def run(): 322 | pass 323 | 324 | def term(self): 325 | if self.hook is not None: 326 | self.hook.unhook() 327 | print "%s unloaded"%self.comment 328 | 329 | def PLUGIN_ENTRY(): 330 | return Aarch64PACPlugin() 331 | -------------------------------------------------------------------------------- /ios12_kernel_cache_helper.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Eloi Benoist-Vanderbeken - Synacktiv 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # * Redistributions of source code must retain the above copyright 7 | # notice, this list of conditions and the following disclaimer. 8 | # * Redistributions in binary form must reproduce the above copyright 9 | # notice, this list of conditions and the following disclaimer in the 10 | # documentation and/or other materials provided with the distribution. 11 | # * Neither the name of Synacktiv nor the 12 | # names of its contributors may be used to endorse or promote products 13 | # derived from this software without specific prior written permission. 14 | 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | import idaapi 27 | 28 | def fix_relocs(base_address, relocs_address, relocs_size): 29 | cursor = relocs_address 30 | end = relocs_address+relocs_size 31 | 32 | multiplier = 4 * (idaapi.get_dword(cursor) & 1) + 4 33 | cursor += 4 34 | 35 | print 'starting to fix relocs...' 36 | nb_relocs = 0 37 | 38 | delta = idaapi.get_dword(cursor) 39 | while delta != 0xFFFFFFFF and cursor < end: 40 | current_reloc = base_address + delta 41 | while True: 42 | decorated_addr = idaapi.get_qword(current_reloc) 43 | if decorated_addr & 0x4000000000000000 == 0: 44 | if decorated_addr & 0x8000000000000000: 45 | #tagged ptr 46 | sign_type = (decorated_addr >> 49) & 3 47 | real_addr = base_address + (decorated_addr & 0xFFFFFFFF) 48 | modifier = ((decorated_addr >> 32) & 0xFFFF) 49 | if decorated_addr & 0x1000000000000: 50 | if modifier == 0: 51 | modifier = current_reloc 52 | modifier_type = 'ptr_addr' 53 | else: 54 | modifier_type = '0x%X << 48 | ptr_addr & 0xFFFFFFFFFFFF'%(modifier) 55 | modifier = (current_reloc & 0xFFFFFFFFFFFF) | (modifier << 48) 56 | else: 57 | modifier_type = '0x%X'%modifier 58 | if sign_type == 0: 59 | decorator = 'PACIA %s'%modifier_type if modifier else 'PACIZA' 60 | elif sign_type == 1: 61 | decorator = 'PACIB %s'%modifier_type if modifier else 'PACIZB' 62 | elif sign_type == 2: 63 | decorator = 'PACDA %s'%modifier_type if modifier else 'PACDZA' 64 | elif sign_type == 3: 65 | decorator = 'PACDB %s'%modifier_type if modifier else 'PACDZB' 66 | idaapi.set_cmt(current_reloc , decorator, 1) 67 | else: 68 | real_addr = ((decorated_addr << 13) & 0xFF00000000000000) | (decorated_addr & 0x7ffffffffff) 69 | if decorated_addr & 0x40000000000: 70 | real_addr |= 0xfffc0000000000 71 | idaapi.patch_qword(current_reloc, real_addr) 72 | idaapi.op_offset(current_reloc, 0, idaapi.REF_OFF64) 73 | nb_relocs += 1 74 | delta_next_reloc = ((decorated_addr >> 51) & 0x7ff) * multiplier 75 | if delta_next_reloc == 0: 76 | break 77 | current_reloc += delta_next_reloc 78 | cursor += 4 79 | delta = idaapi.get_dword(cursor) 80 | print '%d relocs fixed!'%nb_relocs 81 | 82 | def is_iOS_12(mach_header): 83 | if idaapi.get_dword(mach_header) != 0xFEEDFACF: 84 | return False 85 | nb_load_commands = idaapi.get_dword(mach_header + 0x10) 86 | if nb_load_commands > 100: #arbitrary limit 87 | return False 88 | sizeof_load_commands = idaapi.get_dword(mach_header + 0x14) 89 | if sizeof_load_commands > 0x2000: #arbitrary limit 90 | return False 91 | load_command_limit = mach_header + 0x20 + sizeof_load_commands 92 | current_load_command = mach_header + 0x20 93 | for i in xrange(nb_load_commands): 94 | if current_load_command + 8 > load_command_limit: 95 | return False 96 | cmd = idaapi.get_dword(current_load_command) 97 | cmdsize = idaapi.get_dword(current_load_command+4) 98 | if cmdsize < 8 or current_load_command + cmdsize > load_command_limit: 99 | return False 100 | if cmd == 0x32: # LC_BUILD_VERSION 101 | platform = idaapi.get_dword(current_load_command+8) 102 | if platform != 2: # PLATFORM_IOS 103 | return False 104 | minos = idaapi.get_dword(current_load_command+8+4) 105 | if minos >> 16 != 12: # 12.X.X 106 | return False 107 | sdk = idaapi.get_dword(current_load_command+8+8) 108 | ntools = idaapi.get_dword(current_load_command+8+12) 109 | return True 110 | current_load_command += cmdsize 111 | else: 112 | return False 113 | 114 | class IDBHook(idaapi.IDB_Hooks): 115 | is_iOS12 = False 116 | is_kernelcache = False 117 | relocs_address = None 118 | def segm_added(self, segm): 119 | if idaapi.get_segm_name(segm) == 'HEADER': 120 | self.base_address = segm.startEA 121 | self.is_iOS12 = is_iOS_12(self.base_address) 122 | if idaapi.get_segm_name(segm) == '__thread_starts': 123 | self.relocs_address = segm.startEA 124 | self.relocs_size = segm.endEA-segm.startEA 125 | if idaapi.get_segm_name(segm) == '__kmod_info': 126 | self.is_kernelcache = True 127 | if self.is_iOS12 and self.relocs_address is not None: 128 | print 'iOS12 kernelcache detected!' 129 | print 'let\'s fix relocs' 130 | fix_relocs(self.base_address, self.relocs_address, self.relocs_size) 131 | return 0 132 | 133 | 134 | class IOS12KernelcacheHelper(idaapi.plugin_t): 135 | flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE | idaapi.PLUGIN_MOD 136 | wanted_name = 'iOS12 kernelcache helper' 137 | comment = "iOS12 kernelcache helper - (C) Synacktiv" 138 | wanted_hotkey = "" 139 | help = "Runs transparently" 140 | idbhook = None 141 | 142 | def init(self): 143 | if idaapi.ph_get_id() != idaapi.PLFM_ARM or idaapi.cvar.inf.filetype != idaapi.f_MACHO: 144 | return idaapi.PLUGIN_SKIP 145 | self.idbhook = IDBHook() 146 | self.idbhook.hook() 147 | print self.comment 148 | return idaapi.PLUGIN_KEEP 149 | 150 | def run(self, arg): 151 | pass 152 | 153 | def term(self): 154 | if self.idbhook is not None: 155 | self.idbhook.unhook() 156 | 157 | def PLUGIN_ENTRY(): 158 | return IOS12KernelcacheHelper() --------------------------------------------------------------------------------