metasm 1.0.5 → 1.0.6

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +3 -3
  4. data/Rakefile +1 -1
  5. data/cortex.yaml +17 -0
  6. data/metasm/cpu/arm64/decode.rb +87 -11
  7. data/metasm/cpu/arm64/decompile.rb +142 -0
  8. data/metasm/cpu/arm64/opcodes.rb +53 -23
  9. data/metasm/cpu/arm64.rb +1 -0
  10. data/metasm/cpu/dwarf/debug.rb +39 -0
  11. data/metasm/cpu/dwarf/decode.rb +124 -0
  12. data/metasm/cpu/dwarf/decompile.rb +212 -0
  13. data/metasm/cpu/dwarf/encode.rb +49 -0
  14. data/metasm/cpu/dwarf/main.rb +37 -0
  15. data/metasm/cpu/dwarf/opcodes.rb +107 -0
  16. data/metasm/cpu/dwarf.rb +11 -0
  17. data/metasm/cpu/ia32/debug.rb +8 -0
  18. data/metasm/cpu/ia32/decode.rb +25 -1
  19. data/metasm/cpu/ia32/decompile.rb +205 -342
  20. data/metasm/cpu/mips/decode.rb +1 -1
  21. data/metasm/cpu/ppc/decode.rb +1 -1
  22. data/metasm/cpu/sh4/decode.rb +1 -1
  23. data/metasm/cpu/x86_64/decompile.rb +68 -0
  24. data/metasm/cpu/x86_64.rb +1 -0
  25. data/metasm/decode.rb +14 -0
  26. data/metasm/decompile.rb +51 -27
  27. data/metasm/disassemble.rb +24 -15
  28. data/metasm/dynldr.rb +23 -4
  29. data/metasm/encode.rb +11 -0
  30. data/metasm/exe_format/coff_encode.rb +6 -7
  31. data/metasm/exe_format/elf.rb +60 -2
  32. data/metasm/exe_format/elf_decode.rb +201 -6
  33. data/metasm/exe_format/shellcode.rb +39 -0
  34. data/metasm/gui/dasm_decomp.rb +1 -0
  35. data/metasm/os/emulator.rb +7 -0
  36. data/metasm/parse_c.rb +1 -1
  37. data/metasm.gemspec +1 -2
  38. data/metasm.rb +1 -1
  39. data/samples/disassemble-gui.rb +27 -11
  40. data/samples/disassemble.rb +9 -12
  41. data/samples/emudbg.rb +1 -1
  42. data/samples/factorize-headers-elfimports.rb +4 -1
  43. data/samples/lindebug.rb +16 -2
  44. data/tests/shellcode.rb +111 -0
  45. metadata +19 -107
  46. checksums.yaml.gz.sig +0 -0
  47. data.tar.gz.sig +0 -3
  48. metadata.gz.sig +0 -2
@@ -393,7 +393,8 @@ class ELF < ExeFormat
393
393
  15 => 'PC8', 16 => 'DTPMOD64', 17 => 'DTPOFF64',
394
394
  18 => 'TPOFF64', 19 => 'TLSGD', 20 => 'TLSLD',
395
395
  21 => 'DTPOFF32', 22 => 'GOTTPOFF', 23 => 'TPOFF32' },
396
- 'OPENRISC' => { 0 => 'NONE', 1 => '32', 2 => '16', 3 => '8',
396
+ 'OPENRISC' => { 0 => 'NONE',
397
+ 1 => '32', 2 => '16', 3 => '8',
397
398
  4 => 'LO_16_IN_INSN', 5 => 'HI_16_IN_INSN', 6 => 'INSN_REL_26', 7 => 'GNU_VTENTRY',
398
399
  8 => 'GNU_VTINHERIT', 9 => '32_PCREL', 10 => '16_PCREL', 11 => '8_PCREL',
399
400
  12 => 'GOTPC_HI16', 13 => 'GOTPC_LO16', 14 => 'GOT15', 15 => 'PLT26',
@@ -402,6 +403,53 @@ class ELF < ExeFormat
402
403
  24 => 'TLS_LDM_HI16', 25 => 'TLS_LDM_LO16', 26 => 'TLS_LDO_HI16', 27 => 'TLS_LDO_LO16',
403
404
  28 => 'TLS_IE_HI16', 29 => 'TLS_IE_LO16', 30 => 'TLS_LE_HI16', 31 => 'TLS_LE_LO16',
404
405
  32 => 'TLS_TPOFF', 33 => 'TLS_DTPOFF', 34 => 'TLS_DTPMOD' },
406
+ 'AARCH64' => {
407
+ 0x100 => 'NONE',
408
+ 0x101 => 'ABS64', 0x102 => 'ABS32', 0x103 => 'ABS16',
409
+ 0x104 => 'PREL64', 0x105 => 'PREL32', 0x106 => 'PREL16',
410
+ 0x107 => 'MOVW_UABS_G0', 0x108 => 'MOVW_UABS_G0_NC',
411
+ 0x109 => 'MOVW_UABS_G1', 0x10a => 'MOVW_UABS_G1_NC',
412
+ 0x10b => 'MOVW_UABS_G2', 0x10c => 'MOVW_UABS_G2_NC',
413
+ 0x10d => 'MOVW_UABS_G3', 0x10e => 'MOVW_SABS_G0',
414
+ 0x10f => 'MOVW_SABS_G1', 0x110 => 'MOVW_SABS_G2',
415
+ 0x111 => 'LD_PREL_LO19', 0x112 => 'ADR_PREL_LO21',
416
+ 0x113 => 'ADR_PREL_PG_HI21', 0x115 => 'ADD_ABS_LO12_NC',
417
+ 0x116 => 'LDST8_ABS_LO12_NC', 0x117 => 'TSTBR14', 0x118 => 'CONDBR19',
418
+ 0x11a => 'JUMP26', 0x11b => 'CALL26',
419
+ 0x11c => 'LDST16_ABS_LO12_NC', 0x11d => 'LDST32_ABS_LO12_NC',
420
+ 0x11e => 'LDST64_ABS_LO12_NC', 0x12b => 'LDST128_ABS_LO12_NC',
421
+ 0x137 => 'ADR_GOT_PAGE', 0x138 => 'LD64_GOT_LO12_NC',
422
+ 0x20b => 'TLSLD_MOVW_DTPREL_G2',
423
+ 0x20c => 'TLSLD_MOVW_DTPREL_G1', 0x20d => 'TLSLD_MOVW_DTPREL_G1_NC',
424
+ 0x20e => 'TLSLD_MOVW_DTPREL_G0', 0x20f => 'TLSLD_MOVW_DTPREL_G0_NC',
425
+ 0x210 => 'TLSLD_ADD_DTPREL_HI12',
426
+ 0x211 => 'TLSLD_ADD_DTPREL_LO12', 0x212 => 'TLSLD_ADD_DTPREL_LO12_NC',
427
+ 0x213 => 'TLSLD_LDST8_DTPREL_LO12', 0x214 => 'TLSLD_LDST8_DTPREL_LO12_NC',
428
+ 0x215 => 'TLSLD_LDST16_DTPREL_LO12', 0x216 => 'TLSLD_LDST16_DTPREL_LO12_NC',
429
+ 0x217 => 'TLSLD_LDST32_DTPREL_LO12', 0x218 => 'TLSLD_LDST32_DTPREL_LO12_NC',
430
+ 0x219 => 'TLSLD_LDST64_DTPREL_LO12', 0x21a => 'TLSLD_LDST64_DTPREL_LO12_NC',
431
+ 0x21b => 'TLSIE_MOVW_GOTTPREL_G1', 0x21c => 'TLSIE_MOVW_GOTTPREL_G0_NC',
432
+ 0x21d => 'TLSIE_ADR_GOTTPREL_PAGE21', 0x21e => 'TLSIE_LD64_GOTTPREL_LO12_NC',
433
+ 0x21f => 'TLSIE_LD_GOTTPREL_PREL19', 0x220 => 'TLSLE_MOVW_TPREL_G2',
434
+ 0x221 => 'TLSLE_MOVW_TPREL_G1', 0x222 => 'TLSLE_MOVW_TPREL_G1_NC',
435
+ 0x223 => 'TLSLE_MOVW_TPREL_G0', 0x224 => 'TLSLE_MOVW_TPREL_G0_NC',
436
+ 0x225 => 'TLSLE_ADD_TPREL_HI12',
437
+ 0x226 => 'TLSLE_ADD_TPREL_LO12', 0x227 => 'TLSLE_ADD_TPREL_LO12_NC',
438
+ 0x228 => 'TLSLE_LDST8_TPREL_LO12', 0x229 => 'TLSLE_LDST8_TPREL_LO12_NC',
439
+ 0x22a => 'TLSLE_LDST16_TPREL_LO12', 0x22b => 'TLSLE_LDST16_TPREL_LO12_NC',
440
+ 0x22c => 'TLSLE_LDST32_TPREL_LO12', 0x22d => 'TLSLE_LDST32_TPREL_LO12_NC',
441
+ 0x22e => 'TLSLE_LDST64_TPREL_LO12', 0x22f => 'TLSLE_LDST64_TPREL_LO12_NC',
442
+ 0x232 => 'TLSDESC_ADR_PAGE', 0x233 => 'TLSDESC_LD64_LO12_NC',
443
+ 0x234 => 'TLSDESC_ADD_LO12_NC',
444
+ 0x235 => 'TLSDESC_OFF_G1', 0x236 => 'TLSDESC_OFF_G0_NC', # from musl from this line
445
+ 0x237 => 'TLSDESC_LDR', 0x238 => 'TLSDESC_ADD',
446
+ 0x239 => 'TLSDESC_CALL', 0x23a => 'TLSLE_LDST128_TPREL_LO12',
447
+ 0x23b => 'TLSLE_LDST128_TPREL_LO12_NC', 0x23c => 'TLSLD_LDST128_DTPREL_LO12',
448
+ 0x23d => 'TLSLD_LDST128_DTPREL_LO12_NC',
449
+ 0x400 => 'COPY', 0x401 => 'GLOB_DAT',
450
+ 0x402 => 'JUMP_SLOT', 0x403 => 'RELATIVE',
451
+ 0x404 => 'TLS_DTPMOD64', 0x405 => 'TLS_DTPREL64',
452
+ 0x406 => 'TLS_TPREL64', 0x407 => 'TLSDESC' },
405
453
  }
406
454
 
407
455
  DEFAULT_INTERP = '/lib/ld-linux.so.2'
@@ -566,6 +614,7 @@ class ELF < ExeFormat
566
614
 
567
615
  class SerialStruct
568
616
  new_int_field :leb
617
+ new_int_field :uleb
569
618
  end
570
619
 
571
620
  # libdwarf/dwarf.h
@@ -629,6 +678,15 @@ class ELF < ExeFormat
629
678
  end
630
679
  end
631
680
 
681
+ class CIE < SerialStruct
682
+ attr_accessor :offset, :id, :version, :augmentation_string, :eh_data, :code_align,
683
+ :data_align, :return_reg, :augmentation_data, :initial_instrs
684
+ end
685
+
686
+ class FDE < SerialStruct
687
+ attr_accessor :offset, :cie_offset, :pc_begin, :pc_range, :augmentation_data, :instrs
688
+ end
689
+
632
690
  word :cu_len
633
691
  half :version, 2
634
692
  word :abbrev_off
@@ -653,7 +711,7 @@ class ELF < ExeFormat
653
711
  }
654
712
  end
655
713
 
656
- attr_accessor :header, :segments, :sections, :tag, :symbols, :relocations, :endianness, :bitsize, :debug
714
+ attr_accessor :header, :segments, :sections, :tag, :symbols, :relocations, :endianness, :bitsize, :debug, :eh_frame
657
715
  def initialize(cpu=nil)
658
716
  @header = Header.new
659
717
  @tag = {}
@@ -781,13 +781,41 @@ class ELF
781
781
  target = reloc_target(reloc)
782
782
  target = Expression[target, :+, addend] if addend and addend != 0
783
783
  else
784
- puts "W: Elf: unhandled MIPS reloc #{reloc.inspect}" if $VERBOSE
784
+ puts "W: Elf: unhandled OpenRISC reloc #{reloc.inspect}" if $VERBOSE
785
785
  target = nil
786
786
  end
787
787
 
788
788
  Metasm::Relocation.new(Expression[target], :u32, @endianness) if target
789
789
  end
790
790
 
791
+ def arch_decode_segments_reloc_aarch64(reloc)
792
+ if reloc.symbol.kind_of?(Symbol) and n = reloc.symbol.name and reloc.symbol.shndx == 'UNDEF' and @sections and
793
+ s = @sections.find { |s_| s_.name and s_.offset <= @encoded.ptr and s_.offset + s_.size > @encoded.ptr }
794
+ @encoded.add_export(new_label("#{s.name}_#{n}"), @encoded.ptr, true)
795
+ end
796
+
797
+ original_xword = decode_xword
798
+
799
+ # decode addend if needed
800
+ case reloc.type
801
+ when 'NONE' # no addend
802
+ else addend = reloc.addend || Expression.make_signed(original_xword, 64)
803
+ end
804
+
805
+ case reloc.type
806
+ when 'NONE'
807
+ # TODO actual base relocs
808
+ when 'JUMP_SLOT'
809
+ target = reloc_target(reloc)
810
+ target = Expression[target, :+, addend] if addend and addend != 0
811
+ else
812
+ puts "W: Elf: unhandled AARCH64 reloc #{reloc.inspect}" if $VERBOSE
813
+ target = nil
814
+ end
815
+
816
+ Metasm::Relocation.new(Expression[target], :u64, @endianness) if target
817
+ end
818
+
791
819
  class DwarfDebug
792
820
  # decode a DWARF2 'compilation unit'
793
821
  def decode(elf, info, abbrev, str)
@@ -850,19 +878,169 @@ class ELF
850
878
  }
851
879
  end
852
880
  end
881
+
882
+ def self.decode_eh_frame_entry(elf, eh_frame)
883
+ start_ptr = eh_frame.ptr
884
+ len = elf.decode_word(eh_frame)
885
+ return if len == 0
886
+
887
+ if len >= 0xffff_fff0
888
+ puts "W: ELF: unhandled 64-bit .eh_frame entry"
889
+ return
890
+ end
891
+
892
+ id = elf.decode_word(eh_frame)
893
+ if id == 0
894
+ eh_frame.ptr = start_ptr
895
+ CIE.decode(elf, eh_frame)
896
+ else
897
+ eh_frame.ptr = start_ptr
898
+ FDE.decode(elf, eh_frame)
899
+ end
900
+ end
901
+
902
+ class CIE
903
+ def decode(elf, eh_frame)
904
+ @offset = eh_frame.ptr
905
+ len = elf.decode_word(eh_frame)
906
+ return if len == 0
907
+ return if len >= 0xffff_fff0
908
+ start_ptr = eh_frame.ptr
909
+
910
+ @id = elf.decode_word(eh_frame)
911
+ if id != 0
912
+ raise 'Not a CIE' # FDE, decode_eh_frame_entry should have handled that
913
+ end
914
+ @version = elf.decode_byte(eh_frame)
915
+ @augmentation_string = elf.decode_strz(eh_frame)
916
+ if @augmentation_string.include?('eh')
917
+ @eh_data = elf.decode_xword(eh_frame)
918
+ end
919
+
920
+ @code_align = elf.decode_uleb(eh_frame)
921
+ @data_align = elf.decode_leb(eh_frame)
922
+ @return_reg = elf.decode_byte(eh_frame)
923
+
924
+ if @augmentation_string[0, 1] == 'z'
925
+ a_len = elf.decode_uleb(eh_frame)
926
+ @augmentation_data = eh_frame.read(a_len)
927
+ # 'L' => read_byte LSDA pointer encoding
928
+ # 'R' => read_byte FDE address encoding
929
+ # 'P' => read_encoded_value? personality routine
930
+ end
931
+
932
+ if eh_frame.ptr <= start_ptr + len
933
+ # DWARF opcodes
934
+ @initial_instrs = []
935
+ while eh_frame.ptr < start_ptr+len
936
+ @initial_instrs << DwarfDebug.decode_dw_cfa_op(elf, eh_frame)
937
+ end
938
+ @initial_instrs.pop while @initial_instrs.last == [:nop]
939
+ end
940
+
941
+ eh_frame.ptr = start_ptr + len
942
+ end
943
+ end
944
+
945
+ class FDE
946
+ def decode(elf, eh_frame)
947
+ @offset = eh_frame.ptr
948
+ len = elf.decode_word(eh_frame)
949
+ return if len == 0
950
+ return if len >= 0xffff_fff0
951
+ start_ptr = eh_frame.ptr
952
+
953
+ id = elf.decode_word(eh_frame)
954
+ if id == 0
955
+ raise 'Not an FDE' # CIE
956
+ end
957
+ # in .eh_frame, this is a backward offset from current FDE to CIE, in .debug_frame, may be a direct offset from section start to CIE
958
+ @cie_offset = start_ptr - id
959
+ cie = elf.eh_frame.find { |c| c.offset == @cie_offset }
960
+
961
+ @pc_begin = Expression.make_signed(elf.decode_word(eh_frame), 32)
962
+ @pc_range = elf.decode_word(eh_frame)
963
+
964
+ if cie and cie.augmentation_data
965
+ a_len = elf.decode_uleb(eh_frame)
966
+ @augmentation_data = eh_frame.read(a_len)
967
+ end
968
+
969
+ if eh_frame.ptr <= start_ptr + len
970
+ # DWARF opcodes
971
+ @instrs = []
972
+ while eh_frame.ptr < start_ptr+len
973
+ @instrs << DwarfDebug.decode_dw_cfa_op(elf, eh_frame)
974
+ end
975
+ @instrs.pop while @instrs.last == [:nop]
976
+ end
977
+
978
+
979
+ eh_frame.ptr = start_ptr + len
980
+ end
981
+ end
982
+
983
+ # TODO make a cpu
984
+ def self.decode_dw_cfa_op(elf, eh_frame)
985
+ block = lambda {
986
+ blen = elf.decode_leb(eh_frame)
987
+ eh_frame.read(blen)
988
+ }
989
+
990
+ byte = elf.decode_byte(eh_frame)
991
+ case byte & 0xc0
992
+ when 0x40; [:advance_loc, byte & 0x3f]
993
+ when 0x80; [:offset, byte & 0x3f, elf.decode_uleb(eh_frame)]
994
+ when 0xc0; [:restore, byte & 0x3f]
995
+ else
996
+ case byte
997
+ when 0x00; [:nop]
998
+ when 0x01; [:set_loc, elf.decode_word(eh_frame)]
999
+ when 0x02; [:advance_loc, elf.decode_byte(eh_frame)]
1000
+ when 0x03; [:advance_loc, elf.decode_half(eh_frame)]
1001
+ when 0x04; [:advance_loc, elf.decode_word(eh_frame)]
1002
+ when 0x05; [:offset_extended, elf.decode_uleb(eh_frame), elf.decode_uleb(eh_frame)]
1003
+ when 0x06; [:restore_extended, elf.decode_uleb(eh_frame)]
1004
+ when 0x07; [:undefined, elf.decode_uleb(eh_frame)]
1005
+ when 0x08; [:same_value, elf.decode_uleb(eh_frame)]
1006
+ when 0x09; [:register, elf.decode_uleb(eh_frame), elf.decode_uleb(eh_frame)]
1007
+ when 0x0a; [:remember_state]
1008
+ when 0x0b; [:restore_state]
1009
+ when 0x0c; [:def_cfa, elf.decode_uleb(eh_frame), elf.decode_uleb(eh_frame)]
1010
+ when 0x0d; [:def_cfa_register, elf.decode_uleb(eh_frame)]
1011
+ when 0x0e; [:def_cfa_offset, elf.decode_uleb(eh_frame)]
1012
+ when 0x0f; [:def_cfa_expression, block[]]
1013
+ when 0x10; [:expression, elf.decode_uleb(eh_frame), block[]]
1014
+ when 0x11; [:offset_extended_sf, elf.decode_uleb(eh_frame), elf.decode_leb(eh_frame)]
1015
+ when 0x12; [:def_cfa_sf, elf.decode_uleb(eh_frame), elf.decode_leb(eh_frame)]
1016
+ when 0x13; [:def_cfa_offset_sf, elf.decode_leb(eh_frame)]
1017
+ when 0x14; [:val_offset, elf.decode_uleb(eh_frame), elf.decode_uleb(eh_frame)]
1018
+ when 0x15; [:val_offset_sf, elf.decode_uleb(eh_frame), elf.decode_leb(eh_frame)]
1019
+ when 0x16; [:val_expression, elf.decode_uleb(eh_frame), block[]]
1020
+ when 0x1c; [:lo_user]
1021
+ when 0x3f; [:hi_user]
1022
+ else [byte]
1023
+ end
1024
+ end
1025
+ end
853
1026
  end
854
1027
 
855
1028
  # decode an ULEB128 (dwarf2): read bytes while high bit is set, littleendian
856
- def decode_leb(ed = @encoded)
1029
+ def decode_leb(ed = @encoded); decode_uleb(ed, true); end
1030
+
1031
+ def decode_uleb(ed, signed=false)
857
1032
  v = s = 0
858
- loop {
1033
+ while s < 10*7
859
1034
  b = ed.read(1).unpack('C').first.to_i
860
1035
  v |= (b & 0x7f) << s
861
1036
  s += 7
862
- break v if (b&0x80) == 0
863
- }
1037
+ break if (b&0x80) == 0
1038
+ end
1039
+ v = Expression.make_signed(v, s) if signed
1040
+ v
864
1041
  end
865
1042
 
1043
+
866
1044
  # decodes the debugging information if available
867
1045
  # only a subset of DWARF2/3 is handled right now
868
1046
  # most info taken from http://ratonland.org/?entry=39 & libdwarf/dwarf.h
@@ -887,6 +1065,23 @@ class ELF
887
1065
  end
888
1066
  end
889
1067
 
1068
+ def decode_eh_frame
1069
+ return if not @sections
1070
+
1071
+ eh_frame = @sections.find { |sec| sec.name == '.eh_frame' }
1072
+ return if not eh_frame
1073
+
1074
+ eh_frame = @encoded[eh_frame.offset, eh_frame.size]
1075
+
1076
+ @eh_frame = []
1077
+
1078
+ while eh_frame.ptr < eh_frame.length
1079
+ entry = DwarfDebug.decode_eh_frame_entry(self, eh_frame)
1080
+ break if not entry
1081
+ @eh_frame << entry
1082
+ end
1083
+ end
1084
+
890
1085
  # decodes the ELF dynamic tags, interpret them, and decodes symbols and relocs
891
1086
  def decode_segments_dynamic(decode_relocs=true)
892
1087
  return if not dynamic = @segments.find { |s| s.type == 'DYNAMIC' }
@@ -1067,7 +1262,7 @@ EOC
1067
1262
  d.function[Expression[fn]] = noret
1068
1263
  }
1069
1264
  d.function[:default] = @cpu.disassembler_default_func
1070
- when 'openrisc'
1265
+ when 'openrisc', 'arm64'
1071
1266
  old_cp = d.c_parser
1072
1267
  d.c_parser = nil
1073
1268
  d.parse_c <<EOC
@@ -114,5 +114,44 @@ class Shellcode < ExeFormat
114
114
  }
115
115
  c
116
116
  end
117
+
118
+ # returns an assembly `db` directive for the given byte string
119
+ # example: define_data("ABCD".b) => 'db 0x41, 0x42, 0x43, 0x44'
120
+ # returns the empty string for empty input so callers can omit the directive
121
+ def self.define_data(bytes)
122
+ return '' if bytes.nil? || bytes.empty?
123
+
124
+ 'db ' + bytes.each_byte.map { |b| '0x%02x' % b }.join(', ')
125
+ end
126
+
127
+ # returns an assembly `db` directive for a NULL-terminated string
128
+ # printable ASCII runs are emitted as string literals; non-printable bytes
129
+ # break the run and are emitted as hex tokens; a trailing 0 is always appended
130
+ # example: define_cstring("ABCD".b) => 'db "ABCD", 0'
131
+ # example: define_cstring("AB\x05CD".b) => 'db "AB", 0x05, "CD", 0'
132
+ # example: define_cstring("".b) => 'db 0'
133
+ def self.define_cstring(str)
134
+ str = '' if str.nil?
135
+
136
+ parts = []
137
+ buf = String.new
138
+ flush = lambda {
139
+ unless buf.empty?
140
+ parts << '"' + buf.gsub(/[\\"]/) { |c| '\\' + c } + '"'
141
+ buf.clear
142
+ end
143
+ }
144
+ str.each_byte do |b|
145
+ if b >= 0x20 && b <= 0x7e
146
+ buf << b.chr
147
+ else
148
+ flush.call
149
+ parts << ('0x%02x' % b)
150
+ end
151
+ end
152
+ flush.call
153
+ parts << '0'
154
+ 'db ' + parts.join(', ')
155
+ end
117
156
  end
118
157
  end
@@ -230,6 +230,7 @@ class CdecompListingWidget < DrawableWidget
230
230
  # TODO _declspec
231
231
  vt.args.to_a.each_with_index { |a, idx|
232
232
  oa = curfunc.type.args.to_a[idx]
233
+ next if not oa
233
234
  oa.misc ||= {}
234
235
  a.misc ||= {}
235
236
  uan = a.misc[:unalias_name] = oa.misc[:unalias_name] ||= oa.name
@@ -31,6 +31,10 @@ class VirtualMemoryDasm < VirtualString
31
31
  !@disassembler.get_section_at(addr)
32
32
  end
33
33
 
34
+ def get_page(addr, len=@pagelength)
35
+ read_range(addr, len) if !page_invalid?(addr)
36
+ end
37
+
34
38
  # overwrite a section of the file
35
39
  def rewrite_at(addr, data)
36
40
  if e = @disassembler.get_section_at(addr)
@@ -74,6 +78,7 @@ class EmuDebugger < Debugger
74
78
  @breakpoint = {}
75
79
  @breakpoint_memory = {}
76
80
  @breakpoint_thread = {}
81
+ @cpu.initialize_emudbg(self) if @cpu.respond_to?(:initialize_emudbg)
77
82
  end
78
83
 
79
84
  def detach
@@ -160,9 +165,11 @@ class EmuDebugger < Debugger
160
165
  }.each { |k, v|
161
166
  case k
162
167
  when Indirection
168
+ raise "cannot assign value #{v}" if not v.kind_of?(::Integer)
163
169
  v = v & ((1 << (k.len*8)) - 1)
164
170
  memory_write_int(k.pointer, v, k.len)
165
171
  when Symbol
172
+ raise "cannot assign value #{v}" if not v.kind_of?(::Integer)
166
173
  set_reg_value(k, v)
167
174
  when /^dummy_metasm_/
168
175
  else
data/metasm/parse_c.rb CHANGED
@@ -3432,7 +3432,7 @@ EOH
3432
3432
  oldc = nil
3433
3433
  while c = dep_cycle[[t]]
3434
3434
  break if oldc == c
3435
- r << CRenderString.new(t, "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};") if not oldc
3435
+ r << "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};" if not oldc
3436
3436
  oldc = c
3437
3437
  c.each { |s|
3438
3438
  # XXX struct z { struct a* }; struct a { void (*foo)(struct z); };
data/metasm.gemspec CHANGED
@@ -4,7 +4,7 @@ require 'metasm'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'metasm'
7
- s.version = '1.0.5'
7
+ s.version = '1.0.6'
8
8
  s.summary =
9
9
  "Metasm is a cross-architecture assembler, disassembler, linker, and debugger."
10
10
  s.description = ""
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.homepage = 'http://metasm.cr0.org'
17
17
  s.license = 'LGPL-2.1'
18
18
 
19
- s.add_development_dependency "bundler", "~> 1.7"
20
19
  s.add_development_dependency "rake"
21
20
  s.add_development_dependency "test-unit"
22
21
  end
data/metasm.rb CHANGED
@@ -38,7 +38,7 @@ module Metasm
38
38
  'X86_64' => 'cpu/x86_64', 'Sh4' => 'cpu/sh4', 'Dalvik' => 'cpu/dalvik', 'ARC' => 'cpu/arc',
39
39
  'Python' => 'cpu/python', 'Z80' => 'cpu/z80', 'CY16' => 'cpu/cy16', 'BPF' => 'cpu/bpf', 'EBPF' => 'cpu/ebpf',
40
40
  'MSP430' => 'cpu/msp430', 'ARM64' => 'cpu/arm64', 'ST20' => 'cpu/st20', 'MCS51' => 'cpu/mcs51',
41
- 'OpenRisc' => 'cpu/openrisc', 'WebAsm' => 'cpu/webasm',
41
+ 'OpenRisc' => 'cpu/openrisc', 'WebAsm' => 'cpu/webasm', 'Dwarf' => 'cpu/dwarf',
42
42
  'C' => 'compile_c',
43
43
  'MZ' => 'exe_format/mz', 'PE' => 'exe_format/pe',
44
44
  'ELF' => 'exe_format/elf', 'COFF' => 'exe_format/coff',
@@ -25,12 +25,13 @@
25
25
  #
26
26
 
27
27
  require 'metasm'
28
+ include Metasm
28
29
  require 'optparse'
29
30
 
30
31
  $VERBOSE = true
31
32
 
32
33
  # parse arguments
33
- opts = { :sc_cpu => 'Ia32' }
34
+ opts = {}
34
35
  OptionParser.new { |opt|
35
36
  opt.banner = 'Usage: disassemble-gtk.rb [options] <executable> [<entrypoints>]'
36
37
  opt.on('--no-data-trace', 'do not backtrace memory read/write accesses') { opts[:nodatatrace] = true }
@@ -54,31 +55,42 @@ OptionParser.new { |opt|
54
55
  opt.on('-A', '--disassemble-all-entrypoints') { opts[:dasm_all] = true }
55
56
  }.parse!(ARGV)
56
57
 
58
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
59
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
60
+ opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
61
+ opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
62
+
57
63
  case exename = ARGV.shift
58
64
  when /^live:(.*)/
59
65
  t = $1
60
66
  t = t.to_i if $1 =~ /^[0-9]+$/
61
- os = Metasm::OS.current
67
+ os = OS.current
62
68
  raise 'no such target' if not target = os.find_process(t) || os.create_process(t)
63
69
  p target if $VERBOSE
64
- w = Metasm::Gui::DbgWindow.new(target.debugger, "#{target.pid}:#{target.modules[0].path rescue nil} - metasm debugger")
70
+ w = Gui::DbgWindow.new(target.debugger, "#{target.pid}:#{target.modules[0].path rescue nil} - metasm debugger")
65
71
  dbg = w.dbg_widget.dbg
72
+ when /^emu:(.*)/
73
+ t = $1
74
+ exefmt = opts[:exe_fmt] || AutoExe.orshellcode { opts[:sc_cpu] || Ia32.new }
75
+ dbgexe = exefmt.decode_file(t)
76
+ dbgexe.cpu = opts[:sc_cpu] if opts[:sc_cpu]
77
+ dbg = EmuDebugger.new(dbgexe.disassembler)
78
+ w = Gui::DbgWindow.new(dbg, "emudbg")
66
79
  when /^(tcp:|udp:)?..+:/
67
- dbg = Metasm::GdbRemoteDebugger.new(exename, opts[:sc_cpu])
68
- w = Metasm::Gui::DbgWindow.new(dbg, "remote - metasm debugger")
80
+ dbg = GdbRemoteDebugger.new(exename, opts[:sc_cpu] || Ia32.new)
81
+ w = Gui::DbgWindow.new(dbg, "remote - metasm debugger")
69
82
  else
70
- w = Metasm::Gui::DasmWindow.new("#{exename + ' - ' if exename}metasm disassembler")
83
+ w = Gui::DasmWindow.new("#{exename + ' - ' if exename}metasm disassembler")
71
84
  if exename
72
- opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
73
- opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
74
- exe = w.loadfile(exename, opts[:sc_cpu], opts[:exe_fmt])
85
+ exe = w.loadfile(exename, opts[:sc_cpu] || 'Ia32', opts[:exe_fmt])
86
+ exe.disassembler.cpu = exe.cpu = opts[:sc_cpu] if opts[:sc_cpu]
75
87
  exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
76
88
  if opts[:autoload]
77
89
  basename = exename.sub(/\.\w\w?\w?$/, '')
78
90
  opts[:map] ||= basename + '.map' if File.exist?(basename + '.map')
79
91
  opts[:cheader] ||= basename + '.h' if File.exist?(basename + '.h')
80
92
  (opts[:plugin] ||= []) << (basename + '.rb') if File.exist?(basename + '.rb')
81
- opts[:session] ||= basename + '.metasm-session'
93
+ opts[:session] ||= basename + '.metasm-session' if File.exist?(basename + '.metasm-session')
82
94
  end
83
95
  end
84
96
  end
@@ -104,6 +116,10 @@ elsif dbg
104
116
  puts "Error with plugin #{p}: #{$!.class} #{$!}"
105
117
  end
106
118
  }
119
+ if exename[0, 4] == 'emu:' and ep.first
120
+ dbg.pc = ep.first
121
+ w.dbg_widget.code.focus_addr dbg.pc
122
+ end
107
123
  end
108
124
 
109
125
  if dasm
@@ -139,5 +155,5 @@ end
139
155
 
140
156
  opts[:hookstr].to_a.each { |f| eval f }
141
157
 
142
- Metasm::Gui.main
158
+ Gui.main
143
159
 
@@ -15,7 +15,7 @@ include Metasm
15
15
  require 'optparse'
16
16
 
17
17
  # parse arguments
18
- opts = { :sc_cpu => 'Ia32' }
18
+ opts = {}
19
19
  OptionParser.new { |opt|
20
20
  opt.banner = 'Usage: disassemble.rb [options] <executable> [<entrypoints>]'
21
21
  opt.on('--no-data', 'do not display data bytes') { opts[:nodata] = true }
@@ -44,28 +44,25 @@ exename = ARGV.shift
44
44
 
45
45
  t0 = Time.now if opts[:benchmark]
46
46
 
47
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
48
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
49
+ opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
50
+
47
51
  # load the file
48
52
  if exename =~ /^live:(.*)/
49
53
  raise 'no such live target' if not target = OS.current.find_process($1)
50
54
  p target if $VERBOSE
51
- opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
52
- opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of(::String)
53
- opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
54
- exe = Shellcode.decode(target.memory, opts[:sc_cpu])
55
+ exe = Shellcode.decode(target.memory, opts[:sc_cpu] || Ia32.new)
55
56
  else
56
- opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
57
57
  opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
58
58
  if opts[:exe_fmt].kind_of?(::String)
59
59
  exefmt = opts[:exe_fmt] = Metasm.const_get(opts[:exe_fmt])
60
60
  else
61
- exefmt = opts[:exe_fmt] || AutoExe.orshellcode {
62
- opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
63
- opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
64
- opts[:sc_cpu]
65
- }
61
+ exefmt = opts[:exe_fmt] || AutoExe.orshellcode { opts[:sc_cpu] || Ia32.new }
66
62
  end
67
- exefmt = exefmt.withcpu(opts[:sc_cpu]) if exefmt.kind_of?(::Class) and exefmt.name.to_s.split('::').last == 'Shellcode'
63
+ exefmt = exefmt.withcpu(opts[:sc_cpu] || Ia32.new) if exefmt.kind_of?(::Class) and exefmt.name.to_s.split('::').last == 'Shellcode'
68
64
  exe = exefmt.decode_file(exename)
65
+ exe.cpu = opts[:sc_cpu] if opts[:sc_cpu]
69
66
  exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
70
67
  if opts[:autoload]
71
68
  basename = exename.sub(/\.\w\w?\w?$/, '')
data/samples/emudbg.rb CHANGED
@@ -84,7 +84,7 @@ pkt = EncodedData.new(eth + ip + udp + pld)
84
84
  dasm.add_section(pkt, 0x9000)
85
85
 
86
86
  # Initialize the emulator
87
- dbg = Metasm::EmuDebugger.new(dasm)
87
+ dbg = EmuDebugger.new(dasm)
88
88
  # Set the initial state of the registers
89
89
  dbg.set_reg_value(:r10, 0x8900)
90
90
  # :packet is a special register used internally by some opcodes of the EBPF cpu (packet data store/load)
@@ -36,6 +36,9 @@ when ELF
36
36
  funcnames = exe.symbols.map { |s| s.name if s.shndx == 'UNDEF' and s.type == 'FUNC' }
37
37
  opts[:hdrs] << 'stdio.h' << 'stdlib.h' << 'unistd.h'
38
38
  opts[:gcc] = true if not opts[:vs]
39
+ case exe.cpu.shortname
40
+ when 'x64'; opts[:path] << '/usr/include/x86_64-linux-gnu/'
41
+ end
39
42
  else raise "unsupported #{exe.class}"
40
43
  end
41
44
 
@@ -51,7 +54,7 @@ ARGV.each { |n|
51
54
 
52
55
  src = opts[:hdrs].map { |h| "#include <#{h}>" }.join("\n")
53
56
 
54
- parser = Ia32.new.new_cparser
57
+ parser = exe.cpu.new_cparser
55
58
  parser.prepare_gcc if opts[:gcc]
56
59
  parser.prepare_visualstudio if opts[:vs]
57
60
  pp = parser.lexer
data/samples/lindebug.rb CHANGED
@@ -357,6 +357,7 @@ class LinDebug
357
357
  raw[8,8].to_s.unpack('C*').map { |c| '%02x ' % c }.join
358
358
  when 'dw'; text << raw.unpack('S*').map { |c| '%04x ' % c }.join
359
359
  when 'dd'; text << raw.unpack('L*').map { |c| '%08x ' % c }.join
360
+ when 'dq'; text << raw.unpack('Q*').map { |c| '%016x ' % c }.join
360
361
  end
361
362
  text << ' ' << raw.unpack('C*').map { |c| (0x20..0x7e).include?(c) ? c : 0x2e }.pack('C*')
362
363
  text << Ansi::ClearLineAfter << "\n"
@@ -655,6 +656,7 @@ class LinDebug
655
656
  @command['db'] = lambda { |str| @datafmt = 'db' ; @dataptr = @dbg.resolve(str) if str.length > 0 }
656
657
  @command['dw'] = lambda { |str| @datafmt = 'dw' ; @dataptr = @dbg.resolve(str) if str.length > 0 }
657
658
  @command['dd'] = lambda { |str| @datafmt = 'dd' ; @dataptr = @dbg.resolve(str) if str.length > 0 }
659
+ @command['dq'] = lambda { |str| @datafmt = 'dq' ; @dataptr = @dbg.resolve(str) if str.length > 0 }
658
660
  @command['r'] = lambda { |str|
659
661
  r, str = str.split(/\s+/, 2)
660
662
  if r == 'fl'
@@ -700,17 +702,29 @@ end
700
702
 
701
703
  if $0 == __FILE__
702
704
  require 'optparse'
703
- opts = { :sc_cpu => 'Ia32' }
705
+ opts = {}
704
706
  OptionParser.new { |opt|
705
707
  opt.on('-m map', '--map filemap') { |f| opts[:filemap] = f }
706
708
  opt.on('--cpu cpu') { |c| opts[:sc_cpu] = c }
707
709
  }.parse!(ARGV)
708
710
 
709
711
  case ARGV.first
712
+ when /^emu:(.*)/
713
+ exepath = $1
714
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
715
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
716
+ opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
717
+ exe = Metasm::AutoExe.orshellcode { opts[:sc_cpu] || Metasm::Ia32.new }.decode_file(exepath)
718
+ exe.cpu = opts[:sc_cpu] if opts[:sc_cpu]
719
+ dbg = Metasm::EmuDebugger.new(exe.disassembler)
720
+ if ARGV[1]
721
+ dbg.pc = Integer(ARGV[1])
722
+ end
710
723
  when /^(tcp:|udp:)?..+:/, /^ser:/
711
724
  opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
725
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
712
726
  opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
713
- dbg = Metasm::GdbRemoteDebugger.new(ARGV.first, opts[:sc_cpu])
727
+ dbg = Metasm::GdbRemoteDebugger.new(ARGV.first, opts[:sc_cpu] || Metasm::Ia32.new)
714
728
  else
715
729
  dbg = Metasm::LinDebugger.new(ARGV.join(' '))
716
730
  end