Arm64 architecture handler - It uses unicorn and libffi to run iOS arm64 binaries on x86_64 macOS

Overview

aah

arm64 architecture handler.

It uses unicorn and libffi to run iOS arm64 binaries on x86_64 macOS, with varying degrees of success.

Most things will fail to launch because they need frameworks/symbols that aren't available on macOS.

Requirements

  • macOS 10.15

To run iOS apps, aah relies on the Mac Catalyst frameworks that are present on macOS 10.15.

Running the sample app

The sample app is based on UIKitCatalog from the Apple sample code, with an integrated capstone disassembler to inspect functions and methods.

  1. Build the TestApp target. This builds an arm64 iOS app (TestApp.app), and makes a copy with the Mach-O header changed to be emulatable (TestApp-aah.app, in the same output directory).
  2. Edit the aah-TestApp scheme, select the TestApp-app.app executable by choosing “Other...” from the Executable drop-down menu (under Run > Info) and selecting it from the filesystem. Otherwise Xcode will complain that the target doesn't match the current platform.
  3. Run the aah-TestApp scheme.

Steps 1 and 2 are only necessary before the first run.

How it works

  1. The architecture field of the binary is changed so macOS will load it.
  2. libaah.dylib is inserted with DYLD_INSERT_LIBRARIES.
  3. Upon load, it will detect which loaded binaries should be emulated, by looking at the reserved field in the header.
  4. On emulated binaries, the executable sections are changed to be non-executable. This will cause an EXC_BAD_ACCESS exception when it's executed.
  5. A signal handler is set to catch those exceptions, and emulate the code with unicorn.
  6. An unicorn instance will be creatd on each thread if needed, with its address space mirroring the host, except for executable sections:
    1. Sections with arm64 code are marked as executable for unicorn.
    2. Native-executable sections (i.e. system libraries) are marked as non-executable for unicorn. This causes an exception when unicorn tries to execute them, which is used to return execution to the host.

Transitions between native and emulated code

Transitions between host and emulated execution is handled by libffi at function entry/exit points, and requires the function signature to be known. This is done by keeping a mapping of entry points and their signatures (see cif.c).

The file SymbolTable.plist contains the signatures for supported functions (using Objective-C type encoding), and it's added as a section to the libaah.dylib binary. The key objc shims is used by the objc_msgSend shim to call variadic methods. This file is added as a section to the libaah.dylib binary at build time.

When new entry points are found at runtime, they are added with cif_cache_add or cif_cache_add_new. This is used for Objective-C methods, pthreads, blocks and function pointers.

The format of the method signature determines how the call is handled:

  1. A plain method signature will call the function by translating the arguments between the registers and stack of the host and emulator. This is enough for most functions.
  2. $ + shim name: A shim that will be called when emulated code calls the function. The shim is defined with the SHIMDEF macro, it receives an emulator context where it can access the registers, and can return SHIM_RETURN or an address to continue execution. Examples:
    • Variadic functions: printf or NSLog (see nslog.m).
    • Overriding functions with custom behaviour: objc_msgSend, setjmp.
  3. < + method signature + > + wrapper name: Defines wrapper(s) that will be called after and/or before the native function is called. The wrappers are defined with the WRAP_EMULATED_TO_NATIVE and WRAP_NATIVE_TO_EMULATED macros, and have arguments rvalue and avalues that work like those of ffi_call. See libdispatch.c for examples.

Preparing an app

You will need a thin non-encrypted arm64 app to start with.

Repackaging

Repackage with this modified version of marzipanify.

This will do the following:

  1. Repackage it as a macOS app.
  2. Rename some linked libraries to the ones in /System/iOSSupport
  3. Change the executable's architecture to x86_64.
  4. Flag the executable so it's detected by libaah to be emulated (0x456D400C in the reserved field of the header).
  5. Remove the MH_PIE flag (probably not be needed, but makes debugging easier).
  6. Replace the LC_VERSION_MIN_IPHONEOS load command with LC_BUILD_VERSION, or update LC_BUILD_VERSION.
  7. Resign the package.

Libraries

Most binaries won't launch because of missing libraries or symbols. optool might help:

  • weaken linked libraries and symbols
  • rename linked libraries

After modifying the binary, it will have to be resigned:

If a library is present on macOS but missing some symbols, it's possible to build a stub library with the missing symbols, and make it export all of the original library by adding -Xlinker -reexport_library /path/to/original.dylib to its linker flags.

For functions in a native library to be called from emulated code, they must have an entry in SymbolTable.plist, as an Objective-C method signature. This can be automated with the msdecl tool from CParser, how to do this is somewhat documented in SymbolTable/make_symbol_table.sh.

If the app links to libc++, package it with the arm64 version included in lib/libc++em.dylib, and rename the linked library:

optool rename /usr/lib/libc++.1.dylib @executable_path/../lib/libc++em.dylib --target /path/to/package.app/Contents/MacOS/executable

Running

Run the patched executable inserting libaah.dylib:

$ DYLD_INSERT_LIBRARIES=/path/to/libaah.dylib /path/to/executable

Environment Variables

Some useful environment variables recognised by aah:

  • PRINT_DISASSEMBLY=1 will print disassembled instructions as they are executed by the emulator.
  • PRINT_REGS=1 will print the registers after and before function calls, or before printing each executed instruction (when combined with PRINT_DISASSEMBLY).

Debugging

To debug, you'll need a custom build of debugserver that doesn't catch EXC_BAD_ACCESS exceptions, as this prevents them from being caught as signals in libaah:

  1. Download the llvm source:

    $ git clone [email protected]:llvm/llvm-project.git
    
  2. Patch lldb/tools/debugserver/source/MacOSX/MachTask.mm:

    diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
    index 6aa4fb23754..6148b628119 100644
    --- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm
    +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
    @@ -601,7 +601,7 @@ bool MachTask::StartExceptionThread(DNBError &err) {
    
         // Set the ability to get all exceptions on this port
         err = ::task_set_exception_ports(
    -        task, m_exc_port_info.mask, m_exception_port,
    +        task, m_exc_port_info.mask & ~EXC_MASK_BAD_ACCESS, m_exception_port,
             EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
         if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) {
           err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, "
  3. Build debugserver with Xcode and install to /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/debugserver. You might want to save a backup of your original debugserver.

  4. In the aah project, edit the aah scheme and choose an executable/app to debug.

  5. Make sure the environment variables include DYLD_INSERT_LIBRARIES with $(TARGET_BUILD_DIR)/libaah.dylib first.

  6. Build & run

  7. It will hit a SIGBUS when it first encounters code to emulate. To prevent it, run this in the lldb console:

    There is already a shared breakpoint in the project that does this in init_aah.

    (lldb) process handle --pass true --stop false --notify true SIGBUS
    (lldb) continue
    
You might also like...
Create an app using VIPER architecture
Create an app using VIPER architecture

How not to get confused with VIPER Implementation This is a demo app to share in swift study group using VIPER Architechture When using VIPER Architec

A demo project with VIPER architecture
A demo project with VIPER architecture

AQIDemo What is it? A demo project with VIPER architecture. All views, Interactors Presenter and routers are in place with their respective folder. Ap

A proof of concept of the VIPER architecture

This project contains a proof of concept of the VIPER architecture. For this we are going to make use of the public API tmdb, with which we will implement a search engine, a list and a view detail.

A ARM macOS Virtual Machine, using macOS 12's new Virtualization framework.
A ARM macOS Virtual Machine, using macOS 12's new Virtualization framework.

macOS Virtual Machine A ARM macOS Virtual Machine, using macOS 12's new Virtualization framework. I copied KhaosT's code from here, all I did is chang

Joplin - an open source note taking and to-do application with synchronization capabilities for Windows, macOS, Linux, Android and iOS. Forum: https://discourse.joplinapp.org/
Joplin - an open source note taking and to-do application with synchronization capabilities for Windows, macOS, Linux, Android and iOS. Forum: https://discourse.joplinapp.org/

Joplin® is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are sea

XCSnippetsApp - macOS application to explore code snippets from the Swift and iOS community, view and edit the snippets before adding them conveniently to Xcode
XCSnippetsApp - macOS application to explore code snippets from the Swift and iOS community, view and edit the snippets before adding them conveniently to Xcode

XCSnippetsApp macOS application to explore code snippets from the Swift and iOS community, view and edit the snippets before adding them conveniently

AuroraEditor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.
AuroraEditor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.

AuroraEditor AuroraEditor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.

The Outline Client is a cross-platform VPN or proxy client for Windows, macOS, iOS, Android, and ChromeOS

Outline Client The Outline Client is a cross-platform VPN or proxy client for Windows, macOS, iOS, Android, and ChromeOS. The Outline Client is design

An IPFS client/api Swift Package, with the ability to add and pin any data on iOS/iPadOS/macOS

An IPFS client/api Swift Package, with the ability to add and pin any data on iOS/iPadOS/macOS. Originally bundled with GraniteUI, pulled out for independant use by any party.

Comments
  • msdecl usage problem

    msdecl usage problem

    ./msdecl -w -I$SDKROOT/usr/include $SDKROOT/usr/lib/libobjc.A.tbd $SDKROOT/usr/include/objc/{runtime,objc,message,objc-exception,objc-sync}.h libpath: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/libobjc.A.tbd lua: ./msdecl:235: attempt to call a nil value (field 'load') stack traceback: ./msdecl:235: in function <./msdecl:233> (...tail calls...) ./msdecl:265: in main chunk [C]: in ?

    opened by hulovebin 0
Owner
Jesús A. Álvarez
Jesús A. Álvarez
Wasmic allows you to run WebAssembly in a safe way on iOS.

wasmic-ios Bootstrap $ git clone https://github.com/kateinoigakukun/wasmic-ios.git $ git -c submodule."fastlane".update=none submodule update --init -

Yuta Saito 58 Dec 12, 2022
A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators

A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators This demonstration uses an iOS 13.7 sim

Gwendal Roué 0 Nov 1, 2021
Rosetta Enforcer is a macOS utility designed to help developers convert Universal Binary application to Single Architecture, vice versa.

Rosetta Enforcer Rosetta Enforcer is a macOS utility designed to help developers convert Universal Binary application to Single Architecture, vice ver

John Seong 21 Sep 25, 2022
React Native Todo List example app which uses Redux for managing app state

react-native-redux-todo-list A sample todo list app developed by using React Native and Redux. How to run the app Install react-native If you don't ha

null 43 Oct 11, 2022
Wiggles-iOS - Beautiful Puppy adoption app built to Demonstrate the SwiftUI and MVVM Architecture

Wiggles ?? Beautiful Puppy adoption app built to Demonstrate the use of SwiftUI

Sameer Nawaz 186 Dec 18, 2022
A compiler, assembler and emulator for the QCPU 2 architecture

QCPU CLI A CLI for compiling Q-code, assembling extended QCPU 2 assembly and emulating machine code. Tags @PAGE <upper> <lower> @HEADER <label> <argum

Joey Smalen 5 Nov 16, 2022
iOS architectures - MVC, MVP, MVVM, MVVM-C, ReactorKit, VIPER, Clean Architecture

iOS architectures - MVC, MVP, MVVM, MVVM-C, ReactorKit, VIPER, Clean Architecture, RIBs; Repository Pattern, Rxflow, Swinject, Tuist, Xcodegen, Cocoapods, SPM, Carthage + Rome

null 123 Dec 21, 2022
IOS Swift Application using VIPER Architecture

IOS Swift Application using VIPER Architecture. This is a simple design application for E-Commerce using VIPER architecture and CoreData, I prefer using Delegate Protocols rather than Third Party Library such as Rx but at this project I make it as an example on how to using VIPER.The application also support localization.

Mahmoud Salah 0 Dec 1, 2021
Strucure: I used MVVM with Colusre binging modular architecture : Also I used openweathermap to get all information for current weather as it's easy t

Strucure: I used MVVM with Colusre binging modular architecture : Also I used openweathermap to get all information for current weather as it's easy t

Alaa Azab 0 Oct 7, 2021
A sample app that implements MVVM architecture using Swift, ViewModel, Alamofire

MVVM Architecture Android: Template This repository contains a sample app that implements MVVM architecture using Swift, ViewModel, Alamofire, and etc

null 0 Oct 19, 2021