SSブログ

ちょっとしたプログラムプロテクトの話 #2 [プログラム]

ちょっとしたプログラムプロテクトの話 #1」のつづきです。




コード内のメッセージを見たい VS メッセージを覗かれたくない

この戦いに決着をつけるためのアプローチとしては、大きく分けて2つの方法が採られました。

ひとつは、リストを見られないように BASIC プログラム自体に細工をすること。
もうひとつは、コード内にあるメッセージを「暗号化」することでした。




ひとつめのアプローチは BASIC プログラム自体に細工をすることです。

あるゲームではリストの各所に REM 文が挿入してあり、コメントの先頭文字が「CLR(画面消去)」になっていました。 リストを取ると頻繁に画面消去が繰り返され、内容を確認する事がとても困難になります。

また、あるゲームでは 'P' オプション付きでセーブされているのですが、ワークエリアを操作して 'P' オプションを解除しても、リストを取ると特定の場所の表示時に「暴走」するような仕掛けがしてありました。
リスト中に「…:PC=98」という記述があり、この部分に細工がしてありました【PC-9801版 大○○2】

どちらの場合も、「MON」命令が無効化されていたのですが、無効化を解除した後でモニタから直接 BASIC のテキストを操作する事で破られてしまいました。




様々な方法で BASIC のテキストを「不可視化」する試みが行われましたが、ことごとく破られてしまうためか、この後テキスト自体を不可視化することはあまり行われなくなります。

これは BASIC の不可視化よりもディスク自体のプロテクトにウェイトが置かれるようになったのと、C コンパイラでの開発が主流になってきたのが原因と思われますが、真相は定かではありません。




ふたつめのアプローチは、コード内にあるメッセージを暗号化することでした。

これは現在でも使用されている方法です。 もちろん当時よりは高度な暗号化技術が使われているので「暗号強度」は比べるまでもありませんが…。


最初に用いられた暗号化は、「シーザー暗号」と呼ばれる、単純に
'IBM' → 'HAL'
'シンブンシ' → 'スアベアス'
という具合にキャラクタコードをずらしただけのものでした。 しかしこれだと、表示されたメッセージと比較する事で簡単に見破られてしまいます。

また、メッセージ全体を暗号化すると、
'ココ ニハ ナニ モ ナイ' → 'スス#ノヘ#ネノ#ヨ#ネオ'
と、単語の区切りに使用する「スペース」が、特定の文字(この場合は '#' )に変換されることから、元のメッセージとの対応が容易に推測されてしまいます。
(この場合は ' '(0x20) '#'(0x23) が ASCII コードでは +3 の違いなので、
<暗号化された文字> - 3 = <元の文字>
という推測が簡単に導けます。)




雑誌投稿や同人系のゲームでは、単純な「シーザー暗号」でも問題はないでしょうが、パッケージで発売されるゲームとなると、もう少し複雑な仕組みでないと「技術力が低い」と馬鹿にされてしまいます。
そこで「シーザー暗号」よりも、もう少し高度な「ビット反転」が使われます。
ビット反転だけだと見破られてしまう可能性があるのですが、暗号化/復号化の両方で同じルーチンが使える利点があります。


Encrypt_sample
メッセージ暗号化ルーチン サンプル(MSX)

これは、友人に頼まれて作った「簡易暗号化ルーチン」の復刻版(!)です。
このままだとリストが見難いので、テキストに直したものを掲載しておきます。
list
10 DEFINT A-Z
20 A$="ここには Shield が おいてあります"
30 FOR I=1 TO LEN(A$)
40  C=ASC(MID$(A$,I,1))
50  IF C>&H85 THEN E=C XOR &H5C ELSE IF C>&H40 THEN E=C XOR &H1F 
    ELSE E=C
60  PRINT CHR$(E);
70 NEXT
Ok
run
ニニコカ Lwvzs{ ハ♣ ノホソヘ、ウチ
Ok


この暗号化の利点は、単純なので CPU パワーを食わないことと、アルファベット/記号部分(0x21 - 0x7f)とひらがな/カタカナ部分(0x86 - 0xfd)の反転させるビットを違うパターンにする事で、解読され難くなっている事です。

欠点としては、
'`' 0x60 → 0x7f 「削除」
'「' 0xa2 → 0xfe 「未定義文字」
'」' 0xa3 → 0xff 「カーソル表示」
' ' 0x90 → 0xcc 'フ'
' ' 0xa0 → 0xfc 'わ'
と変換されるため、BASIC でプログラミングする場合、ルーチンをこのまま使用すると
「バッククォート(0x60)
「山括弧(0xa2 - 0xa3)
'フ'
'わ'
をメッセージ中に使用できなります。
そのため BASIC で上記の文字を表示させるには例外処理を追加する必要があります。

また、例外処理を追加する事で暗号化されたメッセージに「迷彩」が施され、単純なビット反転だけではなくなるので、逆に破られにくくなる利点もあります。




BASIC でゲームを作る時代は瞬く間に過ぎ去り、C やマシン語での開発も汎用ツール(ゲームエンジン/スクリプトエンジン)を使う方式へと代わりました。
今では「暗号化」はエンジンの一機能として存在していますが、ほとんど誰も気に留めることはありません。

コード内のメッセージを見たい VS メッセージを覗かれたくない

たぶん永遠に続くであろう戦いの「始まりの時代」のお話でした。


(参考)
Wikipedia: 単一換字式暗号
Wikipedia: シーザー暗号
Wikipedia: ゲームエンジン
Wikipedia: スクリプトエンジン (ゲーム)

あー、またやってしまった [近況報告]

またやってしまいました(T_T)

一昨日の夜「My Documents」のバックアップを取ったつもりが、間違ってバックアップから書き戻していたため、せっかく編集した「Dash-80」のバグ修正バージョンが旧版に戻ってしまいました。
バグの場所は判明しているのでソース類はもう一度修正すれば済むのですが、大幅に加筆したドキュメントが「先祖がえり」したのは痛すぎます。
しかも、その時に気付かなくて、今になって気付くとは…(苦笑)

こういうミスって、やってる本人は操作が間違ってるって思ってないんですよね。
ウチの開発システムってヒューマンファクタのエラー多すぎ(大笑)

うーむ、時間の余裕がないときに無理やり作業すると、注意力が散漫になるのかなぁ(反省)
バッチ処理の警告表示は、バックアップ/リストアの色を分けて画面上でもっと目立つように修正せねば…。

早く試したい方は、「RCP Demo」のディスクバージョンが「Dash-80 v1.0a RC-1」を使用してますので、そちらをダウンロードしてみてください。

正式な修正版を待っておられる皆さんには申し訳ないですが、「Dash-80 v1.0a」のリリースはもう少し遅れますm(_ _)m

ちょっとしたプログラムプロテクトの話 #1 【追記あり】 [プログラム]

2010/02/22 初期のプロテクト例を追記

日参している「何とか庵日誌」。
MSX の投稿雑誌打ち込みプログラムでの「ささやかな暗号化の話」が取り上げられていたので、便乗記事です^^;



アドベンチャーやロールプレイングのネタバレやシミュレーションでの「ズル」を防ぐにはプログラムテキストの「不可視化」、表示されるメッセージの「暗号化」が必要ですが、黎明期のゲームでは全く対処していないモノも多くありました。 BASIC でメインプログラムが組まれていた頃は、リストを表示すれば全部バレてしまうモノがほとんどでした。




FM-8/7 の F-BASIC には「UNLIST」命令があり、これを実行すると「LIST」命令を実行しても表示されなくなります。
そのうち、この機能を使ってリスト表示を「阻止」するモノが現れてきました。
しかし、「MON」命令を無効にしなかったため、直接メモリ内に値を書き込んで「UNLIST」を無効にしてしまえば、表示できてしまいました。

F-BASIC の DISK VERSION には「SAVE」命令に 'P' オプションが用意されており、オプション付きでセーブされた BASIC プログラムだと「LIST」命令を実行しても 'Protected Program' と表示され、リストが表示できません。 この場合「POKE」命令も無効にされていますが、「MON」命令は使えるので「UNLIST」と同様に解除できてしまいました。
'P' オプションを付ける事でセーブされたプログラム自体は暗号化されて保存されますが、これでは何も役に立ちません。

ゲームを作る側も、何とか「プロテクト外し」を阻止しようと「技術の向上」で対処します。
「POKE」「MON」命令を無効にしてワークエリアを書き換えられないようにしてしまいました。
しかし、これもあらかじめ 'P' オプションのワークエリアをクリアしたものを「SAVEM」命令でセーブしておき、BASIC プログラムをロードした後に「LOADM」命令でロードする事で、簡単に突破されてしまいました。

2010/02/22 追記ここから

BASIC の内部が解析され色々な情報が明らかになってくると、Disk-BASIC を使用しないテープ版のプログラムでも凝ったプロテクトの物が登場してきました。
LIST 命令処理ルーチン内の「未処理トラップ」を操作して、LIST 命令を実行しても「Ok」だけが表示されるようにして「不可視化」を実現していました。
このプロテクトも上記の方法で「MON」命令の無効化を解除した後、通常は使われていない未処理トラップの中から書き換えられているトラップを探して元に戻す事で、破られてしまいました。

2010/02/22 追記ここまで




FM-8 の後に発売された PC-8801 の N88-Disk BASIC にも F-BASIC と同等の機能が装備されていました。
Disk-BASIC では 'P' オプション付きでセーブされた BASIC のプログラムをロードすると、スクリーンエディタでのプログラムの追加/変更/削除ができません(「DELETE」だけは可能ですが、意味がありませんね)。
また、ダイレクトモードから「LIST」「MON」「POKE」「BLOAD」「CALL」「USR()」が実行できなくなります。
この辺りには、F-BASIC での教訓が生かされているのでしょう。

FM-8 では通用した『あらかじめ 'P' オプションをクリアした状態を「BSAVE」命令でセーブしておき「BLOAD」命令でロードする』もブロックされてしまいます。

そこで考え出されたのが、「コマンド拡張法」です。
将来の拡張用に用意された命令(例えば「IRESET」)を拡張し、'P' オプションのワークエリアをクリアするようにアセンブラでマシン語を組みます(真のプログラマは、直接メモリに16進コードで打ち込みます)。
例えば F320 番地から、21 AD EE 11 30 F3 3E C3 77 23 …(以下は自重)
これを実行すると「IRESET」命令が 'P' オプションのワークエリアをクリアできるように設定されます。
あらかじめ通常の BASIC で起動して「IRESET」命令を使用できるようにしておきます。
プロテクトを解除したいプログラムをロードし「IRESET」命令を実行します。後は同じファイル名で通常('P' オプションをつけない)のセーブをすれば BASIC プログラムのプロテクトは解除されます。
「IRESET」はダイレクトモードから実行してもブロックされない命令だというのがミソです。




コード内のメッセージを見たい VS メッセージを覗かれたくない

この戦いに決着をつけるためのアプローチとしては、大きく分けて2つの方法が採られました。

つづく

デモ行進、いや、デモ更新 [お知らせ]

〔本館〕サイトで公開しているサイトデモのアーカイブに、ディスク版の実行ファイルを追加しました。

テープ版は実行するまでにコマンド打ち込みの手間が掛かったのですが、ディスク版はドライブにディスクをセットしてリセットするだけで起動します。

…って、いつまでデモのネタで引っ張るんだろ。(自戒)

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。