├── .gitignore ├── LICENSE ├── README.md └── scripts ├── kernel_asm_decomp.py ├── kernel_auto_objects.py ├── kernel_init_array.py ├── kernel_priv_smc.py ├── kernel_svc_tables.py └── kernel_x18.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018, SciresM 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kernel-scripts 2 | ![License](https://img.shields.io/badge/license-ISC-blue.svg) 3 | 4 | This is a collection of reverse engineering utility scripts for the Horizon Kernel. 5 | 6 | ## Usage 7 | 8 | Recommended usage flow is as follows: 9 | 10 | * Import types from previous reverse engineering database for older kernel revision. 11 | * SVC types and KAutoObject derivations are the most important ones. Types don't need to be correct per se, but they should exist. 12 | * Run `kernel_svc_tables.py`. 13 | * This will auto-label all SVC functions, covering ~550-600/~1550-1650 functions. 14 | * Run `kernel_auto_objects.py`. 15 | * This will auto-label all KAutoObject derivations' virtual function tables, covering an additional ~150-200 functions, for ~750/~1550-1650. 16 | * Run `kernel_asm_decomp.py` and `kernel_priv_smc.py`. 17 | * This will improve decompiler output for certain aarch64 instructions, and many supervisor smc calls. 18 | * Periodically run `kernel_x18.py` while reversing, and again when done creating the database. 19 | * This sets the "uninitialized" x18 platform register to `KThread *cur_thread;` in the decompiler output for all functions. 20 | * This is mostly not worth running when actually doing reversing, as making a change to a function prototype will undo the modifier. 21 | * After the object container/allocator initialization functions are labeled, run `kernel_init_array.py` 22 | * This script is an enormous hack, ymmv, but with container/allocator ctor funcs labeled, it auto-recognizes + names all the allocator/slabheap data. 23 | * Saves ~30 minutes of mindless manual labeling, so I consider it a must. 24 | -------------------------------------------------------------------------------- /scripts/kernel_asm_decomp.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | from idc import * 4 | from ida_hexrays import * 5 | 6 | def get_disasm(head): 7 | disasm = GetDisasm(head).lower().lstrip().rstrip().replace(',',' ') 8 | if ';' in disasm: 9 | disasm = disasm[:disasm.index(';')] 10 | return disasm.split() 11 | 12 | def get_integral_constant(constant): 13 | if constant.startswith('#'): 14 | constant = constant[1:] 15 | return int(constant, 0) 16 | 17 | def get_udc(head): 18 | disasm = get_disasm(head) 19 | mnem = disasm[0] 20 | if mnem == 'dc': 21 | assert len(disasm) == 3 22 | assert disasm[2].startswith('x') 23 | return 'void __usercall __dc_%s(_QWORD address@<%s>);' % (disasm[1], disasm[2]) 24 | elif mnem == 'tlbi': 25 | if len(disasm) == 3: 26 | assert disasm[2].startswith('x') 27 | return 'void __usercall __tlbi_%s(_QWORD address@<%s>);' % (disasm[1], disasm[2]) 28 | elif len(disasm) == 2: 29 | return 'void __tbli_%s();' % disasm[1] 30 | elif mnem == 'ic': 31 | if len(disasm) == 3: 32 | assert disasm[1] == 'ivau' 33 | assert disasm[2].startswith('x') 34 | return 'void __usercall __ic_%s(_QWORD address@<%s>);' % (disasm[1], disasm[2]) 35 | else: 36 | assert len(disasm) == 2 37 | return 'void __ic_%s();' % disasm[1] 38 | elif mnem == 'dsb': 39 | assert len(disasm) == 2 40 | assert disasm[1] in ['sy', 'ish'] 41 | return 'void __dsb_%s();' % disasm[1] 42 | elif mnem == 'prfm': 43 | assert len(disasm) == 3 44 | assert len(disasm[2]) in [4, 5] and disasm[2].startswith('[x') and disasm[2].endswith(']') 45 | assert disasm[1] == '#0x10' 46 | return 'void __usercall __prefetch_memory_pstl1keep(void *address@<%s>);' % (disasm[2][1:-1]) 47 | elif mnem == 'ldtr': 48 | if len(disasm) == 3 and len(disasm[2]) in [4, 5] and disasm[2].startswith('[x') and disasm[2].endswith(']'): 49 | assert disasm[1][0] in ['w', 'x'] 50 | if disasm[1].startswith('w'): 51 | return 'uint32_t __usercall __ldtr@<%s>(uint32_t *address@<%s>);' % (disasm[1], disasm[2][1:-1]) 52 | elif disasm[1].startswith('x'): 53 | return 'uint64_t __usercall __ldtr@<%s>(uint64_t *address@<%s>);' % (disasm[1], disasm[2][1:-1]) 54 | else: 55 | # how to handle ldtr ?, [x#, #0x?]? 56 | assert len(disasm) == 4 57 | pass 58 | return None 59 | 60 | for segea in Segments(): 61 | for funcea in Functions(segea, get_segm_end(segea)): 62 | udc_map = udcall_map_new() 63 | restore_user_defined_calls(udc_map, funcea) 64 | func_name = get_func_name(funcea) 65 | for (startea, endea) in Chunks(funcea): 66 | for head in Heads(startea, endea): 67 | udc = get_udc(head) 68 | if udc != None: 69 | existing = udcall_map_find(udc_map, head) 70 | if existing != udcall_map_end(udc_map): 71 | udcall_map_erase(udc_map, existing) 72 | c = udcall_t() 73 | parse_user_call(c, udc, False) 74 | udcall_map_insert(udc_map, head, c) 75 | decompile(funcea) 76 | save_user_defined_calls(funcea, udc_map) -------------------------------------------------------------------------------- /scripts/kernel_auto_objects.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | from ida_name import * 4 | from idc import * 5 | from ida_hexrays import * 6 | from ida_frame import * 7 | from ida_struct import * 8 | 9 | INHERITANCE = { 10 | # Auto Object base classes. 11 | 'KAutoObject' : None, 12 | 'KAutoObjectWithList' : 'KAutoObject', 13 | 'KAutoObjectWithListAllocatorAdapter' : 'KAutoObjectWithList', 14 | 'KSynchronizationObject' : 'KAutoObjectWithList', 15 | 'KSynchronizationObjectAllocatorAdapter' : 'KSynchronizationObject', 16 | 'KReadableEvent' : 'KSynchronizationObject', 17 | 'KReadableEventAllocatorAdapter' : 'KReadableEvent', 18 | 'KDebugBase' : 'KSynchronizationObject', 19 | 'KDebugBaseAllocatorAdapter' : 'KDebugBase', 20 | 21 | # Auto Object final classes 22 | 'KClientPort' : 'KSynchronizationObject', 23 | 'KClientSession' : 'KAutoObject', 24 | 'KCodeMemory' : 'KAutoObjectWithListAllocatorAdapter', 25 | 'KDebug' : 'KDebugBaseAllocatorAdapter', 26 | 'KDeviceAddressSpace' : 'KAutoObjectWithListAllocatorAdapter', 27 | 'KEvent' : 'KAutoObjectWithListAllocatorAdapter', 28 | 'KInterruptEvent' : 'KReadableEventAllocatorAdapter', 29 | 'KLightClientSession' : 'KAutoObject', 30 | 'KLightServerSession' : 'KAutoObject', 31 | 'KLightSession' : 'KAutoObjectWithListAllocatorAdapter', 32 | 'KPort' : 'KAutoObjectWithListAllocatorAdapter', 33 | 'KProcess' : 'KSynchronizationObjectAllocatorAdapter', 34 | 'KResourceLimit' : 'KAutoObjectWithListAllocatorAdapter', 35 | 'KServerPort' : 'KSynchronizationObject', 36 | 'KServerSession' : 'KSynchronizationObject', 37 | 'KSession' : 'KAutoObjectWithListAllocatorAdapter', 38 | 'KSessionRequest' : 'KAutoObject', 39 | 'KSharedMemory' : 'KAutoObjectWithListAllocatorAdapter', 40 | 'KThread' : 'KSynchronizationObjectAllocatorAdapter', 41 | 'KTransferMemory' : 'KAutoObjectWithListAllocatorAdapter', 42 | 'KIoPool' : 'KAutoObjectWithListAllocatorAdapter', 43 | 'KIoRegion' : 'KAutoObjectWithListAllocatorAdapter', 44 | } 45 | 46 | CLASS_TOKENS = { 47 | 'KAutoObject' : 0x0, 48 | 'KClientSession' : 0xD00, 49 | 'KResourceLimit' : 0x2500, 50 | 'KLightSession' : 0x4500, 51 | 'KPort' : 0x8500, 52 | 'KSession' : 0x1900, 53 | 'KSharedMemory' : 0x2900, 54 | 'KEvent' : 0x4900, 55 | 'KLightClientSession' : 0x8900, 56 | 'KLightServerSession' : 0x3100, 57 | 'KTransferMemory' : 0x5100, 58 | 'KDeviceAddressSpace' : 0x9100, 59 | 'KSessionRequest' : 0x6100, 60 | 'KCodeMemory' : 0xA100, 61 | 'KIoPool' : 0xC100, 62 | 'KIoRegion' : 0xE00, 63 | 64 | 'KSynchronizationObject' : 0x1, 65 | 'KDebug' : 0xB01, 66 | 'KThread' : 0x1301, 67 | 'KServerPort' : 0x2301, 68 | 'KServerSession' : 0x4301, 69 | 'KClientPort' : 0x8301, 70 | 'KProcess' : 0x1501, 71 | 72 | 'KReadableEvent' : 0x3, 73 | 'KInterruptEvent' : 0x703, 74 | } 75 | 76 | INVERSE_CLASS_TOKENS = {v : k for k,v in CLASS_TOKENS.items()} 77 | 78 | def MakeClassFunction(ret, name): 79 | return (name, lambda cn: '%s %s::%s(%s *__hidden this)' % (ret, cn, name, cn), lambda cn: '%s::%s' % (cn, name)) 80 | 81 | def MakeClassFunctionWithArgs(ret, name, *args): 82 | if len(args) == 0: 83 | return MakeClassFunction(ret, name) 84 | else: 85 | return (name, lambda cn: '%s %s::%s(%s *__hidden this, %s)' % (ret, cn, name, cn, ', '.join(args)), lambda cn: '%s::%s' % (cn, name)) 86 | 87 | VTABLES = { 88 | # Auto Object base classes. 89 | 'KAutoObject' : [ 90 | MakeClassFunction('void', 'Destroy'), 91 | MakeClassFunction('void', 'Finalize'), 92 | MakeClassFunction('KProcess *', 'GetOwnerProcess'), 93 | MakeClassFunction('KTypeObj', 'GetTypeObj'), 94 | MakeClassFunction('const char *', 'GetName'), 95 | ], 96 | 'KAutoObjectWithList': [ 97 | MakeClassFunction('_QWORD', 'GetId'), 98 | ], 99 | 'KSynchronizationObject': [ 100 | MakeClassFunction('void', 'OnFinalizeSynchronization'), 101 | MakeClassFunction('bool', 'IsSignaled'), 102 | #MakeClassFunction('void', 'DumpWaiters'), 103 | ], 104 | 'KReadableEvent': [ 105 | #MakeClassFunction('Result', 'Signal'), 106 | #MakeClassFunction('Result', 'Clear'), 107 | MakeClassFunction('Result', 'Reset'), 108 | ], 109 | 'KAutoObjectWithListAllocatorAdapter': [ 110 | MakeClassFunction('bool', 'IsInitialized'), 111 | MakeClassFunction('uintptr_t', 'GetPostFinalizeArgument'), 112 | ], 113 | 'KSynchronizationObjectAllocatorAdapter': [ 114 | MakeClassFunction('bool', 'IsInitialized'), 115 | MakeClassFunction('uintptr_t', 'GetPostFinalizeArgument'), 116 | ], 117 | 'KReadableEventAllocatorAdapter': [ 118 | MakeClassFunction('bool', 'IsInitialized'), 119 | MakeClassFunction('uintptr_t', 'GetPostFinalizeArgument'), 120 | ], 121 | 'KDebugBase' : [ 122 | MakeClassFunctionWithArgs('Result', 'GetThreadContextImpl', 'ThreadContext *ctx', 'KThread *thread', 'uint32_t flags'), 123 | MakeClassFunctionWithArgs('Result', 'SetThreadContextImpl', 'const ThreadContext *ctx', 'KThread *thread', 'uint32_t flags'), 124 | ], 125 | 'KDebugBaseAllocatorAdapter': [ 126 | MakeClassFunction('bool', 'IsInitialized'), 127 | MakeClassFunction('uintptr_t', 'GetPostFinalizeArgument'), 128 | ], 129 | 130 | # Auto Object final classes 131 | 'KThread' : [ 132 | MakeClassFunction('void', 'OnTimer'), # Really from multiple inheritance KTimerTask 133 | MakeClassFunction('void', 'DoTask'), # Really from multiple inhertiance KWorkerTask 134 | ] 135 | } 136 | 137 | def GetVT(cls): 138 | assert cls in INHERITANCE 139 | funcs = [] 140 | if INHERITANCE[cls] in INHERITANCE: 141 | funcs += GetVT(INHERITANCE[cls]) 142 | if cls in VTABLES: 143 | funcs += VTABLES[cls] 144 | return funcs 145 | 146 | LABELED = { 147 | 148 | } 149 | 150 | NAMES = { 151 | 152 | } 153 | 154 | seg_mapping = {idc.get_segm_name(x): (idc.get_segm_start(x), idc.get_segm_end(x)) for x in Segments()} 155 | 156 | text_start, text_end = seg_mapping['.text'] 157 | ro_start, ro_end = seg_mapping['.rodata'] 158 | 159 | def IsInText(ea): 160 | return text_start <= ea and ea < text_end 161 | 162 | def IsAncestorOf(cls, other): 163 | if cls == other: 164 | return True 165 | elif INHERITANCE[other] in INHERITANCE: 166 | return IsAncestorOf(cls, INHERITANCE[other]) 167 | else: 168 | return False 169 | 170 | def TestCommonAncestor(ancestor, classes): 171 | for cls in classes: 172 | if not IsAncestorOf(ancestor, cls): 173 | return False 174 | return True 175 | 176 | def GetShallowestCommonAncestorOfFunction(cls, others, func_id): 177 | if TestCommonAncestor(cls, others): 178 | for (vt_func_id, _, _) in GetVT(cls): 179 | if vt_func_id == func_id: 180 | return cls 181 | if INHERITANCE[cls] in INHERITANCE: 182 | ancestor = GetShallowestCommonAncestorOfFunction(INHERITANCE[cls], others, func_id) 183 | if ancestor != None: 184 | return ancestor 185 | return None 186 | 187 | def ApplyFunction(func_ea, get_type, get_name, cls): 188 | func_type = get_type(cls) 189 | func_name = get_name(cls) 190 | idc.set_name(func_ea, func_name, SN_CHECK) 191 | idc.SetType(func_ea, func_type) 192 | if func_ea not in LABELED: 193 | LABELED[func_ea] = [cls] 194 | else: 195 | LABELED[func_ea].append(cls) 196 | NAMES[func_name] = func_ea 197 | #print 'Labeled %s (%x)' % (func_name, func_ea) 198 | 199 | def ResolveAncestryConflict(cls, func_ea, func_id, get_type, get_name): 200 | common_ancestor = GetShallowestCommonAncestorOfFunction(cls, LABELED[func_ea], func_id) 201 | assert common_ancestor != None 202 | # Simple case, common ancestor name not in use. 203 | anc_name = get_name(common_ancestor) 204 | if anc_name not in NAMES or NAMES[anc_name] == func_ea: 205 | ApplyFunction(func_ea, get_type, get_name, common_ancestor) 206 | else: 207 | print common_ancestor 208 | print anc_name 209 | print NAMES 210 | print '%X' % NAMES[anc_name] 211 | print '%X' % func_ea 212 | # Common ancestor name is in use. 213 | # TODO: Resolve more complicated ancestry conflict. 214 | assert False 215 | 216 | def ApplyVirtualTable(cls, vt, vt_ea): 217 | for i in xrange(len(vt)): 218 | #print '%X' % (vt_ea + 8 * i) 219 | assert IsInText(ida_bytes.get_64bit(vt_ea + 8 * i)) 220 | for i, (func_id, get_func_type, get_func_name) in enumerate(vt): 221 | func_ea = ida_bytes.get_64bit(vt_ea + 8 * i) 222 | if func_ea not in LABELED: 223 | ApplyFunction(func_ea, get_func_type, get_func_name, cls) 224 | else: 225 | ResolveAncestryConflict(cls, func_ea, func_id, get_func_type, get_func_name) 226 | 227 | def ProcessClass(cls): 228 | if INHERITANCE[cls] in INHERITANCE: 229 | ProcessClass(INHERITANCE[cls]) 230 | vt = GetVT(cls) 231 | ea = get_name_ea(BADADDR, '%s::vt' % cls) 232 | if ea != BADADDR: 233 | # print '%s: %x' % (cls, ea) 234 | ApplyVirtualTable(cls, vt, ea) 235 | 236 | def Disassemble(head): 237 | disasm = GetDisasm(head).lower().lstrip().rstrip().replace(',',' ') 238 | if ';' in disasm: 239 | disasm = disasm[:disasm.index(';')] 240 | return disasm.split() 241 | 242 | def IsGetTypeObj(disasms): 243 | if len(disasms) == 3: 244 | if len(disasms[0]) != 3: 245 | return False 246 | if disasms[0][0] != 'adrl': 247 | return False 248 | if disasms[0][1] != 'x0': 249 | return False 250 | if len(disasms[1]) != 3: 251 | return False 252 | if disasms[1][0] != 'mov': 253 | return False 254 | if disasms[1][1] == 'w1': 255 | if disasms[1][2].startswith('#'): 256 | pass 257 | elif disasms[1][2] == 'wzr': 258 | pass 259 | else: 260 | return False 261 | elif disasms[1][1] == 'x1': 262 | if disasms[1][2].startswith('#'): 263 | pass 264 | elif disasms[1][2] == 'xzr': 265 | pass 266 | else: 267 | return False 268 | if len(disasms[2]) != 1: 269 | return False 270 | return disasms[2][0] == 'ret' 271 | elif len(disasms) == 5: 272 | if len(disasms[0]) != 3: 273 | return False 274 | if disasms[0][0] != 'adrp': 275 | return False 276 | if disasms[0][1] != 'x8': 277 | return False 278 | if len(disasms[1]) != 4: 279 | return False 280 | if disasms[1][0] != 'ldr': 281 | return False 282 | if disasms[1][1] != 'x8': 283 | return False 284 | if disasms[1][2] != '[x8': 285 | return False 286 | if disasms[1][3] != '%soff]' % disasms[0][2]: 287 | return False 288 | if len(disasms[2]) != 3: 289 | return False 290 | if disasms[2][0] != 'ldr': 291 | return False 292 | if disasms[2][1] != 'x0': 293 | return False 294 | if disasms[2][2] != '[x8]': 295 | return False 296 | if len(disasms[3]) != 3: 297 | return False 298 | if disasms[3][0] != 'mov': 299 | return False 300 | if disasms[3][1] == 'w1': 301 | if disasms[3][2].startswith('#'): 302 | pass 303 | elif disasms[3][2] == 'wzr': 304 | pass 305 | else: 306 | return False 307 | elif disasms[3][1] == 'x1': 308 | if disasms[3][2].startswith('#'): 309 | pass 310 | elif disasms[3][2] == 'xzr': 311 | pass 312 | else: 313 | return False 314 | else: 315 | return False 316 | if len(disasms[4]) != 1: 317 | return False 318 | return disasms[4][0] == 'ret' 319 | 320 | 321 | def GetVtableAddress(get_type_obj_ea): 322 | global ID_OBJECT_HELPER_VT 323 | candidates = [] 324 | ofs = ro_start & ~7 325 | while ofs < ro_end: 326 | val = ida_bytes.get_64bit(ofs) 327 | if val == get_type_obj_ea: 328 | candidates.append(ofs - 0x18) 329 | ofs += 8 330 | if len(candidates) == 1: 331 | return candidates[0] 332 | elif len(candidates) == 2: 333 | assert False 334 | # KAutoObject 335 | assert ID_OBJECT_HELPER_VT is None 336 | if ida_bytes.get_64bit(candidates[0] + 0x28) == 0: 337 | ID_OBJECT_HELPER_VT = candidates[1] 338 | return candidates[0] 339 | else: 340 | assert ida_bytes.get_64bit(candidates[1] + 0x28) == 0 341 | ID_OBJECT_HELPER_VT = candidates[0] 342 | return candidates[1] 343 | return None 344 | 345 | # Identify vtables 346 | TYPE_OBJS = {} 347 | INVERSE_TYPE_OBJS = {} 348 | for segea in [text_start]: 349 | for funcea in Functions(segea, get_segm_end(segea)): 350 | chunks = [chunk for chunk in Chunks(funcea)] 351 | if len(chunks) != 1: 352 | continue 353 | startea, endea = chunks[0] 354 | heads = [head for head in Heads(startea, endea)] 355 | if len(heads) != 3 and len(heads) != 5: 356 | continue 357 | disasms = [Disassemble(head) for head in heads] 358 | if not IsGetTypeObj(disasms): 359 | continue 360 | token = disasms[-2][2][1:] 361 | token = int(token, 0) if token != 'zr' else 0 362 | assert token not in INVERSE_TYPE_OBJS 363 | vt_ea = GetVtableAddress(funcea) 364 | assert vt_ea is not None 365 | TYPE_OBJS[vt_ea] = token 366 | INVERSE_TYPE_OBJS[token] = vt_ea 367 | assert set(INVERSE_TYPE_OBJS.keys()) == set(INVERSE_CLASS_TOKENS.keys()) 368 | #assert ID_OBJECT_HELPER_VT is not None 369 | 370 | for token in INVERSE_CLASS_TOKENS.keys(): 371 | vt_name = '%s::vt' % INVERSE_CLASS_TOKENS[token] 372 | vt_ea = INVERSE_TYPE_OBJS[token] 373 | cur_ea = get_name_ea(BADADDR, vt_name) 374 | if cur_ea != BADADDR: 375 | idc.set_name(cur_ea, '', SN_CHECK) 376 | idc.set_name(vt_ea, vt_name, SN_CHECK) 377 | print 'Found vtable for %s at 0x%x' % (INVERSE_CLASS_TOKENS[token], vt_ea) 378 | #idc.set_name(ID_OBJECT_HELPER_VT, 'IdObjectHelper::vt', SN_CHECK) 379 | 380 | # Label identified vtables 381 | for cls in INHERITANCE.keys(): 382 | ProcessClass(cls) -------------------------------------------------------------------------------- /scripts/kernel_init_array.py: -------------------------------------------------------------------------------- 1 | def decompile_func(ea): 2 | if not idaapi.init_hexrays_plugin(): 3 | return False 4 | 5 | f = idaapi.get_func(ea) 6 | if f is None: 7 | return False 8 | 9 | cfunc = idaapi.decompile(f, flags=idaapi.DECOMP_NO_CACHE); 10 | if cfunc is None: 11 | # Failed to decompile 12 | return False 13 | 14 | lines = [] 15 | sv = cfunc.get_pseudocode(); 16 | for sline in sv: 17 | line = idaapi.tag_remove(sline.line); 18 | lines.append(line) 19 | return lines 20 | 21 | seg_mapping = {idc.get_segm_name(x): (idc.get_segm_start(x), idc.get_segm_end(x)) for x in Segments()} 22 | 23 | init_array_start, init_array_end = seg_mapping['.init_array'] 24 | 25 | bss_start, bss_end = seg_mapping['.bss'] 26 | 27 | def is_bss_address(name): 28 | try: 29 | x = int(name, 16) 30 | return bss_start <= x and x < bss_end 31 | except ValueError: 32 | return False 33 | 34 | OBJECT_CONTAINERS = { 35 | 36 | } 37 | 38 | SLAB_HEAPS = { 39 | 40 | } 41 | 42 | def process_bool(obj, suffix, line): 43 | assert line.startswith(' if ( ') and line.endswith(' )') 44 | init_cond = line[len(' if ( '):-len(' )')] 45 | assert ('!' in init_cond) ^ ('== 0' in init_cond) 46 | if '!' in init_cond: 47 | assert '(' not in init_cond and ')' not in init_cond 48 | init_var = init_cond[1:] 49 | assert init_var.count('_') == 1 50 | var_addr = init_var.split('_')[1] 51 | if is_bss_address(var_addr): 52 | var_addr = int(var_addr, 16) 53 | var_name = 'g_Initialized%s%s' % (obj, suffix) 54 | print '%08x: %s' % (var_addr, var_name) 55 | idc.SetType(var_addr, 'bool %s;' % var_name) 56 | idc.set_name(var_addr, var_name, SN_CHECK) 57 | else: 58 | assert ' == 0' in init_cond 59 | assert '(' in init_cond and ')' in init_cond 60 | init_var = init_cond[1:-len(' == 0')-1] 61 | assert init_var.endswith(' & 1') 62 | init_var = init_var[:-len(' & 1')] 63 | assert init_var.count('_') == 1 64 | var_addr = init_var.split('_')[1] 65 | if is_bss_address(var_addr): 66 | var_addr = int(var_addr, 16) 67 | var_name = 'g_Initialized%s%s' % (obj, suffix) 68 | print '%08x: %s' % (var_addr, var_name) 69 | idc.SetType(var_addr, 'bool %s;' % var_name) 70 | idc.set_name(var_addr, var_name, SN_CHECK) 71 | 72 | def process_object_container(obj, line): 73 | assert line.startswith(' KObjectContainer::KObjectContainer(') and line.endswith(');') 74 | arg = line[len(' KObjectContainer::KObjectContainer('):-len(');')] 75 | assert ',' not in arg and ' ' not in arg 76 | if arg.startswith('&'): 77 | arg = arg[len('&'):] 78 | assert arg.count('_') == 1 79 | var_addr = get_name_ea(BADADDR, arg) 80 | assert is_bss_address('%x' % var_addr) 81 | var_name = 'g_%sObjectContainer' % obj 82 | idc.SetType(var_addr, 'KObjectContainer %s;' % var_name) 83 | idc.set_name(var_addr, var_name, SN_CHECK) 84 | print '%08x: %s' % (var_addr, var_name) 85 | global OBJECT_CONTAINERS 86 | OBJECT_CONTAINERS[var_addr] = obj 87 | 88 | def process_slab_heap(obj, line): 89 | assert line.startswith(' ') and line.endswith(' = 0LL;') 90 | var = line[len(' '):-len(' = 0LL;')] 91 | if '.' in var: 92 | assert var.endswith('.atomic_heap_head') 93 | var = var[:-len('.atomic_heap_head')] 94 | if '.' in var: 95 | assert var.endswith('.slab_heap') 96 | var = var[:-len('.slab_heap')] 97 | var_ofs = 0x10 98 | else: 99 | var_ofs = 0x0 100 | var_addr = get_name_ea(BADADDR, var) - var_ofs 101 | assert is_bss_address('%x' % var_addr) 102 | var_name = 'g_%sSlabHeap' % obj 103 | idc.SetType(var_addr, 'KSlabHeap %s;' % var_name) 104 | idc.set_name(var_addr, var_name, SN_CHECK) 105 | print '%08x: %s' % (var_addr, var_name) 106 | global SLAB_HEAPS 107 | SLAB_HEAPS[var_addr] = obj 108 | 109 | addr = init_array_start 110 | while addr < init_array_end: 111 | funcea = ida_bytes.get_64bit(addr) 112 | name = get_name(funcea) 113 | if name.endswith('Allocator::ConstructObjectContainer'): 114 | obj = name[:-len('Allocator::ConstructObjectContainer')] 115 | pseudocode = decompile_func(funcea) 116 | assert len(pseudocode) == 8 117 | process_bool(obj, 'Allocator', pseudocode[2]) 118 | process_object_container(obj, pseudocode[4]) 119 | elif name.endswith('Allocator::ConstructSlabHeap'): 120 | obj = name[:-len('Allocator::ConstructSlabHeap')] 121 | pseudocode = decompile_func(funcea) 122 | assert len(pseudocode) == 12 123 | process_bool(obj, 'SlabHeap', pseudocode[2]) 124 | process_slab_heap(obj, pseudocode[4]) 125 | elif name.endswith('SlabHeap::ConstructStaticObjects') and not name.startswith('KPageBufferSlabHeap'): 126 | obj = name[:-len('SlabHeap::ConstructStaticObjects')] 127 | pseudocode = decompile_func(funcea) 128 | assert len(pseudocode) == 12 129 | process_bool(obj, 'SlabHeap', pseudocode[2]) 130 | process_slab_heap(obj, pseudocode[4]) 131 | addr += 8 132 | 133 | for var_addr in OBJECT_CONTAINERS: 134 | if (var_addr + 0x10) not in SLAB_HEAPS: 135 | continue 136 | if OBJECT_CONTAINERS[var_addr] == SLAB_HEAPS[var_addr + 0x10]: 137 | var_name = 'g_%sAllocator' % OBJECT_CONTAINERS[var_addr] 138 | idc.SetType(var_addr, 'KObjectAllocator %s;' % var_name) 139 | idc.set_name(var_addr, var_name, SN_CHECK) 140 | print '%08x: %s' % (var_addr, var_name) -------------------------------------------------------------------------------- /scripts/kernel_priv_smc.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | from idc import * 4 | from ida_hexrays import * 5 | 6 | SMC_CALLING_CONVENTION = '__usercall __spoils' 7 | 8 | def make_smc_prototype_args(args): 9 | if len(args) == 0: 10 | return 'void' 11 | return ', '.join('%s@' % (arg,i+1) for i,arg in enumerate(args)) 12 | 13 | def make_smc_prototype(name, *args): 14 | return '_DWORD %s smc_%s@(%s);' % (SMC_CALLING_CONVENTION, name, make_smc_prototype_args(list(args))) 15 | 16 | PRIV_SMCS = { 17 | 0xC4000001 : make_smc_prototype('cpu_suspend', '_QWORD power_state', '_QWORD entrypoint', '_QWORD context_id'), 18 | 0x84000002 : make_smc_prototype('cpu_off'), 19 | 0xC4000003 : make_smc_prototype('cpu_on', '_QWORD core_id', '_QWORD entrypoint', '_QWORD context_id'), 20 | 0xC3000004 : make_smc_prototype('get_config', '_QWORD which'), 21 | 0xC3000005 : make_smc_prototype('generate_random_bytes', '_QWORD size'), 22 | 0xC3000006 : make_smc_prototype('panic', '_QWORD color'), 23 | 0xC3000007 : make_smc_prototype('configure_carveout', '_QWORD which', '_QWORD address', '_QWORD size'), 24 | 0xC3000008 : make_smc_prototype('read_write_register', '_QWORD address', '_DWORD mask', '_DWORD value'), 25 | } 26 | 27 | def get_integral_constant(constant): 28 | if constant.startswith('#'): 29 | constant = constant[1:] 30 | return int(constant, 0) 31 | 32 | def get_smc(head): 33 | disasm = GetDisasm(head).lower() 34 | if 'smc ' not in disasm: 35 | return None 36 | disasm = disasm[disasm.index('smc ')+3:].lstrip() 37 | if ' ' in disasm: 38 | disasm = disasm[:disasm.index(' ')] 39 | if ';' in disasm: 40 | disasm = disasm[:disasm.index(';')] 41 | return get_integral_constant(disasm) 42 | 43 | def get_mov_constant(startea, h, head, regs): 44 | disasm = GetDisasm(head).lower().lstrip().rstrip().replace(',',' ') 45 | if ';' in disasm: 46 | disasm = disasm[:disasm.index(';')] 47 | disasm = disasm.split() 48 | if len(disasm) < 2: 49 | return None 50 | if disasm[1] in regs: 51 | if disasm[0] in ['cbz', 'cbnz']: 52 | return None 53 | elif disasm[0] in ['mrs', 'add', 'sub', 'and', 'orr', 'ldr']: 54 | return -1 55 | print '%X' % head 56 | assert disasm[0] == 'mov' and len(disasm) == 3 57 | if disasm[2].startswith('#'): 58 | return get_integral_constant(disasm[2]) 59 | else: 60 | print disasm[2] 61 | assert disasm[2].startswith('x') 62 | mov_constant = None 63 | for new_head in Heads(startea, h): 64 | if new_head >= mov_head: 65 | return mov_constant 66 | cur_mov_constant = get_mov_constant(startea, head, mov_head, [disasm[2], disasm[2].replace('x','w')]) 67 | if cur_mov_constant != None: 68 | mov_constant = cur_mov_constant 69 | return mov_constant 70 | return None 71 | 72 | for segea in Segments(): 73 | for funcea in Functions(segea, get_segm_end(segea)): 74 | udc_map = udcall_map_new() 75 | restore_user_defined_calls(udc_map, funcea) 76 | func_name = get_func_name(funcea) 77 | for (startea, endea) in Chunks(funcea): 78 | for head in Heads(startea, endea): 79 | smc = get_smc(head) 80 | if smc == 0: 81 | print '%s: 0x%x Found User SMC, currently not supported' % (func_name, head) 82 | elif smc == 1: 83 | mov_constant = None 84 | for mov_head in Heads(startea, head): 85 | cur_mov_constant = get_mov_constant(startea, head, mov_head, ['x0', 'w0']) 86 | if cur_mov_constant != None: 87 | mov_constant = cur_mov_constant 88 | if mov_constant == None: 89 | print '%s: 0x%x Found Priv SMC with unknown constant' % (func_name, head) 90 | elif mov_constant not in PRIV_SMCS.keys(): 91 | print '%s: 0x%x Found Priv SMC with unknown constant %x' % (func_name, head, mov_constant) 92 | else: 93 | print '%s: 0x%x Found Priv SMC' % (func_name, head) 94 | existing = udcall_map_find(udc_map, head) 95 | if existing != udcall_map_end(udc_map): 96 | udcall_map_erase(udc_map, existing) 97 | c = udcall_t() 98 | parse_user_call(c, PRIV_SMCS[mov_constant], False) 99 | udcall_map_insert(udc_map, head, c) 100 | elif smc is not None: 101 | print 'Found unknown SMC (%d)' % smc 102 | save_user_defined_calls(funcea, udc_map) -------------------------------------------------------------------------------- /scripts/kernel_svc_tables.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | from idc import * 4 | from ida_hexrays import * 5 | from ida_bytes import * 6 | from ida_funcs import * 7 | 8 | SVC_MAPPINGS = { 9 | 0x01 : ("SetHeapSize", "Result %s(uintptr_t *out_address, size_t size);"), 10 | 0x02 : ("SetMemoryPermission", "Result %s(uintptr_t address, size_t size, MemoryPermission perm);"), 11 | 0x03 : ("SetMemoryAttribute", "Result %s(uintptr_t address, size_t size, uint32_t mask, uint32_t attr);"), 12 | 0x04 : ("MapMemory", "Result %s(uintptr_t dst_address, uintptr_t src_address, size_t size);"), 13 | 0x05 : ("UnmapMemory", "Result %s(uintptr_t dst_address, uintptr_t src_address, size_t size);"), 14 | 0x06 : ("QueryMemory", "Result %s(MemoryInfo *out_memory_info, PageInfo *out_page_info, uintptr_t address);"), 15 | 0x07 : ("ExitProcess", "void %s();"), 16 | 0x08 : ("CreateThread", "Result %s(Handle *out_handle, ThreadFunc func, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id);"), 17 | 0x09 : ("StartThread", "Result %s(Handle thread_handle);"), 18 | 0x0A : ("ExitThread", "void %s();"), 19 | 0x0B : ("SleepThread", "void %s(int64_t ns);"), 20 | 0x0C : ("GetThreadPriority", "Result %s(int32_t *out_priority, Handle thread_handle);"), 21 | 0x0D : ("SetThreadPriority", "Result %s(Handle thread_handle, int32_t priority);"), 22 | 0x0E : ("GetThreadCoreMask", "Result %s(int32_t *out_core_id, uint64_t *out_affinity_mask, Handle thread_handle);"), 23 | 0x0F : ("SetThreadCoreMask", "Result %s(Handle thread_handle, int32_t core_id, uint64_t affinity_mask);"), 24 | 0x10 : ("GetCurrentProcessorNumber", "int32_t %s();"), 25 | 0x11 : ("SignalEvent", "Result %s(Handle event_handle);"), 26 | 0x12 : ("ClearEvent", "Result %s(Handle event_handle);"), 27 | 0x13 : ("MapSharedMemory", "Result %s(Handle shmem_handle, uintptr_t address, size_t size, MemoryPermission map_perm);"), 28 | 0x14 : ("UnmapSharedMemory", "Result %s(Handle shmem_handle, uintptr_t address, size_t size);"), 29 | 0x15 : ("CreateTransferMemory", "Result %s(Handle *out_handle, uintptr_t address, size_t size, MemoryPermission map_perm);"), 30 | 0x16 : ("CloseHandle", "Result %s(Handle handle);"), 31 | 0x17 : ("ResetSignal", "Result %s(Handle handle);"), 32 | 0x18 : ("WaitSynchronization", "Result %s(int32_t *out_index, const Handle *handles, int32_t num_handles, int64_t timeout_ns);"), 33 | 0x19 : ("CancelSynchronization", "Result %s(Handle handle);"), 34 | 0x1A : ("ArbitrateLock", "Result %s(Handle thread_handle, uintptr_t address, uint32_t tag);"), 35 | 0x1B : ("ArbitrateUnlock", "Result %s(uintptr_t address);"), 36 | 0x1C : ("WaitProcessWideKeyAtomic", "Result %s(uintptr_t address, uintptr_t cv_key, uint32_t tag, int64_t timeout_ns);"), 37 | 0x1D : ("SignalProcessWideKey", "void %s(uintptr_t cv_key, int32_t count);"), 38 | 0x1E : ("GetSystemTick", "int64_t %s();"), 39 | 0x1F : ("ConnectToNamedPort", "Result %s(Handle *out_handle, const char *name);"), 40 | 0x20 : ("SendSyncRequestLight", "Result %s(Handle session_handle);"), 41 | 0x21 : ("SendSyncRequest", "Result %s(Handle session_handle);"), 42 | 0x22 : ("SendSyncRequestWithUserBuffer", "Result %s(uintptr_t message_buffer, size_t message_buffer_size, Handle session_handle);"), 43 | 0x23 : ("SendAsyncRequestWithUserBuffer", "Result %s(Handle *out_event_handle, uintptr_t message_buffer, size_t message_buffer_size, Handle session_handle);"), 44 | 0x24 : ("GetProcessId", "Result %s(uint64_t *out_process_id, Handle process_handle);"), 45 | 0x25 : ("GetThreadId", "Result %s(uint64_t *out_thread_id, Handle thread_handle);"), 46 | 0x26 : ("Break", "void %s(BreakReason break_reason, uintptr_t arg, size_t size);"), 47 | 0x27 : ("OutputDebugString", "Result %s(const char *debug_str, size_t len);"), 48 | 0x28 : ("ReturnFromException", "void %s(Result result);"), 49 | 0x29 : ("GetInfo", "Result %s(uint64_t *out, InfoType info_type, Handle handle, uint64_t info_subtype);"), 50 | 0x2A : ("FlushEntireDataCache", "void %s();"), 51 | 0x2B : ("FlushDataCache", "Result %s(uintptr_t address, size_t size);"), 52 | 0x2C : ("MapPhysicalMemory", "Result %s(uintptr_t address, size_t size);"), 53 | 0x2D : ("UnmapPhysicalMemory", "Result %s(uintptr_t address, size_t size);"), 54 | 0x2E : ("GetDebugFutureThreadInfo", "Result %s(SvcLastThreadContext|| *out_context, uint64_t *thread_id, Handle debug_handle, int64_t ns);"), 55 | 0x2F : ("GetLastThreadInfo", "Result %s(SvcLastThreadContext|| *out_context, uintptr_t *out_tls_address, uint32_t *out_flags);"), 56 | 0x30 : ("GetResourceLimitLimitValue", "Result %s(int64_t *out_limit_value, Handle resource_limit_handle, LimitableResource which);"), 57 | 0x31 : ("GetResourceLimitCurrentValue", "Result %s(int64_t *out_current_value, Handle resource_limit_handle, LimitableResource which);"), 58 | 0x32 : ("SetThreadActivity", "Result %s(Handle thread_handle, ThreadActivity thread_activity);"), 59 | 0x33 : ("GetThreadContext3", "Result %s(ThreadContext *out_context, Handle thread_handle);"), 60 | 0x34 : ("WaitForAddress", "Result %s(uintptr_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);"), 61 | 0x35 : ("SignalToAddress", "Result %s(uintptr_t address, SignalType signal_type, int32_t value, int32_t count);"), 62 | 0x36 : ("SynchronizePreemptionState", "void %s();"), 63 | 0x37 : ("GetResourceLimitPeakValue", "Result %s(int64_t *out_peak_value, Handle resource_limit_handle, LimitableResource which);"), 64 | 0x39 : ("CreateIoPool", "Result %s(Handle *out_handle, uint32_t which);"), 65 | 0x3A : ("CreateIoRegion", "Result %s(Handle *out_handle, Handle io_pool, PhysicalAddress physical_address, size_t size, MemoryMapping mapping, MemoryPermission perm);"), 66 | 0x3C : ("KernelDebug", "void %s(KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);"), 67 | 0x3D : ("ChangeKernelTraceState", "void %s(KernelTraceState kern_trace_state);"), 68 | 0x40 : ("CreateSession", "Result %s(Handle *out_server_session_handle, Handle *out_client_session_handle, bool is_light, uintptr_t name);"), 69 | 0x41 : ("AcceptSession", "Result %s(Handle *out_handle, Handle port);"), 70 | 0x42 : ("ReplyAndReceiveLight", "Result %s(Handle handle);"), 71 | 0x43 : ("ReplyAndReceive", "Result %s(int32_t *out_index, const Handle *handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"), 72 | 0x44 : ("ReplyAndReceiveWithUserBuffer", "Result %s(int32_t *out_index, uintptr_t message_buffer, size_t message_buffer_size, const Handle *handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"), 73 | 0x45 : ("CreateEvent", "Result %s(Handle *out_write_handle, Handle *out_read_handle);"), 74 | 0x46 : ("MapIoRegion", "Result %s(Handle iomem_handle, uintptr_t address, size_t size, MemoryPermission perm);"), 75 | 0x47 : ("UnmapIoRegion", "Result %s(Handle iomem_handle, uintptr_t address, size_t size);"), 76 | 0x48 : ("MapPhysicalMemoryUnsafe", "Result %s(uintptr_t address, size_t size);"), 77 | 0x49 : ("UnmapPhysicalMemoryUnsafe", "Result %s(uintptr_t address, size_t size);"), 78 | 0x4A : ("SetUnsafeLimit", "Result %s(size_t limit);"), 79 | 0x4B : ("CreateCodeMemory", "Result %s(Handle *out_handle, uintptr_t address, size_t size);"), 80 | 0x4C : ("ControlCodeMemory", "Result %s(Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);"), 81 | 0x4D : ("SleepSystem", "void %s();"), 82 | 0x4E : ("ReadWriteRegister", "Result %s(uint32_t *out_value, PhysicalAddress address, uint32_t mask, uint32_t value);"), 83 | 0x4F : ("SetProcessActivity", "Result %s(Handle process_handle, ProcessActivity process_activity);"), 84 | 0x50 : ("CreateSharedMemory", "Result %s(Handle *out_handle, size_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);"), 85 | 0x51 : ("MapTransferMemory", "Result %s(Handle trmem_handle, uintptr_t address, size_t size, MemoryPermission owner_perm);"), 86 | 0x52 : ("UnmapTransferMemory", "Result %s(Handle trmem_handle, uintptr_t address, size_t size);"), 87 | 0x53 : ("CreateInterruptEvent", "Result %s(Handle *out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);"), 88 | 0x54 : ("QueryPhysicalAddress", "Result %s(SvcPhysicalMemoryInfo|| *out_info, uintptr_t address);"), 89 | 0x55 : ("QueryIoMapping", "Result %s(uintptr_t *out_address, size_t *out_size, PhysicalAddress physical_address, size_t size);"), 90 | 0x56 : ("CreateDeviceAddressSpace", "Result %s(Handle *out_handle, uint64_t das_address, uint64_t das_size);"), 91 | 0x57 : ("AttachDeviceAddressSpace", "Result %s(DeviceName device_name, Handle das_handle);"), 92 | 0x58 : ("DetachDeviceAddressSpace", "Result %s(DeviceName device_name, Handle das_handle);"), 93 | 0x59 : ("MapDeviceAddressSpaceByForce", "Result %s(Handle das_handle, Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, MemoryPermission device_perm);"), 94 | 0x5A : ("MapDeviceAddressSpaceAligned", "Result %s(Handle das_handle, Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, MemoryPermission device_perm);"), 95 | 0x5B : ("MapDeviceAddressSpace", "Result %s(size_t *out_mapped_size, Handle das_handle, Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, MemoryPermission device_perm);"), 96 | 0x5C : ("UnmapDeviceAddressSpace", "Result %s(Handle das_handle, Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address);"), 97 | 0x5D : ("InvalidateProcessDataCache", "Result %s(Handle process_handle, uint64_t address, uint64_t size);"), 98 | 0x5E : ("StoreProcessDataCache", "Result %s(Handle process_handle, uint64_t address, uint64_t size);"), 99 | 0x5F : ("FlushProcessDataCache", "Result %s(Handle process_handle, uint64_t address, uint64_t size);"), 100 | 0x60 : ("DebugActiveProcess", "Result %s(Handle *out_handle, uint64_t process_id);"), 101 | 0x61 : ("BreakDebugProcess", "Result %s(Handle debug_handle);"), 102 | 0x62 : ("TerminateDebugProcess", "Result %s(Handle debug_handle);"), 103 | 0x63 : ("GetDebugEvent", "Result %s(DebugEventInfo|| *out_info, Handle debug_handle);"), 104 | 0x64 : ("ContinueDebugEvent", "Result %s(Handle debug_handle, uint32_t flags, const uint64_t *thread_ids, int32_t num_thread_ids);"), 105 | 0x65 : ("GetProcessList", "Result %s(int32_t *out_num_processes, uint64_t *out_process_ids, int32_t max_out_count);"), 106 | 0x66 : ("GetThreadList", "Result %s(int32_t *out_num_threads, uint64_t *out_thread_ids, int32_t max_out_count, Handle debug_handle);"), 107 | 0x67 : ("GetDebugThreadContext", "Result %s(ThreadContext *out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);"), 108 | 0x68 : ("SetDebugThreadContext", "Result %s(Handle debug_handle, uint64_t thread_id, const ThreadContext *context, uint32_t context_flags);"), 109 | 0x69 : ("QueryDebugProcessMemory", "Result %s(MemoryInfo *out_memory_info, PageInfo *out_page_info, Handle process_handle, uintptr_t address);"), 110 | 0x6A : ("ReadDebugProcessMemory", "Result %s(uintptr_t buffer, Handle debug_handle, uintptr_t address, size_t size);"), 111 | 0x6B : ("WriteDebugProcessMemory", "Result %s(Handle debug_handle, uintptr_t buffer, uintptr_t address, size_t size);"), 112 | 0x6C : ("SetHardwareBreakPoint", "Result %s(HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);"), 113 | 0x6D : ("GetDebugThreadParam", "Result %s(uint64_t *out_64, uint32_t *out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);"), 114 | 0x6F : ("GetSystemInfo", "Result %s(uint64_t *out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);"), 115 | 0x70 : ("CreatePort", "Result %s(Handle *out_server_handle, Handle *out_client_handle, int32_t max_sessions, bool is_light, uintptr_t name);"), 116 | 0x71 : ("ManageNamedPort", "Result %s(Handle *out_server_handle, const char *name, int32_t max_sessions);"), 117 | 0x72 : ("ConnectToPort", "Result %s(Handle *out_handle, Handle port);"), 118 | 0x73 : ("SetProcessMemoryPermission", "Result %s(Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);"), 119 | 0x74 : ("MapProcessMemory", "Result %s(uintptr_t dst_address, Handle process_handle, uint64_t src_address, size_t size);"), 120 | 0x75 : ("UnmapProcessMemory", "Result %s(uintptr_t dst_address, Handle process_handle, uint64_t src_address, size_t size);"), 121 | 0x76 : ("QueryProcessMemory", "Result %s(MemoryInfo *out_memory_info, PageInfo *out_page_info, Handle process_handle, uint64_t address);"), 122 | 0x77 : ("MapProcessCodeMemory", "Result %s(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"), 123 | 0x78 : ("UnmapProcessCodeMemory", "Result %s(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"), 124 | 0x79 : ("CreateProcess", "Result %s(Handle *out_handle, const CreateProcessParameter *parameters, const uint32_t *caps, int32_t num_caps);"), 125 | 0x7A : ("StartProcess", "Result %s(Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);"), 126 | 0x7B : ("TerminateProcess", "Result %s(Handle process_handle);"), 127 | 0x7C : ("GetProcessInfo", "Result %s(int64_t *out_info, Handle process_handle, ProcessInfoType info_type);"), 128 | 0x7D : ("CreateResourceLimit", "Result %s(Handle *out_handle);"), 129 | 0x7E : ("SetResourceLimitLimitValue", "Result %s(Handle resource_limit_handle, LimitableResource which, int64_t limit_value);"), 130 | 0x7F : ("CallSecureMonitor", "void %s(SecureMonitorArguments *args);"), 131 | } 132 | 133 | seg_mapping = {idc.get_segm_name(x): (idc.get_segm_start(x), idc.get_segm_end(x)) for x in Segments()} 134 | 135 | text_start, text_end = seg_mapping['.text'] 136 | 137 | def IsInText(ea): 138 | return text_start <= ea and ea < text_end 139 | 140 | def Test(ea): 141 | unknowns = [] 142 | for svc_id in xrange(0x80): 143 | ea_svc32 = ea + 8 * svc_id 144 | ea_svc64 = ea_svc32 + 0x80 * 8 145 | val32 = ida_bytes.get_64bit(ea_svc32) 146 | val64 = ida_bytes.get_64bit(ea_svc64) 147 | if svc_id in SVC_MAPPINGS.keys(): 148 | if not IsInText(val32): 149 | return False 150 | if not IsInText(val64): 151 | return False 152 | else: 153 | if val32 == 0 and val64 == 0: 154 | continue 155 | elif val32 == 0 or val64 == 0: 156 | return False 157 | else: 158 | unknowns.append(svc_id) 159 | for unknown in unknowns: 160 | print '[!] Possible unknown SVC 0x%02x' % unknown 161 | return True 162 | 163 | 164 | rodata_mappings = {x: seg_mapping[x] for x in seg_mapping.keys() if x.startswith('.rodata')} 165 | 166 | candidates = [] 167 | for (seg_name, (seg_start, seg_end)) in rodata_mappings.items(): 168 | print 'Looking at %s (%08x-%08x)...' % (seg_name, seg_start, seg_end) 169 | ea = seg_start & ~7 170 | while ea < seg_end: 171 | if Test(ea): 172 | candidates.append(ea) 173 | ea += 8 174 | 175 | assert(len(candidates) == 1) 176 | svc_table32 = candidates[0] 177 | svc_table64 = svc_table32 + 0x80 * 8 178 | print 'Found Svc Tables: %08x %08x' % (svc_table32, svc_table64) 179 | 180 | BLS_32 = {} 181 | BLS_64 = {} 182 | BLS_BOTH = {} 183 | 184 | def GetBl(func_ea): 185 | bls = [] 186 | for (startea, endea) in Chunks(func_ea): 187 | for head in Heads(startea, endea): 188 | disasm = GetDisasm(head).lstrip().rstrip().replace(',',' ') 189 | if ';' in disasm: 190 | disasm = disasm[:disasm.index(';')] 191 | disasm = disasm.split() 192 | if len(disasm) != 2: 193 | continue 194 | if disasm[0].lower() == 'bl': 195 | bls.append(disasm[1]) 196 | assert(len(bls) in [0, 1]) 197 | if len(bls) == 1: 198 | target_func = get_name_ea_simple(bls[0]) 199 | assert(IsInText(target_func)) 200 | return target_func 201 | else: 202 | return None 203 | 204 | def GetMutualBl(func_ea): 205 | bls = [] 206 | disasms = [] 207 | for (startea, endea) in Chunks(func_ea): 208 | for head in Heads(startea, endea): 209 | disasm = GetDisasm(head).lstrip().rstrip().replace(',',' ') 210 | if ';' in disasm: 211 | disasm = disasm[:disasm.index(';')] 212 | disasm = disasm.split() 213 | disasms.append(disasm) 214 | if len(disasm) != 2: 215 | continue 216 | if disasm[0].lower() == 'bl': 217 | bls.append(disasm[1]) 218 | if len(bls) == 1: 219 | target_func = get_name_ea_simple(bls[0]) 220 | if not IsInText(target_func): 221 | target_func = get_name_ea_simple(bls[0].replace('__', '::')) 222 | assert(IsInText(target_func)) 223 | if len(disasms) >= 3: 224 | if disasms[0][0].lower() != 'stp': 225 | return None 226 | if disasms[-1][0].lower() != 'ret': 227 | if disasms[-1][0].lower() == 'bl': 228 | return target_func 229 | return None 230 | if disasms[-2][0].lower() != 'ldp': 231 | return None 232 | return target_func 233 | elif len(disasms) >= 2: 234 | if disasms[0][0].lower() != 'stp': 235 | return None 236 | if disasms[-1][0].lower() != 'bl': 237 | return target_func 238 | return target_func 239 | else: 240 | return None 241 | else: 242 | return None 243 | 244 | def IsTrampoline(func_ea): 245 | disasm = GetDisasm(func_ea).lstrip().rstrip().replace(', ',' ').split() 246 | return disasm[0].lower() == 'b' 247 | 248 | def GetBranch(func_ea): 249 | disasm = GetDisasm(func_ea).lstrip().rstrip().replace(', ',' ').split() 250 | assert disasm[0].lower() == 'b' 251 | target_func = get_name_ea_simple(disasm[1]) 252 | if not IsInText(target_func): 253 | target_func = get_name_ea_simple(disasm[1].replace('__', '::')) 254 | assert IsInText(target_func) 255 | return target_func 256 | 257 | # Process Tables 258 | for svc_id in SVC_MAPPINGS.keys(): 259 | ea_func32 = ida_bytes.get_64bit(svc_table32 + 8 * svc_id) 260 | ea_func64 = ida_bytes.get_64bit(svc_table64 + 8 * svc_id) 261 | assert(get_func_name(ea_func32)) 262 | assert(get_func_name(ea_func64)) 263 | bl32 = GetBl(ea_func32) 264 | bl64 = GetBl(ea_func64) 265 | if bl32 == bl64 and bl32: 266 | BLS_BOTH[svc_id] = bl32 267 | else: 268 | if bl32: 269 | BLS_32[svc_id] = bl32 270 | if bl64: 271 | BLS_64[svc_id] = bl64 272 | for (svc_id, (svc_name, svc_type)) in SVC_MAPPINGS.items(): 273 | ea_func32 = ida_bytes.get_64bit(svc_table32 + 8 * svc_id) 274 | ea_func64 = ida_bytes.get_64bit(svc_table64 + 8 * svc_id) 275 | svc_name32 = '%s64From32' % svc_name 276 | svc_name64 = '%s64' % svc_name 277 | idc.set_name(ea_func32, 'Svc%s' % svc_name32, SN_CHECK) 278 | idc.SetType(ea_func32, 'void %s();' % svc_name) 279 | idc.set_name(ea_func64, 'Svc%s' % svc_name64, SN_CHECK) 280 | idc.SetType(ea_func64, 'void %s();' % svc_name) 281 | if svc_id in BLS_BOTH: 282 | assert('||' not in svc_type) 283 | idc.set_name(BLS_BOTH[svc_id], '%s' % svc_name, SN_CHECK) 284 | idc.SetType(BLS_BOTH[svc_id], svc_type % svc_name) 285 | else: 286 | if svc_id in BLS_32: 287 | idc.set_name(BLS_32[svc_id], svc_name32, SN_CHECK) 288 | idc.SetType(BLS_32[svc_id], svc_type.replace('||', '32') % svc_name32) 289 | if svc_id in BLS_64: 290 | idc.set_name(BLS_64[svc_id], svc_name64, SN_CHECK) 291 | idc.SetType(BLS_64[svc_id], svc_type.replace('||', '64') % svc_name64) 292 | if svc_id in BLS_32 and svc_id in BLS_64: 293 | subbl32 = GetMutualBl(BLS_32[svc_id]) 294 | subbl64 = GetMutualBl(BLS_64[svc_id]) 295 | if subbl32 is not None and subbl64 is not None: 296 | if subbl32 == subbl64: 297 | assert('||' not in svc_type) 298 | idc.set_name(subbl64, svc_name, SN_CHECK) 299 | idc.SetType(subbl64, svc_type % svc_name) 300 | elif IsTrampoline(BLS_32[svc_id]) and IsTrampoline(BLS_64[svc_id]): 301 | b32 = GetBranch(BLS_32[svc_id]) 302 | b64 = GetBranch(BLS_64[svc_id]) 303 | if b32 == b64: 304 | assert('||' not in svc_type) 305 | ida_funcs.del_func(BLS_32[svc_id]) 306 | ida_funcs.del_func(BLS_64[svc_id]) 307 | ida_funcs.del_func(b32) 308 | ida_funcs.add_func(b32) 309 | ida_funcs.add_func(BLS_32[svc_id]) 310 | ida_funcs.add_func(BLS_64[svc_id]) 311 | idc.set_name(b64, svc_name, SN_CHECK) 312 | idc.SetType(b64, svc_type % svc_name) -------------------------------------------------------------------------------- /scripts/kernel_x18.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | from idc import * 4 | from ida_hexrays import * 5 | from ida_frame import * 6 | from ida_struct import * 7 | 8 | class x18_modifier_t(user_lvar_modifier_t): 9 | def __init__(self, funcea): 10 | user_lvar_modifier_t.__init__(self) 11 | self.funcea = funcea 12 | 13 | def modify_lvars(self, lvars): 14 | found = False 15 | tif = ida_typeinf.tinfo_t() 16 | ida_typeinf.parse_decl(tif, None, 'KThread *;', 0) 17 | for idx, var in enumerate(lvars.lvvec): 18 | if var.ll.is_reg_var() and var.ll.get_reg1() == 152: 19 | found = True 20 | var.type = tif 21 | var.name = 'cur_thread' 22 | break 23 | if not found: 24 | v = lvar_saved_info_t() 25 | v.name = 'cur_thread' 26 | v.type = tif 27 | v.size = -1 28 | loc = vdloc_t() 29 | loc.set_reg1(152) 30 | v.ll = lvar_locator_t(loc, self.funcea) 31 | lvars.lvvec.append(v) 32 | return True 33 | 34 | def ProcessFunction(ea): 35 | func = get_func(ea) 36 | name = get_func_name(ea) 37 | for v in decompile(ea).get_lvars(): 38 | if v.location.is_reg() and v.location.reg1() == 152: 39 | if v.name != 'cur_thread': 40 | modify_user_lvars(ea, x18_modifier_t(ea)) 41 | break 42 | for v in decompile(ea).get_lvars(): 43 | if v.location.is_reg() and v.location.reg1() == 152: 44 | assert v.name == 'cur_thread' 45 | 46 | 47 | for segea in Segments(): 48 | for funcea in Functions(segea, get_segm_end(segea)): 49 | ProcessFunction(funcea) --------------------------------------------------------------------------------