R 2 Frida

Download as pdf or txt
Download as pdf or txt
You are on page 1of 28

the ultimate static analysis on dynamic steroids

Debugger Debuggee
Debugger Debuggee

bootstrapper
Debugger Debuggee
bootstrapper-thread

bootstrapper
Debugger Debuggee
bootstrapper-thread

bootstrapper

frida-agent.so
Debugger Debuggee
bootstrapper-thread

bootstrapper

Comm. Channel frida-agent.so


Debugger Debuggee
bootstrapper-thread

bootstrapper

Comm. Channel frida-agent.so

JavaScript
Demos
Motivation behind Frida
Existing tools often not a good fit for the task at hand
Creating a new tool usually takes too much effort
Short feedback loop: reversing is an iterative process
Use one toolkit for multi-platform instrumentation
Future remake of oSpy (see below)
What is Frida?
Dynamic instrumentation toolkit
Debug live processes
Scriptable
Execute your own debug scripts
inside another process
Multi-platform
Windows, Mac, Linux, iOS, Android, QNX
Open Source
Why would you need Frida?
For reverse-engineering
For programmable debugging
For dynamic instrumentation
But ultimately: To
enable rapid
development of new tools for the task
at hand
Architecture
Highly modular and decoupled
Instrumentation core written in C (frida-gum)
C++ and JavaScript language bindings
Easy to use high-level API: "run JS in that process"
Packages instrumentation core in a library
Injects that using a per OS injector component
Communicates with it over a per OS transport
Bindings: C, Node.js, Python, .NET, Swift, Qt
Philosophy: only bare metal building blocks,
community provides use-case-specific modules in
npm, e.g. frida-fs, frida-screenshot, frida-uikit, frida-
uiwebview, etc.
Let's explore the basics
1) Build and run the test app that we will instrument:
#include <stdio.h> $ clang hello.c -o hello
#include <unistd.h> $ ./hello
f() is at 0x106a81ec0
void Number: 0
f (int n) Number: 1
{ Number: 2
printf ("Number: %d\n", n); …
}

int
main ()
{
int i = 0;
2) Make note of the
printf ("f() is at %p\n", f);
address of f(), which is
while (1)
{
0x106a81ec0 here.
f (i++);
sleep (1);
}
}
Hooking f() from Node.js
'use strict'; $ # install Node.js 5.1
$ npm install co frida frida-load
const co = require('co'); $ node app.js
const frida = require('frida'); { type: 'send', payload: 531 }
const fs = require('mz/fs'); { type: 'send', payload: 532 }

let session, script;
co(function *() {
session = yield frida.attach('hello');
const source = yield fs.readFile(
require.resolve('./agent.js'), 'utf-8');
script = yield session.createScript(source);
script.events.listen('message', message => {
console.log(message);
});
yield script.load();
});
'use strict';

Interceptor.attach(ptr('0x106a81ec0'), { Address of f() goes here


onEnter(args) {
send(args[0].toInt32());
}
});
Hooking f() from Python
import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("0x106a81ec0"), {
onEnter: function(args) {
Address of f() goes here
send(args[0].toInt32());
} $ pip install frida
}); $ python app.py
""") {'type': 'send', 'payload': 531}
def on_message(message, data): {'type': 'send', 'payload': 532}
print(message) …
script.on('message', on_message)
script.load()
sys.stdin.read()

There are also language-bindings for QML, .NET, etc. The API is the same except

for local conventions like create_script() vs createScript().

We will stick to the Node.js bindings for the remainder of this presentation.
Modifying function arguments
'use strict'; $ node app.js

const co = require('co'); Number: 1281


const frida = require('frida'); Number: 1282
const fs = require('mz/fs'); Number: 1337
Number: 1337
let session, script; Number: 1337
co(function *() { Number: 1337 Once we stop it
session = yield frida.attach('hello'); Number: 1296 the target is back to
const source = yield fs.readFile( Number:
require.resolve('./agent.js'), 'utf-8'); Number:
1297 normal
1298
script = yield session.createScript(source); …
yield script.load();
});
'use strict';

Interceptor.attach(ptr('0x106a81ec0'), { Address of f() goes here


onEnter(args) {
args[0] = ptr("1337");
}
});
Calling functions
'use strict'; $ node app.js

const co = require('co'); Number: 1879


const frida = require('frida'); Number: 1911
const fs = require('mz/fs'); Number: 1911
Number: 1911
let session, script; Number: 1880
co(function *() { …
session = yield frida.attach('hello');
const source = yield fs.readFile(
require.resolve('./agent.js'), 'utf-8');
script = yield session.createScript(source);
yield script.load();
});
'use strict';

const f = new NativeFunction(


ptr('0x106a81ec0'), 'void', ['int']); Address of f() goes here
f(1911);
f(1911);
f(1911);
Sending messages
'use strict'; $ node app.js

const co = require('co'); { type: 'send',


payload: { user: { name: 'john.doe' }, key: '1234' } }
const frida = require('frida'); { type: 'error',
const fs = require('mz/fs'); description: 'ReferenceError: oops is not defined',
stack: 'ReferenceError: oops is not defined\n at Object.1 (agen
fileName: 'agent.js',
let session, script; lineNumber: 10,
columnNumber: 1 }
co(function *() {
session = yield frida.attach('hello');
const source = yield fs.readFile(
require.resolve('./agent.js'), 'utf-8');
script = yield session.createScript(source);
script.events.listen('message', message => {
console.log(message);
});
yield script.load();
});
'use strict';

send({
user: {
name: 'john.doe'
},
key: '1234'
});

oops;
Receiving messages
'use strict'; $ node app.js

const co = require('co'); { type: 'send', payload: 42 }


const frida = require('frida'); { type: 'send', payload: 36 }
const fs = require('mz/fs');

let session, script;


co(function *() {
session = yield frida.attach('hello');
const source = yield fs.readFile(
require.resolve('./agent.js'), 'utf-8');
script = yield session.createScript(source);
script.events.listen('message', message => {
console.log(message);
});
yield script.load();
yield script.postMessage({ magic: 21 }); 'use strict';
yield script.postMessage({ magic: 12 });
}); let i = 2;
function handleMessage(message) {
send(message.magic * i);
i++;
recv(handleMessage);
}
recv(handleMessage);
Blocking receives
'use strict'; $ node app.js
const co = require('co'); Number: 2183
const frida = require('frida');
Number: 2184
const fs = require('mz/fs');
Number: 4370
let session, script; Number: 4372
co(function *() { Number: 4374
session = yield frida.attach('hello'); Number: 4376
Number: 4378
Once we stop it
const source = yield fs.readFile(
require.resolve('./agent.js'), 'utf-8'); Number: 2190
script = yield session.createScript(source); Number: 2191 the target is back to
script.events.listen('message', message => {
const number = message.payload.number;
Number: 2192 normal

script.postMessage({ number: number * 2 });
});
yield script.load();
});
'use strict';

Interceptor.attach(ptr('0x106a81ec0'), { Address of f() goes here


onEnter: args => {
send({ number: args[0].toInt32() });
const op = recv(reply => {
args[0] = ptr(reply.number);
});
op.wait();
}
});
Launch and spy on iOS app
'use strict'; $ node app.js
{ type: 'send', payload: { event: 'call', name: 'CC_MD5' } }
const co = require('co'); { type: 'send', payload: { event: 'call', name: 'CCDigest' } }
const frida = require('frida'); { type: 'send', payload: { event: 'call', name: 'CNEncode' } }
const load = require('frida-load'); …
let session, script;
co(function *() {
const device = yield frida.getUsbDevice();
const pid = yield device.spawn(['com.apple.AppStore']);
session = yield device.attach(pid);
const source = yield load(
require.resolve('./agent.js'));
script = yield session.createScript(source);
script.events.listen('message', message => {
if (message.type === 'send' && message.payload.event === 'ready')
device.resume(pid);
else
console.log(message);
});
yield script.load();
})
.catch(console.error);

'use strict';

Module.enumerateExports('libcommonCrypto.dylib', {
onMatch: e => {
if (e.type === 'function') {
try {
Interceptor.attach(e.address, {
onEnter: args => {
send({ event: 'call', name: e.name });
}
});
} catch (error) {
console.log('Ignoring ' + e.name + ': ' + error.message);
}
}
},
onComplete: () => {
send({ event: 'ready' });
}
});
But there's an app for that
$ sudo easy_install frida
$ frida-trace -U -f com.apple.AppStore -I libcommonCrypto.dylib
io plugin code walkthrough
Questions?
Twitter: @oleavr
Thanks!
Code is at:
https://github.com/nowsecure/r2frida
Soon also available in r2pm.

You might also like