diff --git a/android-certificate-unpinning.js b/android-certificate-unpinning.js index 33e140a..d9947d8 100644 --- a/android-certificate-unpinning.js +++ b/android-certificate-unpinning.js @@ -113,7 +113,7 @@ const PINNING_FIXES = { 'android.security.net.config.NetworkSecurityConfig': [ { methodName: '$init', - overloads: '*', + overload: '*', replacement: () => { const PinSet = Java.use('android.security.net.config.PinSet'); const EMPTY_PINSET = PinSet.EMPTY_PINSET.value; @@ -366,66 +366,90 @@ setTimeout(function () { Java.perform(function () { if (DEBUG_MODE) console.debug('=== Disablig all recognized unpinning libraries ==='); - Object.keys(PINNING_FIXES).forEach((targetClass) => { - const Cls = getJavaClassIfExists(targetClass); - if (!Cls) { + const classesToPatch = Object.keys(PINNING_FIXES); + + classesToPatch.forEach((targetClassName) => { + const TargetClass = getJavaClassIfExists(targetClassName); + if (!TargetClass) { // We skip patches for any classes that don't seem to be present. This is common // as not all libraries we handle are necessarily used. - if (DEBUG_MODE) console.debug(`[ ] ${targetClass} *`); + if (DEBUG_MODE) console.debug(`[ ] ${targetClassName} *`); + return; } - const patches = PINNING_FIXES[targetClass]; + const patches = PINNING_FIXES[targetClassName]; let patchApplied = false; patches.forEach(({ methodName, getMethod, overload, replacement }) => { const namedTargetMethod = getMethod - ? getMethod(targetClass) - : targetClass[methodName]; + ? getMethod(TargetClass) + : TargetClass[methodName]; const methodDescription = `${methodName}${ - overload + overload === '*' + ? '(*)' + : overload ? '(' + overload.map((argType) => { // Simplify arg names to just the class name for simpler logs: const argClassName = argType.split('.').slice(-1)[0]; if (argType.startsWith('[L')) return `${argClassName}[]`; else return argClassName; }).join(', ') + ')' + // No overload: : '' }` - const targetMethodImplementations = !namedTargetMethod - ? [] // Method not found - : !overload - ? [namedTargetMethod] // No overload specified - : overload === '*' - ? namedTargetMethod.overloads // Targetting _all_ overloads - // Or targetting a specific overload: - : [namedTargetMethod.overload(...overload)]; + let targetMethodImplementations = []; + try { + if (namedTargetMethod) { + if (!overload) { + // No overload specified + targetMethodImplementations = [namedTargetMethod]; + } else if (overload === '*') { + // Targetting _all_ overloads + targetMethodImplementations = namedTargetMethod.overloads; + } else { + // Or targetting a specific overload: + targetMethodImplementations = [namedTargetMethod.overload(...overload)]; + } + } + } catch (e) { + // Overload not present + } + + // We skip patches for any methods that don't seem to be present. This is rarer, but does + // happen due to methods that only appear in certain library versions or whose signatures + // have changed over time. if (targetMethodImplementations.length === 0) { - // We skip patches for any methods that don't seem to be present. This is - // rarer, but happens due to methods that only appear in certain library - // versions or whose signatures have changed over time. - if (DEBUG_MODE) console.debug(`[ ] ${targetClass} ${methodDescription}`); + if (DEBUG_MODE) console.debug(`[ ] ${targetClassName} ${methodDescription}`); return; } - try { - targetMethodImplementations.forEach((targetMethod) => { + targetMethodImplementations.forEach((targetMethod, i) => { + const patchName = `${targetClassName} ${methodDescription}${ + targetMethodImplementations.length > 1 ? ` (${i})` : '' + }`; + + try { targetMethod.implementation = replacement(targetMethod); - if (DEBUG_MODE) console.debug(`[+] ${patchName} ${methodDescription}`); - }) - patchApplied = true; - } catch (e) { - // In theory, errors like this should never happen - it means the patch is broken - // (e.g. an expected method doesn't exist at all) - console.warn(`[!] ERROR: ${patchName} failed: ${e}`); - } + + if (DEBUG_MODE) console.debug(`[+] ${patchName}`); + patchApplied = true; + } catch (e) { + // In theory, errors like this should never happen - it means the patch is broken + // (e.g. some dynamic patch building fails completely) + console.error(`[!] ERROR: ${patchName} failed: ${e}`); + } + }) }); - console.log(`[!] Matched class ${targetClass} but could not patch any methods`); + if (!patchApplied) { + console.log(`[!] Matched class ${targetClassName} but could not patch any methods`); + } }); + console.log('== Certificate unpinning completed =='); }); }); \ No newline at end of file