java_dissassembler 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86d641d10b9d79aef55fda226790ce55102bd03d
4
+ data.tar.gz: b9c1df1dccff8a01540592bb0a556b65d855a61f
5
+ SHA512:
6
+ metadata.gz: f8dd23e5f96f46ca8547f13bef5971d467c8a9e4e2dba9c96111577dbf793cdea470d5a208c393a036ab3ea492d5b147332fea0a48152685cf57b76dcf79b2cf
7
+ data.tar.gz: e851eb92bdc1ca3ffab7fce5f95554a1f65b6d45b043b2f2c78cc3085c12b6ba3a6a89ba44dbd79b664ea45af4e84c95f7af8bca5172a58626d92d70e7ca5b56
@@ -0,0 +1,17 @@
1
+ module Java
2
+ class Annotatable
3
+ attr_reader :annotations
4
+ def initialize(annotations)
5
+ @annotations = annotations
6
+ end
7
+
8
+ def has_annotation?(annotation_name)
9
+ @annotations.any? { |hash| hash.keys.any? { |key| key == annotation_name } }
10
+ end
11
+
12
+ def get_annotation(annotation_name)
13
+ hash = @annotations.find { |a| a.keys.include?(annotation_name) }
14
+ hash[annotation_name] unless hash.nil?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ class Array
2
+ def u2
3
+ self.pack("C*").unpack("s>").first
4
+ end
5
+
6
+ def u4
7
+ self.pack("C*").unpack("N").first
8
+ end
9
+
10
+ def modified_utf8_to_s
11
+ new_str = ""
12
+ bytes = self
13
+ until bytes.empty?
14
+ # Code points in the range '\u0001' to '\u007F' are represented by a single byte
15
+ if (bytes[0] >> 7) == 0
16
+ new_str << bytes[0]
17
+ bytes.shift
18
+ # The null code point ('\u0000') and code points in the range '\u0080' to '\u07FF' are represented by a pair of bytes x and y
19
+ elsif (bytes[0] >> 5) == 0b110
20
+ new_str << [((bytes[0] & 0x1f) << 6) + (bytes[1] & 0x3f)].pack("U")
21
+ bytes.shift(2)
22
+ # Code points in the range '\u0800' to '\uFFFF' are represented by 3 bytes
23
+ elsif ((bytes[0] >> 4) == 0b1110) && (bytes[0] != 0b11101101)
24
+ new_str << [((bytes[0] & 0xf) << 12) + ((bytes[1] & 0x3f) << 6) + (bytes[2] & 0x3f)].pack("U")
25
+ bytes.shift(3)
26
+ # Characters with code points above U+FFFF (so-called supplementary characters) are represented by separately encoding the
27
+ # two surrogate code units of their UTF-16 representation
28
+ elsif (bytes[0] == 0b11101101) && (bytes[3] == 0b11101101)
29
+ new_str << [0x10000 + ((bytes[1] & 0x0f) << 16) + ((bytes[2] & 0x3f) << 10) + ((bytes[4] & 0x0f) << 6) + (bytes[5] & 0x3f)].pack("U")
30
+ bytes.shift(6)
31
+ else
32
+ raise "Invalid \"Modified\" UTF-8 byte `#{bytes[0]}'"
33
+ end
34
+ end
35
+ new_str
36
+ end
37
+ end
@@ -0,0 +1,71 @@
1
+ module Java
2
+ class Class < Annotatable
3
+ attr_reader :flags, :this_klass, :super_klass, :interfaces, :fields, :methods
4
+
5
+ ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
6
+ ACC_FINAL = 0x0010 # Declared final; no subclasses allowed.
7
+ ACC_SUPER = 0x0020 # Treat superclass methods specially when invoked by the invokespecial instruction.
8
+ ACC_INTERFACE = 0x0200 # Is an interface, not a class.
9
+ ACC_ABSTRACT = 0x0400 # Declared abstract; must not be instantiated.
10
+ ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
11
+ ACC_ANNOTATION = 0x2000 # Declared as an annotation type.
12
+ ACC_ENUM = 0x4000 # Declared as an enum type.
13
+
14
+ def initialize(major, minor, flags, this_klass, super_klass, interfaces, fields, methods, annotations)
15
+ super(annotations)
16
+ @major = major
17
+ @minor = minor
18
+ @flags = flags
19
+ @this_klass = this_klass
20
+ @super_klass = super_klass
21
+ @interfaces = interfaces
22
+ @fields = fields
23
+ @methods = methods
24
+ end
25
+
26
+ def is_public?
27
+ (@flags & ACC_PUBLIC) != 0
28
+ end
29
+
30
+ def is_final?
31
+ (@flags & ACC_FINAL) != 0
32
+ end
33
+
34
+ def is_super?
35
+ (@flags & ACC_SUPER) != 0
36
+ end
37
+
38
+ def is_interface?
39
+ (@flags & ACC_INTERFACE) != 0
40
+ end
41
+
42
+ def is_abstract?
43
+ (@flags & ACC_ABSTRACT) != 0
44
+ end
45
+
46
+ def is_synthetic?
47
+ (@flags & ACC_SYNTHETIC) != 0
48
+ end
49
+
50
+ def is_annotation?
51
+ (@flags & ACC_ANNOTATION) != 0
52
+ end
53
+
54
+ def is_enum?
55
+ (@flags & ACC_ENUM) != 0
56
+ end
57
+
58
+ def java_version
59
+ case @major
60
+ when 46 then "1.2"
61
+ when 47 then "1.3"
62
+ when 48 then "1.4"
63
+ when 49 then "5"
64
+ when 50 then "6"
65
+ when 51 then "7"
66
+ when 52 then "8"
67
+ when 53 then "9"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,193 @@
1
+ module Java
2
+ class Method
3
+
4
+ # JNI Call Java from C/C++
5
+ # =========================================
6
+ def jni_c_parameters(prefix = "param")
7
+ cparams = Array.new
8
+ java_params.each_with_index do |param, i|
9
+ cparams << "#{java_to_c(param)} #{prefix}#{i}"
10
+ end
11
+ cparams
12
+ end
13
+
14
+ def jni_c_return_type
15
+ java_to_c(java_return_type)
16
+ end
17
+
18
+ def jni_return_type
19
+ return "" if java_return_type == "void"
20
+ java_to_jni(java_return_type)
21
+ end
22
+
23
+ def jni_convert_c_to_jni(prefix = "param")
24
+ conversions = Array.new
25
+ java_params.each_with_index do |param, i|
26
+ case param
27
+ when "java.lang.String"
28
+ conversions << "jstring jni#{prefix}#{i} = env->NewStringUTF(#{prefix}#{i})"
29
+ end
30
+ end
31
+ conversions
32
+ end
33
+
34
+ def jni_method_call
35
+ "Call#{is_static? ? "Static" : ""}#{java_return_type.capitalize}Method"
36
+ end
37
+
38
+ def jni_method_call_parameters(prefix = "param")
39
+ java_params.enum_for(:each_with_index).map do |param, i|
40
+ case param
41
+ when "java.lang.String" then "jni#{prefix}#{i}"
42
+ else "#{prefix}#{i}"
43
+ end
44
+ end
45
+ end
46
+
47
+ # JNI Call C from Java
48
+ # =========================================
49
+ def jni_mangled_name(klass_name, is_override)
50
+ mangled_name = mangle(name)
51
+ mangled_name << "__#{mangle(vm_signature_params)}" if is_override and not vm_signature_params.empty?
52
+ "Java_#{klass_name.gsub(".", "_")}_#{mangled_name}"
53
+ end
54
+
55
+ def jni_native_method_parameters(prefix = "param")
56
+ native_method_parameters = ["JNIEnv* env"]
57
+ native_method_parameters << (is_static? ? "jobject caller" : "jclass klass")
58
+ native_method_parameters + java_params.enum_for(:each_with_index).map { |param,i| "#{java_to_jni(param)} #{prefix}#{i}" }
59
+ end
60
+
61
+ def jni_convert_jni_to_c(prefix = "param")
62
+ conversions = Array.new
63
+ java_params.each_with_index do |param, i|
64
+ case param
65
+ when "java.lang.String"
66
+ conversions << "const char* c#{prefix}#{i} = env->GetStringUTFChars(#{prefix}#{i}, nullptr /* iscopy */)"
67
+ end
68
+ end
69
+ conversions
70
+ end
71
+
72
+ def jni_native_method_converted_parameters(prefix = "param")
73
+ conversions = Array.new
74
+ java_params.each_with_index do |param, i|
75
+ case param
76
+ when "java.lang.String"
77
+ conversions << "c#{prefix}#{i}"
78
+ else
79
+ conversions << "#{prefix}#{i}"
80
+ end
81
+ end
82
+ conversions
83
+ end
84
+
85
+ def jni_release_converted_to_c(prefix = "param")
86
+ releases = Array.new
87
+ java_params.each_with_index do |param, i|
88
+ case param
89
+ when "java.lang.String"
90
+ releases << "env->ReleaseStringUTFChars(#{prefix}#{i}, c#{prefix}#{i})"
91
+ end
92
+ end
93
+ releases
94
+ end
95
+
96
+ def vm_signature_params
97
+ vm_signature.match(/^\(([^\)]*)\)/)[1]
98
+ end
99
+
100
+ def java_params
101
+ java_signature.drop(1)
102
+ end
103
+
104
+ def java_return_type
105
+ java_signature.at(0)
106
+ end
107
+
108
+ def jni_params
109
+ java_params.map { |java_type| java_to_jni(java_type) }
110
+ end
111
+
112
+ def jni_return_value
113
+ java_to_jni(java_return_type)
114
+ end
115
+
116
+ #private
117
+
118
+ def java_signature
119
+ types = vm_signature.scan(/(\[?([ZBCSIJFDV]|L[^;]*;))/).map do |match|
120
+ is_array = match[0].start_with? "["
121
+ val = vm_to_java(match[0])
122
+ if is_array
123
+ val << "[]"
124
+ val[1..-1]
125
+ else
126
+ val
127
+ end
128
+ end
129
+ back = types.pop
130
+ types.unshift(back)
131
+ end
132
+
133
+ def vm_to_java(vm_type)
134
+ case vm_type
135
+ when "Z" then "boolean"
136
+ when "B" then "byte"
137
+ when "C" then "char"
138
+ when "S" then "short"
139
+ when "I" then "int"
140
+ when "J" then "long"
141
+ when "F" then "float"
142
+ when "D" then "double"
143
+ when "V" then "void"
144
+ else
145
+ vm_type.gsub("L", "").gsub(";", "").gsub("/", ".")
146
+ end
147
+ end
148
+
149
+ def java_to_c(java_type)
150
+ case java_type
151
+ when "boolean" then "bool"
152
+ when "byte" then "byte"
153
+ when "char" then "char"
154
+ when "short" then "short"
155
+ when "int" then "int"
156
+ when "long" then "long"
157
+ when "float" then "float"
158
+ when "double" then "double"
159
+ when "java.lang.String" then "const char*"
160
+ when "void" then "void"
161
+ else "jobject" end
162
+ end
163
+
164
+ def java_to_jni(java_type)
165
+ case java_type
166
+ when "boolean" then "jboolean"
167
+ when "byte" then "jbyte"
168
+ when "char" then "jchar"
169
+ when "short" then "jshort"
170
+ when "int" then "jint"
171
+ when "long" then "jlong"
172
+ when "float" then "jfloat"
173
+ when "double" then "jdouble"
174
+ when "java.lang.String" then "jstring"
175
+ when "void" then raise "Cannot convert `void' to JNI type"
176
+ else "jobject" end
177
+ end
178
+
179
+ # See https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html Table 2-1)
180
+ def mangle(str)
181
+ mangled_name = str.gsub("_", "_1")
182
+ .gsub(";", "_2")
183
+ .gsub("[", "_3")
184
+ .gsub("/", "_")
185
+ mangled_name.codepoints.map do |codepoint|
186
+ case
187
+ when codepoint <= 0xFF then codepoint.chr
188
+ else "_0#{"%04x" % codepoint}"
189
+ end
190
+ end.join
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,58 @@
1
+ module Java
2
+ class Field < Annotatable
3
+ attr_reader :flags, :name, :vm_type, :java_type, :annotations
4
+
5
+ ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
6
+ ACC_PRIVATE = 0x0002 # Declared private; usable only within the defining class.
7
+ ACC_PROTECTED = 0x0004 # Declared protected; may be accessed within subclasses.
8
+ ACC_STATIC = 0x0008 # Declared static.
9
+ ACC_FINAL = 0x0010 # Declared final; never directly assigned to after object construction (JLS §17.5).
10
+ ACC_VOLATILE = 0x0040 # Declared volatile; cannot be cached.
11
+ ACC_TRANSIENT = 0x0080 # Declared transient; not written or read by a persistent object manager.
12
+ ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
13
+ ACC_ENUM = 0x4000 # Declared as an element of an enum.
14
+
15
+ def initialize(flags, name, vm_type, annotations)
16
+ super(annotations)
17
+ @flags = flags
18
+ @name = name
19
+ @vm_type = vm_type
20
+ end
21
+
22
+ def is_public?
23
+ (@flags & ACC_PUBLIC) != 0
24
+ end
25
+
26
+ def is_private?
27
+ (@flags & ACC_PRIVATE) != 0
28
+ end
29
+
30
+ def is_protected?
31
+ (@flags & ACC_PROTECTED) != 0
32
+ end
33
+
34
+ def is_static?
35
+ (@flags & ACC_STATIC) != 0
36
+ end
37
+
38
+ def is_final?
39
+ (@flags & ACC_FINAL) != 0
40
+ end
41
+
42
+ def is_volatile?
43
+ (@flags & ACC_VOLATILE) != 0
44
+ end
45
+
46
+ def is_transient?
47
+ (@flags & ACC_TRANSIENT) != 0
48
+ end
49
+
50
+ def is_synthetic?
51
+ (@flags & ACC_SYNTHETIC) != 0
52
+ end
53
+
54
+ def is_enum?
55
+ (@flags & ACC_ENUM) != 0
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,91 @@
1
+ module Java
2
+ class Method < Annotatable
3
+ attr_reader :flags, :name, :vm_signature
4
+
5
+ ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
6
+ ACC_PRIVATE = 0x0002 # Declared private; accessible only within the defining class.
7
+ ACC_PROTECTED = 0x0004 # Declared protected; may be accessed within subclasses.
8
+ ACC_STATIC = 0x0008 # Declared static.
9
+ ACC_FINAL = 0x0010 # Declared final; must not be overridden (§5.4.5).
10
+ ACC_SYNCHRONIZED = 0x0020 # Declared synchronized; invocation is wrapped by a monitor use.
11
+ ACC_BRIDGE = 0x0040 # A bridge method, generated by the compiler.
12
+ ACC_VARARGS = 0x0080 # Declared with variable number of arguments.
13
+ ACC_NATIVE = 0x0100 # Declared native; implemented in a language other than Java.
14
+ ACC_ABSTRACT = 0x0400 # Declared abstract; no implementation is provided.
15
+ ACC_STRICT = 0x0800 # Declared strictfp; floating-point mode is FP-strict.
16
+ ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
17
+
18
+ def initialize(flags, name, vm_signature, annotations)
19
+ super(annotations)
20
+ @flags = flags
21
+ @name = name
22
+ @vm_signature = vm_signature
23
+ #java_signature = vm_signature.scan(/\[?[ZBCSIJFDV]|\[?L[^\;]*;/).map { |m| vm_to_java(m) }
24
+ end
25
+
26
+ def is_public?
27
+ (@flags & ACC_PUBLIC) != 0
28
+ end
29
+
30
+ def is_private?
31
+ (@flags & ACC_PRIVATE) != 0
32
+ end
33
+
34
+ def is_protected?
35
+ (@flags & ACC_PROTECTED) != 0
36
+ end
37
+
38
+ def is_static?
39
+ (@flags & ACC_STATIC) != 0
40
+ end
41
+
42
+ def is_final?
43
+ (@flags & ACC_FINAL) != 0
44
+ end
45
+
46
+ def is_synchronized?
47
+ (@flags & ACC_SYNCHRONIZED) != 0
48
+ end
49
+
50
+ def is_bridge?
51
+ (@flags & ACC_BRIDGE) != 0
52
+ end
53
+
54
+ def is_varargs?
55
+ (@flags & ACC_VARARGS) != 0
56
+ end
57
+
58
+ def is_native?
59
+ (@flags & ACC_NATIVE) != 0
60
+ end
61
+
62
+ def is_abstract?
63
+ (@flags & ACC_ABSTRACT) != 0
64
+ end
65
+
66
+ def is_strict?
67
+ (@flags & ACC_STRICT) != 0
68
+ end
69
+
70
+ def is_synthetic?
71
+ (@flags & ACC_SYNTHETIC) != 0
72
+ end
73
+
74
+ #private
75
+ # def vm_to_java(vm_type)
76
+ # suffix = vm_type =~ /^\[/ ? "[]" : ""
77
+ # case vm_type
78
+ # when "V" then "void"
79
+ # when "Z" then "boolean"
80
+ # when "B" then "byte"
81
+ # when "C" then "char"
82
+ # when "S" then "short"
83
+ # when "I" then "int"
84
+ # when "J" then "long"
85
+ # when "F" then "float"
86
+ # when "D" then "double"
87
+ # else vm_type.gsub(/[L;]/, "").gsub("/", ".")
88
+ # end + suffix
89
+ # end
90
+ end
91
+ end
@@ -0,0 +1,226 @@
1
+ require 'java_dissassembler/array'
2
+ require 'java_dissassembler/annotatable'
3
+ require 'java_dissassembler/class'
4
+ require 'java_dissassembler/method'
5
+ require 'java_dissassembler/field'
6
+
7
+ # See https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html for JVM Class Binary Specification
8
+ module Java
9
+ class Dissassembler
10
+ class << self
11
+ CONSTANT_UTF8 = 1
12
+ CONSTANT_INTEGER = 3
13
+ CONSTANT_FLOAT = 4
14
+ CONSTANT_LONG = 5
15
+ CONSTANT_DOUBLE = 6
16
+ CONSTANT_CLASS = 7
17
+ CONSTANT_STRING = 8
18
+ CONSTANT_FIELDREF = 9
19
+ CONSTANT_METHODREF = 10
20
+ CONSTANT_INTERFACEMETHODREF = 11
21
+ CONSTANT_NAMEANDTYPE = 12
22
+ CONSTANT_METHODHANDLE = 15
23
+ CONSTANT_METHODTYPE = 16
24
+ CONSTANT_INVOKEDYNAMIC = 18
25
+
26
+ MAGIC = "\xCA\xFE\xBA\xBE".each_byte.to_a.freeze
27
+
28
+ def dissassemble(bytes)
29
+ if bytes.is_a?(Array)
30
+ do_dissassemble(bytes)
31
+ elsif bytes.is_a?(String)
32
+ do_dissassemble(bytes.each_byte.to_a)
33
+ else
34
+ raise "Expected `bytes' to be Array of bytes or String of bytes"
35
+ end
36
+ end
37
+
38
+ def dissassemble_file(path)
39
+ File.open(path) do |file|
40
+ dissassemble(file.read)
41
+ end
42
+ end
43
+
44
+ private
45
+ def do_dissassemble(bytes)
46
+ raise "Missing magic number 0xCAFEBABE. Not a .class file" unless bytes[0..3] == MAGIC
47
+
48
+ minor = bytes[4..5].u2
49
+ major = bytes[6..7].u2
50
+ offset = 8
51
+
52
+ # Constant Pool
53
+ constant_pool = {}
54
+ constant_pool_size = bytes[offset..offset + 1].u2 - 1
55
+ offset += 2
56
+ i = 0
57
+ while i < constant_pool_size
58
+ consumed, val, tag = constant_pool_info_size(bytes[offset..-1])
59
+ i += 1 if !tag.nil? && ((tag == 5) || (tag == 6)) # Floats and Doubles take up two constant pool entries :/
60
+ constant_pool[i + 1] = val unless val.nil?
61
+ offset += consumed
62
+ i += 1
63
+ end
64
+
65
+ # Class Info: 3 x u2 (access_flags, this_klass, super_klass)
66
+ access_flags = bytes[offset..offset + 1].u2
67
+ this_klass = constant_pool[constant_pool[bytes[offset + 2..offset + 3].u2]]
68
+ super_klass = constant_pool[constant_pool[bytes[offset + 4..offset + 5].u2]]
69
+ offset += 6
70
+
71
+ # Interfaces
72
+ interfaces = Array.new
73
+ interfaces_count = bytes[offset..offset + 1].u2
74
+ offset += 2
75
+ interfaces_count.times do
76
+ interface_index = bytes[offset..offset + 1].u2
77
+ interfaces << constant_pool[interface_index]
78
+ offset += 2
79
+ end
80
+
81
+ # Fields
82
+ fields = Array.new
83
+ fields_count = bytes[offset..offset + 1].u2
84
+ offset += 2
85
+ fields_count.times do
86
+ flags = bytes[offset..offset + 1].u2
87
+ name = bytes[offset + 2..offset + 3].u2
88
+ sig = bytes[offset + 4..offset + 5].u2
89
+ offset += 6 # Skip the field header
90
+
91
+ consumed, annotations = parse_attributes(bytes[offset..-1], constant_pool)
92
+ fields << Java::Field.new(flags, constant_pool[name], constant_pool[sig], annotations.compact)
93
+ offset += consumed
94
+ end
95
+
96
+ # Parse methods
97
+ methods = Array.new
98
+ methods_count = bytes[offset..offset + 1].u2
99
+ offset += 2
100
+ methods_count.times do
101
+ flags = bytes[offset..offset + 1].u2
102
+ name = bytes[offset + 2..offset + 3].u2
103
+ sig = bytes[offset + 4..offset + 5].u2
104
+ offset += 6
105
+
106
+ consumed, annotations = parse_attributes(bytes[offset..-1], constant_pool)
107
+ methods << Java::Method.new(flags, constant_pool[name], constant_pool[sig], annotations.compact)
108
+ offset += consumed
109
+ end
110
+
111
+ # Parse class attributes
112
+ _, annotations = parse_attributes(bytes[offset..-1], constant_pool)
113
+ Java::Class.new(major, minor, access_flags, this_klass, super_klass, interfaces, fields, methods, annotations)
114
+ end
115
+
116
+ def to_double(fixnum)
117
+ case fixnum
118
+ when 0x7ff0000000000000 then Float::INFINITY
119
+ when 0xfff0000000000000 then -Float::INFINITY
120
+ #TODO: NaN
121
+ else
122
+ s = (fixnum >> 63) == 0 ? 1 : -1
123
+ e = (fixnum >> 52) & 0x7ff
124
+ m = e == 0 ? (fixnum & 0xfffffffffffff) << 1 : (fixnum & 0xfffffffffffff) | 0x10000000000000
125
+ (s * m * 2**(e - 1075)).to_f
126
+ end
127
+ end
128
+
129
+ def to_float(fixnum)
130
+ case fixnum
131
+ when 0x7f800000 then Float::INFINITY
132
+ when 0xff800000 then Float::INFINITY
133
+ #TODO: NaN
134
+ else
135
+ s = ((fixnum >> 31) == 0) ? 1 : -1
136
+ e = ((fixnum >> 23) & 0xff)
137
+ m = (e == 0) ? (fixnum & 0x7fffff) << 1 : (fixnum & 0x7fffff) | 0x800000;
138
+ (s * m * 2**(e - 150)).to_f
139
+ end
140
+ end
141
+
142
+ def constant_pool_info_size(bytes)
143
+ constant_pool_tag = bytes[0]
144
+ case constant_pool_tag
145
+ when CONSTANT_UTF8
146
+ tag_content_size = bytes[1..2].u2
147
+ val = bytes[3..tag_content_size + 2].modified_utf8_to_s
148
+ return 3 + tag_content_size, val
149
+ when CONSTANT_INTEGER
150
+ val = bytes[1..4].u4
151
+ return 5, val
152
+ when CONSTANT_FLOAT
153
+ val = to_float(bytes[1..4].u4)
154
+ return 5, val
155
+ when CONSTANT_LONG
156
+ high_bytes = bytes[1..4].u4
157
+ low_bytes = bytes[5..8].u4
158
+ return 9, (high_bytes << 32) + low_bytes, constant_pool_tag
159
+ when CONSTANT_DOUBLE
160
+ high_bytes = bytes[1..4].u4
161
+ low_bytes = bytes[5..8].u4
162
+ return 9, to_double((high_bytes << 32) + low_bytes), constant_pool_tag
163
+ when CONSTANT_STRING, CONSTANT_CLASS
164
+ val = bytes[1..2].u2
165
+ return 3, val
166
+ when CONSTANT_FIELDREF, CONSTANT_METHODREF, CONSTANT_INTERFACEMETHODREF
167
+ klass_index = bytes[1..2].u2
168
+ name_and_type_index = bytes[3..4].u2
169
+ return 5, [klass_index, name_and_type_index]
170
+ when CONSTANT_NAMEANDTYPE
171
+ name_index = bytes[1..2].u2
172
+ descriptor_index = bytes[3..4].u2
173
+ return 5, [name_index, descriptor_index]
174
+ when CONSTANT_METHODHANDLE
175
+ reference_kind = bytes[1].u1
176
+ reference_index = bytes[2..3].u2
177
+ return 4, [reference_kind, reference_index]
178
+ when CONSTANT_METHODTYPE
179
+ return 3, bytes[1..2].u2
180
+ when CONSTANT_INVOKEDYNAMIC
181
+ bootstrap_method_attr_index = bytes[1..2].u2
182
+ name_and_type_index = bytes[3..4].u2
183
+ return 5, [bootstrap_method_attr_index, name_and_type_index]
184
+ else raise "Unknown Constant Pool Tag: 0x#{constant_pool_tag.to_s(16)}"
185
+ end
186
+ end
187
+
188
+ def parse_annotation(bytes, constant_pool)
189
+ annotations = {}
190
+ num_annotations = bytes[0..1].u2
191
+ offset = 2
192
+ num_annotations.times do |_annotation|
193
+ annotation_name_index = bytes[offset..offset + 1].u2
194
+ num_value_pairs = bytes[offset + 2..offset + 3].u2
195
+ offset += 4
196
+ key_values = annotations[ constant_pool[annotation_name_index] ] = []
197
+ num_value_pairs.times do |_value_pairs|
198
+ name = bytes[offset..offset + 1].u2
199
+ # TODO: Handle 4.7.16.1. The element_value structure
200
+ value = bytes[offset + 3..offset + 4].u2
201
+ key_values << constant_pool[name]
202
+ key_values << constant_pool[value]
203
+ end
204
+ end
205
+ return nil if annotations.empty?
206
+ annotations
207
+ end
208
+
209
+ def parse_attributes(bytes, constant_pool)
210
+ annotations = []
211
+ attr_count = bytes[0..1].u2
212
+ offset = 2 # Consume attr_count
213
+ attr_count.times do |_attr|
214
+ attr_name_index = bytes[offset..offset + 1].u2
215
+ attr_size = bytes[offset + 2..offset + 5].u4
216
+ offset += 6
217
+ if (constant_pool[attr_name_index] == "RuntimeInvisibleAnnotations") && (attr_size > 0)
218
+ annotations << parse_annotation(bytes[offset..offset + attr_size], constant_pool)
219
+ end
220
+ offset += attr_size
221
+ end
222
+ [offset, annotations]
223
+ end
224
+ end
225
+ end
226
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: java_dissassembler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josh Bodily
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A Java .class file dissassembler. Similar to javap, but w/ no JDK dependencies. Pure
14
+ Ruby
15
+ email: joshbodily@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/java_dissassembler.rb
21
+ - lib/java_dissassembler/annotatable.rb
22
+ - lib/java_dissassembler/array.rb
23
+ - lib/java_dissassembler/class.rb
24
+ - lib/java_dissassembler/erb_helper.rb
25
+ - lib/java_dissassembler/field.rb
26
+ - lib/java_dissassembler/method.rb
27
+ homepage: http://rubygems.org/gems/java_dissassembler
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.9.3
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.6.7
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Java .class file dissassembler
51
+ test_files: []