# This file is part of Metasm, the Ruby assembly manipulation suite # Copyright (C) 2006-2009 Yoann GUILLOT # # Licence is LGPL, see LICENCE in the top-level directory require 'metasm/exe_format/main' require 'metasm/encode' require 'metasm/decode' module Metasm class JavaClass < ExeFormat MAGIC = "\xCA\xFE\xBA\xBE" CONSTANT_TAG = {0x1 => 'Utf8', 0x3 => 'Integer', 0x4 => 'Float', 0x5 => 'Long', 0x6 => 'Double', 0x7 => 'Class', 0x8 => 'String', 0x9 => 'Fieldref', 0xa => 'Methodref', 0xb => 'InterfaceMethodref', 0xc => 'NameAndType' } class SerialStruct < Metasm::SerialStruct new_int_field :u1, :u2, :u4 end class Header < SerialStruct mem :magic, 4, MAGIC u2 :minor_version u2 :major_version end class ConstantPool < SerialStruct u2 :constant_pool_count attr_accessor :constant_pool def decode(c) super(c) @constant_pool = [nil] i = 1 while i < @constant_pool_count entry = ConstantPoolInfo.decode(c) entry.idx = i @constant_pool << entry i += 1 if entry.tag =~ /Long|Double/ # we must insert a phantom cell # for long and double constants @constant_pool << nil i += 1 end end end def encode(c) cp = super(c) @constant_pool.each { |entry| next if entry.nil? cp << entry.encode(c) } cp end def [](idx) @constant_pool[idx] end def []=(idx, val) raise 'cannot be used to add a cp entry' if @constant_pool[idx].nil? @constant_pool[idx] = val end end class ConstantPoolInfo < SerialStruct u1 :tag fld_enum :tag, CONSTANT_TAG attr_accessor :info, :idx def decode(c) super(c) case @tag when 'Utf8' @info = ConstantUtf8.decode(c) when /Integer|Float/ @info = ConstantIntFloat.decode(c) when /Long|Double/ @info = ConstantLongDouble.decode(c) when /Class|String/ @info = ConstantIndex.decode(c) when /ref$/ @info = ConstantRef.decode(c) when 'NameAndType' @info = ConstantNameAndType.decode(c) else raise 'unkown constant tag' return end end def encode(c) super(c) << @info.encode(c) end end class ConstantUtf8 < SerialStruct u2 :length attr_accessor :bytes def decode(c) super(c) @bytes = c.encoded.read(@length) end def encode(c) super(c) << @bytes end end class ConstantIntFloat < SerialStruct u4 :bytes end class ConstantLongDouble < SerialStruct u4 :high_bytes u4 :low_bytes end class ConstantIndex < SerialStruct u2 :index end class ConstantRef < SerialStruct u2 :class_index u2 :name_and_type_index end class ConstantNameAndType < SerialStruct u2 :name_index u2 :descriptor_index end class ClassInfo < SerialStruct u2 :access_flags u2 :this_class u2 :super_class end class Interfaces < SerialStruct u2 :interfaces_count attr_accessor :interfaces def decode(c) super(c) @interfaces = [] @interfaces_count.times { @interfaces << ConstantIndex.decode(c) } end def encode(c) ret = super(c) @interfaces.each { |e| ret << e.encode(c) } ret end def [](idx) @interfaces[idx] end end class Fields < SerialStruct u2 :fields_count attr_accessor :fields def decode(c) super(c) @fields = [] @fields_count.times { @fields << FieldMethodInfo.decode(c) } end def encode(c) ret = super(c) @fields.each { |e| ret << e.encode(c) } ret end def [](idx) @fields[idx] end end class Methods < SerialStruct u2 :methods_count attr_accessor :methods def decode(c) super(c) @methods = [] @methods_count.times { @methods << FieldMethodInfo.decode(c) } end def encode(c) ret = super(c) @methods.each { |e| ret << e.encode(c) } ret end def [](idx) @methods[idx] end end class FieldMethodInfo < SerialStruct u2 :access_flags u2 :name_index u2 :descriptor_index attr_accessor :attributes def decode(c) super(c) @attributes = Attributes.decode(c) end def encode(c) super(c) << @attributes.encode(c) end end class Attributes < SerialStruct u2 :attributes_count attr_accessor :attributes def decode(c) super(c) @attributes = [] @attributes_count.times { |i| @attributes << AttributeInfo.decode(c) } end def encode(c) ret = super(c) @attributes.each { |e| ret << e.encode(c) } ret end def [](idx) @attributes[idx] end end class AttributeInfo < SerialStruct u2 :attribute_name_index u4 :attribute_length attr_accessor :data def decode(c) super(c) @data = c.encoded.read(@attribute_length) end def encode(c) super(c) << @data end end def encode_u1(val) Expression[val].encode(:u8, @endianness) end def encode_u2(val) Expression[val].encode(:u16, @endianness) end def encode_u4(val) Expression[val].encode(:u32, @endianness) end def decode_u1(edata = @encoded) edata.decode_imm(:u8, @endianness) end def decode_u2(edata = @encoded) edata.decode_imm(:u16, @endianness) end def decode_u4(edata = @encoded) edata.decode_imm(:u32, @endianness) end def sizeof_u1 ; 1 ; end def sizeof_u2 ; 2 ; end def sizeof_u4 ; 4 ; end attr_accessor :header, :constant_pool, :class_info, :interfaces, :fields, :methods, :attributes def initialize(endianness=:big) @endianness = endianness @encoded = EncodedData.new super() end def decode @header = Header.decode(self) @constant_pool = ConstantPool.decode(self) @class_info = ClassInfo.decode(self) @interfaces = Interfaces.decode(self) @fields = Fields.decode(self) @methods = Methods.decode(self) @attributes = Attributes.decode(self) end def encode @encoded = EncodedData.new @encoded << @header.encode(self) @encoded << @constant_pool.encode(self) @encoded << @class_info.encode(self) @encoded << @interfaces.encode(self) @encoded << @fields.encode(self) @encoded << @methods.encode(self) @encoded << @attributes.encode(self) @encoded.data end def cpu_from_headers raise 'JVM' end def each_section raise 'n/a' end def get_default_entrypoints [] end def string_at(idx) loop do tmp = @constant_pool[idx].info return tmp.bytes if tmp.kind_of? ConstantUtf8 idx = tmp.index end end def decode_methodref(mref) class_idx = mref.info.class_index nt_idx = mref.info.name_and_type_index name_idx = @constant_pool[nt_idx].info.name_index desc_idx = @constant_pool[nt_idx].info.descriptor_index string_at(class_idx) + '/' + string_at(name_idx) + string_at(desc_idx) end def cp_add(cpi, tag) cpe = ConstantPoolInfo.new cpe.tag = tag cpe.info = cpi cpe.idx = @constant_pool.constant_pool_count @constant_pool.constant_pool << cpe @constant_pool.constant_pool_count += 1 @constant_pool.constant_pool_count += 1 if tag =~ /Long|Double/ cpe.idx end def cp_find(tag) constant_pool.constant_pool.each { |e| next if !e or e.tag != tag if yield(e.info) return e.idx end } nil end def cp_auto_utf8(string) if idx = cp_find('Utf8') { |i| i.bytes == string } return idx end cpi = ConstantUtf8.new cpi.bytes = string cpi.length = string.length cp_add(cpi, 'Utf8') end def cp_auto_class(classname) if idx = cp_find('Class') { |i| string_at(i.index) == classname } return idx end cpi = ConstantIndex.new cpi.index = cp_auto_utf8(classname) cp_add(cpi, 'Class') end def cp_add_methodref(classname, name, descriptor) nat = ConstantNameAndType.new nat.name_index = cp_auto_utf8(name) nat.descriptor_index = cp_auto_utf8(descriptor) natidx = cp_add(nat, 'NameAndType') cpi = ConstantRef.new cpi.class_index = cp_auto_class(classname) cpi.name_and_type_index = natidx cp_add(cpi, 'Methodref') end def attribute_create(name, data) a = AttributeInfo.new a.attribute_name_index = cp_auto_utf8(name) a.attribute_length = data.size a.data = data a end end end