Awesome
swift-frida
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.)
Status
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.
Usage
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
More Information
You can have a look at a video recording and slides of a presentation about this project at r2con 2018.
Available APIs
Right now, the following functions are available in the Swift
namespace, when the script is loaded:
-
available
Allows 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 fromModule
objects), 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 fromModule
objects), 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)
. -
_api
This is an object providing access to many low-level functions in the Swift runtime. -
_typeFromCanonical(metadata)
This function allows you to create aType
constructor from a low-levelMetadata*
. -
makeFunctionType(args, returnType, flags)
Creates a new SwiftType
constructor for a function type.args
should be an array with the types of each argument,flags
is an optional object withdoesThrow
and/orconvention
properties. -
makeTupleType(labels, innerTypes)
Creates a new SwiftType
constructor for a tuple type.labels
andinnerTypes
are 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 aMap
storing 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 atoString
method returning the fully qualified name of the type, including generic bounds (if possible) and a string propertykind
that tells you which kind of type (e.g."Class"
,"Struct"
) it is. TheType
property returns aType
constructor for the meta type of the represented type. Depending on the kind of type, additional methods are available:Kind available method Enum enumCases
returns an array with all defined cases, with their names, types, and attributes.Class, Struct fields
returns an array with all fields of this class or struct, with names, offsets, types, and attributes.Tuple tupleElements
returns an array with the types and labels of the elements in the tuple.Function returnType
returns the return type of the function.Function functionFlags
returns an object telling you the calling convention and whether the function throws or not.Function getArguments
returns an array with the type and flags (inout
) for every function argument.ObjCClassWrapper getObjCObject
returns anObjC.Object
describing this class.Uninstantiated generic types (you can check this with the
isGeneric
method), do not have any of these methods available. You must callwithGenericParams
to 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* $pointer
the pointer where this value resides in memory* $type
the dynamic type of this value* $staticType
the 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 ofother
to this variable.other
must 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 $isa
the isa pointer. (Only for Existential types when they are class bound.)Class, Existential $retainCounts
the 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 returnsundefined
if 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 $value
allows 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
SwiftValue
objects, except for integers and booleans, which are transparently converted into their JavaScript equivalents. For strings, you can usetoString()
and$assignWithCopy
to convert between the JavaScript and Swift types.
But, again, this is completely unstable and might change at any time.
License
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.