-
-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathmtk.py
More file actions
executable file
·347 lines (278 loc) · 16.5 KB
/
mtk.py
File metadata and controls
executable file
·347 lines (278 loc) · 16.5 KB
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#!/usr/bin/env python3
# MTK Flash Client (c) B.Kerler 2018-2025 (refactored & cleaned 2025/2026)
# Licensed under GPLv3 License
import argparse
import logging
import os
import sys
from mtkclient.Library.mtk_main import Main, metamodes
INFO = "MTK Flash/Exploit Client V2.1.4 (c) B.Kerler 2018-2026"
CMDS_HELP = {
"printgpt": "Print GPT Table information",
"gpt": "Save gpt table to given directory",
"r": "Read flash to filename",
"rl": "Read all partitions from flash to a directory",
"rf": "Read whole flash to file",
"rs": "Read sectors starting at start_sector to filename",
"ro": "Read flash starting at offset to filename",
"fs": "Mount the device as a FUSE filesystem",
"w": "Write partition from filename",
"wf": "Write flash from filename",
"wl": "Write partitions from directory path to flash",
"wo": "Write flash starting at offset from filename",
"e": "Erase partition",
"es": "Erase partition with sector count",
"ess": "Erase sector with sector count",
"footer": "Read crypto footer from flash",
"reset": "Send mtk reset command",
"meta": "Switch to meta mode",
"meta2": "Switch to meta mode (wdt)",
"dumpbrom": "Try to dump the bootrom",
"dumpsram": "Try to dump the sram",
"dumppreloader": "Try to dump the preloader",
"payload": "Run a specific kamakiri / da payload",
"crash": "Try to crash the preloader",
"brute": "Bruteforce the kamakiri var1",
"gettargetconfig": "Get target config (sbc, daa, etc.)",
"logs": "Get target logs",
"peek": "Read memory in patched preloader mode",
"stage": "Run stage2 payload via boot rom mode (kamakiri)",
"plstage": "Run stage2 payload via preloader mode (send_da)",
"da": "Run da xflash/legacy special commands",
"script": "Run multiple commands using text script",
"multi": "Run multiple commands using semicolon-separated list",
"devices": "List supported devices"
}
# ================== Argument Groups ==================
def add_connection_group(parser):
g = parser.add_argument_group("Connection & Interface")
g.add_argument('--vid', type=str)
g.add_argument('--pid', type=str)
g.add_argument('--serialport', nargs='?', const='DETECT', default=None,
help='Use serial port (can be DETECT)')
g.add_argument('--noreconnect', action='store_true')
g.add_argument('--stock', action='store_true', help='use stock da')
g.add_argument('--uartloglevel', help='Set uart log level (0=Trace, 2=Normal)')
g.add_argument('--loglevel', help='Set log level (0=Trace, 2=Normal)')
g.add_argument('--write_preloader_to_file', action='store_true', help='Dump preloader to file')
g.add_argument('--generatekeys', action='store_true', help='Derive HW keys')
g.add_argument('--iot', help='Use special mode for iot MT6261/2301', action="store_true",
default=False)
g.add_argument('--socid', action='store_true', help='Read Soc ID')
def add_auth_group(parser):
g = parser.add_argument_group("Authentication")
g.add_argument('--auth', type=str, help="Use auth file (auth_sv5.auth)")
g.add_argument('--cert', type=str, help="Use cert file")
def add_debug_group(parser):
g = parser.add_argument_group("Debug & Sector")
g.add_argument('--debugmode', action='store_true', help='Enable verbose mode')
def add_exploit_group(parser):
g = parser.add_argument_group("Bootrom / Preloader Exploit")
g.add_argument('--loader', type=str, help='Use specific DA loader, disable autodetection')
g.add_argument('--preloader', help='Set the preloader filename for dram config')
g.add_argument('--ptype', help='Payload type: amonet, kamakiri, kamakiri2, carbonara')
g.add_argument('--var1', help='Set kamakiri specific var1 value')
g.add_argument('--uart_addr', help='Set payload uart_addr value')
g.add_argument('--da_addr', help='Set a specific da payload addr')
g.add_argument('--brom_addr', help='Set a specific brom payload addr')
g.add_argument('--mode', help='Set a crash mode (0=dasend1,1=dasend2,2=daread)')
g.add_argument('--wdt', help='Set a specific watchdog addr')
g.add_argument('--skipwdt', action='store_true', help='Skip wdt init')
g.add_argument('--crash', action='store_true', help='Enforce crash if device is in pl mode')
g.add_argument('--appid', help='Use app id (hexstring)')
def add_gpt_group(parser):
g = parser.add_argument_group("GPT & Partition")
g.add_argument("--sectorsize", default='0x200', help='Set default sector size')
g.add_argument('--gpt-num-part-entries', default='0', help='Set GPT entry count')
g.add_argument('--gpt-part-entry-size', default='0', help='Set GPT entry size')
g.add_argument('--gpt-part-entry-start-lba', default='0', help='Set GPT entry start lba sector')
g.add_argument('--parttype', help='Partition type (user/boot1/rpmb/lu0 etc.)')
g.add_argument('--skip', help='Skip partitions (comma separated names)')
# ================== Base Parser ==================
def create_base_parser():
parser = argparse.ArgumentParser(
description=INFO,
add_help=False
)
add_connection_group(parser)
add_auth_group(parser)
add_debug_group(parser)
add_exploit_group(parser)
add_gpt_group(parser)
return parser
# ================== Helper for common flash commands ==================
def add_common_flash_cmd(subparsers, name, title, parent_parser):
return subparsers.add_parser(
name,
help=title,
parents=[parent_parser]
)
def main():
base = create_base_parser()
parser = argparse.ArgumentParser(
description=INFO,
parents=[base],
formatter_class=argparse.RawDescriptionHelpFormatter
)
subparsers = parser.add_subparsers(dest='cmd', required=True, metavar='command')
# ─── Commands using most common options ──────────────────────────────
common_cmds = [
"printgpt", "gpt", "r", "rl", "rf", "rs", "ro",
"w", "wf", "wl", "wo", "e", "es", "ess", "footer"
]
cmd_parsers = {}
for cmd in common_cmds:
cmd_parsers[cmd] = add_common_flash_cmd(
subparsers, cmd, CMDS_HELP.get(cmd, cmd), base
)
# Specific positional/required arguments
# Read partition(s)
cmd_parsers["r"].add_argument("partitionname", help="Partitions (comma sep)")
cmd_parsers["r"].add_argument("filename", help="Output files (comma sep)")
cmd_parsers["r"].add_argument("--offset", help="Offset to read from")
cmd_parsers["r"].add_argument("--length", help="Length to read")
# Write partition(s)
cmd_parsers["w"].add_argument("partitionname", help="Partitions (comma sep)")
cmd_parsers["w"].add_argument("filename", help="Input files (comma sep)")
cmd_parsers["w"].add_argument('--gpt_file', help='Use a gpt file instead of trying to read gpt from flash')
# Directory based
for cmd in ["rl", "gpt", "wl"]:
cmd_parsers[cmd].add_argument("directory", help="Directory path")
# Full flash image
for cmd in ["rf", "wf"]:
cmd_parsers[cmd].add_argument("filename", help="Flash image file")
cmd_parsers[cmd].add_argument("--offset", help="Byte offset")
cmd_parsers[cmd].add_argument("--length", help="Length")
cmd_parsers[cmd].add_argument('--disable_internal_flash',
help='Disable internal flash read for iot MT6261/2301',
action="store_true",
default=False)
# Sector based
for cmd in ["rs", "ess"]:
cmd_parsers[cmd].add_argument("startsector", help="Start sector")
cmd_parsers[cmd].add_argument("sectors", help="Sector count")
# Offset based
for cmd in ["ro", "wo"]:
cmd_parsers[cmd].add_argument("offset", help="Byte offset")
cmd_parsers[cmd].add_argument("length", help="Length")
cmd_parsers[cmd].add_argument("filename", help="File")
# Erase
cmd_parsers["e"].add_argument("partitionname", help="Partition to erase")
cmd_parsers["es"].add_argument("partitionname", help="Partition to erase")
cmd_parsers["es"].add_argument("sectors", help="Sectors count")
# Footer
cmd_parsers["footer"].add_argument("filename", help="Filename to store footer")
# ─── Other commands ──────────────────────────────────────────────────
cmd_fs = subparsers.add_parser("fs", help="Mount as FUSE filesystem")
cmd_fs.add_argument("mountpoint")
cmd_fs.add_argument("--rw", action="store_true")
subparsers.add_parser("reset", help="Send reset command", parents=[base])
subparsers.add_parser("meta", help="Enter meta mode", parents=[base]) \
.add_argument("metamode", nargs="?", default=None, help=f"[{metamodes}]")
subparsers.add_parser("meta2", help="Enter meta mode (wdt)", parents=[base])
subparsers.add_parser("dumpbrom", help=CMDS_HELP["dumpbrom"], parents=[base]
).add_argument('--filename', help='Optional filename')
subparsers.add_parser("dumpsram", help=CMDS_HELP["dumpsram"], parents=[base]
).add_argument('--filename', help='Optional filename')
subparsers.add_parser("dumppreloader", help=CMDS_HELP["dumppreloader"], parents=[base]
).add_argument('--filename', help='Optional filename')
parser_payload = subparsers.add_parser("payload", help=CMDS_HELP["payload"], parents=[base])
parser_payload.add_argument("--payload", type=str, help="Payload file (optional)")
parser_payload.add_argument("--metamode", type=str, default=None, help=f"metamode to use [{metamodes}]")
subparsers.add_parser("crash", help=CMDS_HELP["crash"], parents=[base])
subparsers.add_parser("brute", help=CMDS_HELP["brute"], parents=[base])
subparsers.add_parser("gettargetconfig", help=CMDS_HELP["gettargetconfig"], parents=[base])
subparsers.add_parser("logs", help=CMDS_HELP["logs"], parents=[base])
cmd_peek=subparsers.add_parser("peek", help=CMDS_HELP["peek"], parents=[base])
cmd_peek.add_argument('address', help='Address to read from memory')
cmd_peek.add_argument('length', help='Bytes to read from memory')
cmd_peek.add_argument("--filename", help="Save to file (optional)")
stage=subparsers.add_parser("stage", help=CMDS_HELP["stage"], parents=[base])
stage.add_argument('--verifystage2', help='Verify if stage2 data has been written correctly')
stage.add_argument('--stage2', help='Set stage2 filename')
stage.add_argument('--stage2addr', help='Set stage2 loading address')
stage.add_argument('--filename', help='Set stage1 loader filename')
plstage=subparsers.add_parser("plstage", help=CMDS_HELP["plstage"], parents=[base])
plstage.add_argument('--startpartition', help='Option for plstage - Boot to (lk, tee1)')
plstage.add_argument('--offset', help='Address to read from memory')
plstage.add_argument('--length', help='Bytes to read from memory')
plstage.add_argument('--pl', help='pl stage filename (optional)')
plstage.add_argument("--metamode", type=str, help="metamode to use [off,usb,uart]")
# DA subcommands
p_da = subparsers.add_parser("da", help=CMDS_HELP["da"], parents=[base])
da_subs = p_da.add_subparsers(dest="subcmd", required=True)
da_peek = da_subs.add_parser("peek", parents=[base])
da_peek.add_argument('address', type=str, help="Address to read from (hex value)")
da_peek.add_argument('length', type=str, help="Length to read")
da_peek.add_argument('--filename', type=str, help="Save to file (optional)")
da_subs.add_parser("efuse", parents=[base], help="Read efuses")
da_subs.add_parser("generatekeys", parents=[base], help="Generate keys")
da_subs.add_parser("keyserver", parents=[base], help="Enable key server")
da_meta = da_subs.add_parser("meta", parents=[base], help="MetaMode Tools")
da_meta.add_argument("metamode", type=str, help="metamode to use [off,usb,uart]")
da_vbmeta = da_subs.add_parser("vbmeta", parents=[base], help="Patch vbmeta partition")
da_vbmeta.add_argument("vbmode", type=str,
help="vbmeta mode (0=locked, 1=disable_verity, 2=disable_verification, 3=disable verity+verification)")
da_nvitem = da_subs.add_parser("nvitem", parents=[base], help="nvitem decryption/encryption")
da_nvitem.add_argument('filename',type=str, help="LD0B_001.bin")
da_nvitem.add_argument('--encrypt', action="store_true", default=False, help='Encrypt')
da_nvitem.add_argument('--seed', type=str, default="3132616263646566", help='seed')
da_nvitem.add_argument('--aeskey', type=str, default="0102030405060708090A0B0C0D0E0F1011120B1415161718191A1B1C00000000", help='aeskey')
da_patchmodem = da_subs.add_parser("patchmodem", parents=[base], help="patch modem for imei")
da_imei = da_subs.add_parser("imei", parents=[base], help="imei decryption/encryption")
da_imei.add_argument('imeis', type=str, help="imeis, separated by ,",nargs="?", default="")
da_imei.add_argument('--product', type=str, default="thunder", help='productname')
da_imei.add_argument('--write', action="store_true", default=False, help='Write')
da_imei.add_argument('--seed', type=str, default="3132616263646566", help='seed')
da_imei.add_argument('--aeskey', type=str,
default="0102030405060708090A0B0C0D0E0F1011120B1415161718191A1B1C00000000", help='aeskey')
da_rpmb = da_subs.add_parser("rpmb", parents=[base], help="RPMB Tools")
da_rpmb_cmds = da_rpmb.add_subparsers(dest='rpmb_subcmd', help='Commands: r w')
da_rpmb_r = da_rpmb_cmds.add_parser("r", help="Read rpmb")
da_rpmb_r.add_argument('filename', type=str, help="Filename to write data into", default="rpmb.bin", nargs="?")
da_rpmb_r.add_argument('--sector', help='Start sector (offset/0x100 bytes)')
da_rpmb_r.add_argument('--sectors', help='Sector count')
da_rpmb_w = da_rpmb_cmds.add_parser("w", help="Write rpmb")
da_rpmb_w.add_argument('filename', type=str, help="Filename to write from", default="rpmb.bin", nargs="?")
da_rpmb_w.add_argument('--sector', help='Start sector (offset/0x100 bytes)')
da_rpmb_w.add_argument('--sectors', help='Sector count')
da_rpmb_e = da_rpmb_cmds.add_parser("e", help="Erase rpmb")
da_rpmb_e.add_argument('--sector', help='Start sector (offset/0x100 bytes)')
da_rpmb_e.add_argument('--sectors', help='Sector count')
da_rpmb_a = da_rpmb_cmds.add_parser("a", help="Auth rpmbkey")
da_rpmb_a.add_argument('--rpmbkey', help='rpmb key (hexstring, 32 bytes)')
da_dump = da_subs.add_parser("memdump", parents=[base], help="Dump whole memory areas")
da_dump.add_argument('directory', type=str, help="Directory to dump ram dump files")
da_dump.add_argument('--startpartition', help='Option for plstage - Boot to (lk, tee1)')
da_dramdump = da_subs.add_parser("memdram", parents=[base], help="Dump dram memory")
da_dramdump.add_argument('directory', type=str, help="Directory to dump ram dump files")
da_dramdump.add_argument('--startpartition', help='Option for plstage - Boot to (lk, tee1)')
da_dumpbrom = da_subs.add_parser("dumpbrom", parents=[base], help="Dump whole memory areas")
da_poke = da_subs.add_parser("poke", parents=[base], help="Write memory")
da_poke.add_argument('address', type=str, help="Address to read from (hex value)")
da_poke.add_argument('data', type=str, help="Data to write")
da_unlock = da_subs.add_parser("seccfg", parents=[base], help="Unlock device / Configure seccfg")
da_unlock.add_argument('flag', type=str, help="Needed flag (unlock,lock)")
# Minimal commands
subparsers.add_parser("devices", help=CMDS_HELP["devices"])\
.add_argument("--filter", help="Optional Filter string")
subparsers.add_parser("script", help=CMDS_HELP["script"])\
.add_argument("script")
subparsers.add_parser("multi", help=CMDS_HELP["multi"])\
.add_argument("commands")
# ─── Parse & Run ─────────────────────────────────────────────────────
args = parser.parse_args()
if not args.cmd:
parser.print_help()
sys.exit(0)
logging.basicConfig(
level=logging.DEBUG if args.debugmode else logging.INFO,
format="%(levelname)s: %(message)s"
)
if args.debugmode:
if not os.path.exists("logs"):
os.makedirs("logs")
mtk = Main(args)
return mtk.run(parser)
if __name__ == '__main__':
sys.exit(main() or 0)