Swift runtime interop from Frida -- interact with iOS apps written in Swift, using Frida. (See frida-swift instead, if you're looking to script the Frida debugging session using Swift code you write.)
The functionality described below is mostly stable and working, but probably still has some bugs that need to be fixed (please report them!).
I'm mainly testing things on iOS 11.1.2 (64bit), and on iOS 9.3.5 (32bit). Other operating systems are not supported for now. Only apps using Swift 4.0.* are supported at the moment.
Clone the project, and install its dependencies:
git clone https://github.com/maltek/swift-frida.git
cd swift-frida
npm install
In your script, add this line:
const Swift = require('/path/to/swift-frida/');
Afterwards, compile your script with frida-compile like this:
frida-compile -w -o /tmp/compiled.js your-script.js
To play around with the API interactively, you can load the compiled
loader.js into the REPL:
$ frida-compile -w -o /tmp/swift.js loader.js
$ frida -U -n Foo -l /tmp/swift.js
____
/ _ | Frida 12.2.14 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
[iOS Device::Foo]-> Swift.available
true
You can have a look at a video recording and slides of a presentation about this project at r2con 2018.
Right now, the following functions are available in the Swift namespace, when the script is loaded:
-
availableAllows you to check if the current process uses Swift. Don't use any of the other functions here if this property isfalse. -
isSwiftName(name)Takes a function/symbol name (like you can get fromModuleobjects), and returns a boolean indicating whether it is a mangled Swift name or not. -
demangle(name)Takes a mangled Swift name (like you can get fromModuleobjects), and returns a demangled, human-readable String for it. -
getMangled(name)The opposite ofdemangle(name). Right now, this only works for names that were previously returned bydemangle(name). -
_apiThis is an object providing access to many low-level functions in the Swift runtime. -
_typeFromCanonical(metadata)This function allows you to create aTypeconstructor from a low-levelMetadata*. -
makeFunctionType(args, returnType, flags)Creates a new SwiftTypeconstructor for a function type.argsshould be an array with the types of each argument,flagsis an optional object withdoesThrowand/orconventionproperties. -
makeTupleType(labels, innerTypes)Creates a new SwiftTypeconstructor for a tuple type.labelsandinnerTypesare arrays with the labels and types of the tuple elements. Use empty strings for unlabeled elements. -
enumerateTypesSync(module)Searches for types defined inmodule, or in all loaded modules if that parameter isundefined. Returns aMapstoring information about all known Swift data types defined in the Swift program. The keys are the names of the data types (as strings), and the values are objects describing the type. These objects have atoStringmethod returning the fully qualified name of the type, including generic bounds (if possible) and a string propertykindthat tells you which kind of type (e.g."Class","Struct") it is. TheTypeproperty returns aTypeconstructor for the meta type of the represented type. Depending on the kind of type, additional methods are available:Kind available method Enum enumCasesreturns an array with all defined cases, with their names, types, and attributes.Class, Struct fieldsreturns an array with all fields of this class or struct, with names, offsets, types, and attributes.Tuple tupleElementsreturns an array with the types and labels of the elements in the tuple.Function returnTypereturns the return type of the function.Function functionFlagsreturns an object telling you the calling convention and whether the function throws or not.Function getArgumentsreturns an array with the type and flags (inout) for every function argument.ObjCClassWrapper getObjCObjectreturns anObjC.Objectdescribing this class.Uninstantiated generic types (you can check this with the
isGenericmethod), do not have any of these methods available. You must callwithGenericParamsto get a fully concrete type.For fully instantiated generic types or non-generic types, you can use these type objects as constructors -- you provide a pointer where a value of that type is stored, and get back an object that lets you interact with this variable. Note that this object only stays valid for as long as this pointer is valid and is not modified (except using methods directly on this object). Depending on the kind of type, the following properties exist:
Kind available property * toString()returns a string with the debug representation of this variable* $pointerthe pointer where this value resides in memory* $typethe dynamic type of this value* $staticTypethe static type of this value* $destroy()destroys an owned Swift value. (On a best-effort basis this also happens when the object is garbage-collected.)* $assignWithCopy(other)assigns a copy ofotherto this variable.othermust have a compatible type (this is not checked).* allocCopy()creates and returns an owned copy of this value. Use$destroy()on the return value when you are done using it!Function The object is a function that you can call. (Other properties are still available.) ObjCClassWrapper The object is an ObjC.Object. See the documentation there. (None of the properties for Swift values are available.)Class, Existential $isathe isa pointer. (Only for Existential types when they are class bound.)Class, Existential $retainCountsthe number of references to this value. (Only for Existential types when they are class bound.)Enum $enumCase' the index of the active case of this enum, see$type.enumCases`.Enum $enumPayloadCopy()creates and returns an owned copy of the payload of this enum, or returnsundefinedif the active case has no payload. Use$destroy()on the return value when you are done using it!Class, Struct Getters and setters for every field (see $type.fields()). When the field name collides with a property that exists on every JS object, or starts with a$, use$$as a name to access such a field.Existential $valueallows you to get/set the value in this variable. (Only for Existential types when they are not class bound.)Tuple Getters and setters for every tuple element (see $type.tupleElements(), by index or (when available) by label.Returned values are normally the same kind of
SwiftValueobjects, except for integers and booleans, which are transparently converted into their JavaScript equivalents. For strings, you can usetoString()and$assignWithCopyto convert between the JavaScript and Swift types.
But, again, this is completely unstable and might change at any time.
Code in metadata.js is based on Apache-2.0 licensed Swift compiler source
code. Code in runtime-api.js is based on wxWindows-3.1 licensed frida-objc
source code.
The compatible intersection of those licenses is LGPL-3.0 (or later) with wxWindows exceptions. So that's also the license terms under which we release the original code in this repository.