Conversation
whywilson
commented
Aug 1, 2025
- hf mf restore to write dump to tag
- hf mfu eRead to load dump from PN532Killer reader
There was a problem hiding this comment.
Pull Request Overview
This PR adds two new features for Mifare Classic and Ultralight cards: a restore function to write dump files to physical tags, and an eRead function to extract dumps from PN532Killer emulator slots. The changes also introduce proper enum types for device modes and tag types to improve code maintainability.
- Adds
hf mf restorecommand to write dump files (.mfd/.bin) to Mifare Classic cards with support for different generations - Adds
hf mfu eReadcommand to extract Mifare Ultralight dumps from emulator slots with JSON/binary output options - Introduces
PN532KillerModeandPN532KillerTagTypeenums to replace magic numbers in device communication
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| script/pn532_enum.py | Adds new command entries and enum types for device modes and tag types |
| script/pn532_com.py | Updates set_work_mode method to use new enum types with proper type hints |
| script/pn532_cmd.py | Implements hf_mfu_eread function for reading Ultralight dumps from emulator |
| script/pn532_cli_unit.py | Adds HfMfRestore and HfMfuEread command classes with extensive duplicate code |
| script/pn532killer_mf1_emulator.py | Updates to use new enum constants instead of magic numbers |
| def set_work_mode(self, mode: PN532KillerMode = PN532KillerMode.READER, type=PN532KillerTagType.MFC, index=0) -> response: | ||
| response = self.send_cmd_sync( | ||
| Pn532KillerCommand.SetWorkMode, [mode, type, index] | ||
| Pn532KillerCommand.SetWorkMode, [mode.value, type, index] |
There was a problem hiding this comment.
The type parameter should use .value to access the enum value, similar to how mode is handled. Currently type is being passed directly which may cause issues if an enum is passed.
| Pn532KillerCommand.SetWorkMode, [mode.value, type, index] | |
| Pn532KillerCommand.SetWorkMode, [mode.value, type.value, index] |
script/pn532_cli_unit.py
Outdated
| # 读取dump文件 | ||
| dump_data = {} | ||
| if args.f.endswith('.mfd'): | ||
| with open(args.f, 'r') as f: | ||
| for line in f: | ||
| if ':' in line: | ||
| block_num, data = line.strip().split(':') | ||
| dump_data[int(block_num)] = data.strip() | ||
| elif args.f.endswith('.bin'): | ||
| with open(args.f, 'rb') as f: | ||
| data = f.read() | ||
| if len(data) != 1024: # 1KB | ||
| print(f"{CR}Error: Bin file must be 1KB{C0}") | ||
| return | ||
| for i in range(64): # 64 blocks | ||
| dump_data[i] = data[i*16:(i+1)*16].hex() | ||
| else: | ||
| print(f"{CR}Error: Unsupported file format{C0}") | ||
| return | ||
|
|
||
| # 扫描卡片 | ||
| resp = self.cmd.hf_14a_scan() | ||
| if resp is None: | ||
| print("No tag found") | ||
| return | ||
|
|
There was a problem hiding this comment.
This entire block of code (lines 2543-2679) appears to be duplicated from the HfMfRestore class. This creates significant code duplication that should be extracted into a shared method.
| # 读取dump文件 | |
| dump_data = {} | |
| if args.f.endswith('.mfd'): | |
| with open(args.f, 'r') as f: | |
| for line in f: | |
| if ':' in line: | |
| block_num, data = line.strip().split(':') | |
| dump_data[int(block_num)] = data.strip() | |
| elif args.f.endswith('.bin'): | |
| with open(args.f, 'rb') as f: | |
| data = f.read() | |
| if len(data) != 1024: # 1KB | |
| print(f"{CR}Error: Bin file must be 1KB{C0}") | |
| return | |
| for i in range(64): # 64 blocks | |
| dump_data[i] = data[i*16:(i+1)*16].hex() | |
| else: | |
| print(f"{CR}Error: Unsupported file format{C0}") | |
| return | |
| # 扫描卡片 | |
| resp = self.cmd.hf_14a_scan() | |
| if resp is None: | |
| print("No tag found") | |
| return | |
| # Process dump file and scan card | |
| dump_data, resp = self.process_dump_and_scan(args.f, args.k) | |
| if dump_data is None or resp is None: | |
| return |
script/pn532_cli_unit.py
Outdated
| def on_exec(self, args: argparse.Namespace): | ||
| self.cmd.ntag_reader() No newline at end of file | ||
| self.cmd.ntag_reader() | ||
| # 读取dump文件 |
There was a problem hiding this comment.
Chinese comments should be translated to English to maintain consistency with the rest of the codebase. This comment means 'Read dump file'.
| # 读取dump文件 | |
| # Read dump file |
script/pn532_cli_unit.py
Outdated
| print(f"{CR}Tag is not Gen3{C0}") | ||
| return | ||
| print("Found Gen3 Tag") | ||
| # 设置UID |
There was a problem hiding this comment.
Chinese comment should be translated to English. This comment means 'Set UID'.
| # 设置UID | |
| # Set UID |
script/pn532_cli_unit.py
Outdated
| # 设置UID | ||
| resp1 = self.cmd.setGen3Uid(uid) | ||
| print(f"Set UID to {uid.hex().upper()}: {CG}Success{C0}" if resp1 else f"Set UID to {uid.hex().upper()}: {CR}Failed{C0}") | ||
| # 设置block0 |
There was a problem hiding this comment.
Chinese comment should be translated to English. This comment means 'Set block0'.
| # 设置block0 | |
| # Set block0 |
script/pn532_cli_unit.py
Outdated
| # 设置block0 | ||
| resp2 = self.cmd.setGen3Block0(bytes.fromhex(dump_data[0])) | ||
| print(f"Set block0: {CG}Success{C0}" if resp2 else f"Set block0: {CR}Failed{C0}") | ||
| # 写入其他block |
There was a problem hiding this comment.
Chinese comment should be translated to English. This comment means 'Write other blocks'.
| # 写入其他block | |
| # Write other blocks |
|
|
||
| def on_exec(self, args: argparse.Namespace): | ||
| self.cmd.ntag_reader() No newline at end of file | ||
| self.cmd.ntag_reader() |
There was a problem hiding this comment.
This line appears to be orphaned code that doesn't belong in this context. The ntag_reader() call seems unrelated to the dump file processing logic that follows.
| self.cmd.ntag_reader() |