不具合解析するにもソースがないからリバースエンジニアリングして実施した。
といった話を最近聞く機会があり、過去にリバースエンジニアリング対策で難読化を実施したなと思ったのが本記事を書こうと思ったきっかけです。実施したのが結構前で覚えていない部分が多いので、実際に試しながら見ていきたいと思います。
難読化とは
そもそも難読化とは
ソフトウェアにおける難読化とは、コンピュータプログラムの動作を変えずに、プログラムコードの内部的なサブルーチンの内容・構造・データなどを、人間にとって読み取りにくくなるように改変・加工すること。
引用(https://ja.wikipedia.org/wiki/難読化_(ソフトウェア))
難読化する目的は悪用されないよう、読みにくくすることで解析しずらくすること。実際にどのようになるのか、試していきます。
試してみた(Java)
下記環境で実施(括弧内はバージョン)
・言語:Java(1.11)
・ビルド:gradle(6.9)
・難読化:Proguard(6.2.2)
ProguardはAndroidアプリで使用したことがあるため選択。ネットで検索したところほとんどAndroidの記事でヒットしたため、Android以外の環境でできるのかも含めて調査。公式サイトもAndroid向けに記載されており、探したところ下記サイトにある方法で実施できました。
実際に試していきます。下記のようなクラスがある場合。
public class NandokukaTest {
// 飲み会費用
public int nomihi = 5000; //単位(円)
// とある人の財布の中
private int toaruhitoNoSaifunoNaka = 1260; //単位(円)
public int plusMoney(int okodukai) {
return toaruhitoNoSaifunoNaka = toaruhitoNoSaifunoNaka + okodukai;
}
private int minusMoney(int pay) {
return toaruhitoNoSaifunoNaka = toaruhitoNoSaifunoNaka - pay;
}
}
コンパイルして作成されたクラスファイルを jad使用し逆コンパイル。
(jadの使用方法は下記参照)
public class NandokukaTest
{
public NandokukaTest()
{
nomihi = 5000;
toaruhitoNoSaifunoNaka = 1260;
}
public int plusMoney(int okodukai)
{
return toaruhitoNoSaifunoNaka = toaruhitoNoSaifunoNaka + okodukai;
}
private int minusMoney(int pay)
{
return toaruhitoNoSaifunoNaka = toaruhitoNoSaifunoNaka - pay;
}
public int nomihi;
private int toaruhitoNoSaifunoNaka;
}
一部記載に差異はありますが、ビルド前の内容を復元できていることが確認できました。
続いて難読化を実施してみるにあたり、proguardはオプションが多数あり、今回は下記のようにpublicクラスのpublicとつくものについては対象外で実施。
-keep public class * {
public * ;
}
難読化したjarファイルを解凍し、クラスファイルをjadで逆コンパイル。
public class NandokukaTest
{
public NandokukaTest()
{
nomihi = 5000;
a = 1260;
}
public int plusMoney(int i)
{
return a += i;
}
public int nomihi;
private int a;
}
privateの変数がリネームされ、privateメソッドが表示されません。また引数もリネームされています。念のためこのソースが動くのか、確認用に下記内容を追加。
//(中略)
public static void main(String[] args) {
new NandokukaTest();
}
public NandokukaTest() {
System.out.println(plusMoney(1000));
System.out.println(minusMoney(500));
}
//(中略)
ビルド後に難読化を実施し、下記コマンドで実行。
>java -jar NandokukaTest_proguard.jar
2260
1760
結果、privateメソッドも動作することを確認。実際に上記追加したソースでもjadで復元したところ、
// (中略)
System.out.println(a = (nandokukatest = this).a - 500);
// (中略)
メソッドの内容が直接記載されています。今回は簡素なソースなのでそれほど読めないわけではありませんが、同じメソッドを使用している箇所がすべて上記のようになることや、privateメソッドがprivateメソッドを読んでいる場合、上記のように変換されることを考えると・・・読みづらくなるのは間違いなさそうです。
そのほか(JavaScript)
ネットで探したところ、JavaScriptでは下記を発見。
使い方やどのようになるのか、上記サイトに詳しく記載されているので割愛。ちょっとした処理がすごく長くなるので、確かに読みづらい。
まとめ
難読化は読みづらくすることで悪用されるのを防ぐといった意味では有効な気がしますが、JavaScriptなど非コンパイル資産の場合、実装した人からみてもわかりづらくなるため、何かしらエラーが起きたときに解析するのは逆に困難にしてしまう気がします。また今回使用したProguardもリフレクションやpublicメソッドや変数など、リネーム不可のものは難読化できないため、すべてに対応できるわけではありません。実施するにもどこまでやるのか、メリット・デメリットを検討したうえでする必要があると考えます。
それにしても過去実施した際も大変だった記憶があるのですが、ビルドが通らないで再度ドはまりするとは思いませんでした。