-
Notifications
You must be signed in to change notification settings - Fork 328
/
disable-certificate-pinning.ts
103 lines (85 loc) 路 3.25 KB
/
disable-certificate-pinning.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import * as path from 'path'
import * as fs from '../utils/fs'
import globby from 'globby'
import escapeStringRegexp from 'escape-string-regexp'
import { Observable } from 'rxjs'
import { ListrTaskWrapper } from 'listr'
const INTERFACE_LINE = '.implements Ljavax/net/ssl/X509TrustManager;'
/** 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) {
return new Observable(observer => {
(async () => {
observer.next('Finding smali files...')
const smaliFiles = await globby(path.join(directoryPath, 'smali*/**/*.smali'))
let pinningFound = false
for (const filePath of smaliFiles) {
observer.next(`Scanning ${path.basename(filePath)}...`)
const originalContent = await fs.readFile(filePath, 'utf-8')
// Don't scan classes that don't implement the interface
if (!originalContent.includes(INTERFACE_LINE)) continue
let patchedContent = originalContent
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')
},
)
}
if (originalContent !== patchedContent) {
pinningFound = true
await fs.writeFile(filePath, patchedContent)
}
}
if (!pinningFound) task.skip('No certificate pinning logic found.')
observer.complete()
})()
})
}