Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Automatically extract KeyStore objects and relative password from Android applications with Frida - Read more: https://ceres-c.it/2018/12/16/frida-android-keystore/
#!/usr/bin/python3
'''
author: ceres-c
usage: ./frida-extract-keystore.py
Once the keystore(s) have been exported you have to convert them to PKCS12 using keytool
'''
import frida, sys, time
app_name = 'com.app.mobile'
i = 0
ext = ''
def on_message(message, data):
global i, ext
if (message['type'] == 'send' and 'event' in message['payload']):
if (message['payload']['event'] == '+found'):
i += 1
print("\n[+] Hooked keystore" + str(i) + "...")
elif (message['payload']['event'] == '+type'):
print(" [+] Cert Type: " + ''.join(message['payload']['certType']))
if (message['payload']['certType'] == 'PKCS12'):
ext = '.jks'
elif (message['payload']['event'] == '+pass'):
print(" [+] Password: " + ''.join(message['payload']['password']))
elif (message['payload']['event'] == '+write'):
print(" [+] Writing to file: keystore" + str(i) + ext)
f = open('keystore' + str(i) + ext, 'wb')
f.write(bytes.fromhex(message['payload']['cert']))
f.close()
else:
print(message)
jscode = """
setTimeout(function() {
Java.perform(function () {
var keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');
/* following function hooks to a Keystore.load(InputStream stream, char[] password) */
keyStoreLoadStream.implementation = function(stream, charArray) {
/* sometimes this happen, I have no idea why, tho... */
if (stream == null) {
/* just to avoid interfering with app's flow */
this.load(stream, charArray);
return;
}
/* just to notice the client we've hooked a KeyStore.load */
send({event: '+found'});
/* read the buffer stream to a variable */
var hexString = readStreamToHex (stream);
/* send KeyStore type to client shell */
send({event: '+type', certType: this.getType()});
/* send KeyStore password to client shell */
send({event: '+pass', password: charArray});
/* send the string representation to client shell */
send({event: '+write', cert: hexString});
/* call the original implementation of 'load' */
this.load(stream, charArray);
/* no need to return anything */
}
});
},0);
/* following function reads an InputStream and returns an ASCII char representation of it */
function readStreamToHex (stream) {
var data = [];
var byteRead = stream.read();
while (byteRead != -1)
{
data.push( ('0' + (byteRead & 0xFF).toString(16)).slice(-2) );
/* <---------------- binary to hex ---------------> */
byteRead = stream.read();
}
stream.close();
return data.join('');
}
"""
print("[.] Attaching to device...")
try:
device = frida.get_usb_device()
except:
print("[-] Can't attach. Is the device connected?")
sys.exit()
print("[.] Spawning the app...")
try:
pid = device.spawn(app_name)
device.resume(pid)
time.sleep(1)
except:
print("[-] Can't spawn the App. Is filename correct?")
sys.exit()
print("[.] Attaching to process...")
try:
process = device.attach(pid)
except:
print("[-] Can't connect to App.")
sys.exit()
print("[.] Launching js code...")
print(" (run the app until needed, close it and then kill this script)")
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
try:
sys.stdin.read()
except KeyboardInterrupt:
print ("\nExiting now")
exit(0)
@elliott-wen
Copy link

elliott-wen commented Jun 11, 2022

Hi, I noticed one issue in the readStreamToHex function, where the stream is closed on behalf of the application. This may disrupt the application flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment