M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

ProGuardマニュアル(6) 難読化オプション

原文は、ProGuard Manual”(Eric Lafortune)です(2011年3月29日取得)。

イントロダクションと索引はこちら

*****************************

難読化オプション

-dontobfuscate

入力クラスファイルを難読化しないよう指定します。デフォルトでは、難読化が実施されます。難読化により、種々の-keepオプションのリストに含まれていないクラスとクラス・メンバーは、新しい短くて無作為な名前に変更されます。ソースコードファイル名やローカル変数名、そして行数などのデバッグに便利な属性も削除されます。

-printmapping [filename]

名前変更の対象となったクラスとクラス・メンバーの新旧の名前の対照表を出力するよう、指定します。この対照表データは標準出力もしくはfilenameとして与えられたファイルに対して出力されます。例えば、後々の漸進的難読化(incremental obfuscation*1)のため、あるいは処理済みのコードのスタックトレース情報を理解するため、といった目的が考えられます。このオプションは難読化処理に対してのみ有効です。

-applymapping filename

以前の難読化処理実施時に生成された名前対照表情報を再利用するよう指定します。(訳者:すでに)対照表に名前の挙げられているクラスとクラス・メンバーは、対照表にしたがって名前変更されます。それ以外のクラスとクラス・メンバーは、新しい名前に変更されます(訳者:ふつうに難読化処理の対象になります)。対照表は、入力クラスをライブラリ・クラスと同じように参照します。このオプションは、──アドオンや小さなパッチを既存のコード断片に適用するような──漸進的難読化処理に便利です。このようなケースでは、-useuniqueclassmembernamesオプションもまた必要になるかどうか、考慮すべきです。読み込める対照表は1つだけです。このオプションは難読化処理に対してのみ有効です。

-obfuscationdictionary filename

難読化対象となったフィールドとメソッドの名前として使用される妥当なワードからなるテキストファイルを指定します。デフォルトでは、難読化によって割り振られる名前は“a”とか“b”とかの形式になります。(訳者:これに対して)難読化処理用の辞書を指定することにより、例えば、あらかじめ決めておいたキーワードのリストや、外国語の文字を識別子として指定することができます。空白文字、句読点、重複したワード、そして#文字につづくコメントの記述は無視されます。辞書の使用が難読化処理をほとんど改善しない点には注意してください。親切なコンパイラのいくつかは、それら(訳者:メソッド名やフィールド名)を自動的に置き換え、適切かつ簡潔に、再度の難読化を必要なくしています。もっとも便利な方法は、典型的にはすでにクラスファイル内に存在する文字列(例えば“Code”のような)を指定することです。こうすることでクラスファイルのサイズをより小さなものにできます。このオプションは難読化処理に対してのみ有効です。

-classobfuscationdictionary filename

難読化対象となったクラスの名前として使用される妥当なワードからなるテキストファイルを指定します。この難読化処理用辞書は-obfuscationdictionaryオプションのそれと似通ったものです。このオプションは難読化処理に対してのみ有効です。

-packageobfuscationdictionary filename

難読化対象となったパッケージの名前として使用される妥当なワードからなるテキストファイルを指定します。この難読化処理用辞書は-obfuscationdictionaryオプションのそれと似通ったものです。このオプションは難読化処理に対してのみ有効です。

-overloadaggressively

難読化の過程で、積極的な多重定義を実施するよう指定します。複数のフィールドとメソッドを──(引数の型がちがう場合はもちろんのこと)それらの引数と返値の型が異なる限り(訳者:つまりシグネチャが異なる限り)──同じ名前にしてしまいます。

使用すべきでないケース: 処理結果のクラスファイルは、Javaバイトコード仕様に準拠したものであるにもかかわらず(Java仮想マシン仕様、第2版、節4.5と節4.6の最初の段落を参照)、この種の多重定義は、Java言語としては許されていません(Java言語仕様、第2版、第8章3節および同4節7項を参照)。いくつかのツールではこのことが問題になります。
顕著なケース:

  • サンマイクロシステムズのJDK 1.2.2に含まれるjavacコンパイラは、上述の処理を行ったライブラリをともなうコンパイルを行う際、例外を発生させます(バグ #4216736 を参照)。ライブラリの処理に際してはこのオプションはおそらく使用すべきではありません。
  • サンマイクロシステムズのJRE 1.4とそれ以降では、多重定義された基本型フィールドを持つオブジェクトの直列化に失敗します。
  • サンマイクロシステムズのJRE 1.5のpack200ツールは、報告によれば、多重定義されたクラス・メンバーに関して問題を抱えているということです。
  • GoogleのDalvik VMは、多重定義されたstaticなフィールドを処理できません。
-useuniqueclassmembernames

同じ名前を持つクラス・メンバーには同じ難読化された名前を、そして異なる名前を持つクラス・メンバーには(クラス・メンバーのシグネチャごとに)異なる難読化された名前を割り当てるよう指定します。このオプションが指定されなかった場合、より多くのクラス・メンバーが、同じ短い名前──“a”や“b”といった──に割り付けられます。したがってこのオプションを使用することで、処理結果のコードのサイズは若干増加しますが、難読化された名前は、後続の漸進的難読化のステップでかならず順守することができることが保障されます。

例えば、同じ名前とシグネチャのメソッドを持つ、2つの異なるインターフェースについて考えてみましょう。このオプションが指定されなかったとき、これらのメソッドは最初の難読化のステップで2つの異なる名前を与えられるでしょう。このとき、もし仮に双方のインターフェースを実装したクラスを含むパッチがアプリケーションに追加されたとき、漸進的難読化のステップ内で、ProGuardは2つのメソッドに同じ名前を強制しなくてはならなくなります。処理結果のコードの整合性を保つため、元の難読化されたコードは変更されます。最初の難読化ステップでこのオプションが使用されていれば、このような名前変更は不要になります。

このオプションは難読化処理のときのみ有効です。実際、もし漸進的難読化を試みようとしているのであれば、ダウンサイジングや最適化処理の一切を回避したいと思われるかもしれません。これらのステップで、漸進的難読化で追加しようとしているコードからすれば必要不可欠なコードが削除や変更の対象となりかねないからです。

-dontusemixedcaseclassnames

難読化処理中に大文字小文字を混ぜた名前を生成しないように指定します。デフォルトでは、難読化されたクラス名は、大文字と小文字を含んでいます。完全に仕様に準拠した、実行可能なjarファイルが生成されます。しかし例えばこのjarファイルが大文字小文字を区別しないファイルシステム(例えば、Windowsです)を採用するプラットフォーム上で解凍されたとき、解凍ツールは似た名前(訳者:大文字小文字を区別しないと同じ字面になってしまう名前)のファイルで別のファイルを上書きしてしまうでしょう。解凍された段階で、コードは自動消滅してしまうのです。実際にWindows上で解凍する必要があるのであれば、このオプションによって、ProGuardの挙動を切り替えることができます。このとき結果的に、難読化したjarファイルのサイズが肥大してしまうことに注意してください。このオプションは難読化処理のときのみ有効です。

-keeppackagenames [package_filter]

package_filterとして与えられたパッケージ名を難読化しないように指定します。オプションのフィルターはカンマ区切りのパッケージ名記述です。パッケージ名記述はワイルドカードとして?、*、**、そして否定素子として!を含むことができます。このオプションは難読化処理のときのみ有効です。

-flattenpackagehierarchy [package_name]

名前変更されたすべてのパッケージをpackage_nameで指定された単一のパッケージ中に移動し、再パッケージするよう指定します。引数を指定しないか、もしくは空文字列(“”)を指定した場合、これらのパッケージはrootパッケージに移動されます。このオプションはパッケージ名の難読化のより進んだ一例です。これにより処理済みコードはより小さく、より理解しにくくなります。このオプションは難読化処理のときのみ有効です。

-repackageclasses [package_name]

名前変更されたすべてのクラスファイルをpackage_nameで指定された単一のパッケージ内に移動し、再パッケージするように指定します。引数を指定しないか、もしくは空文字列(“”)を指定した場合、パッケージは完全に削除されます(訳者:これらのクラスからパッケージ情報は取り除かれます)。このオプションは-flattenpackagehierarchy オプションを上書きします。このオプションはパッケージ名の難読化のより進んだ別の例です。処理済みコードはより小さく、より理解しにくくなります。-defaultpackageは廃止予定です。このオプションは難読化処理のときのみ有効です。

使用すべきでないケース: パッケージ・ディレクトリに格納されたリソースファイルを使用するクラスは、もしそれらが別の場所に移動させられてしまった場合ニは、もはや正常に動作しないでしょう。不確かなところがあるのであれば、このオプションは使用せず、パッケージ構成には手を触れずにおくべきです。

-keepattributes [attribute_filter]

attribute_filterとして指定された属性をすべて保全するように指定します。保全すべき属性は、1つもしくはそれ以上の-keepattributesディレクティブを使用して指定することができます。フィルタには、属性名記述はワイルドカードとして?、*、**、そして否定素子として!を含むことができます。オプションとして典型的な指定される属性としては、Exceptions、Signature、Deprecated、SourceFile、SourceDir、LineNumberTable、LocalVariableTable、LocalVariableTypeTable、Synthetic、 EnclosingMethod、 RuntimeVisibleAnnotations、 RuntimeInvisibleAnnotations、 RuntimeVisibleParameterAnnotations、 RuntimeInvisibleParameterAnnotations、そしてAnnotationDefaultがあります。属性名InnerClassesの指定すれば、この属性のソース名部分の参照がうまくいきます。例えば、ライブラリを処理する場合、少なくともExceptionsInnerClassesSignature属性の保全が必要になるでしょう。あるいはまた、(訳者:クラスやクラス・メンバーが難読化されてもなお)使いものになるスタックトレースを生成させるには、SourceFileLineNumberTable属性を保全する必要があるはずです。そしてもしあなたのコードがアノテーションに依存している場合、アノテーションに関する属性の保全も必要になるでしょう。このオプションは難読化処理のときのみ有効です。

-keepparameternames

メソッドの引数名と型を保全するように指定します。このオプションは実際には、デバッグ用属性LocalVariableTableLocalVariableTypeTableの不要な部分を取り除いたものを保全します。これはライブラリの処理には便利です。いくつかの統合開発環境は、──例えばツールチップや自動補完などの機能によって──ライブラリを使用する開発者を補助するために、これらの情報を使用することができます。このオプションは難読化処理のときのみ有効です。

-renamesourcefileattribute [string]

クラスファイルのSourceFile属性(そしてSourceDir属性)に登録する文字列を指定します。この属性は事前に存在している必要がある点に注意してください。つまり明示的に-keepattributesオプションを使用する必要があるのです。例えば、処理済みライブラリとアプリケーションに、使いものになるスタックトレース情報を生成させたいということがあります。このオプションは難読化処理のときのみ有効です。

-adaptclassstrings [class_filter]

クラス名同様それらに対応する文字列定数もまた難読化されるよう指定します。フィルターを指定しないと、クラス名に対応するすべての文字列定数が難読化の対象になります。フィルターを指定した場合、フィルターにマッチしたクラスの名称に対応する文字列定数のみが、難読化されたクラス名にあわせて変更されます。例えば、あなたのコードがクラスを参照するために多数のハードコードされた文字列を含んでいる場合、そしてあなたがそれらの名前が保全されることを好ましく感じない場合、このオプションを使用することになるでしょう。このオプションはまず何よりも難読化処理のときに有効です。その上、指定されたクラスはダウンサイジング処理の際にも自動的に保護対象となります。

-adaptresourcefilenames [file_filter]

もし対応するクラスファイルがあれば、そのファイルの難読化された名前に基づき、リソースファイルも名前変更するよう指定します。フィルターを指定しない場合、対応するクラスファイルを持つすべてのリソースファイルは名前変更されます。フィルターを指定した場合は、マッチしたリソースファイルのみが名前変更されます。リソースファイルの難読化の項に、例が示されています。このオプションは難読化処理のときのみ有効です。

-adaptresourcefilecontents [file_filter]

そのコンテンツが更新されるべきリソースファイルを指定します。リソースファイル内に記述されたクラス名は、もし対応するクラスファイルがあれば、そのファイルの難読化された名前に基づき、更新されます。フィルターを指定しない場合、すべてのリソースファイルの内容が更新されます。フィルターを指定した場合、マッチしたリソースファイルのみが更新対象になります。リソースファイルはプラットフォームのデフォルトキャラクターセットを用いて読み込まれ、書き込まれます。このデフォルトキャラクターセットは、環境変数LANGもしくはJavaシステムプロパティファイル.encodingによって変更することが出来ます。リソースファイルの難読化の項に、例が示されています。このオプションは難読化処理のときのみ有効です。

******************************

ProGuardマニュアル(7)につづく。

*1:訳者:すでに難読化されているアプリケーションに対して、コードを追加してコンパイルすることを指すようです。