-
Notifications
You must be signed in to change notification settings - Fork 0
/
apkdumper.py
184 lines (137 loc) · 5 KB
/
apkdumper.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import argparse
import os
import sys
import subprocess
import shutil
import tempfile
from hashlib import sha256
from pathlib import Path
adb_path = 'D:\\Nox\\bin\\nox_adb.exe' # x64
# adb_path = 'adb' # ARM Physical
def print_paths(paths):
for p in paths:
print(f' -> {p}')
# Check connected devices
def adb_devices():
cmd = f'{adb_path} devices'
try:
result = subprocess.check_output(cmd.split(), universal_newlines=True)
print(result)
except subprocess.CalledProcessError as e:
print('[Error] adb is not installed | in_path. The application will close.')
sys.exit(1)
# Exclude internal list based on substring
# Input: app.name
def filter_app(app):
bad_apps = ['android.ext', 'com.android','com.google', 'com.huawei', 'org.chromium']
for f in bad_apps:
if f in app:
return None
return app
# Get the path of apk on device, and extract al splitted paths
# input: app.name
# return: [app.name, is_splitted, paths]
def get_path_apk(app_name):
is_splitted = False
paths = []
cmd = f'{adb_path} shell pm path {app_name}'
r = subprocess.check_output(cmd.split(), universal_newlines=True).split('\n')
for line in r:
path = line[8:].strip()
if path:
paths.append(path)
# Check if splitted
if len(paths) > 1:
is_splitted = True
return [app_name, is_splitted, paths]
def list_packages():
cmd = f'{adb_path} shell pm list packages'
packages = []
try:
r = subprocess.check_output(cmd.split(), universal_newlines=True).split('\n')
for line in r:
app_name = filter_app(line[8:].strip()) # Ignore filtered packages
if app_name:
app = get_path_apk(app_name)
packages.append(app)
except subprocess.CalledProcessError as e:
print('[Error] Check if device is connected.')
sys.exit(1)
return packages
def pull_apk(src, dst):
cmd = f'{adb_path} pull {src} {dst}'
print(f'Current download -> {src}')
subprocess.run(cmd.split(), check=True, stdout=subprocess.PIPE)
def sign_apk(path):
cmd = f'java -jar ./utils/uber-apk-signer-1.3.0.jar --apks {path}'
subprocess.run(cmd.split(), check=True, stdout=subprocess.PIPE)
# Delete apk.idsig
idsig = f'{path[:-4]}-aligned-debugSigned.apk.idsig'
os.remove(idsig)
# Delete unsigned apk
os.remove(path)
# Rename signed apk
path_signed = f'{path[:-4]}-aligned-debugSigned.apk'
os.rename(path_signed, path)
def merge_apk(paths, dst_path):
tmp_dir = tempfile.mkdtemp()
print(f'Working in: {tmp_dir}')
for path in paths:
pull_apk(path, tmp_dir)
cmd = f'java -jar ./utils/APKEditor-1.3.9.jar m -i {tmp_dir} -o {dst_path}'
subprocess.run(cmd.split(), check=True, stdout=subprocess.PIPE)
shutil.rmtree(tmp_dir)
print(f'Signing APK {dst_path}')
sign_apk(dst_path)
def dump_apk(app_name, is_splitted, paths, out_path):
if out_path == '.':
dst_path = os.path.join(os.getcwd(), app_name)
else:
dst_path = os.path.join(out_path, app_name)
dst_path = f'{dst_path}.apk'
# Check if file exist
if os.path.exists(dst_path):
print(f'APK is already downloaded in {dst_path}')
sys.exit(1)
# if splitted -> join
if is_splitted:
merge_apk(paths, dst_path)
else:
pull_apk(paths[0], dst_path)
print(f'APK downloaded in -> {dst_path}')
return dst_path
def rename_apk(path):
print(f'Preparing malware sample for analysis -> {path}')
apk_path = Path(path)
sample_name = sha256(apk_path.name.encode()).hexdigest()
sample_path = apk_path.parent.joinpath(sample_name)
os.rename(path, sample_path)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--search', help='search packet in packet list', metavar='')
parser.add_argument('-p', '--pull', help='adb extracts APK into dir', metavar='')
parser.add_argument('-o', '--output', help='output must be a folder', metavar='')
parser.add_argument('-m', '--malware', help='extract sample for malware analysis', metavar='')
args = parser.parse_args()
# Check connected devices
adb_devices()
# Default operation (list)
apks = list_packages()
if not (args.search or args.pull or args.malware):
for app in apks:
print(app[0])
if args.search:
for app, is_splitted, paths in apks:
if args.search in app:
print(f'\nApp Name: {app} | is_splitted: {is_splitted}')
print_paths(paths)
if args.pull and args.output:
for app, is_splitted, paths in apks:
if args.pull in app: # Some hackish way to download even if input is bad
dst_apk = dump_apk(app, is_splitted, paths, args.output)
if args.malware:
rename_apk(dst_apk)
else:
print('apkdumper -p <app> -o <output_dir>')
if __name__ == '__main__':
main()