frida hook 双向证书秘钥

spawn 模式启动:

frida -U -f 包名 -l keystore.js –no-pause

加 – no-pause与不加 –no-pause的区别主要是 hook时机的早晚。 –no-pause是即时hook, 而不加 –no-pause 这个参数的话。frida会拉起进程,停在加载主界面之前,这时候 应用会出现白屏状态,就像

恢复主线程运行的话,这里要手动用 %resume关键字。

这种方法的原理是, 在手动用%resume关键字恢复主线程运行之前,frida已经拉起了进程,外壳程序已经在启动,所以这里等个几秒用%resume关键字恢复,有一定几率是可以hook到的。

/*Android keystore hooks + utilitiesAll instances of keystore are captured by hooking any getInstance() calls.You can find them in keystoreList variable.Utilities:KeystoreListAllAliases()ListAliasesStatic()* List all aliases in keystores of known hardcoded types(in keystoreTypes)KeystoreListAllAliasesOnAllInstances()ListAliasesRuntime()* List all aliases in keystores of all instances obtained during app runtime.* Instances that will be dumped are collected via hijacking* Keystore.getInstance() -> hookKeystoreGetInstance()ListAliasesAndroid()* List all aliases in AndroidKeyStore.ListAliasesType(String type)* List all aliases in a keystore of given 'type'.* Example: ListAliasesType('AndroidKeyStore');ListAliasesObj(Object obj)* List all aliases for a given keystore object.* Example: ListAliasesObj(keystoreObj);GetKeyStore(String name)* Retrieve keystore instance from keystoreList.* Example: GetKeyStore("KeyStore...@af102a");AliasInfo(String alias)* List keystore key properties in a JSON object.* Example: AliasInfo('secret');*/Java.perform(function () {hookKeystoreGetInstance();hookKeystoreGetInstance_Provider();hookKeystoreGetInstance_Provider2();hookKeystoreConstructor();hookKeystoreLoad(false);hookKeystoreLoadStream(false);hookKeystoreGetKey();hookKeystoreSetKeyEntry();//hookKeystoreGetCertificate();hookKeystoreGetCertificateChain();hookKeystoreGetEntry();hookKeystoreSetEntry();hookKeystoreSetKeyEntry();hookKeystoreSetKeyEntry2();hookKeystoreStore();hookKeystoreStoreStream()});console.log("KeyStore hooks loaded!");var keystoreList = [];var StringCls = null;Java.perform(function () {StringCls = Java.use('java.lang.String');});function hookKeystoreConstructor() {var keyStoreConstructor = Java.use('java.security.KeyStore').$init.overload("java.security.KeyStoreSpi", "java.security.Provider", "java.lang.String");keyStoreConstructor.implementation = function (keyStoreSpi, provider, type) {//console.log("[Call] Keystore(java.security.KeyStoreSpi, java.security.Provider, java.lang.String )")console.log("[Keystore()]: KeyStoreSpi: " + keyStoreSpi + ", Provider: " + provider + ", type: " + type);return this.$init(keyStoreSpi, provider, type);}}function hookKeystoreGetInstance() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String");keyStoreGetInstance.implementation = function (type) {//console.log("[Call] Keystore.getInstance(java.lang.String )")console.log("[Keystore.getInstance()]: type: " + type);var tmp = this.getInstance(type);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}}function hookKeystoreGetInstance_Provider() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.lang.String");keyStoreGetInstance.implementation = function (type, provider) {//console.log("[Call] Keystore.getInstance(java.lang.String, java.lang.String )")console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);var tmp = this.getInstance(type, proivder);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}}function hookKeystoreGetInstance_Provider2() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.security.Provider");keyStoreGetInstance.implementation = function (type, provider) {//console.log("[Call] Keystore.getInstance(java.lang.String, java.security.Provider )")console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);var tmp = this.getInstance(type, proivder);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}}/** Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.*/function hookKeystoreLoad(dump) {var keyStoreLoad = Java.use('java.security.KeyStore')['load'].overload('java.security.KeyStore$LoadStoreParameter');/* following function hooks to a Keystore.load(java.security.KeyStore.LoadStoreParameter) */keyStoreLoad.implementation = function (param) {//console.log("[Call] Keystore.load(java.security.KeyStore.LoadStoreParameter)")console.log("[Keystore.load(LoadStoreParameter)]: keystoreType: " + this.getType() + ", param: " + param);this.load(param);if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));}}/** Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.*/function hookKeystoreLoadStream(dump) {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) {//console.log("[Call] Keystore.load(InputStream stream, char[] password)")//var hexString = readStreamToHex (stream);console.log("[Keystore.load(InputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', inputSteam: " + stream);this.load(stream, charArray);if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));}}function hookKeystoreStore() {var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.security.KeyStore$LoadStoreParameter');/* following function hooks to a Keystore.store(java.security.KeyStore$LoadStoreParameter) */keyStoreStoreStream.implementation = function (param) {console.log("[Keystore.store()]: keystoreType: " + this.getType() + ", param: '" + param);this.store(stream, charArray);}}function hookKeystoreStoreStream() {var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.io.OutputStream', '[C');/* following function hooks to a Keystore.store(OutputStream stream, char[] password) */keyStoreStoreStream.implementation = function (stream, charArray) {console.log("[Keystore.store(OutputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', outputSteam: " + stream);this.store(stream, charArray);}}function hookKeystoreGetKey() {var keyStoreGetKey = Java.use('java.security.KeyStore')['getKey'].overload("java.lang.String", "[C");keyStoreGetKey.implementation = function (alias, charArray) {//console.log("[Call] Keystore.getKey(java.lang.String, [C )")console.log("[Keystore.getKey()]: alias: " + alias + ", password: '" + charArrayToString(charArray) + "'");return this.getKey(alias, charArray);}}function hookKeystoreSetEntry() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setEntry'].overload("java.lang.String", "java.security.KeyStore$Entry", "java.security.KeyStore$ProtectionParameter");keyStoreSetKeyEntry.implementation = function (alias, entry, protection) {//console.log("[Call] Keystore.setEntry(java.lang.String, java.security.KeyStore$Entry, java.security.KeyStore$ProtectionParameter )")console.log("[Keystore.setEntry()]: alias: " + alias + ", entry: " + dumpKeyStoreEntry(entry) + "', protection: " + dumpProtectionParameter(protection));return this.setEntry(alias, entry, protection);}}function hookKeystoreSetKeyEntry() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "java.security.Key", "[C", "[Ljava.security.cert.Certificate;");keyStoreSetKeyEntry.implementation = function (alias, key, charArray, certs) {//console.log("[Call] Keystore.setKeyEntry(java.lang.String, java.security.Key, [C, [Ljava.security.cert.Certificate; )console.log("[Keystore.setKeyEntry()]: alias: " + alias + ", key: " + key + ", password: '" + charArrayToString(charArray) + "', certs: " + certs);return this.setKeyEntry(alias, key, charArray, certs);}}function hookKeystoreSetKeyEntry2() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "[B", "[Ljava.security.cert.Certificate;");keyStoreSetKeyEntry.implementation = function (alias, key, certs) {//console.log("[Call] Keystore.setKeyEntry(java.lang.String, [B, [Ljava.security.cert.Certificate; )")console.log("[Keystore.setKeyEntry2()]: alias: " + alias + ", key: " + key + "', certs: " + certs);return this.setKeyEntry(alias, key, certs);}}/** Usually used to load certs for cert pinning.*/function hookKeystoreGetCertificate() {var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificate'].overload("java.lang.String");keyStoreGetCertificate.implementation = function (alias) {//console.log("[Call] Keystore.getCertificate(java.lang.String )")console.log("[Keystore.getCertificate()]: alias: " + alias);return this.getCertificate(alias);}}/** Usually used to load certs for cert pinning.*/function hookKeystoreGetCertificateChain() {var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificateChain'].overload("java.lang.String");keyStoreGetCertificate.implementation = function (alias) {//console.log("[Call] Keystore.getCertificateChain(java.lang.String )")console.log("[Keystore.getCertificateChain()]: alias: " + alias);return this.getCertificateChain(alias);}}function hookKeystoreGetEntry() {var keyStoreGetEntry = Java.use('java.security.KeyStore')['getEntry'].overload("java.lang.String", "java.security.KeyStore$ProtectionParameter");keyStoreGetEntry.implementation = function (alias, protection) {//console.log("[Call] Keystore.getEntry(java.lang.String, java.security.KeyStore$ProtectionParameter )")console.log("[Keystore.getEntry()]: alias: " + alias + ", protection: '" + dumpProtectionParameter(protection) + "'");var entry = this.getEntry(alias, protection);console.log("[getEntry()]: Entry: " + dumpKeyStoreEntry(entry));return entry;}}function dumpProtectionParameter(protection) {if (protection != null) {// android.security.keystore.KeyProtection, java.security.KeyStore.CallbackHandlerProtection, java.security.KeyStore.PasswordProtection, android.security.KeyStoreParametervar protectionCls = protection.$className;if (protectionCls.localeCompare("android.security.keystore.KeyProtection") == 0) {return "" + protectionCls + " [implement dumping if needed]";}else if (protectionCls.localeCompare("java.security.KeyStore.CallbackHandlerProtection") == 0) {return "" + protectionCls + " [implement dumping if needed]";}else if (protectionCls.localeCompare("java.security.KeyStore.PasswordProtection") == 0) {getPasswordMethod = Java.use('java.security.KeyStore.PasswordProtection')['getPassword'];password = getPasswordMethod.call(protection);return "password: " + charArrayToString(password);}else if (protectionCls.localeCompare("android.security.KeyStoreParameter") == 0) {isEncryptionRequiredMethod = Java.use('android.security.KeyStoreParameter')['isEncryptionRequired'];result = isEncryptionRequiredMethod.call(protection);return "isEncryptionRequired: " + result;}elsereturn "Unknown protection parameter type: " + protectionCls;}elsereturn "null";}function dumpKeyStoreEntry(entry) {// java.security.KeyStore$PrivateKeyEntry, java.security.KeyStore$SecretKeyEntry, java.security.KeyStore$TrustedCertificateEntry, android.security.WrappedKeyEntryif (entry != null) {var entryCls = entry.$className;var castedEntry = Java.cast(entry, Java.use(entryCls));if (entryCls.localeCompare("java.security.KeyStore$PrivateKeyEntry") == 0) {var getPrivateKeyEntryMethod = Java.use('java.security.KeyStore$PrivateKeyEntry')['getPrivateKey'];var key = getPrivateKeyEntryMethod.call(castedEntry);return "" + entryCls + " [implement key dumping if needed] " + key.$className;}else if (entryCls.localeCompare("java.security.KeyStore$SecretKeyEntry") == 0) {var getSecretKeyMethod = Java.use('java.security.KeyStore$SecretKeyEntry')['getSecretKey'];var key = getSecretKeyMethod.call(castedEntry);var keyGetFormatMethod = Java.use(key.$className)['getFormat'];var keyGetEncodedMethod = Java.use(key.$className)['getEncoded'];//console.log(""+key.$className);if (key.$className.localeCompare("android.security.keystore.AndroidKeyStoreSecretKey") == 0)return "keyClass: android.security.keystore.AndroidKeyStoreSecretKey can't dump";return "keyFormat: " + keyGetFormatMethod.call(key) + ", encodedKey: '" + keyGetEncodedMethod.call(key) + "', key: " + key;}else if (entryCls.localeCompare("java.security.KeyStore$TrustedCertificateEntry") == 0) {return "" + entryCls + " [implement key dumping if needed]";}else if (entryCls.localeCompare("android.security.WrappedKeyEntry") == 0) {return "" + entryCls + " [implement key dumping if needed]";}elsereturn "Unknown key entry type: " + entryCls;}elsereturn "null";}/** Dump all aliasses in keystores of all types(predefined in keystoreTypes)*/function ListAliasesStatic() {// BCPKCS12/PKCS12-DEF - exceptionsvar keystoreTypes = ["AndroidKeyStore", "AndroidCAStore", /*"BCPKCS12",*/ "BKS", "BouncyCastle", "PKCS12", /*"PKCS12-DEF"*/];keystoreTypes.forEach(function (entry) {console.log("[ListAliasesStatic] keystoreType: " + entry + " \nAliases: " + ListAliasesType(entry));});return "[done]";}/** Dump all aliasses in keystores of all instances obtained during app runtime.* Instances that will be dumped are collected via hijacking Keystre.getInstance() -> hookKeystoreGetInstance()*/function ListAliasesRuntime() {Java.perform(function () {console.log("[ListAliasesRuntime] Instances: " + keystoreList);keystoreList.forEach(function (entry) {console.log("[ListAliasesRuntime] keystoreObj: " + entry + " type: " + entry.getType() + " \n" + ListAliasesObj(entry));});});return "[done]";}/** Dump all aliasses in AndroidKey keystore.*/function ListAliasesAndroid() {return ListAliasesType("AndroidKeyStore");}/** Dump all aliasses in keystore of given 'type'.* Example: ListAliasesType('AndroidKeyStore');*/function ListAliasesType(type) {var result = [];Java.perform(function () {var keyStoreCls = Java.use('java.security.KeyStore');var keyStoreObj = keyStoreCls.getInstance(type);keyStoreObj.load(null);var aliases = keyStoreObj.aliases();//console.log("aliases: " + aliases.getClass());while (aliases.hasMoreElements()) {result.push("'" + aliases.nextElement() + "'");}});return result;}/** Dump all aliasses for a given keystore object.* Example: ListAliasesObj(keystoreObj);*/function ListAliasesObj(obj) {var result = [];Java.perform(function () {var aliases = obj.aliases();while (aliases.hasMoreElements()) {result.push(aliases.nextElement() + "");}});return result;}/** Retrieve keystore instance from keystoreList* Example: GetKeyStore("KeyStore...@af102a");*/function GetKeyStore(keystoreName) {var result = null;Java.perform(function () {for (var i = 0; i < keystoreList.length; i++) {if (keystoreName.localeCompare("" + keystoreList[i]) == 0)result = keystoreList[i];}});return result;}/** Dump keystore key properties in JSON object* Example: AliasInfo('secret');*/function AliasInfo(keyAlias) {var result = {};Java.perform(function () {var keyStoreCls = Java.use('java.security.KeyStore');var keyFactoryCls = Java.use('java.security.KeyFactory');var keyInfoCls = Java.use('android.security.keystore.KeyInfo');var keySecretKeyFactoryCls = Java.use('javax.crypto.SecretKeyFactory');var keyFactoryObj = null;var keyStoreObj = keyStoreCls.getInstance('AndroidKeyStore');keyStoreObj.load(null);var key = keyStoreObj.getKey(keyAlias, null);if (key == null) {console.log('key does not exist');return null;}try {keyFactoryObj = keyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');} catch (err) {keyFactoryObj = keySecretKeyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');}var keyInfo = keyFactoryObj.getKeySpec(key, keyInfoCls.class);result.keyAlgorithm = key.getAlgorithm();result.keySize = keyInfoCls['getKeySize'].call(keyInfo);result.blockModes = keyInfoCls['getBlockModes'].call(keyInfo);result.digests = keyInfoCls['getDigests'].call(keyInfo);result.encryptionPaddings = keyInfoCls['getEncryptionPaddings'].call(keyInfo);result.keyValidityForConsumptionEnd = keyInfoCls['getKeyValidityForConsumptionEnd'].call(keyInfo);if (result.keyValidityForConsumptionEnd != null) result.keyValidityForConsumptionEnd = result.keyValidityForConsumptionEnd.toString();result.keyValidityForOriginationEnd = keyInfoCls['getKeyValidityForOriginationEnd'].call(keyInfo);if (result.keyValidityForOriginationEnd != null) result.keyValidityForOriginationEnd = result.keyValidityForOriginationEnd.toString();result.keyValidityStart = keyInfoCls['getKeyValidityStart'].call(keyInfo);if (result.keyValidityStart != null) result.keyValidityStart = result.keyValidityStart.toString();result.keystoreAlias = keyInfoCls['getKeystoreAlias'].call(keyInfo);result.origin = keyInfoCls['getOrigin'].call(keyInfo);result.purposes = keyInfoCls['getPurposes'].call(keyInfo);result.signaturePaddings = keyInfoCls['getSignaturePaddings'].call(keyInfo);result.userAuthenticationValidityDurationSeconds = keyInfoCls['getUserAuthenticationValidityDurationSeconds'].call(keyInfo);result.isInsideSecureHardware = keyInfoCls['isInsideSecureHardware'].call(keyInfo);result.isInvalidatedByBiometricEnrollment = keyInfoCls['isInvalidatedByBiometricEnrollment'].call(keyInfo);try { result.isTrustedUserPresenceRequired = keyInfoCls['isTrustedUserPresenceRequired'].call(keyInfo); } catch (err) { }result.isUserAuthenticationRequired = keyInfoCls['isUserAuthenticationRequired'].call(keyInfo);result.isUserAuthenticationRequirementEnforcedBySecureHardware = keyInfoCls['isUserAuthenticationRequirementEnforcedBySecureHardware'].call(keyInfo);result.isUserAuthenticationValidWhileOnBody = keyInfoCls['isUserAuthenticationValidWhileOnBody'].call(keyInfo);try { result.isUserConfirmationRequired = keyInfoCls['isUserConfirmationRequired'].call(keyInfo); } catch (err) { }//console.log(" result: " + JSON.stringify(result));//console.log("aliases: " + aliases.getClass());});return result;}/* 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));/*  */byteRead = stream.read();}stream.close();return data.join('');}function charArrayToString(charArray) {if (charArray == null)return '(null)';elsereturn StringCls.$new(charArray);}