├── 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()
--------------------------------------------------------------------------------