@@ -90,7 +90,7 @@ def find_keys(filename: str | Path) -> set[str]:
9090 f"Found { ('new key' if is_new_key else 'key again' )} "
9191 f"at offset { global_offset :,} = 0x{ global_offset :_x} "
9292 f"(using magic bytes { magic_bytes .hex ()} ): { priv_key_wif } "
93- f"({ key_count :,} keys total, { len (keys ):,} unique keys"
93+ f"({ key_count :,} keys total, { len (keys ):,} unique keys) "
9494 )
9595 pos += 1
9696
@@ -126,7 +126,11 @@ def setup_logging(log_filename: Optional[str | Path] = None) -> logging.Logger:
126126 return logger
127127
128128
129- def main_keyhunter (haystack_filename : str | Path , log_path : Optional [str | Path ] = None ):
129+ def main_keyhunter (
130+ haystack_file_path : str | Path ,
131+ log_path : Optional [str | Path ] = None ,
132+ output_keys_file_path : Optional [str | Path ] = None ,
133+ ):
130134 setup_logging (log_path )
131135 logger .info ("Starting keyhunter" )
132136
@@ -135,10 +139,10 @@ def main_keyhunter(haystack_filename: str | Path, log_path: Optional[str | Path]
135139 else :
136140 logger .info ("Logging to console only." )
137141
138- if not Path (haystack_filename ).is_file ():
139- raise FileNotFoundError (f"File not found: { haystack_filename } " )
142+ if not Path (haystack_file_path ).is_file ():
143+ raise FileNotFoundError (f"File not found: { haystack_file_path } " )
140144
141- keys = find_keys (haystack_filename )
145+ keys = find_keys (haystack_file_path )
142146
143147 keys = sorted (list (keys ))
144148 logger .info (f"Found { len (keys )} keys: { keys } " )
@@ -148,19 +152,40 @@ def main_keyhunter(haystack_filename: str | Path, log_path: Optional[str | Path]
148152 for key in keys :
149153 print (key )
150154
155+ if output_keys_file_path :
156+ with open (output_keys_file_path , "w" ) as f :
157+ for key in keys :
158+ f .write (key + "\n " )
159+
151160 logger .info (f"Finished keyhunter. Found { len (keys ):,} keys." )
152161
153162
154163def get_args ():
155164 parser = argparse .ArgumentParser (description = "Find Bitcoin private keys in a file." )
156- parser .add_argument ("filename" , help = "The file to search for keys." )
165+ parser .add_argument (
166+ "-i" ,
167+ "--input" ,
168+ required = True ,
169+ dest = "input_file_path" ,
170+ help = "The input file (disk image, corrupt wallet.dat, etc.) to search for keys." ,
171+ )
157172 parser .add_argument ("-l" , "--log" , dest = "log_path" , help = "Log file to write logs to." )
173+ parser .add_argument (
174+ "-o" ,
175+ "--output" ,
176+ dest = "output_file_path" ,
177+ help = "Output file to write the WIF write keys to." ,
178+ )
158179 return parser .parse_args ()
159180
160181
161182def main_cli ():
162183 args = get_args ()
163- main_keyhunter (args .filename , log_path = args .log_path )
184+ main_keyhunter (
185+ args .input_file_path ,
186+ log_path = args .log_path ,
187+ output_keys_file_path = args .output_file_path ,
188+ )
164189
165190
166191if __name__ == "__main__" :
0 commit comments