added --write dump.mfd option
[perl-Mifare-MAD.git] / nfc-card-dumper.pl
index 143ac04..b1e780a 100755 (executable)
@@ -6,15 +6,24 @@ use RFID::Libnfc::Reader;
 use RFID::Libnfc::Constants;
 use File::Slurp;
 use Digest::MD5 qw(md5_hex);
+use Getopt::Long::Descriptive;
 
 use Data::Dump qw(dump);
 
+my ($opt,$usage) = describe_options(
+       '%c %c [dump_with_keys]',
+       [ 'write=s',    'write dump to card' ],
+       [ 'debug|d',    'show debug dumps' ],
+       [ 'help|h',             'usage' ],
+);
+print $usage->text, exit if $opt->help;
+
 my $debug = $ENV{DEBUG} || 0;
 my $keyfile = shift @ARGV;
 
 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) {
@@ -24,24 +33,22 @@ if ($r->init()) {
         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!
-
-       warn "UID: $uid\n";
+       my $uid = sprintf "%02x%02x%02x%02x", @{ $tag->uid };
 
-       $keyfile ||= "cards/$uid.key";
+       my $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, 
@@ -56,7 +63,11 @@ if ($r->init()) {
                        # disconnect from reader so we can run mfoc
                        RFID::Libnfc::nfc_disconnect($r->{_pdi});
 
-                       my $file = "cards/$uid.key";
+                       print "Dump this card with mfoc? [y] ";
+                       my $yes = <STDIN>; chomp $yes;
+                       exit unless $yes =~ m/y/i || $yes eq '';
+
+                       my $file = "cards/$uid.keys";
                        unlink $file;
                        warn "# finding keys for card $uid with: mfoc -O $file\n";
                        exec "mfoc -O $file" || die $!;
@@ -76,27 +87,41 @@ if ($r->init()) {
                        . 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";
+               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";
+       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";
+               warn "$out_file ", -s $out_file, " bytes key: $card_key_file\n";
+               if ( ! -e $card_key_file ) {
+                       $out_file =~ s{^cards/}{} || die "can't strip directory from out_file";
+                       symlink $out_file, $card_key_file || die "$card_key_file: $!";
+                       warn "$card_key_file symlink created as default key for $uid\n";
+               }
+       }
+
+       if ( $opt->write ) {
+               my $card = read_file $opt->write;
+               print STDERR "writing $uid block ";
+               foreach my $block ( 0 .. $tag->blocks ) {
+                       my $offset = 0x10 * $block;
+                       $tag->write_block( $block, substr($card,$offset,0x10) );
+                       print STDERR "$block ";
                }
-               $ENV{MAD} && system "./mifare-mad.pl $out_file | vi -R -";
+               print STDERR "done\n";
        }
 
+       # view dump
+       system "./mifare-mad.pl $out_file > $out_file.txt";
+       $ENV{MAD} && system "vi $out_file.txt";
 }