Merge pull request #2593 from Akury83/master
[RRG-proxmark3.git] / tools / mfc / pm3_gen_dictionary.py
blob02275642e168bca498949b35f2d8c8f6f7957a48
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 import pexpect
5 from colors import color
6 import re
7 import argparse
8 import os
9 import fnmatch
11 '''
12 # pm3_gen_dictionary.py
13 # Christian Herrmann, Iceman, <iceman@icesql.se> 2023
14 # version = 'v1.0.0'
16 # This code is copyright (c) Christian Herrmann, 2023, All rights reserved.
17 # For non-commercial use only, the following terms apply - for all other
18 # uses, please contact the author:
20 # This code is free software; you can redistribute it and/or modify
21 # it under the terms of the GNU General Public License as published by
22 # the Free Software Foundation; either version 3 of the License, or
23 # (at your option) any later version.
25 # This code is distributed in the hope that it will be useful,
26 # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 # GNU General Public License for more details.
31 # Dependencies:
33 # pip3 install pexpect ansicolors
35 # Usage:
37 # ./pm3_gen_dictionary.py --path folder --fn mydictionary.dic -v
39 # Info:
40 # Will search all dump files files in given folder and all its subfolders
41 # With the option to save found keys to a text file.
43 '''
45 def escape_ansi(line):
46 ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
47 return ansi_escape.sub('', str(line)).lower()
49 def parse_keys(line):
50 """
51 Parse keys from a line and return them as a set.
52 Keys must be 12 hex characters long
53 :param line: string containing keys.
54 :return: A set of keys read from the line
55 """
56 keys = set()
57 key_regex = re.compile('[0-9a-fA-F]{12}')
59 key = key_regex.findall(line)
60 if not key:
61 return []
63 try:
64 keys.add(key[0])
65 keys.add(key[1])
66 except AttributeError:
67 pass
68 return keys
70 def main():
71 parser = argparse.ArgumentParser()
72 parser.add_argument("--path", help="Path to folder")
73 parser.add_argument("--fn", help="Dictionary file name")
74 parser.add_argument("-v", help="verbose output", action="store_true")
75 args = parser.parse_args()
77 path = args.path
78 verbose = args.v
79 # Check if the directory exists
80 if not os.path.isdir(path):
81 print("The provided directory does not exist.")
82 return
84 # start pm3
85 child = pexpect.spawnu('./pm3 -o')
86 i = child.expect('pm3 --> ')
87 print("[+] Proxmark3 client open")
90 # MIFARE CLASSIC dumps
91 pattern = 'hf-mf-*-dump*'
93 print(f'[+] Iterating all dumpfiles in... ', color(f'{path}', fg='cyan'))
94 # Walk through the directory
95 keys = set()
96 for root, dirs, files in os.walk(path):
97 for file in files:
98 # Check if the file name starts with the given prefix
99 if fnmatch.fnmatch(file, pattern):
100 if ":Zone.Identifier" in file:
101 continue
102 if ":OECustomProperty" in file:
103 continue
105 f = os.path.join(root, file)
106 cmd = f'hf mf view -v -f {f}'
107 if verbose:
108 print(cmd)
110 # Send proxmark3 commnad
111 child.sendline(cmd)
112 i = child.expect('pm3 --> ')
113 msg = escape_ansi(str(child.before))
115 # extract key table from msg
116 found = False
117 for line in msg.splitlines():
119 if found == False:
120 key_row = line.find('000 | 003')
121 if key_row > -1:
122 found = True
124 if found:
125 foo = parse_keys(line)
126 if not foo:
127 found = False
128 continue
130 # append found set
131 keys |= foo
133 # shut down proxmark3 client connection
134 child.sendline('quit')
135 child.expect(pexpect.EOF)
136 print("[+] Proxmark3 client closed")
138 # print all found keys
139 if verbose:
140 for k in keys:
141 print(f'{k}')
142 print("")
144 # save keys
145 if args.fn:
146 print(f'[+] Writing keys to dictionary file... ', color(f'{args.fn}', fg='cyan'))
147 with open(args.fn, 'w') as f:
148 for k in keys:
149 f.write(f'{k}\n')
151 return 0
153 if __name__ == "__main__":
154 main()