PicProtectDongle


はじめに

時々仕事でプログラムを貸し出せないかと相談されることがあります。 貸す貸さないの判断は置いといて、 万一貸し出すとなると一応念のためなんらかのコピープロテクトをかけたいものです。 MAC アドレスを使うとか HDD のボリュームシリアル番号を使うとかも考えられますが、 PC に縛られるため使い勝手が悪くなります。

PC に縛られない方法として USB のプロテクトドングルを使う方法があります。 調べてみると こんな ちゃんとした製品が存在しているようですが少し大げさです。 USB メモリのシリアルナンバを使うというのはお手軽でいいのですが、 いくら周りを堅牢に作ったとしてもシリアルナンバを偽造さえすれば破られます。 そして USB マイコンを使えば偽造なんて結構簡単そうです。 というわけでもう少し手軽で堅牢なプロテクト手段を作ってみました。

しくみと弱点

本システムの概要は以下の通りです。

まずプロテクトをかける人が鍵生成プログラム (Key-Creater) を用いて鍵 A と B を作ります。 鍵 A は PIC 用の HEX ファイルと埋め込まれ、 PIC に焼きこまれることでプロテクトドングルになります。 プログラム利用者には焼きこみ済の PIC のみを渡します (HEX ファイルを渡した場合いくらでも複製を作れます)。 PIC マイコンにはコードプロテクト機能があるため PIC に焼きこんだ A は 見ることができません。

一方鍵 B はプロテクトをかけたい PC プログラムの中に埋め込みます。 プログラム(PC-Prog) が起動しプロテクトチェックが始まると プログラムの中で適当な乱数列が生成され USB を介して PIC にわたります。 PIC では受け取った乱数列と持っている鍵 A を用いて、適当な計算式を用いて 20byte の数列 (Hash) を求め返します。 一方 PC プログラムの中でも鍵 B から鍵 A を復号し同様の計算を行います。 この二つの数列が一致していたらプロテクトチェックにパスしたとみなします。 毎回通信する内容が異なりますから、通信を傍受して鍵 A を導くことは まず不可能です。

しかし、察しのいい方は気づいたと思いますがこの中に重大な欠点があります。 それは鍵 B から鍵 A が作れることです (一応多少は分かりにくくしているので、 逆アセンブル等をしないかぎり解析は無理でしょうが)。 先述したように PIC から鍵 A を取り出すことはできませんが、 新しい値 Z 鍵とした HEX ファイルを生成し焼くことは可能です。 そのため鍵 B から A が取り出せれば簡単にドングルが複製できてしまいます。

この欠点は公開鍵暗号方式を利用することで回避可能です。 しかしこの暗号方式は計算量が多く PIC18 で実現するのは困難です。 さらに、そもそも鍵 B のありかが分かるのなら、 鍵生成プログラムから新たな鍵のセット A', B' を作って上書きすれば終りです。 この攻撃には公開鍵暗号方式と言えども無力です。 この攻撃方法に対抗するには鍵 B を巧みに隠蔽するしかありません。 隠蔽方法はこの場で公開すると意味がなくなるので、 各実装者が考える必要があります。

プログラム

プログラム一式は こちらです。取得して展開すると 以下の四つのファイルがあるはずです。

  1. DongleData.exe
  2. BaseData.obj
  3. PrtctDongleLib.h
  4. PrtctDongleLib.lib
このうち 1, 2 が上述の鍵生成プログラム (Key-Creater) です。 起動するとベンダー ID (VID),プロダクト ID (PID), 名称とシリアルナンバの入力欄があります。 名称は 25 文字、シリアルナンバは 12 文字以内で考えてください。 VID と PID は PC が識別する USB 機器固有の番号で、 (表向きには) 勝手に番号をつけるわけにはいけません。 しかし正式に取ろうとすると 50 万円近くの費用がかかります PIC マイコンを用いているので、それなりに手続きすれば サブライセンスが入手可能でしょう。 また、用法を守れば V-USBの 番号も使えるようです (HID クラスのを使ってください)。

必要項目を入れて Create ボタンを押すと PrtcDongle_xxxx.cpp と PrtcDongle_xxxx.hex という二つのファイルが生成されます。 この hex ファイルが鍵 A が含まれた PIC 用プログラムです。 秋月の 「PIC18F14K50 使用 USB 対応超小型マイコンボード (K-05499)」 をターゲットに作ってますが、単純な回路なので自作も可能でしょう。 焼きこみに成功すると HID デバイスとなります (ドライバのインストールは不要)。

一方 cpp は、鍵 B を含めたサンプル関数が入っています。 プロテクトをかけたいプログラムにてサンプルのように関数を呼び出します (3. の PrtctDongleLib.h をインクルードし、4. の lib をリンクする必要があります)。 関数はプロテクトチェックに成功すると 0 を返します。 ドングルが刺さってないと 1 か 2 を、 Hash 値が一致してないと 5 を返します。 残りのエラーの意味はヘッダファイルのコメントを参照してください。

注意点としてもし一つのプログラムに対しユーザ毎に異なるドングルを持たせるなど、 鍵 B をプログラム外部にファイルとして置く必要がある場合、 鍵 B をそのままファイルにしてはいけません。 先述したように簡単に差し替えられるからです。 多少は隠蔽する必要があります。

一例ですがプログラム内部に共通の鍵 KEY[] を保持し

 fread(kn, 1, 64, fp);
 for (int i=0;i < 64;i++)
   kn[i] ^= KEY[i];
と書くと多少は隠蔽できます。

また逆アセンブルしてチェック関数の番地を調べ、 常に成功 (0) を返すようにされてもプロテクトは破られます。 この対策として、チェックの中にわざと間違った鍵を渡しちゃんと失敗するかどうかも確認する方法が考えられます。 ただし原理的に一極回に一回くらい間違った鍵でも成功と返す可能性があるので留意ください (対策を入れてもいいですし、まずありえないと無視してもいいですし…)。


[戻る]