Instantly share code, notes, and snippets.

Embed
What would you like to do?
powersaves-amiibo-frida stuffs
'''
Input MD5 : BB4E83D7A77AADD7F62728314EF09461
File Name : C:\Program Files (x86)\Powersaves For AMIIBO\Powersaves For AMIIBO.exe
0x108fd0 : schannel_recv -> log buffer on end
0x1090d0 : schannel_recv end
0x108d10 : schannel_send -> log buffer on start
0xce61 : deals with https "Token"
0xceab : deals with https "Vuid"
0x9366 : the usb initialization func
0xe0fd0 : libusb_open_device_with_vid_pid -> return 0xdeadbeef
0xe14d0 : libusb_claim_interface -> return 0
0xe1590 : libusb_release_interface -> nop
0xe1310 : libusb_close -> nop
0xe2460 : libusb_bulk_transfer
0xe2480 : libusb_interrupt_transfer
.restart
bp psfa+0x936b "r @eax=0xdeadbeef; gc"
bp psfa+0x9376 "r @eip=@eip+5; gc"
bp psfa+0x93c3 "r @eip=@eip+0x1e; gc"
g
'''
import frida
import time
def create_script(session, name):
return session.create_script(name = name, source = open('./' + name + '.js', 'r').read())
pid = frida.spawn(['..\psfa.exe'])
session = frida.attach(pid)
print('attached to pid %i' % (pid))
ssl_log = create_script(session, 'schannel-logger')
ssl_log.load()
usb_sim = create_script(session, 'usb-sim')
usb_sim.load()
frida.resume(pid)
while True:
time.sleep(1)
base = Module.findBaseAddress('psfa.exe');
/*
static ssize_t
schannel_send(struct connectdata *conn, int sockindex,
const void *buf, size_t len, CURLcode *err)
*/
schannel_send_addr = base.add(0x108d10);
schannel_recv_addr = base.add(0x108fd0);
Interceptor.attach(schannel_send_addr, {
onEnter: function(args) {
var buf = args[2];
var len = args[3];
console.log('schannel_send{buf:' + buf.toString(16) + ', len: ' + len.toString(16) + '}');
try {
console.log(Memory.readCString(buf, len.toInt32()));
var f = new File('schannel-send.' + buf.toString(16) + '.bin', 'wb');
f.write(Memory.readByteArray(buf, len.toInt32()));
f.flush();
//f.close(); // does not work for some reason
} catch (e) {
console.log(e);
}
}
});
Interceptor.attach(schannel_recv_addr, {
onEnter: function(args) {
this.buf = args[2];
this.len = args[3];
console.log('schannel_recv{buf:' + this.buf.toString(16) + ', len: ' + this.len.toString(16) + '}');
},
onLeave: function(retval) {
try {
console.log(Memory.readCString(this.buf, this.len.toInt32()));
} catch (e) {
console.log(e);
}
}
});
/*
0xe0fd0 : libusb_open_device_with_vid_pid -> return 0xdeadbeef
0xe14d0 : libusb_claim_interface -> return 0
0xe1590 : libusb_release_interface -> nop
0xe1310 : libusb_close -> nop
0xe2460 : libusb_bulk_transfer -> simulate
0xe2480 : libusb_interrupt_transfer -> simulate
*/
base = Module.findBaseAddress('psfa.exe');
libusb_open_device_with_vid_pid_addr = base.add(0xe0fd0)
libusb_claim_interface_addr = base.add(0xe14d0);
libusb_release_interface_addr = base.add(0xe1590);
libusb_close_addr = base.add(0xe1310);
do_sync_bulk_transfer_addr = base.add(0xe2310);
use_real_device = false;
if (!use_real_device) {
Interceptor.replace(libusb_open_device_with_vid_pid_addr, new NativeCallback(function() {
//console.log('libusb_open_device_with_vid_pid');
return 0xdeadbeef;
}, 'int', ['pointer', 'uint32'], 'thiscall')); // not actual signature, but the compiler was fucky
Interceptor.replace(libusb_claim_interface_addr, new NativeCallback(function() {
console.log('libusb_claim_interface');
return 0;
}, 'int', [])); // luckily sig doesn't matter (fucked by LTCG)
Interceptor.replace(libusb_release_interface_addr, new NativeCallback(function(interface_number) {
console.log('libusb_release_interface');
return 0;
}, 'int', ['int'], 'stdcall'));// device handle in edi
Interceptor.replace(libusb_close_addr, new NativeCallback(function(dev_handle) {
//console.log('libusb_close');
}, 'void', ['pointer'], 'stdcall'));
// simply patch out the desc.bcdDevice check
var descriptor_check_addr = base.add(0x93c3);
var skip_descriptor_check = [
0x59, // pop ecx
0xE9, 0x19, 0x00, 0x00, 0x00 // jmp +0x1d
];
Memory.protect(descriptor_check_addr, skip_descriptor_check.length, 'rwx');
Memory.writeByteArray(descriptor_check_addr, skip_descriptor_check);
}
function b2h_byte(value) {
var str = value.toString(16);
return '00'.substring(str.length) + str;
}
function b2h(arr) {
var str = '';
for (var i = 0; i < arr.length; i++) {
str += ' ' + b2h_byte(arr[i]);
}
return str;
}
/*
It only uses endpoint 1, with bulk for OUT and interrupt for IN transfers and constant buffer sizes
*/
var xfer_cmd = 0;
var xfer_page = 0;
var do_sync_bulk_transfer = new NativeFunction(do_sync_bulk_transfer_addr,
'int', ['pointer', 'uchar', 'pointer', 'pointer', 'uchar']);
Interceptor.replace(do_sync_bulk_transfer_addr,
new NativeCallback(function(dev_handle, endpoint, buffer, transferred, type) {
var is_interrupt = type == 3;
// the compiler has been smart enough to lower the constants length and timeout into the implementation
var length = 0x40;
if (is_interrupt) {
var data = [];
for (var i = 0; i < length; i++) {
//data.push(Math.random() * 0xff);
data.push(0);
}
switch (xfer_cmd) {
case 2:
// This gets a string identifier:
// 4e 46 43 2d 50 6f 72 74 61 6c - "NFC-Portal"
// Result isn't checked
break;
case 0x90:
// This appears to get some model/unique ID info or something.
// 01 ff ff 16 a3 66 30 43 62 6c 23 bd 69 5d c3 33 f0 2d 3f
// Result isn't checked.
data = [0, 0];
for (var i = 0; i < 0x13; i++) {
data.push(Math.random() * 0xff);
}
break;
case 0x80:
// decrypt Vuid via "SAM12"
// Result checked by server
if (use_real_device) {
do_sync_bulk_transfer(dev_handle, endpoint, buffer, transferred, type);
console.log('DEC(Vuid): ' + Memory.readCString(buffer.add(2), 16));
} else {
data = [0, 0];
for (var i = 0; i < 0x10; i++) {
data.push(Math.random() * 0xff);
}
}
break;
case 0x11:
case 0x10:
case 0x12:
case 0x20:
break;
case 0x1c: // read page
data = [0, 0];
for (var i = 0; i < 0x10; i++) {
data.push(Math.random() * 0xff);
}
break;
case 0x1d: // write page
break;
}
if (!use_real_device || xfer_cmd != 0x80) {
Memory.writeByteArray(buffer, data);
}
} else if (use_real_device) {
switch (Memory.readU8(buffer)) {
case 0x80:
// Need to forward the bulk xfers so the real device knows what's coming!
do_sync_bulk_transfer(dev_handle, endpoint, buffer, transferred, type);
break;
}
}
var buf = Memory.readByteArray(buffer, length);
if (!is_interrupt) {
xfer_cmd = buf[0];
}
var msg = is_interrupt ? '<-' : '->';
msg += b2h(buf);
console.log(msg);
Memory.writeU32(transferred, length + (is_interrupt ? 0 : 1));
return 0;
}, 'int', ['pointer', 'uchar', 'pointer', 'pointer', 'uchar'])
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment