UNPKG

rms-runtime-mobile-security

Version:

Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

456 lines (399 loc) 16.9 kB
/**************************************************************************************************************************** * Name: Cipher class hooks and utilities * OS: Android * Author: FSecureLABS * Source: https://github.com/FSecureLABS/android-keystore-audit/blob/master/frida-scripts/tracer-cipher.js * Info: Hooks will attempt to trace calls to Cipher class and hexdump buffer passed/returned during encryption/decryption. All instances of Cipher class are captured by hooking any getInstance() call. You can them it in the cipherList variable. Utilities: ListCiphers() * List Cipher instances collected in cipherList GetCipher(cipherName) * Get Cipher instance from cipherList using it's name * example: GetCipher("javax.crypto.Cipher@b6859ee") doUpdate(cipherName, bytes) * Call doUpdate on Cipher instance from cipherList using it's name * you can pass buffer into it which will be processed. * The bytes buffer must be Java [B * Example: doUpdate('javax.crypto.Cipher@b6859ee', buffer) doFinal(cipherName) * Call doFinal on Cipher instance from cipherList using it's name * Example: doFinal('javax.crypto.Cipher@b6859ee') *****************************************************************************************************************************/ send("Cipher hooks loaded!"); Java.perform(function () { hookCipherGetInstance(); hookCipherGetInstance2(); hookCipherGetInstance3(); hookCipherInit(); hookCipherInit2(); hookCipherInit3(); hookCipherInit4(); hookCipherInit5(); hookCipherInit6(); hookCipherInit7(); hookCipherInit8(); hookDoFinal(); hookDoFinal2(); hookDoFinal3(); hookDoFinal4(); hookDoFinal5(); hookDoFinal6(); hookDoFinal7(); hookUpdate(); hookUpdate2(); hookUpdate3(); hookUpdate4(); hookUpdate5(); }); var cipherList = []; var StringCls = null; Java.perform(function () { StringCls = Java.use('java.lang.String'); }); /* .overload('java.lang.String') .overload('java.lang.String', 'java.security.Provider') .overload('java.lang.String', 'java.lang.String') */ function hookCipherGetInstance() { var cipherGetInstance = Java.use('javax.crypto.Cipher')['getInstance'].overload("java.lang.String"); cipherGetInstance.implementation = function (type) { send("[Cipher.getInstance()]: type: " + type); var tmp = this.getInstance(type); send("[Cipher.getInstance()]: cipherObj: " + tmp); cipherList.push(tmp); return tmp; } } function hookCipherGetInstance2() { var cipherGetInstance = Java.use('javax.crypto.Cipher')['getInstance'].overload('java.lang.String', 'java.security.Provider'); cipherGetInstance.implementation = function (transforamtion, provider) { send("[Cipher.getInstance2()]: transforamtion: " + transforamtion + ", provider: " + provider); var tmp = this.getInstance(transforamtion, provider); send("[Cipher.getInstance2()]: cipherObj: " + tmp); cipherList.push(tmp); return tmp; } } function hookCipherGetInstance3() { var cipherGetInstance = Java.use('javax.crypto.Cipher')['getInstance'].overload('java.lang.String', 'java.lang.String'); cipherGetInstance.implementation = function (transforamtion, provider) { send("[Cipher.getInstance3()]: transforamtion: " + transforamtion + ", provider: " + provider); var tmp = this.getInstance(transforamtion, provider); send("[Cipher.getInstance3()]: cipherObj: " + tmp); cipherList.push(tmp); return tmp; } } /* .overload('int', 'java.security.cert.Certificate') .overload('int', 'java.security.Key') .overload('int', 'java.security.Key', 'java.security.AlgorithmParameters') //.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec') .overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom') .overload('int', 'java.security.Key', 'java.security.SecureRandom') .overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom') .overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom') */ function hookCipherInit() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.cert.Certificate'); cipherInit.implementation = function (mode, cert) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", cert: " + cert + " , cipherObj: " + this); var tmp = this.init(mode, cert); } } function hookCipherInit2() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key'); cipherInit.implementation = function (mode, secretKey) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " , cipherObj: " + this); var tmp = this.init(mode, secretKey); } } function hookCipherInit3() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key', 'java.security.AlgorithmParameters'); cipherInit.implementation = function (mode, secretKey, alParam) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " alParam:" + alParam + " , cipherObj: " + this); var tmp = this.init(mode, secretKey, alParam); } } function hookCipherInit4() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec'); cipherInit.implementation = function (mode, secretKey, spec) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " spec:" + spec + " , cipherObj: " + this); var tmp = this.init(mode, secretKey, spec); } } function hookCipherInit5() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom'); cipherInit.implementation = function (mode, cert, secureRandom) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", cert: " + cert + " secureRandom:" + secureRandom + " , cipherObj: " + this); var tmp = this.init(mode, cert, secureRandom); } } function hookCipherInit6() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key', 'java.security.SecureRandom'); cipherInit.implementation = function (mode, secretKey, secureRandom) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " secureRandom:" + secureRandom + " , cipherObj: " + this); var tmp = this.init(mode, secretKey, secureRandom); } } function hookCipherInit7() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom'); cipherInit.implementation = function (mode, secretKey, spec, secureRandom) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " spec:" + spec + " secureRandom: " + secureRandom + " , cipherObj: " + this); var tmp = this.init(mode, secretKey, spec, secureRandom); } } function hookCipherInit8() { var cipherInit = Java.use('javax.crypto.Cipher')['init'].overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom'); cipherInit.implementation = function (mode, secretKey, alParam, secureRandom) { send("[Cipher.init()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " alParam:" + alParam + " secureRandom: " + secureRandom + " , cipherObj: " + this); var tmp = this.init(mode, secretKey, alParam, secureRandom); } } /* .overload() .overload('[B') .overload('[B', 'int') .overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer') .overload('[B', 'int', 'int') .overload('[B', 'int', 'int', '[B') .overload('[B', 'int', 'int', '[B', 'int') */ function hookDoFinal() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload(); cipherInit.implementation = function () { send("[Cipher.doFinal()]: " + " cipherObj: " + this); var tmp = this.doFinal(); dumpByteArray('Result', tmp); return tmp; } } function hookDoFinal2() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('[B'); cipherInit.implementation = function (byteArr) { send("[Cipher.doFinal2()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.doFinal(byteArr); dumpByteArray('Result', tmp); return tmp; } } function hookDoFinal3() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('[B', 'int'); cipherInit.implementation = function (byteArr, a1) { send("[Cipher.doFinal3()]: " + " cipherObj: " + this); dumpByteArray('Out buffer', byteArr); var tmp = this.doFinal(byteArr, a1); dumpByteArray('Out buffer', byteArr); return tmp; } } function hookDoFinal4() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer'); cipherInit.implementation = function (a1, a2) { send("[Cipher.doFinal4()]: " + " cipherObj: " + this); dumpByteArray('In buffer', a1.array()); var tmp = this.doFinal(a1, a2); dumpByteArray('Out buffer', a2.array()); return tmp; } } function hookDoFinal5() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('[B', 'int', 'int'); cipherInit.implementation = function (byteArr, a1, a2) { send("[Cipher.doFinal5()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.doFinal(byteArr, a1, a2); dumpByteArray('Out buffer', tmp); return tmp; } } function hookDoFinal6() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('[B', 'int', 'int', '[B'); cipherInit.implementation = function (byteArr, a1, a2, outputArr) { send("[Cipher.doFinal6()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.doFinal(byteArr, a1, a2, outputArr); dumpByteArray('Out buffer', outputArr); return tmp; } } function hookDoFinal7() { var cipherInit = Java.use('javax.crypto.Cipher')['doFinal'].overload('[B', 'int', 'int', '[B', 'int'); cipherInit.implementation = function (byteArr, a1, a2, outputArr, a4) { send("[Cipher.doFinal7()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.doFinal(byteArr, a1, a2, outputArr, a4); dumpByteArray('Out buffer', outputArr); return tmp; } } /* .overload('[B') .overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer') .overload('[B', 'int', 'int') .overload('[B', 'int', 'int', '[B') .overload('[B', 'int', 'int', '[B', 'int') */ function hookUpdate() { var cipherInit = Java.use('javax.crypto.Cipher')['update'].overload('[B'); cipherInit.implementation = function (byteArr) { send("[Cipher.update()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.update(byteArr); dumpByteArray('Out buffer', tmp); return tmp; } } function hookUpdate2() { var cipherInit = Java.use('javax.crypto.Cipher')['update'].overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer'); cipherInit.implementation = function (byteArr, outputArr) { send("[Cipher.update2()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr.array()); var tmp = this.update(byteArr, outputArr); dumpByteArray('Out buffer', outputArr.array()); return tmp; } } function hookUpdate3() { var cipherInit = Java.use('javax.crypto.Cipher')['update'].overload('[B', 'int', 'int'); cipherInit.implementation = function (byteArr, a1, a2) { send("[Cipher.update3()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.update(byteArr, a1, a2); dumpByteArray('Out buffer', tmp); return tmp; } } function hookUpdate4() { var cipherInit = Java.use('javax.crypto.Cipher')['update'].overload('[B', 'int', 'int', '[B'); cipherInit.implementation = function (byteArr, a1, a2, outputArr) { send("[Cipher.update4()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.update(byteArr, a1, a2, outputArr); dumpByteArray('Out buffer', outputArr); return tmp; } } function hookUpdate5() { var cipherInit = Java.use('javax.crypto.Cipher')['update'].overload('[B', 'int', 'int', '[B', 'int'); cipherInit.implementation = function (byteArr, a1, a2, outputArr, a4) { send("[Cipher.update5()]: " + " cipherObj: " + this); dumpByteArray('In buffer', byteArr); var tmp = this.update(byteArr, a1, a2, outputArr, a4); dumpByteArray('Out buffer', outputArr); return tmp; } } /* * List Cipher instances collected in cipherList */ function ListCiphers() { Java.perform(function () { for (var i = 0; i < cipherList.length; i++) { send("[" + i + "] " + cipherList[i]); } }); return "[done]"; } /* * Get Cipher instance from cipherList using it's name e.g. Cipger.toString() (like: Cipher@a0b1c2) * Example: GetCipher('Cipher@a0b1c2') */ function GetCipher(cipherName) { var result = null; Java.perform(function () { for (var i = 0; i < cipherList.length; i++) { if (cipherName.localeCompare("" + cipherList[i]) == 0) result = cipherList[i]; } }); return result; } /* * Call doUpdate on Cipher instance from cipherList using it's name e.g. Cipger.toString() (like: Cipher@a0b1c2), you can pass buffer into it which will be processed. * The bytes buffer must be Java [B * Example: doUpdate('Cipher@a0b1c2', buffer) */ function doUpdate(cipherName, bytes) { Java.perform(function () { var cipher = GetCipher(cipherName); cipher.update(bytes); //cipher.doFinal(); }); } /* * Call doFinal on Cipher instance from cipherList using it's name e.g. Cipger.toString() (like: Cipher@a0b1c2) * Example: doFinal('Cipher@a0b1c2') */ function doFinal(cipherName) { Java.perform(function () { var cipher = GetCipher(cipherName); cipher.final(bytes); //cipher.doFinal(); }); } function decodeMode(mode) { if (mode == 1) return "Encrypt mode"; else if (mode == 2) return "Decrypt mode"; else if (mode == 3) return "Wrap mode"; else if (mode == 4) return "Unwrap mode"; } /* All below is hexdump implementation*/ function dumpByteArray(title, byteArr) { if (byteArr != null) { try { var buff = new ArrayBuffer(byteArr.length) var dtv = new DataView(buff) for (var i = 0; i < byteArr.length; i++) { dtv.setUint8(i, byteArr[i]); // Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..). It occured even when Array.copyOf was done to work on copy. } send(title + ":\n"); send(hexdumpJS(dtv.buffer, 0, byteArr.length)) } catch (error) { send("Exception has occured in hexdump") } } else { send("byteArr is null!"); } } function _fillUp(value, count, fillWith) { var l = count - value.length; var ret = ""; while (--l > -1) ret += fillWith; return ret + value; } function hexdumpJS(arrayBuffer, offset, length) { var view = new DataView(arrayBuffer); offset = offset || 0; length = length || arrayBuffer.byteLength; var out = _fillUp("Offset", 8, " ") + " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"; var row = ""; for (var i = 0; i < length; i += 16) { row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " "; var n = Math.min(16, length - offset); var string = ""; for (var j = 0; j < 16; ++j) { if (j < n) { var value = view.getUint8(offset); string += (value >= 32 && value < 128) ? String.fromCharCode(value) : "."; row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " "; offset++; } else { row += " "; string += " "; } } row += " " + string + "\n"; } out += row; return out; };