とにかく、私の質問はこれです、なぜJavaアプリを逆コンパイルできるのですか?
あらゆる言語のあらゆるアプリを逆コンパイルできます。これは、コンパイルされたプログラミング言語でのプログラミング経験が少しある人には明らかなはずです。
コンパイラはバイトを受け取り、同じ命令セットを表す別のバイト セットを作成します。逆コンパイラはバイトを受け取り、同じ命令セットを表す別のバイト セットを作成します。違いは、それらのバイトが何であるかだけです。コンパイラは、(比較的) 人間が判読できるバイトを取り、(比較的) 機械で判読できるバイトを作成します。逆コンパイラはその逆です。
ただし、特定の逆コンパイラがその仕事をどれだけうまく実行できるかは、プログラミング言語と逆コンパイラ自体の実装によって異なります。
C などの言語の場合、コンパイラは CPU 用の機械語命令を生成します。アセンブラー命令は機械語命令にかなり密接に 1:1 でマッピングされるため、これらはアセンブリ言語に容易に逆コンパイルできます。十分に洗練された逆コンパイラは、C を出力として出力できますが、おそらく C を書くのは自然なことではありません。さらに重要なことに、逆コンパイルされた出力は、コンパイルされた C コードにデバッグ シンボルが含まれていない限り、逆コンパイラが生成した関数と変数の名前を大部分使用します。
Java のような言語は、概念が大きく異なるわけではありません。Java または Dalvik バイトコードは CPU ではなく VM 用ですが、基本的なアプローチは同じです。難読化は、逆コンパイルされた結果の可読性を低下させるために、結果のバイトコードに最小限の数の人間が判読できるシンボルが存在することを保証するのに役立ちます。また、VM バイトコードから言語ステートメントへのマッピングは、マシン コードから C ステートメントへのマッピングよりもはるかに近い傾向があるため、Java 構文 (smali/baksmali など) に近い逆コンパイラを簡単に作成できます。
たとえば、NDK を介してライセンス管理ロジックをネイティブ コードに移動することが推奨されるのは、逆コンパイルされたマシン コードを解釈する際の難易度です。ただし、C コンパイラの結果がまったく逆コンパイルできないというわけではありません。