(57)【要約】【課題】  汎用性のある、逆コンパイル時に異常終了または誤った出力結果を引き起こすことが可能なプログラム難読化装置を提供する。【解決手段】  プログラム難読化装置は、プログラムの中から複数の命令コードを含む関数（サブルーチン）を抽出する抽出手段と、抽出された関数に含まれる命令を非定型の関数呼出／復帰命令に変換する変換手段とを含む。関数呼出／復帰命令を定型的ではなく、非定型定とすることで、プログラムの逆コンパイル時に、異常終了または誤った結果を引き起こさせることが可能となる。【選択図】  図８
2007304726.tif 000002
【特許請求の範囲】
【請求項１】
プログラムの難読化処理を行うプログラム難読化装置であって、
プログラムの中から、複数の命令コードを含む関数を抽出する抽出手段と、
抽出された関数に含まれる命令を非定型の関数呼出および復帰命令に変換する変換手段と、
を含む、プログラム難読化装置。
【請求項２】
前記非定型の関数呼出および復帰命令は、非入れ子の形を含む、請求項１に記載のプログラム難読化装置。
【請求項３】
前記非定型の関数呼出および復帰命令は、関数本体の途中への分岐命令を含む、請求項１または２に記載のプログラム難読化装置。
【請求項４】
前記非定型の関数呼出および復帰命令は、コード中のアドレス値を操作する命令の挿入を含む、請求項１ないし３いずれか１つに記載のプログラム難読化装置。
【請求項５】
前記非定型の関数呼出および復帰命令は、無条件分岐命令や条件分岐命令の挿入を含む、請求項１ないし４いずれか１つに記載のプログラム難読化装置。
【請求項６】
前記抽出手段は、プログラムの中から少なくとも一つの関数を抽出する、請求項１に記載のプログラム難読化装置。
【請求項７】
プログラムは、実行形式がJavaバイトコードを含む、請求項１に記載のプログラム難読化装置。
【請求項８】
関数呼出命令としてjsr命令を用いる、請求項７に記載のプログラム難読化装置。
【請求項９】
関数復帰命令としてret命令を用いる、請求項７または８に記載のプログラム難読化装置。
【請求項１０】
プログラムは、実行形式がｘ８６のマシンコードを含む、請求項１に記載のプログラム難読化装置。
【請求項１１】
関数呼出命令としてcall命令を用いる、請求項１０に記載のプログラム難読化装置。
【請求項１２】
プログラムの難読化を行うプログラム難読化方法であって、
プログラムの中から、複数の命令コードを含む関数を抽出する抽出ステップと、
抽出された関数に含まれる命令を非定型の関数呼出および復帰命令に変換するステップと、
を含む、プログラム難読化方法。
【請求項１３】
前記変換ステップは、入れ子の形とならない形の関数呼出および復帰命令への変換を含む、請求項１２に記載のプログラム難読化方法。
【請求項１４】
前記非定型の関数呼出および復帰命令は、関数本体の途中への分岐命令の挿入、コード中のアドレス値を操作する命令の挿入、無条件分岐命令または条件分岐命令の挿入を含む、請求項１２に記載のプログラム難読化方法。
【請求項１５】
プログラム難読化方法はさらに、プログラムをアセンブリ言語のコードに変換するステップを含み、前記抽出ステップは、アセンブリ言語のコードから少なくとも一つの関数を抽出する、請求項１２に記載のプログラム難読化方法。
【請求項１６】
プログラムの難読化を行う難読化プログラムであって、
プログラムの中から、複数の命令コードを含む関数を抽出する抽出ステップと、
抽出された関数に含まれる命令を非定型の関数呼出および復帰命令に変換するステップと、
を含む、難読化プログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、プログラムを難読化するための装置、方法およびプログラムに関し、特に、プログラムの逆コンパイルを困難にする難読化技術に関する。
【背景技術】
【０００２】
プログラムを悪意あるバースエンジニアリングから守るために、様々なプログラム難読化技術が提案されている。プログラム難読化とは、プログラムの実行時動作を変えない限りにおいて、プログラムを人間にとって理解しづらくなるように、あるいは複合化し難くなるように変換する技術である。プログラムに難読化を施すことにより、リバースエンジニアリングにかかる費用や時間を大きくすることができる。
【０００３】
プログラム難読化の技術がいくつか報告されている。特許文献１は、プログラムのソースコードに対して関数へのポインタ変数を新たに複数個導入し、関数ポインタ変数のいずれかに関数のアドレスを代入する命令文を複数個挿入し、プログラムにおける関数の呼出を関数ポインタ変数の値に応じて関数を呼出すように変換する。特許文献２は、プログラムに含まれる繰り返し制御文を検出し、当該繰り返し制御文のインデックス変数に入力される値の順番を変更するようにプログラムの内容を変換するものである。
【０００４】
特許文献３は、アセンブリプログラムを構成する複数の命令コードを偽の命令コードＹで偽装するとともに、偽の命令コードＹを元の正しい命令コードｙに戻すルーチンＸと、ルーチンＸが戻した正しい命令コードｙを再度偽の命令コードＹで偽装するルーチンＺとを生成し、生成したルーチンＸ及びＺをアセンブリプログラム上の所定の位置に書き込み、自己書換えプログラムを生成する。特許文献４は、プログラム中のデータを符号化規則Ｃ（ａ，ｂ）＝ａｘ＋ｂを用いて符号化し、符号化された符号化データを含む演算式の各演算子に演算子変換規則Ｒ（ａ，ｂ）〜Ｒ１０（ａ，ｂ）を適用することにより演算式を符号化演算式に変換し、符号化演算式を復号化規則Ｄ（ａ，ｂ）を用いて復号化演算式とするものである。さらに同様のプログラムの難読化を行う文献が非特許文献１ないし６に開示されている。
【０００５】
【特許文献１】特開２００３−３３７６２９号公報
【特許文献２】特開２００４−１８５０６４号公報
【特許文献３】特開２００４−１９２０６８号公報
【特許文献４】特開２００５−４９９２５号公報
【非特許文献１】C. Collberg, C. Thomborson, and D. Low, “Manufacturing Cheap, Resilient, and Stealthy Opaque Constructs”, Proceedings of the 25th ACM Symposium on Principles of Programming Languages, 1998
【非特許文献２】C. Collberg, C. Thomborson, and D. Low, “Breaking Abstractions and Unstructuring Data Structures”, Proceedings of the 1998 IEEE International Conference on Computer Languages, 1998
【非特許文献３】C. Wang and J. Hill and J. Knight and J. Davidson, “Software Tamper Resistance: Obstructing Static Analysis of Programs”, Technical Report CS-2000-12, Department of Computer Science, University of Virginia, 2000
【非特許文献４】門田暁人, 高田義広, 鳥居宏次, “ループを含むプログラムを難読化する方法の提案”, 電子情報通信学会論文誌, Vol. J80-D-I, No. 7, 1997
【非特許文献５】J. Chan and W. Yang, “Advanced Obfuscations on Java Bytecode”,Journal of Systems and Software, vol. 71(1−2), 2004
【非特許文献６】C. Linn and S. Debry, “Obfuscation of Executable Code to Improve Resistance to Static Disassembly”, Proceedings of the 10th ACM Conference on Computer and Communications Security, 2003
【発明の開示】
【発明が解決しようとする課題】
【０００６】
しかしながら、上述した特許文献１ないし４や非特許文献１ないし４をはじめとする従来の多くの難読化手法は、逆コンパイラの異常終了や誤った出力結果を引き起こすことを可能にする実行形式を生成することはできなかった。
【０００７】
その一方で、非特許文献５や非特許文献６は、特許文献１ないし４や非特許文献１ないし４と異なり、逆コンパイラや逆アセンブラ等のツールを欺くことを目的としている。非特許文献５は、Java（登録商標）の逆コンパイラの欠陥を利用することで逆コンパイルされたとしても再びコンパイルすることが困難なJavaバイトコードを生成する手法を提案し、非特許文献６は、インテル製マイクロプロセッサに代表される、ｘ８６のマシン語の逆アセンブラの出力結果を欺く手法を提案している。しかしながら、非特許文献５の手法はJava バイトコードにしか適用できず、非特許文献６の手法は可変長命令の実マシン語にしか適用できないという課題があった。
【０００８】
そこで本発明は、上記従来の課題を解決し、種々の実行形式のプログラムに適用可能な汎用性を備え、かつ、プログラムの逆コンパイル時に異常終了または誤った出力結果を引き起こすことが可能なプログラム難読化装置、難読化方法および難読化プログラムを提供することを目的とする。
【課題を解決するための手段】
【０００９】
本発明に係るプログラム難読化装置は、プログラムの中から、複数の命令コードを含む関数を抽出する抽出手段と、抽出された関数に含まれる命令を非定型の関数呼出命令および復帰命令（以下、関数呼出／復帰命令という）に変換する変換手段とを含む。関数（サブルーチン）呼出／復帰命令を定型的ではなく、非定型的とすることで、プログラムの逆コンパイル時に、異常終了または誤った結果を引き起こさせることが可能となる。関数呼出／復帰命令が定型的とは、関数呼出命令と復帰命令とが入れ子状態にあり、関数呼出／復帰命令が非定型的とは、関数呼出命令と復帰命令とが非入れ子状態にあることをいう。
【００１０】
非定型の関数呼出／復帰命令は、それらの命令形態が非入れ子の形となるもの、関数本体の途中への分岐命令を挿入するもの、コード中のアドレス値を操作する命令を挿入するもの、無条件分岐命令や条件分岐命令を挿入するものであってもよい。本発明は、難読化対象の実行形式がJavaバイトコードの場合、関数呼出命令としてjsr命令を用い、関数復帰命令としてret命令を用い、これらを非定型の関数呼出／復帰命令とする。また、本発明は、難読化対象の実行形式がｘ８６のマシンコードであってもよく、関数呼出命令としてcall命令を用い、関数復帰命令としてret命令を用い、これらを非定型の関数呼出／復帰命令とする。
【００１１】
さらに本発明のプログラム難読化方法または難読化プログラムは、プログラムの中から、複数の命令コードを含む関数を抽出する抽出ステップと、抽出された関数に含まれる命令を非定型の関数呼出／復帰命令に変換するステップとを含んで構成される。
【００１２】
本発明のプログラム難読化方法の概要について説明する。なお、これより以降は、関数とサブルーチンのことを合わせて関数と略する。本発明は、上記したように、与えられたプログラムの実行形式に対し、関数呼出／復帰命令やコード中の番地を表す値を操作する命令や無条件分岐命令や条件分岐命令を挿入したり、又はこれらの命令により前記実行形式中に含まれる無条件分岐命令や条件分岐命令を置き換えることによりプログラムを難読化する。
【００１３】
本発明のプログラムの難読化により、一般的なコンパイラによる定型的な関数呼出／復帰命令の使用とは異なる非定型の関数呼出／復帰命令の使用を実行形式に含めることができる。一例として、本発明は、入れ子の形にならない関数呼出／復帰命令の使用を含める。例えば、図１に示すような擬似的な実行形式のコードを考える。先ず、このプログラムのgoto L1 とgoto L2 を、それぞれ関数呼出命令callを用いてcall L1とcall L2に変換する。また、L1 の直後にcall L1によって生成されるアドレスを保存する命令を追加する。さらに、関数Sの呼出に対応するret 命令をL1の呼出に対応するret 命令に置き換え、関数Sの呼出に対応するret命令をcall L1の直後に移動する。これにより、図２に示すプログラムが生成される。このプログラムには、call L2に対応した関数復帰命令(ret 命令) は存在しない。つまり、入れ子の形になるように関数呼出／復帰命令が使われていない。
【００１４】
また別の例として、本発明は、本体の途中から実行され得る関数を含めることが出来る。例えば、図３に示すような擬似的な実行形式のコードを考える。このプログラムのifle L2 の直後にmove a → b にジャンプするcall 命令(call L2’) を挿入すると、図４のプログラムが生成される。ここで、このプログラムのL2’をひとつの関数とみなしてしまうと、関数L2’の呼出に対応する関数復帰命令が存在しないとみなされるだけでなく、ifle L2は、関数L2’の本体の途中への分岐とみなされる。
【００１５】
通常、関数呼出／復帰命令は、ソース言語での関数呼出をコンパイルする際にのみ用いられる。従って、上述したような入れ子の形にならない関数呼出／復帰命令の使用や関数本体の途中への分岐命令は、特殊な場合を除き、実行形式に含まれることはない。このように、本発明は、一般的なコンパイラによる実行形式には通常含まれない、非定型な関数呼出／復帰命令の使用を実行形式に含めることができる。本発明により、定型的な関数呼出／復帰命令の使用を仮定している逆コンパイラの異常終了や、間違ったまたは可読性の低い逆コンパイル結果を引き起こすことが可能となる。さらに、本発明は、関数呼出／復帰命令の備わっている機械語に適用できる。多くの機械語には、関数呼出／復帰命令が備わっているため、本発明は、従来の手法よりも汎用性が高い。
【発明の効果】
【００１６】
本発明により、実行形式の動作を変えることなく、非定型な関数呼出／復帰命令の使用を実行形式に含めることができる。これにより、変換後の実行形式を入力とした逆コンパイラは、異常終了したり、誤ったソースコードを出力するようになる。また、本発明により、従来の技術よりも汎用性のある、つまりはより多くの機械語に適用可能なプログラムの難読化方法を提供することができる。
【発明を実施するための最良の形態】
【００１７】
以下、本発明の最良の実施形態について図面を参照して説明する。
【実施例】
【００１８】
図５は、本発明の実施例に用いられるプログラム難読化装置の構成を示すブロック図である。プログラム難読化装置１０は、情報を入力するキーボード等を含む入力装置１２、表示装置１４、主記憶装置１６、記憶装置１８、中央処理装置（ＣＰＵ）２０、これらを接続するバス２２を含んでいる。主記憶装置１６は、ＲＯＭまたはＲＡＭを含み、プログラム難読化を行うプログラムや演算処理されたデータ等を記憶する。ＣＰＵ（Central Processing Unit）２０は、主記憶装置１６に記憶されたプログラムに従い各部を制御する。
【００１９】
次に、本発明の第１の実施例に係るプログラム難読化方法について、図６ないし図１８を参照して説明する。図６はJavaバイトコードを難読化するＣＰＵ２０の機能を示したブロック図である。ＣＰＵ２０は、プログラムのソースコードを入力する入力部３０、入力されたプログラムのソースコードの難読化処理を行う難読化処理部３２、および難読化されたプログラムを出力する出力部３４から構成される。
【００２０】
ＣＰＵ２０は、入力部３０から入力されたJava バイトコードを難読化し、これを出力部３４から出力する。ここで、Javaバイトコードとは、Java仮想マシン用の実行形式のことを指す。以降、Javaバイトコードを単にバイトコードと略することがある。
【００２１】
入力部３０は、バイトコードを受け取り、そのバイトコードと等価なアセンブリ言語のコードに変換し、これを難読化処理部３２へ出力する。図７（ａ）に、Javaのアセンブリ言語によるコード例を示す。分かり易くするため、実際的なアセンブリコードを簡略化している。また、図７（ｂ）に示すコードは、図７（ａ）に示すソースコードにおける関数example と等価である。
【００２２】
難読化処理部３２は、入力部３０の生成したアセンブリ言語コードに難読化処理を施す。入力として受け取ったコードに含まれる全ての関数を変換対象としてもよいし、難読化対象の指定３６からの指定に基づき、特定の(１個以上の)関数のみを難読化の対象としてもよい。指定は、ＣＰＵ２０がランダムに関数を選択するようにしてもよいし、入力装置１２を介してユーザーが指定するようにしてもよい。
【００２３】
出力部３４は、難読化処理部３２により変換されたアセンブリ言語のコードを受け取り、それをバイトコードに変換する。そのバイトコードが難読化装置の全体としての出力となる。難読化されたプログラムは、例えば記憶装置１８に格納される。
【００２４】
図７のアセンブリ言語に含まれる命令の意味を説明する。
「invokestatic」は、関数を呼び出す命令である。
(例) invokestatic a()Z : 返り値が真偽値(true かfalse)である関数aを呼び出す。返り値は、オペランドスタックの先頭に積まれる。
(例) invokestatic d()V :返り値の無い関数d を呼び出す。
「ifne L」は、条件分岐命令。
(例) ifne L : オペランドスタックの一番上に積まれている値が0 でなければ、L に飛ぶ。
「ifeq L」も同じく条件分岐の命令。
(例) ifeq L : オペランドスタックの一番上に積まれている値が0 であれば、L に飛ぶ。
「goto L」は、無条件分岐命令。
「return」は、当関数の呼出もとに返る命令。返り値は伴わない。
【００２５】
次に、難読化処理部の動作について図８のフローを参照して説明する。難読化処理部３２は、受け取ったアセンブリコードに含まれている関数ごと難読化処理に行う。まず、難読化処理部３２は、難読化処理を施していない関数が残っているか否かを調べる（ステップＳ１０１）。ここでは、すべての関数について難読化を行うものとする。難読化処理を施していない関数mが残っている場合、関数mに施す難読化処理の手順パターンp1, p2, ..., pn(n ≧ 1)を決定する（ステップＳ１０２）。手順パターンは、難読化のバリエーションを示すものであり、少なくとも１つが選択される。どの手順パターンを選択するかは、難読化処理部３２がランダムに決定してもよいし、ユーザーが入力装置１２を介して決定しても良い。
【００２６】
難読化処理部３２は、手順パターンを決定をしたら、手順パターンp1, p2,..., pnによる処理手順パターンを順番に関数mに施す(ステップＳ１０３)。そして、ステップＳ１０１において、関数の有無を調べ、全ての関数の難読化処理をした場合、難読化処理は終了する。
【００２７】
次に、難読化処理手順パターンの例を２つ示す。どちらの処理手順パターンも、Java仮想マシンにおけるサブルーチン呼出／復帰命令(jsr, ret)を利用する。図９にサブルーチン命令を含むコード例を示す。
【００２８】
「jsr命令」は、オペランドとしてラベル(アドレス)をとる。jsr L が実行された時、この命令の次の命令のアドレスがオペランドスタックの先頭に積まれると同時に、ラベルLの示すアドレスに制御が移る。
「ret命令」は、オペランドにローカル変数をとる。ret x が実行された時、変数x に格納されているアドレスに制御が移る。
【００２９】
図９のコード（１）が実行されると、制御はＬのアドレス、つまりコード（２）に飛ぶ。同時に、コード（４）のアドレス値がスタックに積まれる。コード（２）の命令は、そのコード（４）のアドレス値をローカル変数xにストアしている。その後、制御がコード（３）まで移ると、ret命令によりxに格納されているアドレス値を用いて、コード（４）にジャンプする。
【００３０】
第１の難読化処理手順パターンについて図１０のフローを参照して説明する。
ステップＳ２１０： まず最初に、変換すべき関数のコードのコントロールフローグラフ(以後、CFGと略する)を作成する。例として図１１に、図７に示すアセンブリ言語のコードから作成したCFGを示している(図１１では、invokestatic 命令を略してinvokeと記している)。
【００３１】
作成するCFGは、後の手順の便宜を図るため、ノード間の遷移を
（ａ）無条件分岐(goto) による遷移、
（ｂ）条件分岐(if) による遷移(条件分岐が成り立つ時)、
（ｃ）条件分岐(if) による遷移(条件分岐が成り立たたない時)、
の３つに区別する。上記のそれぞれの遷移を、(ａ)による遷移、（ｂ）による遷移、（ｃ）による遷移という。図１１のCFGでは、（ｂ）による遷移、（ｃ）による遷移に、それぞれT(True)、F(False)という目印をつけている。
【００３２】
ステップＳ２２０〜Ｓ２５０：CFG 中の（ａ）または（ｃ）による遷移の各エッジｎ１→ｎ２について、ｎ１→ｎ２が、次の条件１、条件２のいずれかを満たすのであれば、ｎ１→ｎ２をjsr 命令による遷移に変える。
(条件１)ｎ２がｎ１を支配していない、
(条件２)ｎ２によって支配されていないノード○からの、（ｂ）による遷移○→ｎ２が存在する。
【００３３】
「ｎ２がｎ１を支配している」とは、「スタートノードからｎ１に到達するためには必ずｎ２を経由しなければならない」状況をさす。例えば、図１２に示すのCFGの場合であれば、ノード２はノード６を支配しているが、ノード３はノード６を支配していない。なぜなら、ノード1 → 2 → 4(5) → 6 という経路がある
からである。
【００３４】
jsr命令による遷移は、具体的に次の操作を行う。新たにノードｎ２’を生成し、これをｎ２の直前に置く(ステップＳ２４０)。ただし、既にｎ１→ｎ２とは別のエッジを処理した時にｎ２’を生成している場合は、この処理は省略する。この時点では、ｎ２’には何の命令列も置かれていないことに注意する。
【００３５】
ｎ１→ｎ２の遷移を、jsr 命令によるｎ１→ｎ２’の遷移に変える(ステップＳ２５０)。
【００３６】
jsr 命令によってオペランドスタックの先頭に積まれるアドレスを捨てるための命令列(例えば、pop やastore dummy_var など)を生成し、ｎ２’に置く(ステップＳ２６０)。
【００３７】
図１１に示すCFGでいえば、A → B、B → D、C → E、E → R の４つの遷移は上記の条件１を満たしている。また、F → C は条件２を満たしている。従って、A → B、B → D、C → E、E → R, F → C の５つの遷移は、上記のjsr命令による変遷処理の対象となる。
【００３８】
まず、ステップＳ２４０により、B, D, E, R, C の直前に、それぞれノードB’, D’, E’, R’, C’が置かれる。次に、ステップＳ２５０により、A → B、B → D、C →E、E → R, F → C の遷移が、jsr 命令による遷移A → B’、B → D’、C → E’、E →  R’, F → C’にそれぞれ置き換えられる。最後に、ステップＳ２６０により、B’, D’,E’, R’, C’それぞれに、jsr 命令によってオペランドスタックの先頭に積まれるアドレスを捨てるための命令列が置かれる。A → B、B → D、C → E、E →R, F → C をjsr 命令による遷移に変換した後のCFGを図１３に示す。図中、太字で表した遷移が、jsr 命令による遷移を表している。図１４は、図１３のCFGのバイトコードを示している。
【００３９】
なお、上記のステップＳ２３０において、変換対象の遷移を条件１と条件２によって制限している理由は、変換後のCFGに再帰的なjsr 命令の使用を含めないようにするためである。例えば仮に、図１１のD → B の遷移をjsr 命令による遷移に書き換えると図１５に示すようなCFGが生成される。このCFGでは、D → B’を実現するjsr B’は、A → B’を実現するjsr B’によるサブルーチン呼出の「途中」で、必ず実行される。つまり、A → B’を実現するjsr B’が実行されたがまだjsr B’の直後へのret 命令は実行されていない状態で、必ず実行される。しかし、このような再帰的なjsr 命令の使用を含むバイトコードは、非特許文献１に定めるバイトコードの仕様に反しており、実行前にバイトコード・ベリファイヤにより拒絶される。（F. Yellin and T. Lindholm, “The Java Virtual Machine Specification, Second Edition”, Addison-Wesley Longman Publishing Co., Inc., 1999.に示される文献を参照）。
【００４０】
次に、もう一つの難読化処理の手順パターンの例を図１６のフローを参照して説明する。関数のコードの先頭に、jsr S を挿入する。ここで、Sは、当関数の元々の先頭(jsr S を挿入する前までの先頭)を表すラベルとする。さらに、ラベルS の直後にjsr S 命令によってオペランドスタックに積まれたアドレスを適当なローカル変数x に保存する命令列(例えば、astore x など) を挿入する(ステップＳ３１０)。
【００４１】
関数中の適当なreturn 命令を１つ選ぶ(ステップＳ３２０)。上記で選んだretrun 命令を、ret x に置き換える(ステップＳ２３０)。ステップＳ３２０で選んだreturn 命令を、１で追加したjsr S の直後に挿入する(ステップＳ３４０)。例として、上記の手順を図１４のバイトコードに施した後に生成されるバイトコードを図１７に示す。
【００４２】
図１７の変換後バイトコードには、非定型なサブルーチン命令の使用が含まれている。例えば、図１７のバイトコードには、jsr B’に対応するret 命令は明らかに存在しないため、入れ子の形にならないjsr, ret（関数呼出／復帰命令）命令の使用を含む。また、ノードC’をサブルーチンの先頭とみなしたとすると、ノードB’からノードC の遷移は、サブルーチンC’の外から中への遷移とみなされる。つまり、図１７のバイトコードには、サブルーチンC’の本体途中への遷移B’→ C が含まれる。
【００４３】
発明者らは、図１７の変換後バイトコードを以下に示す５つの逆コンパイラ１−５に入力として与えた。そして、どの逆コンパイラもexample 関数の中身をソースコードとして出力することなく異常終了したことを確認した。このことから、発明者らは本発明の難読化手法の効果を実験的に確認できた。
（逆コンパイラ１）  P. Kouznetsov, “Jad - the fast JAva Decompiler”:
http://www.kpdus.com/jad.html.
（逆コンパイラ２）  J. Hoenicke, “Java Optimize and Decompile Environment(JODE)”: http://jode.sourceforge.net/.
（逆コンパイラ３）  J. Miecznikowski and L. Hendren, “Decompiling Java Bytecode: Problems, Traps and Pitfalls”, Proceedings of the 11th International Conference on Compiler Construction, 2002.
（逆コンパイラ４）  K, Kumar, “JReversePro −Java Decompiler/Disassembler”: http://jrevpro.sourceforge.net/.
（逆コンパイラ５）  H. Vliet, “Mocha, the Java Decompiler”:
http://www.brouhaha.com/~eric/software/mocha/.
【００４４】
次に、本発明の第２の実施例について説明する。第２の実施例は、インテル系マイクロプロセッサのｘ８６マシンコードを難読化するものである。難読化の処理内容は、基本的に第１の実施例と変わらないため、説明を割愛する。ただし、図５において、入力／出力するコードが、Javaバイトコードからｘ８６の実行形式に、また難読化処理部での処理の対象がJavaアセンブリコードからx８６アセンブリコードにそれぞれ変わっていることに注意する。
【００４５】
図１８（ａ）にｘ８６のアセンブリ言語によるコード例を示す。これは、第１の実施例の図７（ａ）に示したJavaアセンブリ言語のコードをｘ８６アセンブリ言語で翻訳したコードである。図１８（ａ）のコードは、図１８（ｂ）のソースコードにおけるexample 関数と等価である。
【００４６】
図２３（ａ）、（ｂ）、（ｃ）に、push, pop, call命令の概要を示す。
「push」は、オペランドの中身をスタックの一番上に積む命令。push %ebpという命令が実行された時、%ebpレジスタの中身がスタックの先頭に積まれ、さらにスタックの先頭を指すレジスタ%espが更新される。
【００４７】
「pop」は、スタックの先頭に置かれている値をレジスタにコピーする命令。pop %ebpという命令により、スタックの先頭に置かれている値が、%ebpレジスタにコピーされ、さらにスタックの先頭を指すレジスタ%espが更新される。
【００４８】
「call」は、関数を呼び出す命令。call L という命令が実行された時、call L の次の命令の番地をスタックの一番上に積んだ上で、L 番地に飛ぶ。この際、スタックの一番上を示すレジスタ%espが更新される。
「mov」は、レジスタの中身をコピーする命令。mov %esp %ebp という命令が実行された時、%espの中身が%ebpに代入される。
「je L」は、条件分岐命令であり、直前のcmp 命令の２つのオペランドが等しければ、Lにジャンプする。
「jmp L」は、無条件分岐命令。
「ret」は、スタックの先頭に積まれているアドレスを取り出し、そのアドレスにジャンプする命令。実行された際、スタックの一番上を示すレジスタ%esp が更新される。
【００４９】
第２の難読化処理部の手順は、第１の実施例のときと同様に、図８のフローに従い行われる。また、難読化処理手順パターンの例を１つ示す。このパターンは第１の実施例で示したパターン１に類似する。したがって、第１の実施例のときの図１０を参照して、難読化処理手順パターンの手順を説明する。
【００５０】
まず最初に、変換すべき関数のコードのCFGを作成する。例として図１９に、図１８（ａ）のコードから作成したCFG を示している。作成するCFG においては、第１の実施例と同様に、ノード間の遷移を、
（ａ）無条件分岐(jmp)による遷移、
（ｂ）条件分岐(je)による遷移(条件分岐が成り立つ時)、
（ｃ）条件分岐(je)による遷移(条件分岐が成り立たない時)、
の３つに区別する。以後、上記のそれぞれの遷移を、（ａ）による遷移、（ｂ）による遷移、（ｃ）による遷移という具合に略する。図１９では、（ｂ）、（ｃ）による遷移にそれぞれT, F という目印をつけている。
【００５１】
CFG中の（ａ）または（ｃ）の各エッジｎ１→ｎ２について、ｎ１→ｎ２が次の条件１、条件２のいずれかを満たすのであれば、ｎ１→ｎ２をcall 命令による遷移に変える。
(条件１)ｎ２がｎ１を支配していない、
(条件２)ｎ２によって支配されていないノード○からの、（ｂ）による遷移○→ｎ２が存在する
【００５２】
新たにノードｎ２’を生成し、ｎ２の直前に置く。ただし、既にｎ１→ｎ２とは別のエッジを処理した時にｎ２’を生成している場合は、この処理は省略する。この時点では、ｎ２’には何の命令列も置かれていないことに注意する。
【００５３】
ｎ１→ｎ２の遷移を、call 命令によるｎ１→ｎ２’の遷移に変える。call 命令によってスタックの先頭に積まれるアドレスを捨てるための命令列(例えば、add $4 %esp など)を生成し、ｎ２’に置く。図１９に示すCFGでいえば、A → B、B → D、C → E、E → R の４つの遷移は、上記の条件１を満たしている。また、F → C は条件２を満たしている。従って、A→ B、B → D、C → E、E → R, F → C の5 つの遷移は、変換の対象となる。
【００５４】
まず、B, D, E, R, C の直前にそれぞれノードB’, D’, E’, R’, C’ が置かれる。次に、A → B、B → D、C→ E、E → R, F → C の遷移が、call 命令による遷移A →B’、B → D’、C →E’、E → R’、 F → C’にそれぞれ置き換えられる。最後に、B’,D’, E’, R’, C’それぞれに、call 命令によってスタックの先頭に積まれるアドレスを捨てるための命令列が置かれる。A → B、B → D、C → E、E → R, F →C をcall 命令による遷移に変換した後のCFGを図２０に示す。太字の遷移がcall 命令による遷移を表している。
【００５５】
図２１は、図１９のCFGと等価なｘ８６アセンブリコードであり、図２２は、図２１の別の正しい変換例を示している。太字の遷移がcall 命令による遷移を表している。
【００５６】
第２の実施例は、第１の実施例の処理手順パターン１とは異なり、D → B の遷移を、call による遷移に置き換えることができる。また、図１０に示すアルゴリズムはあくまで１つの例であり、この遷移を置き換えることのできる別の処理手順パターンを構築することが可能である。
【００５７】
第２の実施例により得られた図２２のCFGの変換後マシンコードには、非定型なcall, ret 命令(関数呼出／復帰命令)の使用が含まれている。例えば、図２２のマシンコードは、call B’に対応するret 命令は明らかに存在しないため、入れ子の形にならないcall, ret 命令の使用を含む。また、ノードC’を関数の先頭とみなしたとすると、ノードB’からノードCの遷移は、関数C’の外から中への遷移とみなされる。つまり、図２２のマシンコードには関数C’の本体途中への遷移B’ → C が含まれる。
【００５８】
以上、本発明の好ましい実施の形態について詳述したが、本発明に係る特定の実施形態に限定されるものではなく、特許請求の範囲に記載された本発明の要旨の範囲内において、種々の変形・変更が可能である。
【産業上の利用可能性】
【００５９】
本発明は、悪意のあるプログラムのリバースエンジニア等から保護するための難読化装置、難読化方法および難読化プログラムとして利用される。
【図面の簡単な説明】
【００６０】
【図１】(a+b)*(a-b) を求める関数Ｓの擬似的なプログラムを示す図である。
【図２】本発明のプログラムの難読化による図１の関数Ｓの変換例を示す図である。
【図３】a,bの小さい方を返す関数Ｓの擬似的なプログラムを示す図である。
【図４】本発明のプログラムの難読化による図１の関数Ｓの変換例を示す図である。
【図５】本発明の実施例に係るプログラム難読化装置の構成を示すブロック図である。
【図６】ＣＰＵによる難読化処理の機能ブロック図である。
【図７】図７(ａ)はJavaのアセンブリ言語によるコード例、図７(ｂ)は図７（ａ）のソースコードにおける関数exampleと等価なコードである。
【図８】本発明の実施例による難読化処理部の動作フローである。
【図９】サブルーチン命令を含むコード例である。
【図１０】本発明の第１の実施例による第１の難読化処理手順パターンの動作フローを示す図である。
【図１１】図７に示すアセンブリ言語のコードから作成したCFGを示す図である。
【図１２】ｎ１とｎ２の支配関係を説明するためのCFGである。
【図１３】jsr命令による遷移に変換した後のCFGを示す図である。
【図１４】図１３のCFGのバイトコードである。
【図１５】図１１のD → B の遷移をjsr命令による遷移に書き換えたおきのCFGである。
【図１６】本発明の第１の実施例による第２の難読化処理手順パターンの動作フローを示す図である。
【図１７】図１４のバイトコードに施した後に生成されるバイトコードである。
【図１８】図１８（ａ）はｘ８６のアセンブリ言語によるコード例、図１８（ｂ）は図１８(ａ)のソースコードにおけるexample関数と等価なコードである。
【図１９】本発明の第２の実施例における図１８（ａ）のコードから作成したCFGである。
【図２０】call 命令による遷移に変換した後のCFGである。
【図２１】図１９のCFGと等価なｘ８６アセンブリコードである。
【図２２】本発明の第２の実施例による図２１の別の正しい変換例である。
【図２３】図２３（ａ）、（ｂ）、（ｃ）は、それぞれ、push, pop, call命令が実行された時のスタックの変化の概要を示す図である。
【符号の説明】
【００６１】
１０：プログラム難読化装置            ２０：ＣＰＵ
３０：入力部                          ３２：難読化処理部
３４：出力部                          ３６：難読化対象の指定
【図１】
2007304726.tif 000003
【図２】
2007304726.tif 000004
【図３】
2007304726.tif 000005
【図４】
2007304726.tif 000006
【図５】
2007304726.tif 000007
【図６】
2007304726.tif 000008
【図７】
2007304726.tif 000009
【図８】
2007304726.tif 000010
【図９】
2007304726.tif 000011
【図１０】
2007304726.tif 000012
【図１１】
2007304726.tif 000013
【図１２】
2007304726.tif 000014
【図１３】
2007304726.tif 000015
【図１４】
2007304726.tif 000016
【図１５】
2007304726.tif 000017
【図１６】
2007304726.tif 000018
【図１７】
2007304726.tif 000019
【図１８】
2007304726.tif 000020
【図１９】
2007304726.tif 000021
【図２０】
2007304726.tif 000022
【図２１】
2007304726.tif 000023
【図２２】
2007304726.tif 000024
【図２３】
2007304726.tif 000025