Skip to content

Instantly share code, notes, and snippets.

@miticollo
Last active June 7, 2023 17:40
Show Gist options
  • Save miticollo/c8303ca061304d0b0372258ad6259c6c to your computer and use it in GitHub Desktop.
Save miticollo/c8303ca061304d0b0372258ad6259c6c to your computer and use it in GitHub Desktop.
A tccd tracer. It logs all INSERT queries that tccd does to store permissions for a third-party app.
#!/usr/bin/env python3
import signal
import threading
import _frida
import frida
from frida.core import Device, Session, Script, ScriptMessage
signal_event: threading.Event = threading.Event()
def signal_handler(sig, frame):
print("Interrupted by Ctrl-C, stopping...")
signal_event.set()
signal.signal(signal.SIGINT, signal_handler)
compiler: frida.Compiler = frida.Compiler()
compiler.on("diagnostics", lambda diag: print(f"on_diagnostics: {diag}"))
bundle: str = compiler.build('tccd.ts',
project_root='tccd.ts', compression='terser')
def on_message(message: ScriptMessage, data):
if message["type"] == "send":
payload: dict = message["payload"]
mtype: str = payload["type"]
if mtype == "tccd":
if payload["pid"] != -1:
process: _frida.Process = device.enumerate_processes(pids=[payload["pid"]])[0]
print(f"""\
Detect analysis permissions:
service: {payload["query"][0]},
client: {payload["query"][1]} (PID {process.pid}),
auth_value: {payload["query"][3]}""")
else:
print(f'Skipping client: {payload["query"][1]}')
else:
print("Unhandled message:", message)
device: Device = frida.get_usb_device()
session: Session = device.attach('tccd')
script: Script = session.create_script(source=bundle)
script.on("message", on_message)
script.load()
print('Press Ctrl-C to exit...')
signal_event.wait()
session.detach()
/*
* tccd is an iOS daemon that saves permissions (e.g., popups that ask "... Would
* Like to Access the Camera") of third-party apps.
* It stores the permission in an SQLCipher DB: /private/var/mobile/Library/TCC/.
* This agent prints all INSERT queries that tccd does.
*
* To use it run
* frida -U -n 'tccd' -l agent.ts
*/
const LIBSQLITE_PATH: string = '/usr/lib/libsqlite3.dylib'
let stmt: NativePointer | undefined;
const sqlQuery: (string | number | null)[] = [];
const sqlite3_expanded_sql = new NativeFunction(
Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_expanded_sql'),
'pointer', // pStmt
['pointer']
);
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_prepare_v2'), {
onEnter(args): void {
if (args[1].readUtf8String()?.startsWith("INSERT")) {
if (sqlQuery.length != 0) throw new Error("sqlQuery is not empty!");
this.ppStmt = args[3];
}
},
onLeave(): void {
if (this.ppStmt !== undefined) {
stmt = this.ppStmt.readPointer()
if (stmt?.isNull()) throw new Error("There is an error in sqlite3_prepare_v2!");
}
}
});
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_bind_text'), {
onEnter(args): void {
if (stmt?.equals(args[0])) sqlQuery[args[1].toInt32() - 1] = args[2].readUtf8String();
}
});
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_bind_int'), {
onEnter(args): void {
if (stmt?.equals(args[0])) sqlQuery[args[1].toInt32() - 1] = args[2].toInt32();
}
});
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_bind_int64'), {
onEnter(args): void {
if (stmt?.equals(args[0])) sqlQuery[args[1].toInt32() - 1] = int64(args[2].toString()).toNumber();
}
});
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_bind_null'), {
onEnter(args): void {
if (stmt?.equals(args[0])) sqlQuery[args[1].toInt32() - 1] = null;
}
});
Interceptor.attach(Module.getExportByName(LIBSQLITE_PATH, 'sqlite3_finalize'), {
onEnter(args): void {
if (stmt?.equals(args[0])) {
send({
type: "tccd",
query: sqlQuery,
expandedQuery: sqlite3_expanded_sql(stmt).readUtf8String(),
pid: getPidForApplication(<string>sqlQuery[1])
});
sqlQuery.length = 0;
stmt = undefined;
}
}
});
function getPidForApplication(bundleID: string): number {
// https://github.com/frida/frida-core/blob/53d3724dd7/src/darwin/springboard.h#L24-L46
const {FBSSystemService} = ObjC.classes;
// https://github.com/frida/frida-core/blob/53d3724dd7/src/darwin/frida-helper-backend-glue.m#L1369-L1371
const service = FBSSystemService.sharedService();
return service.pidForApplication_(bundleID);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment