-
Notifications
You must be signed in to change notification settings - Fork 28
/
patch.py
121 lines (92 loc) · 3.19 KB
/
patch.py
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import os
import sys
import shutil
from pathlib import Path
import xml.etree.ElementTree as ET
import subprocess as sp
def usage():
print("Usage: python {} <APK file to patch>".format(sys.argv[0]))
def die(msg):
print(msg)
exit(1)
def patch_manifest_file(manifest_file):
tree = ET.parse(manifest_file)
root = tree.getroot()
application = root.find("application")
network_security_config = application.get(
"{http://schemas.android.com/apk/res/android}networkSecurityConfig"
)
# if network security config attribute not exists, inject it
if network_security_config is None:
application.set(
"{http://schemas.android.com/apk/res/android}networkSecurityConfig",
"@xml/network_security_config",
)
with open(manifest_file, "w", encoding="utf-8") as f:
f.write(ET.tostring(root, encoding="utf-8").decode())
def patch_network_security_config(config_file):
cfg = """<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
"""
with open(config_file, "w") as f:
f.write(cfg)
def main():
shutil.which("java") or die("Java is not installed")
if len(sys.argv) < 2:
usage()
exit(1)
apktool = "apktool.jar"
sign = "sign.jar"
target_apk = sys.argv[1]
if not target_apk.endswith(".apk"):
print(
"The extension of `{}` is not .apk, is it really a APK file?".format(
target_apk
)
)
exit(1)
target_apk_unpacked = os.path.splitext(target_apk)[0]
target_apk_repacked = target_apk_unpacked + ".repack.apk"
# Unpacking
print(f"[*] Unpacking {target_apk}...")
sp.run(["java", "-jar", apktool, "d", target_apk])
# Patch security config of APK to trust user root certificate
manifest_file = Path(target_apk_unpacked) / "AndroidManifest.xml"
patch_manifest_file(str(manifest_file))
config_file = (
Path(target_apk_unpacked) / "res" / "xml" / "network_security_config.xml"
)
patch_network_security_config(str(config_file))
# Repacking
build_cmd = ["java", "-jar", apktool, "b", target_apk_unpacked, "-o", target_apk_repacked]
print(f"[*] Repacking {target_apk_unpacked} to {target_apk_repacked}...")
sp.run(
build_cmd
)
if not os.path.exists(target_apk_repacked):
print(f"[*] Failed to repack {target_apk_unpacked}. Retrying with --use-aapt2 flags")
sp.run(
build_cmd + ["--use-aapt2"]
)
if not os.path.exists(target_apk_repacked):
die(f"[-] Failed to repack {target_apk_unpacked}")
# Signing
print(f"[*] Signing {target_apk_repacked}...")
sp.run(["java", "-jar", sign, "-a", target_apk_repacked])
# Clean up
shutil.rmtree(target_apk_unpacked)
os.remove(target_apk_repacked)
if __name__ == "__main__":
main()