pad dumps to 4k for correct key loading
[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     printf ("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", unpack('C4', $tag->{_nai}->abtUid);
28         # @{ $tag->uid }; # FIXME this doesn't work with tags which have 00 in UID!
29
30         warn "UID: $uid\n";
31
32         $keyfile ||= "cards/$uid.key";
33
34         if ( -e $keyfile ) {
35                 warn "# loading keys from $keyfile";
36             $tag->load_keys($keyfile);
37                 warn "## _keys = ", dump($tag->{_keys});
38         }
39
40     $tag->select if ($tag->can("select")); 
41
42         my $card;
43
44         print STDERR "reading";
45     for (my $i = 0; $i < $tag->blocks; $i++) {
46         if (my $data = $tag->read_block($i)) {
47             # if we are dumping an ultralight token, 
48             # we receive 16 bytes (while a block is 4bytes long)
49             # so we can skip next 3 blocks
50             $i += 3 if ($tag->type eq "ULTRA");
51                         $card .= $data;
52                         print STDERR "$i ";
53                 } elsif ( $tag->error =~ m/auth/ ) {
54                         warn $tag->error,"\n";
55
56                         # disconnect from reader so we can run mfoc
57                         RFID::Libnfc::nfc_disconnect($r->{_pdi});
58
59                         my $file = "cards/$uid.key";
60                         unlink $file;
61                         warn "# finding keys for card $uid with: mfoc -O $file\n";
62                         exec "mfoc -O $file" || die $!;
63         } else {
64             die $tag->error."\n";
65         }
66     }
67         print STDERR "done\n";
68
69         # re-insert keys into dump
70         my $keys = $tag->{_keys} || die "can't find _keys";
71         foreach my $i ( 0 .. $#$keys ) {
72                 my $o = $i * 0x40 + 0x30;
73                 last if $o > length($card);
74                 $card
75                         = substr($card, 0,   $o) . $keys->[$i]->[0]
76                         . substr($card, $o+6, 4) . $keys->[$i]->[1]
77                         . substr($card, $o+16)
78                         ;
79                 warn "sector $i keys re-inserted at $o\n";
80         }
81
82         if ( my $padding = 4096 - length($card) ) {
83                 warn "add $padding bytes up to 4k dump (needed for keys loading)\n";
84                 $card .= "\x00" x $padding;
85         }
86
87         my $md5 = md5_hex($card);
88         if ( glob "cards/$uid.$md5.*" ) {
89                 warn "SKIPPING, same dump allready exits\n";
90         } else {
91                 my $out_file = "cards/$uid.$md5";
92                 write_file $out_file, $card;
93                 print "$out_file ", -s $out_file, " bytes\n";
94                 if ( ! -e "cards/$uid.key" ) {
95                         symlink $out_file, "cards/$uid.key" || die "cards/$uid.key: $!";
96                         warn "using keys as default for card $uid\n";
97                 }
98                 $ENV{MAD} && system "./mifare-mad.pl $out_file | vi -R -";
99         }
100
101 }
102