Molybdenum

あれこれ記録がてらに書いていきます。

Javaのクラスファイルをバイナリで読んでみたい

やりたいこと

Javaのクラスファイル読解入門として、バイナリから出力内容の場所を特定する。

環境

端末: MacBook Pro (Retina, 13-inch, Mid 2014) OS: macOS 10.14 Mojave

本題

基本的なHello Worldのコードは次の通りです。

public class HelloWorld {
  public static void main(String[] args){
    System.out.println("Hello World!");
  }
}

実際にコンパイルして実行すると、言うまでもないですが次のとおりになります。

kmori@edelweiss ~/Desktop/javahex $ javac HelloWorld.java
kmori@edelweiss ~/Desktop/javahex $ java HelloWorld
Hello World!

さてここからが本題。Macでバイナリを読むには「hexdump」が便利です。
早速clasファイルを読んでみましょう。

kmori@edelweiss ~/Desktop/javahex $ hexdump -C HelloWorld.class

出力結果(見やすくするためにわざとスペースを追加しております)

00000000    ca fe ba be 00 00 00 34 00 1d 0a 00 06 00 0f 09    |…….4……..|
00000010    00 10 00 11 08 00 12 0a 00 13 00 14 07 00 15 07    |…………….|
00000020    00 16 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29    |…..<init>…()|
00000030    56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e    |V…Code…LineN|
00000040    75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69    |umberTable…mai|
00000050    6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67    |n…([Ljava/lang|
00000060    2f 53 74 72 69 6e 67 3b 29 56 01 00 0a 53 6f 75    |/String;)V…Sou|
00000070    72 63 65 46 69 6c 65 01 00 0f 48 65 6c 6c 6f 57    |rceFile…HelloW|
00000080    6f 72 6c 64 2e 6a 61 76 61 0c 00 07 00 08 07 00    |orld.java…….|
00000090    17 0c 00 18 00 19 01 00 0c 48 65 6c 6c 6f 20 57    |………Hello W|
000000a0    6f 72 6c 64 21 07 00 1a 0c 00 1b 00 1c 01 00 0a    |orld!………..|
000000b0    48 65 6c 6c 6f 57 6f 72 6c 64 01 00 10 6a 61 76    |HelloWorld…jav|
000000c0    61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 01 00 10    |a/lang/Object…|
000000d0    6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d    |java/lang/System|
000000e0    01 00 03 6f 75 74 01 00 15 4c 6a 61 76 61 2f 69    |…out…Ljava/i|
000000f0    6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b 01 00    |o/PrintStream;..|
00000100    13 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74    |.java/io/PrintSt|
00000110    72 65 61 6d 01 00 07 70 72 69 6e 74 6c 6e 01 00    |ream…println..|
00000120    15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72    |.(Ljava/lang/Str|
00000130    69 6e 67 3b 29 56 00 21 00 05 00 06 00 00 00 00    |ing;)V.!……..|
00000140    00 02 00 01 00 07 00 08 00 01 00 09 00 00 00 1d    |…………….|
00000150    00 01 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00    |……..*…….|
00000160    01 00 0a 00 00 00 06 00 01 00 00 00 01 00 09 00    |…………….|
00000170    0b 00 0c 00 01 00 09 00 00 00 25 00 02 00 01 00    |……….%…..|
00000180    00 00 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 01    |…………….|
00000190    00 0a 00 00 00 0a 00 02 00 00 00 03 00 08 00 04    |…………….|
000001a0    00 01 00 0d 00 00 00 02 00 0e                      |……….|
000001aa

目的の文字列は00000090のところにありそうですね。
JavaのClassFileは以下の構造になっており、上記のバイナリに1対1で対応する模様です。

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

uxのxはバイト数。つまり、 u4 magic;は4バイトのマジックナンバーであり、上記のバイナリならばca fe ba beがそれを意味します。
出力内容だと、可変長であるためそこからあたりをつけます。 今回出力されている文字列はcp_info付近にありそう。ってことで更に見てみます。

cp_info {
    u1 tag;
    u1 info[];
}

tagがなんの変数なのかを示すものであり、その後のinfoにて情報が記述されています。cp_infoには00 1dより29-1=28個ある模様。

Constant Type Value
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

つまり、Stringの部分は、valueが8のところを探して、下記に照らし合わせたindexを探せばよさそう。

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}

そんな具合でたどっていけば見つかる・・・はずだと思います。

次回はもっと細かく見ていきたいと思います。