最新 RSS

horiday blog

2011/03/01

[felica] felicalib を使ってカードにデータを書き込む

以前 pasori, felicalibcygwin から使うという記事を書きましたが,書き込みはどうやってやるんだと職場の方に聞かれたのでやり方をまとめてみました.

私の環境は Windows XP + cygwin なので,あまり参考にならないかもしれませんが同じような感じで他の環境でも出来ると思います.

まず事前準備として,felicalib の felicalib.dll を cygwin からも利用できるように変換します.felicalib に同封されている felicalib.dll, felicalib.lib を同じディレクトリに置いておきます.

% ls
felicalib.h dump.c felicalib.dll  felicalib.lib

そして dlltool を使って cgywin と互換性のあるインポートライブラリを作成します.

% echo EXPORTS > cygfelicalib.def
% nm felicalib.lib | grep ' T _' | sed 's/.* T _//' >> cygfelicalib.def
% dlltool --def cygfelicalib.def --dllname felicalib.dll --output-lib cygfelicalib.dll

そして dump.c (felicalib 同封) を下記のようにコンパイルします.私のところでは tchar.h がないとエラーがでたので,/usr/include/mingw を追加しました.

% gcc-3 -mno-cygwin ./dump.c -o dump.exe -L. -lcygfelicalib -I/usr/include/mingw
% ./dump.exe
# IDm: XX XX XX XX XX XX XX XX
# PMm: XX XX XX XX XX XX XX XX

# System code: XXXX
....

そしてカードにデータの書き込みですが,たまたま私の持っている ID カード(職場の職員証)を dump してみると下記のようにサービスコード 39C9 に R/W が可能な領域がありました.どうも 32 x 12 byte 分のデータが書けるようです.

# Serivce code = 39C9 : Random Access R/W
39C9:0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0001 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0002 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0003 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0004 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0005 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0006 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0007 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0008 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:0009 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:000A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
39C9:000B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

ということで本記事の末尾のコード write-data.c をコンパイルして,カードにデータを書き込みします.データの書き込みは felica_write_without_encryption() を使いました.

コンパイル,実行すると下記のように,データが書き込みされたことがわかります.

% gcc-3 -mno-cygwin ./write-data.c -o write-data.exe -L. -lcygfelica -I/usr/include/mingw	       
% ./write-data.exe 1 0 255
write block: 0001 addr: 0 -> FF
reading card information
# Serivce code = 39C9 : Random Access R/W
39C9:0001 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
writing data...
result...
# Serivce code = 39C9 : Random Access R/W
39C9:0001 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          ^^ -> ここが FF になる
  • コード
#include <stdio.h>
#include <stdlib.h>
#include "felicalib.h"

static void printserviceinfo(uint16 s);
static void hexdump(uint8 *addr, int n);

// file: write-data.c
// compile: gcc-3 -mno-cygwin ./write-data.c -o write-data.exe -L. -lcygfelicalib -I/usr/include/mingw
// usage: write-data.exe block addr num

int _tmain(int argc, _TCHAR *argv[])
{
  pasori *p;
  felica *f, *f2;
  int i, j, k;

  int block = atoi(argv[1]);
  int addr = atoi(argv[2]);
  uint8 num = atoi(argv[3]);
  uint8 line[16];
  uint8 data[16];
  uint16 service;

  p = pasori_open(NULL);
  if (!p) {
    fprintf(stderr, "PaSoRi open failed.\n");
    exit(1);
  }
  pasori_init(p);
    
  f = felica_polling(p, POLLING_ANY, 0, 0);
  if (!f) {
    fprintf(stderr, "Polling card failed.\n");
    exit(1);
  }

  f = felica_enum_systemcode(p);
  if (!f) {
    exit(1);
  }

  printf("write block: %04X addr: %d -> %02X\n",
	 block, addr, num);

  f2 = felica_enum_service(p, N2HS(f->system_code[1]));
  service = f2->service_code[10];
  if (service == 0x39c9) {
    printf("reading card information\n");
    printserviceinfo(service);
    k = block;
    felica_read_without_encryption02(f2, service, 0, (uint8)k, data);
    printf("%04X:%04X ", service, k);
    hexdump(data, 16);
    printf("\n");
  }

  data[addr] = num;
    
  f2 = felica_enum_service(p, N2HS(f->system_code[1]));
  service = f2->service_code[10];
  if (service == 0x39c9) {
    printf("writing data...\n");
    felica_write_without_encryption(f2, service, (uint8)k, data);
  }

  printf("result...\n");

  f2 = felica_enum_service(p, N2HS(f->system_code[1]));
  service = f2->service_code[10];
  if (service == 0x39c9) {
    printserviceinfo(service);
    k = block;
    felica_read_without_encryption02(f2, service, 0, (uint8)k, data);
    printf("%04X:%04X ", service, k);
    hexdump(data, 16);
    printf("\n");
  }

  printf("\n");
    
  felica_free(f2);
  felica_free(f);
  pasori_close(p);

  return 0;
}

static void printserviceinfo(uint16 s)
{
  char *ident;

  switch ((s >> 1) & 0xf) {
  case 0: ident = "Area Code"; break;
  case 4: ident = "Random Access R/W"; break; 
  case 5: ident = "Random Access Read only"; break; 
  case 6: ident = "Cyclic Access R/W"; break; 
  case 7: ident = "Cyclic Access Read only"; break; 
  case 8: ident = "Purse (Direct)"; break;
  case 9: ident = "Purse (Cashback/decrement)"; break;
  case 10: ident = "Purse (Decrement)"; break;
  case 11: ident = "Purse (Read only)"; break;
  default: ident = "INVALID or UNKOWN"; break;
  }

  printf("# Serivce code = %04X : %s", s, ident);
  if ((s & 0x1) == 0) {
    printf(" (Protected)");
  }
  printf("\n");
}

static void hexdump(uint8 *addr, int n)
{
  int i;
  for (i = 0; i < n; i++) {
    printf("%02X ", addr[i]);
  }
}

関連リンク

2010/10/12

[felica] felicalib を cygwin から使う

去年くらいにpasori を使って fcf フォーマットから ID,氏名を抽出するというエントリを書きましたが,最近 felicalib を使って IC カードに情報の書き込みをする必要が出てきたので,felicalib を cygwin から使う方法についてまとめておきます.

まず felicalibには felicalib.dll を cygwin からも利用できるように変換します.felicalib に同封されている felicalib.dll, felicalib.lib を同じディレクトリに置いておきます.

% ls
felicalib.h dump.c felicalib.dll  felicalib.lib

そして dlltool を使って cgywin と互換性のあるインポートライブラリを作成します.

% echo EXPORTS > cygfelicalib.def
% nm felicalib.lib | grep ' T _' | sed 's/.* T _//' >> cygfelicalib.def
% dlltool --def cygfelicalib.def --dllname felicalib.dll --output-lib cygfelicalib.dll

これで cygwin と互換性のあるインポートライブラリ cygfelicalib.dll ができました.次に felicalib に同封されていた dump.c (カードの情報を dump するだけのコード)を試しにコンパイル,実行してみます.

% gcc-3 -mno-cygwin ./dump.c -o dump.exe -L. -lcygfelicalib
% ./dump.exe
# IDm: XX XX XX XX XX XX XX XX
# PMm: XX XX XX XX XX XX XX XX

# System code: XXXX
# Number of area = 9
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Area: XXXX - XXXX
# Number of service code = 33
...

ということで,うまく動いたぽいです.

関連リンク

2009/07/07

[felica] pasori を使って fcf フォーマットから ID,氏名を抽出する

私の職場の ID カードは 1 年ほど前から非接触型のカードになりました.

職場の知り合いから「カードから ID と氏名を抜き出すプログラムを作って」と言われたので,さっそく USB 接続のリーダ pasori を買って試してみました.ここではその方法をまとめておきます.私は cygwin + Windows XP で試しました.

調べてみると,felicalib を使ってカードの情報が dump できるらしいので, felicalib を入れてみました.

felicalib と一緒に配布されている FelicaDump.exe を cygwin 経由で実行すると,下記のようになぜか失敗しました.

% ./FelicaDump.exe
Polling card failed.

Windows 上のコマンドプロンプトで FelicaDump を実行すると,出力されたので環境変数を調べてみると,下記を PATH に入れていないと FelicaDump は実行できませんでした. felicalib は felica.dll を叩くと書いてあったので felica.dll が PATH に入っていないとダメでしたね.

# FelicaDump を実行するには下記を PATH に追加する必要あり
/cygdrive/c/Program Files/Common Files/Sony Shared/FeliCaLibrary

felica.dll の場所を PATH に入れると下記のように無事カードの情報を取得することができました.

% ./FelicaDump.exe
# IDm: 01 XX XX XX XX XX XX XX
# PMm: XX XX XX XX XX XX XX XX

# System code: 80DE
# Number of area = 9
# Area: 0000 - FFFE
# Area: 0040 - 07FF
# Area: 0800 - 0FBF
# Area: 0FC0 - 7FFF
# Area: 1000 - 17BF
# Area: 17C0 - 7FFF
# Area: 3380 - 33BF
# Area: 8000 - FFFE
# Area: C800 - C83F
# Number of service code = 33
# Serivce code = 0048 : Random Access R/W (Protected)
# Serivce code = 004A : Random Access Read only (Protected)
004A:0000 00 FF XX XX XX XX XX XX XX XX XX XX XX XX XX XX
(snip)
..........

次はここから ID と氏名の抽出なのですが,felica fcf のフォーマットであることはわかっているのですが,どこにどのように入っているかさっぱりわかりません.FCF Felica 共通利用フォーマット 推進フォーラムを見ても技術的なことは一切ないので困りました.

とりあえず下記のようなコードを書いて,全サービスコードの値を出力したのですがどうも ID と氏名はありません.

#!/usr/bin/env perl
# hoge.pl
# usage:
#  FelicaDump.exe | hoge.pl
use strict;
use Data::Dumper;
while (<STDIN>) {
  if (.{4}:.{4} (.*)/) {
    my $buf = $1;
    my @buf = split /\s/, $buf;
    my $value = pack ("H*", @buf);
    print Dumper $value;
  }
} 

カードに関するもう少し細い仕様をもらおうかなと考えていたときに,他のひとのデータと差分を取れば,どこが ID で氏名なのかがわかると思い,他人のカードを借りてその人のデータを FelicaDump で出力しました.

私のカードの情報と他人の情報を diff で比較すると,何か所か ID と氏名だけの差分じゃないかと思える Service code がわかりました.

 # Serivce code = 090F : Cyclic Access Read only
-090F:0000 19 07 00 00 XX XX XX XX 00 00 00 00 00 00 XX C0 
+090F:0000 19 07 00 00 XX XX XX XX 00 00 00 00 00 00 XX C0 
@@ -6469,1037 +6469,1037 @@
 # Serivce code = 1A8B : Random Access Read only
-1A8B:0000 XX 31 XX XX XX XX XX XX XX XX 00 00 00 00 XX XX 
-1A8B:0001 XX XX XX XX XX XX 00 00 00 00 00 00 00 00 00 00 
+1A8B:0000 XX 31 XX XX XX XX XX XX XX XX 00 00 00 00 XX XX 
+1A8B:0001 XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00 00 

ということで Service code 1A8B の 0001 と 0002 について,下記のように pack してみると...

  # $str には XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00 00 
  # みたいな値が入ってます
  my @data = split /\s+/, $str;
  for (my $i = 0; $i < @data; $i++) {
    $name .= pack "H2", $data[$i] if ($data[$i] ne '00');
  }

無事 ID と氏名が抽出できました.私の職場では氏名はなぜか半角仮名で入っていました...

% perl felica-dump.pl ./myid-dump.txt |nkf -e
$VAR1 = 'XXXXXX54';
$VAR2 = 'ホリ ナマエ';

関連リンク