Yabu.log

ITなどの雑記

逆アセンブラと逆コンパイルの違い

こんな恥を晒したので

こんな記事を書いてみました

qiita.com

javaを例にとって両者の違いを説明します

アセンブル(javap)

バイトコードニーモニック(アセブリ言語)に変換する Javaならjavapで.classファイルからJVMニーモニックを表示する

dev.classmethod.jp

これが

$ 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分あたり)

コードとアセンブラ言語の対応を整理するのって慣れてても大変だと思う。*2 C言語の自作コンパイラ作っている人たちってすごいな・・・

そういえば、昔COBOLJavaのコードに手作業で変換するという仕事をやっていましたが、 丁寧にJavaのコードに対応するCOBOLのコードをコメントとして一行一行追加した辛い思い出がフラッシュバックしてきました。

参考

https://reverseengineering.stackexchange.com/questions/4635/whats-the-difference-between-a-disassembler-debugger-and-decompiler

*1:ただ単にcatすれば読めるものを16進数でダンプした訳ではないことに注意。.classファイルはcatで出しても一部読めるがほとんど意味のない文字列になります

*2:javapで出てくる奴はcpuのアセンブラとかとはまた違う質のものだけど