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.13.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.13-py3-none-any.whl (85.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ida_ios_helper-1.0.13.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.13.tar.gz
Algorithm Hash digest
SHA256 fb520ff55d4910773f7551471608be6234cdbfdcc2abef06d3cebb893ee8065a
MD5 9f9ceb02aca0ee35e4737c4134aa0b53
BLAKE2b-256 2ec2e4e15a4c2fa1cc86cef3ca34777be4e4d21885e663fbafb2f942563432ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for ida_ios_helper-1.0.13.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.13-py3-none-any.whl.

File metadata

File hashes

Hashes for ida_ios_helper-1.0.13-py3-none-any.whl
Algorithm Hash digest
SHA256 fdd8fa381535c0c91ffc96b0ec117a05581b76cabd420639baf2cd52a2a1bd84
MD5 c834c62fe2a52d87126b3d1a05173daf
BLAKE2b-256 c0b355437c74a788f1b4efdde7ca4ef87ea406c83ebff0812c6ce3f7494b644e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ida_ios_helper-1.0.13-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