Skip to main content

IDA Plugin for ease the reversing of iOS' usermode and kernelcache.

Project description

IDA iOS Helper

A plugin for IDA Pro 9.0+ to help with iOS code analysis.

Supported features

  • KernelCache
    • Calls to OSBaseClass::safeMetaCast apply type info on the result.
    • Calls to OSObject_typed_operator_new apply type info on the result.
    • When the keyboard is on a virtual call (cls->vcall()), Shift+X will show a dialog with all the possible implementations of the virtual method. It requires vtable symbols to be present.
    • When in a C++ method named Class::func, Ctrl+T will change the first argument to Class* this. Also works for Obj-C instance methods.
    • Name globals from OSSymbol::fromConst* calls, locals from get/setProperty calls, ...
    • Rename and type all global kalloc_type_view. Use their signature to mark fields as pointers for the actual types.
    • Create a struct from a kalloc_type_view.
  • Objective-C
    • Hide memory management functions - objc_retain, objc_release, objc_autorelease, objc_retainAutoreleasedReturnValue.
      • Optimize _objc_storeStrong to an assignment.
    • collapse __os_log_impl calls.
    • Hide selectors and static classes from Objective-c calls.
    • When in Obj-C method, Ctrl+4 will show xrefs to the selector.
  • Common
    • Remove __break calls.
    • collapse blocks initializers and detect __block variables (use Alt+Shift+S to trigger detection).
    • Use Ctrl+S to jump to function by a string constant found in the code
    • Transform ranged conditions to a more readable form.
    • Try to detect outline functions and mark them as such.
    • Use Ctrl+Shift+X to find xrefs to a field inside a segment. This will decompile the whole segment and then search for the field.

Installation

  1. Install this package using your IDA's python pip: pip install ida-ios-helper
  2. copy ida-plugin.json and ida_plugin_stub.py to your IDA's plugins folder: ~/.idapro/plugins/ida-ios-helper.
  3. Restart IDA.

Examples

Solve condition constraints

Before:

if ( valueLength - 21 <= 0xFFFFFFFFFFFFFFEFLL ) 
{ 
  ... 
}

After:

if ( 4 < valueLength || valueLength < 21 )
{
  ...
}

Remove __break

Before:

    if ( ((v6 ^ (2 * v6)) & 0x4000000000000000LL) != 0 )
      __break(0xC471u);

After: removed.

Hide selectors of Obj-C calls

Before:

   -[NSFileManager removeItemAtPath:error:](
      +[NSFileManager defaultManager](&OBJC_CLASS___NSFileManager, "defaultManager"),
      "removeItemAtPath:error:",
      +[NSString stringWithUTF8String:](&OBJC_CLASS___NSString, "stringWithUTF8String:", *(_QWORD *)&buf[v5]),
      0LL);

After:

   -[NSFileManager removeItemAtPath:error:](
      +[NSFileManager defaultManager](),
      +[NSString stringWithUTF8String:](*(_QWORD *)&buf[v5]),
      0LL);

Block initializers

Before:

v10 = 0LL;
v15 = &v10;
v16 = 0x2000000000LL;
v17 = 0;
if ( a1 )
{
  x0_8 = *(NSObject **)(a1 + 16);
  v13.isa = _NSConcreteStackBlock;
  *(_QWORD *)&v13.flags = 0x40000000LL;
  v13.invoke = func_name_block_invoke;
  v13.descriptor = &stru_100211F48;
  v13.lvar3 = a1;
  v13.lvar4 = a2;
  v13.lvar1 = a3;
  v13.lvar2 = &v10;
  dispatch_sync(queue: x0_8, block: &v13);
  v11 = *((_BYTE *)v15 + 24);
}
else
{
  v11 = 0;
}
_Block_object_dispose(&v10, 8);
return v11 & 1;

After:

v10 = _byref_block_arg_init(0);
v10.value = 0;
if ( a1 )
{
  v6 = *(NSObject **)(a1 + 16);
  v9 = _stack_block_init(0x40000000, &stru_100211F48, func_name_block_invoke);
  v9.lvar3 = a1;
  v9.lvar4 = a2;
  v9.lvar1 = a3;
  v9.lvar2 = &v10;
  dispatch_sync(queue: v6, block: &v9);
  value = v10.forwarding->value;
}
else
{
  value = 0;
}
return value & 1;

Collapse os_log

Before:

  v9 = gLogObjects;
  v10 = gNumLogObjects;
  if ( gLogObjects && gNumLogObjects >= 46 )
  {
    v11 = *(NSObject **)(gLogObjects + 360);
  }
  else
  {
    v11 = (NSObject *)&_os_log_default;
    if ( ((v6 ^ (2 * v6)) & 0x4000000000000000LL) != 0 )
      __break(0xC471u);
    if ( os_log_type_enabled(oslog: (os_log_t)&_os_log_default, type: OS_LOG_TYPE_ERROR) )
    {
      *(_DWORD *)buf = 134218240;
      *(_QWORD *)v54 = v9;
      *(_WORD *)&v54[8] = 1024;
      *(_DWORD *)&v54[10] = v10;
      if ( ((v6 ^ (2 * v6)) & 0x4000000000000000LL) != 0 )
        __break(0xC471u);
      _os_log_error_impl(
        dso: (void *)&_mh_execute_header,
        log: (os_log_t)&_os_log_default,
        type: OS_LOG_TYPE_ERROR,
        format: "Make sure you have called init_logging()!\ngLogObjects: %p, gNumLogObjects: %d",
        buf: buf,
        size: 0x12u);
    }
  }
  if ( ((v6 ^ (2 * v6)) & 0x4000000000000000LL) != 0 )
    __break(0xC471u);
  if ( os_log_type_enabled(oslog: v11, type: OS_LOG_TYPE_INFO) )
  {
    if ( a1 )
      v12 = *(_QWORD *)(a1 + 8);
    else
      v12 = 0LL;
    *(_DWORD *)buf = 138412290;
    *(_QWORD *)v54 = v12;
    if ( ((v6 ^ (2 * v6)) & 0x4000000000000000LL) != 0 )
      __break(0xC471u);
    _os_log_impl(
      dso: (void *)&_mh_execute_header,
      log: v11,
      type: OS_LOG_TYPE_INFO,
      format: "Random log %@",
      buf: buf,
      size: 0xCu);
  }

after:

  if ( oslog_info_enabled() )
  {
    if ( a1 )
      v4 = *(_QWORD *)(a1 + 8);
    else
      v4 = 0LL;
    oslog_info("Random log %@", v4);
  }

Automatic casts with OSBaseClass::safeMetaCast

Before:

 OSObject *v5;
 v5 = OSBaseClass::safeMetaCast(a2, &IOThunderboltController::metaClass);

After:

 IOThunderboltController *v5;
 v5 = OSDynamicCast<IOThunderboltController>(a2);

Automatic typing for OSObject_typed_operator_new

Run Edit->Plugins->iOSHelper->Locate all kalloc_type_view before.

Before:

IOAccessoryPowerSourceItemUSB_TypeC_Current *sub_FFFFFFF009B2AA14()
{
  OSObject *v0; // x19

  v0 = (OSObject *)OSObject_typed_operator_new(&UNK_FFFFFFF007DBC480, size: 0x38uLL);
  OSObject::OSObject(this: v0, &IOAccessoryPowerSourceItemUSB_TypeC_Current::gMetaclass)->__vftable = (OSObject_vtbl *)off_FFFFFFF007D941B0;
  OSMetaClass::instanceConstructed(this: &IOAccessoryPowerSourceItemUSB_TypeC_Current::gMetaclass);
  return (IOAccessoryPowerSourceItemUSB_TypeC_Current *)v0;
}

After:

IOAccessoryPowerSourceItemUSB_TypeC_Current *sub_FFFFFFF009B2AA14()
{
  IOAccessoryPowerSourceItemUSB_TypeC_Current *v0; // x19

  v0 = OSObjectTypeAlloc<IOAccessoryPowerSourceItemUSB_TypeC_Current>(0x38uLL);
  OSObject::OSObject(this: v0, &IOAccessoryPowerSourceItemUSB_TypeC_Current::gMetaclass)->__vftable = (OSObject_vtbl *)off_FFFFFFF007D941B0;
  OSMetaClass::instanceConstructed(this: &IOAccessoryPowerSourceItemUSB_TypeC_Current::gMetaclass);
  return v0;
}

Jump to virtual call

Use Shift+X on a virtual call to jump.

Jump to virtual call

Xrefs to selector

Use Ctrl+4 inside an Objective-C method to list xrefs to its selector.

Jump to selector

Rename function by argument of logging function

Given that the code contains calls like:

log("func_name", ....);

You could use rename_function_by_arg to mass rename all functions that contain such calls.

rename_function_by_arg(func_name="log", arg_index=0, prefix="_", force_name_change=False)

This will run on all the functions that call the log function, and rename them to the first argument of the call.

Call the plugin from python

import idaapi

# Call global analysis
idaapi.load_and_run_plugin("iOS Helper", 1)


# Call local analysis
def write_ea_arg(ea: int):
    n = idaapi.netnode()
    n.create("$ idaioshelper")
    n.altset(1, ea, "R")


write_ea_arg(func_ea)
idaapi.load_and_run_plugin("iOS Helper", 2)

Development

In order to have autocomplete while developing, you need to add IDA's include folder ( $IDA_INSTALLATION/python/3 ) to your IDE.

  • on Visual Studio code you can add the folder to the analyzer's extra paths in the settings.json file:
{
  "python.analysis.extraPaths": [
    "$IDA_INSTALLATION\\python\\3"
  ]
}
  • on PyCharm you can add the folder to the interpreter's paths in the project settings. Alternatively, you can create idapython.pth in $VENV_FOLDER/Lib/site-packages and add the path to it.

Inside IDA, you can use ioshelper.reload() to reload the plugin during development. If you create file name DEBUG inside src/, then you can use F2 to reload the plugin.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ida_ios_helper-1.0.14.tar.gz (859.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ida_ios_helper-1.0.14-py3-none-any.whl (85.6 kB view details)

Uploaded Python 3

File details

Details for the file ida_ios_helper-1.0.14.tar.gz.

File metadata

  • Download URL: ida_ios_helper-1.0.14.tar.gz
  • Upload date:
  • Size: 859.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for ida_ios_helper-1.0.14.tar.gz
Algorithm Hash digest
SHA256 370f7182a733132e4202aa9207c717ced6f6c9520a50d376f61269ae6bbebbce
MD5 7b2928e679067e9d0f39435a673b8a09
BLAKE2b-256 726cc57a26bd60e51b72c0c198a4fc59261ecf01bad5d511caf1ff727107d4ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for ida_ios_helper-1.0.14.tar.gz:

Publisher: pypi.yaml on yoavst/ida-ios-helper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ida_ios_helper-1.0.14-py3-none-any.whl.

File metadata

File hashes

Hashes for ida_ios_helper-1.0.14-py3-none-any.whl
Algorithm Hash digest
SHA256 4fded06371c41c451745e8c9ff5b5d996db3c23685b59072a3bc8f8b8a98ef2f
MD5 b0db9ee6d0d7280079dcfa38038b7f01
BLAKE2b-256 9d2e624a88b572b38c1c8853cdc6bd2d231ed764589a911e1a18f15069ce343f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ida_ios_helper-1.0.14-py3-none-any.whl:

Publisher: pypi.yaml on yoavst/ida-ios-helper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page