ask before running mfoc
[perl-Mifare-MAD.git] / nfc-card-dumper.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4
5 use RFID::Libnfc::Reader;
6 use RFID::Libnfc::Constants;
7 use File::Slurp;
8 use Digest::MD5 qw(md5_hex);
9
10 use Data::Dump qw(dump);
11
12 my $debug = $ENV{DEBUG} || 0;
13 my $keyfile = shift @ARGV;
14
15 my $r = RFID::Libnfc::Reader->new(debug => $debug);
16 if ($r->init()) {
17     warn "reader: %s\n", $r->name;
18     my $tag = $r->connect(IM_ISO14443A_106);
19
20     if ($tag) {
21         $tag->dump_info;
22     } else {
23         warn "No TAG";
24         exit -1;
25     }
26
27         my $uid = sprintf "%02x%02x%02x%02x", @{ $tag->uid };
28
29         my $card_key_file = "cards/$uid.key";
30         $keyfile ||= $card_key_file;
31
32         if ( -e $keyfile ) {
33                 warn "# loading keys from $keyfile";
34             $tag->load_keys($keyfile);
35                 warn "## _keys = ", dump($tag->{_keys}) if $debug;
36         }
37
38     $tag->select if ($tag->can("select")); 
39
40         my $card;
41
42         print STDERR "$uid reading blocks ";
43     for (my $i = 0; $i < $tag->blocks; $i++) {
44         if (my $data = $tag->read_block($i)) {
45             # if we are dumping an ultralight token, 
46             # we receive 16 bytes (while a block is 4bytes long)
47             # so we can skip next 3 blocks
48             $i += 3 if ($tag->type eq "ULTRA");
49                         $card .= $data;
50                         print STDERR "$i ";
51                 } elsif ( $tag->error =~ m/auth/ ) {
52                         warn $tag->error,"\n";
53
54                         # disconnect from reader so we can run mfoc
55                         RFID::Libnfc::nfc_disconnect($r->{_pdi});
56
57                         print "Dump this card with mfoc? [y] ";
58                         my $yes = <STDIN>; chomp $yes;
59                         exit unless $yes =~ m/y/i || $yes eq '';
60
61                         my $file = "cards/$uid.keys";
62                         unlink $file;
63                         warn "# finding keys for card $uid with: mfoc -O $file\n";
64                         exec "mfoc -O $file" || die $!;
65         } else {
66             die $tag->error."\n";
67         }
68     }
69         print STDERR "done\n";
70
71         # re-insert keys into dump
72         my $keys = $tag->{_keys} || die "can't find _keys";
73         foreach my $i ( 0 .. $#$keys ) {
74                 my $o = $i * 0x40 + 0x30;
75                 last if $o > length($card);
76                 $card
77                         = substr($card, 0,   $o) . $keys->[$i]->[0]
78                         . substr($card, $o+6, 4) . $keys->[$i]->[1]
79                         . substr($card, $o+16)
80                         ;
81                 warn "# sector $i keys re-inserted at $o\n" if $debug;
82         }
83
84         if ( my $padding = 4096 - length($card) ) {
85                 warn "# add $padding bytes up to 4k dump (needed for keys loading)\n" if $debug;
86                 $card .= "\x00" x $padding;
87         }
88
89         my $md5 = md5_hex($card);
90         my $out_file = "cards/$uid.$md5";
91         if ( -e $out_file ) {
92                 warn "$out_file allready exists, not overwriting\n";
93         } else {
94                 write_file $out_file, $card;
95                 warn "$out_file ", -s $out_file, " bytes key: $card_key_file\n";
96                 if ( ! -e $card_key_file ) {
97                         $out_file =~ s{^cards/}{} || die "can't strip directory from out_file";
98                         symlink $out_file, $card_key_file || die "$card_key_file: $!";
99                         warn "$card_key_file symlink created as default key for $uid\n";
100                 }
101         }
102
103         # view dump
104         system "./mifare-mad.pl $out_file > $out_file.txt";
105         $ENV{MAD} && system "vi $out_file.txt";
106 }
107