Remove unnecessary (maybe problematic) setTimeout delays

This commit is contained in:
Tim Perry
2023-10-18 18:15:27 +02:00
parent 7800b44d53
commit 99b6adcd5d
2 changed files with 142 additions and 145 deletions
+76 -78
View File
@@ -368,94 +368,92 @@ const getJavaClassIfExists = (clsName) => {
}
}
setTimeout(function () {
Java.perform(function () {
if (DEBUG_MODE) console.log('\n === Disabling all recognized unpinning libraries ===');
Java.perform(function () {
if (DEBUG_MODE) console.log('\n === Disabling all recognized unpinning libraries ===');
const classesToPatch = Object.keys(PINNING_FIXES);
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.log(`[ ] ${targetClassName} *`);
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.log(`[ ] ${targetClassName} *`);
return;
}
const patches = PINNING_FIXES[targetClassName];
let patchApplied = false;
patches.forEach(({ methodName, getMethod, overload, replacement }) => {
const namedTargetMethod = getMethod
? getMethod(TargetClass)
: TargetClass[methodName];
const methodDescription = `${methodName}${
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:
: ''
}`
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) {
if (DEBUG_MODE) console.log(`[ ] ${targetClassName} ${methodDescription}`);
return;
}
const patches = PINNING_FIXES[targetClassName];
targetMethodImplementations.forEach((targetMethod, i) => {
const patchName = `${targetClassName} ${methodDescription}${
targetMethodImplementations.length > 1 ? ` (${i})` : ''
}`;
let patchApplied = false;
patches.forEach(({ methodName, getMethod, overload, replacement }) => {
const namedTargetMethod = getMethod
? getMethod(TargetClass)
: TargetClass[methodName];
const methodDescription = `${methodName}${
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:
: ''
}`
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)];
}
}
targetMethod.implementation = replacement(targetMethod);
if (DEBUG_MODE) console.log(`[+] ${patchName}`);
patchApplied = true;
} catch (e) {
// Overload not present
// 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}`);
}
// 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) {
if (DEBUG_MODE) console.log(`[ ] ${targetClassName} ${methodDescription}`);
return;
}
targetMethodImplementations.forEach((targetMethod, i) => {
const patchName = `${targetClassName} ${methodDescription}${
targetMethodImplementations.length > 1 ? ` (${i})` : ''
}`;
try {
targetMethod.implementation = replacement(targetMethod);
if (DEBUG_MODE) console.log(`[+] ${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}`);
}
})
});
if (!patchApplied) {
console.warn(`[!] Matched class ${targetClassName} but could not patch any methods`);
}
})
});
console.log('== Certificate unpinning completed ==');
if (!patchApplied) {
console.warn(`[!] Matched class ${targetClassName} but could not patch any methods`);
}
});
console.log('== Certificate unpinning completed ==');
});
+66 -67
View File
@@ -13,81 +13,80 @@
* in the native connect() hook script.
*/
setTimeout(() => {
Java.perform(() => {
// Set default JVM system properties for the proxy address. Notably these are used
// to initialize WebView configuration.
Java.use('java.lang.System').setProperty('http.proxyHost', PROXY_HOST);
Java.use('java.lang.System').setProperty('http.proxyPort', PROXY_PORT.toString());
Java.use('java.lang.System').setProperty('https.proxyHost', PROXY_HOST);
Java.use('java.lang.System').setProperty('https.proxyPort', PROXY_PORT.toString());
Java.perform(() => {
// Set default JVM system properties for the proxy address. Notably these are used
// to initialize WebView configuration.
Java.use('java.lang.System').setProperty('http.proxyHost', PROXY_HOST);
Java.use('java.lang.System').setProperty('http.proxyPort', PROXY_PORT.toString());
Java.use('java.lang.System').setProperty('https.proxyHost', PROXY_HOST);
Java.use('java.lang.System').setProperty('https.proxyPort', PROXY_PORT.toString());
Java.use('java.lang.System').clearProperty('http.nonProxyHosts');
Java.use('java.lang.System').clearProperty('https.nonProxyHosts');
Java.use('java.lang.System').clearProperty('http.nonProxyHosts');
Java.use('java.lang.System').clearProperty('https.nonProxyHosts');
// Some Android internals attempt to reset these settings to match the device configuration.
// We block that directly here:
const controlledSystemProperties = [
'http.proxyHost',
'http.proxyPort',
'https.proxyHost',
'https.proxyPort',
'http.nonProxyHosts',
'https.nonProxyHosts'
];
Java.use('java.lang.System').clearProperty.implementation = function (property) {
if (controlledSystemProperties.includes(property)) {
if (DEBUG_MODE) console.log(`Ignoring attempt to clear ${property} system property`);
return this.getProperty(property);
}
return this.clearProperty(...arguments);
// Some Android internals attempt to reset these settings to match the device configuration.
// We block that directly here:
const controlledSystemProperties = [
'http.proxyHost',
'http.proxyPort',
'https.proxyHost',
'https.proxyPort',
'http.nonProxyHosts',
'https.nonProxyHosts'
];
Java.use('java.lang.System').clearProperty.implementation = function (property) {
if (controlledSystemProperties.includes(property)) {
if (DEBUG_MODE) console.log(`Ignoring attempt to clear ${property} system property`);
return this.getProperty(property);
}
Java.use('java.lang.System').setProperty.implementation = function (property) {
if (controlledSystemProperties.includes(property)) {
if (DEBUG_MODE) console.log(`Ignoring attempt to override ${property} system property`);
return this.getProperty(property);
}
return this.setProperty(...arguments);
return this.clearProperty(...arguments);
}
Java.use('java.lang.System').setProperty.implementation = function (property) {
if (controlledSystemProperties.includes(property)) {
if (DEBUG_MODE) console.log(`Ignoring attempt to override ${property} system property`);
return this.getProperty(property);
}
return this.setProperty(...arguments);
}
// Configure the app's proxy directly, via the app connectivity manager service:
const ConnectivityManager = Java.use('android.net.ConnectivityManager');
const ProxyInfo = Java.use('android.net.ProxyInfo');
ConnectivityManager.getDefaultProxy.implementation = () => ProxyInfo.$new(PROXY_HOST, PROXY_PORT, '');
// (Not clear if this works 100% - implying there are ConnectivityManager subclasses handling this)
// Configure the app's proxy directly, via the app connectivity manager service:
const ConnectivityManager = Java.use('android.net.ConnectivityManager');
const ProxyInfo = Java.use('android.net.ProxyInfo');
ConnectivityManager.getDefaultProxy.implementation = () => ProxyInfo.$new(PROXY_HOST, PROXY_PORT, '');
// (Not clear if this works 100% - implying there are ConnectivityManager subclasses handling this)
console.log(`== Proxy system configuration overridden to ${PROXY_HOST}:${PROXY_PORT} ==`);
console.log(`== Proxy system configuration overridden to ${PROXY_HOST}:${PROXY_PORT} ==`);
// Configure the proxy indirectly, by overriding the return value for all ProxySelectors everywhere:
const Collections = Java.use('java.util.Collections');
const ProxyType = Java.use('java.net.Proxy$Type');
const InetSocketAddress = Java.use('java.net.InetSocketAddress');
const ProxyCls = Java.use('java.net.Proxy'); // 'Proxy' is reserved in JS
// Configure the proxy indirectly, by overriding the return value for all ProxySelectors everywhere:
const Collections = Java.use('java.util.Collections');
const ProxyType = Java.use('java.net.Proxy$Type');
const InetSocketAddress = Java.use('java.net.InetSocketAddress');
const ProxyCls = Java.use('java.net.Proxy'); // 'Proxy' is reserved in JS
const targetProxy = ProxyCls.$new(
ProxyType.HTTP.value,
InetSocketAddress.$new(PROXY_HOST, PROXY_PORT)
const targetProxy = ProxyCls.$new(
ProxyType.HTTP.value,
InetSocketAddress.$new(PROXY_HOST, PROXY_PORT)
);
const getTargetProxyList = () => Collections.singletonList(targetProxy);
const ProxySelector = Java.use('java.net.ProxySelector');
// Find every implementation of ProxySelector by quickly scanning method signatures, and
// then checking whether each match actually implements java.net.ProxySelector:
const proxySelectorClasses = Java.enumerateMethods('*!select(java.net.URI): java.util.List/s')
.flatMap((matchingLoader) => matchingLoader.classes
.map((classData) => Java.use(classData.name))
.filter((Cls) => ProxySelector.class.isAssignableFrom(Cls.class))
);
const getTargetProxyList = () => Collections.singletonList(targetProxy);
const ProxySelector = Java.use('java.net.ProxySelector');
// Find every implementation of ProxySelector by quickly scanning method signatures, and
// then checking whether each match actually implements java.net.ProxySelector:
const proxySelectorClasses = Java.enumerateMethods('*!select(java.net.URI): java.util.List/s')
.flatMap((matchingLoader) => matchingLoader.classes
.map((classData) => Java.use(classData.name))
.filter((Cls) => ProxySelector.class.isAssignableFrom(Cls.class))
);
// Replace the 'select' of every implementation, so they all send traffic to us:
proxySelectorClasses.forEach(ProxySelectorCls => {
if (DEBUG_MODE) {
console.log('Rewriting', ProxySelectorCls.toString());
}
ProxySelectorCls.select.implementation = () => getTargetProxyList()
});
console.log(`== Proxy configuration overridden to ${PROXY_HOST}:${PROXY_PORT} ==`);
// Replace the 'select' of every implementation, so they all send traffic to us:
proxySelectorClasses.forEach(ProxySelectorCls => {
if (DEBUG_MODE) {
console.log('Rewriting', ProxySelectorCls.toString());
}
ProxySelectorCls.select.implementation = () => getTargetProxyList()
});
});
console.log(`== Proxy configuration overridden to ${PROXY_HOST}:${PROXY_PORT} ==`);
});