use RFID::Libnfc::Constants;
use File::Slurp;
use Digest::MD5 qw(md5_hex);
+use Getopt::Long::Descriptive;
use Data::Dump qw(dump);
-my $keyfile = shift @ARGV;
+my ($opt,$usage) = describe_options(
+ '%c %c [dump_with_keys]',
+ [ 'write=s', 'write dump to card' ],
+ [ 'verify!', 'verify writes', { default => 1 } ],
+ [ 'debug|d', 'show debug dumps' ],
+ [ 'help|h', 'usage' ],
+);
+print $usage->text, exit if $opt->help;
-my $r = RFID::Libnfc::Reader->new(debug => 1);
+my $debug = $ENV{DEBUG} || 0;
+our $keyfile = shift @ARGV;
+our ( $tag, $uid, $card_key_file );
+
+sub write_card_dump;
+
+my $r = RFID::Libnfc::Reader->new(debug => $debug);
if ($r->init()) {
- printf ("Reader: %s\n", $r->name);
+ warn "reader: %s\n", $r->name;
my $tag = $r->connect(IM_ISO14443A_106);
if ($tag) {
exit -1;
}
- my $uid = sprintf "%02x%02x%02x%02x", unpack('C4', $tag->{_nai}->abtUid);
- # @{ $tag->uid }; # FIXME this doesn't work with tags which have 00 in UID!
+ $uid = sprintf "%02x%02x%02x%02x", @{ $tag->uid };
- warn "UID: $uid\n";
-
- $keyfile ||= "cards/$uid.key";
+ $card_key_file = "cards/$uid.key";
+ $keyfile ||= $card_key_file;
if ( -e $keyfile ) {
warn "# loading keys from $keyfile";
$tag->load_keys($keyfile);
- warn "## _keys = ", dump($tag->{_keys});
+ warn "## _keys = ", dump($tag->{_keys}) if $debug;
}
$tag->select if ($tag->can("select"));
my $card;
- print STDERR "reading";
+ print STDERR "reading $uid blocks ";
for (my $i = 0; $i < $tag->blocks; $i++) {
if (my $data = $tag->read_block($i)) {
# if we are dumping an ultralight token,
# disconnect from reader so we can run mfoc
RFID::Libnfc::nfc_disconnect($r->{_pdi});
+ print "Dump this card with mfoc? [y] ";
+ my $yes = <STDIN>; chomp $yes;
+ exit unless $yes =~ m/y/i || $yes eq '';
+
my $file = "cards/$uid.key";
unlink $file;
warn "# finding keys for card $uid with: mfoc -O $file\n";
}
print STDERR "done\n";
+ my $out_file = write_card_dump $tag => $card;
+
+ if ( $opt->write ) {
+ $card = read_file $opt->write;
+ foreach my $block ( 0 .. $tag->blocks ) {
+ my $offset = 0x10 * $block;
+ my $data = substr($card,$offset,0x10);
+ print STDERR "writing $uid block $block";
+ $tag->write_block( $block, $data );
+ if ( $opt->verify ) {
+ print STDERR " verify ";
+ my $verify = $tag->read_block( $block );
+ print STDERR $verify eq $data ? "OK" : "ERROR";
+ }
+ print STDERR "\n";
+ }
+ print STDERR "done\n";
+ unlink $card_key_file;
+ $out_file = write_card_dump $tag => $card;
+ } else {
+ # view dump
+ my $txt_file = $out_file;
+ $txt_file =~ s/\.mfd/.txt/ || die "can't change extension of $out_file to txt";
+ system "./mifare-mad.pl $out_file > $txt_file";
+ $ENV{MAD} && system "vi $txt_file";
+ }
+}
+
+sub write_card_dump {
+ my ( $tag, $card ) = @_;
+
# re-insert keys into dump
my $keys = $tag->{_keys} || die "can't find _keys";
foreach my $i ( 0 .. $#$keys ) {
. substr($card, $o+6, 4) . $keys->[$i]->[1]
. substr($card, $o+16)
;
- warn "sector $i keys re-inserted at $o\n";
+ warn "# sector $i keys re-inserted at $o\n" if $debug;
+ }
+
+ if ( my $padding = 4096 - length($card) ) {
+ warn "# add $padding bytes up to 4k dump (needed for keys loading)\n" if $debug;
+ $card .= "\x00" x $padding;
}
my $md5 = md5_hex($card);
- if ( glob "cards/$uid.$md5.*" ) {
- warn "SKIPPING, same dump allready exits\n";
+ my $out_file = "cards/$uid.$md5.mfd";
+ if ( -e $out_file ) {
+ warn "$out_file allready exists, not overwriting\n";
} else {
- my $out_file = "cards/$uid.$md5";
write_file $out_file, $card;
- print "$out_file ", -s $out_file, " bytes\n";
- if ( ! -e "cards/$uid.key" ) {
- symlink $out_file, "cards/$uid.key" || die "cards/$uid.key: $!";
- warn "using keys as default for card $uid\n";
- }
- system "./mifare-mad.pl $out_file | vi -R -";
+ warn "$out_file ", -s $out_file, " bytes key: $card_key_file\n";
}
-}
+ if ( ! -e $card_key_file ) {
+ my $source = $out_file;
+ $source =~ s{^cards/}{} || die "can't strip directory from out_file";
+ symlink $source, $card_key_file || die "$card_key_file: $!";
+ warn "$card_key_file symlink created as default key for $uid\n";
+ }
+ return $out_file;
+}