Skip to content

Commit

Permalink
Adding frida FaceTime implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Natalie Silvanovich committed Oct 15, 2019
1 parent d5703be commit c74c937
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 0 deletions.
29 changes: 29 additions & 0 deletions FaceTime/frida/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FaceTime Replay for Frida!

This implementation has some limitations compared to the native implementation, specifically it is slow enough to alter the behavior of the audio and video streams. That said, it is much faster to set up than the native version.

Set-up instructions:

1) Add the line:

(subpath "/out")

to the (allow file-read* file-write* section of /System/Library/Sandbox/Profiles/com.apple.avconferenced.sb, and restart the host

2) Create the directory /out and make it world-readable

3) Install frida, and run the following in the local directory

python3 dumpIncomingMessages.py

Note that this runs on an MacBook Air on version 10.14.6. For a different version, the offsets of the hooked methods in the JS files need to be recalculated.

4) Use FaceTime to call the target and answer the call. This call will be recorded in /out

To reproduce the call:

1) Run:

python3 replay.py

2) Use FaceTime to call the target and answer the call.
49 changes: 49 additions & 0 deletions FaceTime/frida/dumpIncomingMessages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0

import frida
import sys
import os

vid_index=0
aud_index = 0

def on_message(message, data):
global vid_index
global aud_index
print(message)
if 'payload' in message:
payload = message['payload']
print(payload)
f = open(payload, 'rb')
s = f.read()
f.close()
pt = s[1]&0x7f;

if pt == 0x7b:
f = open("/out/vid" + str(vid_index), 'wb')
f.write(s)
f.close()
vid_index = vid_index + 1
if pt == 0x68:
f = open("/out/aud" + str(aud_index), 'wb')
f.write(s)
f.close()
aud_index = aud_index + 1
os.remove(payload)



session = frida.attach("avconferenced")
code = open('dumpMessages.js', 'r').read()
script = session.create_script(code);
script.on("message", on_message)
script.load()

print("Press Ctrl-C to quit")
sys.stdin.read()
80 changes: 80 additions & 0 deletions FaceTime/frida/dumpMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0

var ind = 0;
var unpaired =[]
var paired = []

send("Hooking VTP_Send");
Interceptor.attach(Module.getExportByName(null, "hwrandom").add(0x10085c), {
onEnter: function(args) {
console.log("pack len " + args[2])
var p1 = args[1].readByteArray(args[2].toInt32());
var p = new Uint8Array(p1);
var ext = p[0]&0x10;
var len = 0
var pt = p[1]&0x7f;
console.log(p[1])
console.log(pt)
if(pt == 0x7b || pt == 0x68){

if(ext){
len = p[15]*4+4 +12;

}else{
len = 12;
}
var this_pair=0;
for(var key in paired){
var e = new Uint8Array(paired[key][1]);
if(e.length == p.length - len){
match = true;
for(var i = 0; i < e.length; i++){
if(e[i] != p[i+len]){
match = false
}
if(match){
this_pair = paired[key];
}
}
}
}
if(!this_pair){
send("PAIR ERROR");
}

var s = "/out/test" + ind;
ind = ind + 1;
var f = new File(s, "wb")
f.write(Array.prototype.slice.call(p, 0, len));
console.log("pair length " + this_pair[0].byteLength)
f.write(this_pair[0])
f.close()
send(s);
}
}
});

Interceptor.attach(Module.getExportByName(null, "CCCryptorUpdate"), {
onEnter: function(args) {

var p = args[1].readByteArray(args[2].toInt32());

this.unencrypted = p;
this.ptr = args[1];
this.len = args[2].toInt32();

},
onLeave: function(retval) {

var p = this.ptr.readByteArray(this.len);

paired.push([this.unencrypted, p])

}
});
205 changes: 205 additions & 0 deletions FaceTime/frida/replay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0

var ind = 0;
var unpaired =[]
var paired = []
var aud_thread=0
var vid_thread=0
var audind = 0;
var vidind=0;
var audq = [];
var vidq = []
var first_aud_seq = 0;
var first_aud_seqr = 0;
var first_vid_seq = 0;
var first_vid_seqr = 0;
var save = [];
send("Hooking VTP_Send");

Interceptor.attach(Module.getExportByName(null, "hwrandom").add(0xdd820), {
onEnter: function(args) {
var old = aud_thread;
if(aud_thread && old != aud_thread){

console.log("error aud");
}

aud_thread = Process.getCurrentThreadId();


}
});

Interceptor.attach(Module.getExportByName(null, "hwrandom").add(0x229c9), {
onEnter: function(args) {
var old = vid_thread;
if(vid_thread && old != vid_thread){

console.log("error vid");
}

vid_thread = Process.getCurrentThreadId();

}
});

Interceptor.attach(Module.getExportByName(null, "hwrandom").add(0x10085c), {
onEnter: function(args) {

var p1 = args[1].readByteArray(args[2].toInt32());
var p = new Uint8Array(p1, 0, args[2].toInt32());
var ext = p[0]&0x10;
var len = 0
var pt = p[1]&0x7f;

if(pt == 0x7b || pt == 0x68){

pack = 0
var is_vid = 0;
if(pt==0x7b){
is_vid=1;
pack = vidq[0];
vidq.shift()
if(first_vid_seq==0){
first_vid_seq = p[2] + (p[3] << 8);
var data = pack[0];
var q = new Uint8Array(pack[0].readByteArray(20), 0, 20);
first_vid_seqr = q[2] + (q[3] << 8);
}
}else if(pt == 0x68){
pack = audq[0];
audq.shift()
if(first_aud_seq==0){
first_aud_seq = p[2] + (p[3] << 8);
var data = pack[0];
var q = new Uint8Array(pack[0].readByteArray(20), 0, 20);
first_aud_seqr = q[2] + (q[3] << 8);
}
}else{
console.log("NO PACKET");
return;
}


var tdata = pack[0];
var data = Memory.alloc(2048);
Memory.copy(data, tdata, pack[1]);

var q = new Uint8Array(pack[0].readByteArray(20), 0, 20);
if(is_vid){
var curr_seq = q[2] + (q[3] << 8);
var seq = first_vid_seq + (curr_seq- first_vid_seqr);
console.log("seq "+seq + " q " + q.byteLength);
data.add(2).writeByteArray([seq&0xff, (seq&0xff00) >> 8]);

}else{
var curr_seq = q[2] + (q[3] << 8);
var seq = first_aud_seq + (curr_seq- first_aud_seqr);
console.log("seq "+seq + " q " + q.byteLength);
data.add(2).writeByteArray([seq&0xff, (seq&0xff00) >> 8]);

}

data.add(4).writeByteArray([q[4], q[5], q[6], q[7], p[8], p[9], p[10], p[11]]);
args[1] = data;
args[2] = new NativePointer(pack[1]);


var s = 0
if(is_vid){
s = "/out/vidtest" + vidind;
}else{

s = "/out/audtest" + audind;
}
var f = new File(s, "wb")

f.write(data.readByteArray(pack[1]));
f.close()
save.push(data);
console.log("sent" + args[2]);

}
}
});

var ccc = Interceptor.attach(Module.getExportByName(null, "CCCryptorUpdate"), {
onEnter: function(args) {
if(Process.getCurrentThreadId()==aud_thread){
console.log("START AUD " + audind);
var d = ObjC.classes.NSData.dataWithContentsOfFile_("/out/aud" + audind);
audind++;
var realbytes = ptr(d.bytes());
var tmp = realbytes.readByteArray(d.length());
var m = Memory.alloc(d.length());


Memory.copy(m, realbytes, d.length());
var p = new Uint8Array(tmp);
var ext = p[0]&0x10;
var len = 0;
if(ext){
len = p[15]*4+4 +12;

}else{
len = 12;
}



console.log("/out/aud" + audind + " u "+ d);
console.log(m);
console.log(m.add(len));
console.log(d.length()-len);
args[1] = m.add(len);
args[2] = new NativePointer(d.length()-len);
args[3] = m.add(len);
args[4] = new NativePointer(d.length()-len);
console.log(m);

audq.push([m, d.length(), d]);

console.log("aud end");
}

if(Process.getCurrentThreadId()==vid_thread){
console.log("START VID" + vidind);
var d = ObjC.classes.NSData.dataWithContentsOfFile_("/out/vid" + vidind);

vidind++;
var realbytes = ptr(d.bytes());
var tmp = realbytes.readByteArray(d.length());
var m = Memory.alloc(d.length());
Memory.copy(m, realbytes, d.length());
var p = new Uint8Array(tmp);
var ext = p[0]&0x10;
var len = 0;
if(ext){
len = p[15]*4+4 +12;

}else{
len = 12;
}

console.log(d);
console.log(m.add(len));
console.log(d.length()-len);

console.log(m);
args[1] = m.add(len);
args[2] = new NativePointer(d.length()-len);
args[3] = m.add(len);
args[4] = new NativePointer(d.length()-len);


vidq.push([m, d.length(), d]);
console.log("vid end");
}
}
});
31 changes: 31 additions & 0 deletions FaceTime/frida/replay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0

import frida
import sys
import os

vid_index=0
aud_index = 0

def on_message(message, data):
global vid_index
global aud_index
print(message)




session = frida.attach("avconferenced")
code = open('replay.js', 'r').read()
script = session.create_script(code);
script.on("message", on_message)
script.load()

print("Press Ctrl-C to quit")
sys.stdin.read()

0 comments on commit c74c937

Please sign in to comment.