逆アセンブラと逆コンパイルの違い
こんな恥を晒したので
逆コンパイルと逆アセンブラは違うとの指摘を頂いた。javapしかしらなかったけどjd?jda?とかいう逆コンパイツールもあるらしい。
— yuyabu (@yuyabu2) 2018年3月17日
こんな記事を書いてみました
javaを例にとって両者の違いを説明します
逆アセンブル(javap)
バイトコードをニーモニック(アセブリ言語)に変換する Javaならjavapで.classファイルからJVMのニーモニックを表示する
これが
$ od -Ad -tx HelloKt.class 0000000 bebafeca 32000000 00013300 6c654807 0000016 744b6f6c 01010007 616a1000 6c2f6176 0000032 2f676e61 656a624f 00077463 04000103 0000048 6e69616d 28160001 616a4c5b 6c2f6176 0000064 2f676e61 69727453 293b676e 23000156 0000080 67726f4c 74656a2f 69617262 612f736e 0000096 746f6e6e 6f697461 4e2f736e 754e746f 0000112 013b6c6c 72610400 00087367 1e000108 0000128 6c746f6b 6a2f6e69 692f6d76 7265746e 0000144 2f6c616e 72746e49 69736e69 00077363 0000160 1700010a 63656863 7261506b 74656d61 0000176 73497265 4e746f4e 016c6c75 4c282700 0000192 6176616a 6e616c2f 624f2f67 7463656a 0000208 616a4c3b 6c2f6176 2f676e61 69727453 0000224 293b676e 0c000c56 000a0d00 010e000b 0000240 65480d00 2c6f6c6c 726f5720 0821646c 0000256 00011000 76616a10 616c2f61 532f676e 0000272 65747379 1200076d 6f030001 00017475 0000288 616a4c15 692f6176 72502f6f 53746e69 0000304 61657274 000c3b6d 09150014 16001300 0000320 6a130001 2f617661 502f6f69 746e6972 0000336 65727453 00076d61 07000118 6e697270 0000352 016e6c74 4c281500 6176616a 6e616c2f 0000368 624f2f67 7463656a 0c56293b 1b001a00 0000384 0019000a 1300011c 616a4c5b 6c2f6176 0000400 2f676e61 69727453 013b676e 6b4c1100 0000416 696c746f 654d2f6e 61646174 013b6174 0000432 766d0200 00000003 00000301 00010900 0000448 03766202 00000000 00000003 01000102 0000464 0200016b 00013164 1280c035 0a80c00a 0000480 0a021002 020a80c0 020a1110 c00a0e10 0000496 10191a80 021a80c0 0c320130 081a0210 0000512 02120412 03300430 0206a2c2 00010510 0000528 01326402 00010000 6c656808 6b2e6f6c 0000544 04000174 65646f43 4c120001 6c61636f 0000560 69726156 656c6261 6c626154 0f000165 0000576 656e694c 626d754e 61547265 01656c62 0000592 75522400 6d69746e 766e4965 62697369 0000608 6150656c 656d6172 41726574 746f6e6e 0000624 6f697461 0001736e 756f530a 46656372 0000640 01656c69 6f531400 65637275 75626544 0000656 74784567 69736e65 00016e6f 6e755219 0000672 656d6974 69736956 41656c62 746f6e6e 0000688 6f697461 3100736e 04000200 00000000 0000704 19000100 06000500 2c000200 3f000000 0000720 02000200 11000000 b809122a 11120f00 0000736 1700b24c 1d00b62b 000000b1 002d0002 0000752 000c0000 00000001 00080011 0000001e 0000768 0000002e 0002000a 00020006 00030010 0000784 0000002f 01000107 00000700 30000300 0000800 02000000 31002b00 45000000 50414d53 0000816 6c65680a 6b2e6f6c 6f4b0a74 6e696c74 0000832 20532a0a 6c746f4b 2a0a6e69 202b0a46 0000848 65682031 2e6f6c6c 480a746b 6f6c6c65 0000864 2a0a744b 23310a4c 3a342c31 452a0a31 0000880 0032000a 00460000 001f0001 5b200005 0000896 00490300 21004921 00220049 03005b23 0000912 49210049 00492400 49260025 27002500 0000928 7301005b 29002800 7306005b 00730500 0000944 0800732a 732a0073 00732a00 00000006 0000957
こうなる*1
$ javap -c HelloKt.class Compiled from "hello.kt" public final class HelloKt { public static final void main(java.lang.String[]); Code: 0: aload_0 1: ldc #9 // String args 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: ldc #17 // String Hello, World! 8: astore_1 9: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; 12: aload_1 13: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 16: return }
逆コンパイル(jd/jad)
バイトコードをソースコード(java)に変換する jadの場合、-aオプションでニーモニックをコメントとして表示できるのが便利だと思う。
上記の16進の内容がこうなる。
import java.io.PrintStream; import kotlin.jvm.internal.Intrinsics; public final class HelloKt { public static final void main(String args[]) { Intrinsics.checkParameterIsNotNull(args, "args"); // 0 0:aload_0 // 1 1:ldc1 #9 <String "args"> // 2 3:invokestatic #15 <Method void Intrinsics.checkParameterIsNotNull(Object, String)> String s = "Hello, World!"; // 3 6:ldc1 #17 <String "Hello, World!"> // 4 8:astore_1 System.out.println(s); // 5 9:getstatic #23 <Field PrintStream System.out> // 6 12:aload_1 // 7 13:invokevirtual #29 <Method void PrintStream.println(Object)> // 8 16:return } }
Turing Complete FM第10回でコンパイルするときにソースコードのコメントを出力できると便利、みたいな話が出たのを思い出しました。(49分あたり)
#tcfm 第10回公開しました。@kw_udon_ くんとCコンパイラ自作話やコンピュータサイエンスの話をしてます。Cコンパイラをスクラッチから書いたことのある2人が話するのはかなり珍しいポッドキャストなのでは。 https://t.co/VhMGEpvy2e
— Rui Ueyama (@rui314) 2018年3月18日
コードとアセンブラ言語の対応を整理するのって慣れてても大変だと思う。*2 C言語の自作コンパイラ作っている人たちってすごいな・・・
そういえば、昔COBOLをJavaのコードに手作業で変換するという仕事をやっていましたが、 丁寧にJavaのコードに対応するCOBOLのコードをコメントとして一行一行追加した辛い思い出がフラッシュバックしてきました。