diff --git a/package.json b/package.json index 7347c36..01c7994 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,11 @@ "@tybys/cross-zip": "^3.0.4", "chalk": "^4.1.0", "env-paths": "^2.2.0", + "escape-string-regexp": "^4.0.0", "execa": "^4.0.2", "follow-redirects": "^1.12.1", "globby": "^11.0.1", "listr": "^0.14.3", - "replace-string": "^3.1.0", "rxjs": "^6.5.5", "tempy": "^0.5.0", "xml-js": "^1.6.11", diff --git a/src/tasks/disable-certificate-pinning.ts b/src/tasks/disable-certificate-pinning.ts index f2ea0e9..b88064f 100644 --- a/src/tasks/disable-certificate-pinning.ts +++ b/src/tasks/disable-certificate-pinning.ts @@ -2,17 +2,38 @@ import * as path from 'path' import * as fs from '../utils/fs' import globby from 'globby' -import replaceAll from 'replace-string' +import escapeStringRegexp from 'escape-string-regexp' import { Observable } from 'rxjs' import { ListrTaskWrapper } from 'listr' -const methodSignatures = [ - '.method public checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', - '.method public final checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', - '.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', - '.method public final checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', - '.method public getAcceptedIssuers()[Ljava/security/cert/X509Certificate;', - '.method public final getAcceptedIssuers()[Ljava/security/cert/X509Certificate;', +/** The methods that need to be patched to disable certificate pinning. */ +const METHOD_SIGNATURES = [ + 'checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', + 'checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V', + 'getAcceptedIssuers()[Ljava/security/cert/X509Certificate;', +] + +/** Patterns used to find the methods defined in `METHOD_SIGNATURES`. */ +const METHOD_PATTERNS = METHOD_SIGNATURES.map(signature => { + const escapedSignature = escapeStringRegexp(signature) + return new RegExp( + `(\\.method public (?:final )?${escapedSignature})\\n([^]+?)\\n(\\.end method)`, + 'g', + ) +}) + +/** Code inserted into `checkClientTrusted` and `checkServerTrusted`. */ +const RETURN_VOID_FIX = [ + '.locals 0', + 'return-void', +] + +/** Code inserted into `getAcceptedIssuers`. */ +const RETURN_EMPTY_ARRAY_FIX = [ + '.locals 1', + 'const/4 v0, 0x0', + 'new-array v0, v0, [Ljava/security/cert/X509Certificate;', + 'return-object v0', ] export default async function disableCertificatePinning(directoryPath: string, task: ListrTaskWrapper) { @@ -29,11 +50,37 @@ export default async function disableCertificatePinning(directoryPath: string, t const originalContent = await fs.readFile(filePath, 'utf-8') let patchedContent = originalContent - for (const signature of methodSignatures) { - patchedContent = replaceAll( - patchedContent, - signature, - `${signature}\n return-void`, + for (const pattern of METHOD_PATTERNS) { + patchedContent = patchedContent.replace( + pattern, ( + _, + openingLine: string, + body: string, + closingLine: string, + ) => { + const bodyLines = body + .split('\n') + .map(line => line.replace(/^ /, '')) + + const fixLines = openingLine.includes('getAcceptedIssuers') + ? RETURN_EMPTY_ARRAY_FIX + : RETURN_VOID_FIX + + const patchedBodyLines = [ + '# inserted by apk-mitm to disable certificate pinning', + ...fixLines, + '', + '# commented out by apk-mitm to disable old method body', + '# ', + ...bodyLines.map(line => `# ${line}`) + ] + + return [ + openingLine, + ...patchedBodyLines.map(line => ` ${line}`), + closingLine, + ].map(line => line.trimEnd()).join('\n') + }, ) } diff --git a/yarn.lock b/yarn.lock index fe357f6..d87079c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -260,6 +260,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + execa@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" @@ -610,11 +615,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -replace-string@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/replace-string/-/replace-string-3.1.0.tgz#77a087d88580fbac59851237891aa4b0e283db72" - integrity sha512-yPpxc4ZR2makceA9hy/jHNqc7QVkd4Je/N0WRHm6bs3PtivPuPynxE5ejU/mp5EhnCv8+uZL7vhz8rkluSlx+Q== - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"