metasm 1.0.0
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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/exe_format/main'
|
|
8
|
+
|
|
9
|
+
module Metasm
|
|
10
|
+
# a shellcode is a simple sequence of instructions
|
|
11
|
+
class Shellcode < ExeFormat
|
|
12
|
+
# the array of source elements (Instr/Data etc)
|
|
13
|
+
attr_accessor :source
|
|
14
|
+
# the base address of the shellcode (nil if unspecified)
|
|
15
|
+
attr_accessor :base_addr
|
|
16
|
+
|
|
17
|
+
def initialize(cpu=nil, base_addr=nil)
|
|
18
|
+
@base_addr = base_addr
|
|
19
|
+
@source = []
|
|
20
|
+
super(cpu)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse_init
|
|
24
|
+
@cursource = @source
|
|
25
|
+
super()
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# allows definition of the base address
|
|
29
|
+
def parse_parser_instruction(instr)
|
|
30
|
+
case instr.raw.downcase
|
|
31
|
+
when '.base', '.baseaddr', '.base_addr'
|
|
32
|
+
# ".base_addr <expression>"
|
|
33
|
+
# expression should #reduce to integer
|
|
34
|
+
@lexer.skip_space
|
|
35
|
+
raise instr, 'syntax error' if not @base_addr = Expression.parse(@lexer).reduce
|
|
36
|
+
raise instr, 'syntax error' if tok = @lexer.nexttok and tok.type != :eol
|
|
37
|
+
else super(instr)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def get_section_at(addr)
|
|
42
|
+
base = @base_addr || 0
|
|
43
|
+
if not addr.kind_of? Integer
|
|
44
|
+
[@encoded, addr] if @encoded.ptr = @encoded.export[addr]
|
|
45
|
+
elsif addr >= base and addr < base + @encoded.virtsize
|
|
46
|
+
@encoded.ptr = addr - base
|
|
47
|
+
[@encoded, addr]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def each_section
|
|
52
|
+
yield @encoded, (@base_addr || 0)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def addr_to_fileoff(addr)
|
|
56
|
+
addr - (base_addr || 0)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def fileoff_to_addr(foff)
|
|
60
|
+
foff + (base_addr || 0)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# encodes the source found in self.source
|
|
64
|
+
# appends it to self.encoded
|
|
65
|
+
# clears self.source
|
|
66
|
+
# the optional parameter may contain a binding used to fixup! self.encoded
|
|
67
|
+
# uses self.base_addr if it exists
|
|
68
|
+
def assemble(*a)
|
|
69
|
+
parse(*a) if not a.empty?
|
|
70
|
+
@encoded << assemble_sequence(@source, @cpu)
|
|
71
|
+
@source.clear
|
|
72
|
+
encode
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def encode(binding={})
|
|
76
|
+
@encoded.fixup! binding
|
|
77
|
+
@encoded.fixup @encoded.binding(@base_addr)
|
|
78
|
+
@encoded.fill @encoded.rawsize
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def decode
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.disassemble(cpu, str, eip=0)
|
|
86
|
+
sc = decode(str, cpu)
|
|
87
|
+
sc.disassemble(eip)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def init_disassembler
|
|
91
|
+
d = super()
|
|
92
|
+
d.function[:default] = @cpu.disassembler_default_func
|
|
93
|
+
d
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def compile_setsection(src, section)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def dump_section_header(addr, edata)
|
|
100
|
+
''
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def get_default_entrypoints
|
|
104
|
+
[@base_addr || 0]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# returns a virtual subclass of Shellcode whose cpu_from_headers will return cpu
|
|
108
|
+
def self.withcpu(cpu)
|
|
109
|
+
c = Class.new(self)
|
|
110
|
+
c.send(:define_method, :cpu_from_headers) { cpu }
|
|
111
|
+
c
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
require 'metasm/exe_format/main'
|
|
7
|
+
require 'metasm/encode'
|
|
8
|
+
require 'metasm/decode'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class XCoff < ExeFormat
|
|
12
|
+
FLAGS = { 1 => 'RELFLG', 2 => 'EXEC', 4 => 'LNNO',
|
|
13
|
+
0x200 => 'AR32W', 0x400 => 'PATCH', 0x1000 => 'DYNLOAD',
|
|
14
|
+
0x2000 => 'SHROBJ', 0x4000 => 'LOADONLY' }
|
|
15
|
+
|
|
16
|
+
SECTION_FLAGS = { 8 => 'PAD', 0x20 => 'TEXT', 0x40 => 'DATA', 0x80 => 'BSS',
|
|
17
|
+
0x100 => 'EXCEPT', 0x200 => 'INFO', 0x1000 => 'LOADER',
|
|
18
|
+
0x2000 => 'DEBUG', 0x4000 => 'TYPCHK', 0x8000 => 'OVRFLO' }
|
|
19
|
+
|
|
20
|
+
class SerialStruct < Metasm::SerialStruct
|
|
21
|
+
new_int_field :xword, :xhalf
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Header < SerialStruct
|
|
25
|
+
mem :sig, 2
|
|
26
|
+
decode_hook { |exe, hdr|
|
|
27
|
+
exe.endianness, exe.intsize =
|
|
28
|
+
case @sig
|
|
29
|
+
when "\1\xdf"; [:big, 32]
|
|
30
|
+
when "\xdf\1"; [:little, 32]
|
|
31
|
+
when "\1\xef"; [:big, 64]
|
|
32
|
+
when "\xef\1"; [:little, 64]
|
|
33
|
+
else raise InvalidExeFormat, "invalid a.out signature"
|
|
34
|
+
end
|
|
35
|
+
}
|
|
36
|
+
half :nsec
|
|
37
|
+
word :timdat
|
|
38
|
+
xword :symptr
|
|
39
|
+
word :nsym
|
|
40
|
+
half :opthdr
|
|
41
|
+
half :flags
|
|
42
|
+
fld_bits :flags, FLAGS
|
|
43
|
+
|
|
44
|
+
def set_default_values(xcoff)
|
|
45
|
+
@sig ||= case [xcoff.endianness, xcoff.intsize]
|
|
46
|
+
when [:big, 32]; "\1\xdf"
|
|
47
|
+
when [:little, 32]; "\xdf\1"
|
|
48
|
+
when [:big, 64]; "\1\xef"
|
|
49
|
+
when [:little, 64]; "\xef\1"
|
|
50
|
+
end
|
|
51
|
+
@nsec ||= xcoff.sections.size
|
|
52
|
+
@symptr ||= xcoff.symbols ? xcoff.new_label('symptr') : 0
|
|
53
|
+
@nsym ||= xcoff.symbols ? xcoff.symbols.length : 0
|
|
54
|
+
@opthdr ||= xcoff.optheader ? OptHeader.size(xcoff) : 0
|
|
55
|
+
super(xcoff)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class OptHeader < SerialStruct
|
|
60
|
+
halfs :magic, :vstamp
|
|
61
|
+
xwords :tsize, :dsize, :bsize, :entry, :text_start, :data_start, :toc
|
|
62
|
+
halfs :snentry, :sntext, :sndata, :sntoc, :snloader, :snbss, :aligntext, :aligndata, :modtype, :cpu
|
|
63
|
+
xwords :maxstack, :maxdata
|
|
64
|
+
new_field(:res, lambda { |exe, me| exe.encoded.read(exe.intsize == 32 ? 8 : 120) }, lambda { |exe, me, val| val }, '')
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def self.size(xcoff)
|
|
68
|
+
xcoff.intsize == 32 ? 2*2+7*4+10*2+2*4+2+8 : 2*2+7*8+10*2+2*8+2+120
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def set_default_values(xcoff)
|
|
72
|
+
@vstamp ||= 1
|
|
73
|
+
@snentry ||= 1
|
|
74
|
+
@sntext ||= 1
|
|
75
|
+
@sndata ||= 2
|
|
76
|
+
@sntoc ||= 3
|
|
77
|
+
@snloader||= 4
|
|
78
|
+
@snbss ||= 5
|
|
79
|
+
super(xcoff)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class Section < SerialStruct
|
|
84
|
+
str :name, 8
|
|
85
|
+
xwords :paddr, :vaddr, :size, :scnptr, :relptr, :lnnoptr
|
|
86
|
+
xhalfs :nreloc, :nlnno, :flags
|
|
87
|
+
fld_bits :flags, SECTION_FLAGS
|
|
88
|
+
|
|
89
|
+
def set_defalut_values(xcoff)
|
|
90
|
+
@name ||= @flags.kind_of?(::Array) ? ".#{@flags.first.to_s.downcase}" : ''
|
|
91
|
+
@vaddr ||= @paddr ? @paddr : @encoded ? xcoff.label_at(@encoded, 0, 's_vaddr') : 0
|
|
92
|
+
@paddr ||= @vaddr
|
|
93
|
+
@size ||= @encoded ? @encoded.size : 0
|
|
94
|
+
@scnptr ||= xcoff.new_label('s_scnptr')
|
|
95
|
+
super(xcoff)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# basic immediates decoding functions
|
|
100
|
+
def decode_half( edata = @encoded) edata.decode_imm(:u16, @endianness) end
|
|
101
|
+
def decode_word( edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
|
102
|
+
def decode_xhalf(edata = @encoded) edata.edoced_imm((@intsize == 32 ? :u16 : :u32), @endianness) end
|
|
103
|
+
def decode_xword(edata = @encoded) edata.decode_imm((@intsize == 32 ? :u32 : :u64), @endianness) end
|
|
104
|
+
def encode_half(w) Expression[w].encode(:u16, @endianness) end
|
|
105
|
+
def encode_word(w) Expression[w].encode(:u32, @endianness) end
|
|
106
|
+
def encode_xhalf(w) Expression[w].encode((@intsize == 32 ? :u16 : :u32), @endianness) end
|
|
107
|
+
def encode_xword(w) Expression[w].encode((@intsize == 32 ? :u32 : :u64), @endianness) end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
attr_accessor :header, :segments, :relocs, :intsize, :endianness
|
|
111
|
+
|
|
112
|
+
def initialize(cpu=nil)
|
|
113
|
+
@header = Header.new
|
|
114
|
+
@sections = []
|
|
115
|
+
if @cpu
|
|
116
|
+
@intsize = @cpu.size
|
|
117
|
+
@endianness = @cpu.endianness
|
|
118
|
+
else
|
|
119
|
+
@intsize = 32
|
|
120
|
+
@endianness = :little
|
|
121
|
+
end
|
|
122
|
+
super(cpu)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def decode_header(off = 0)
|
|
126
|
+
@encoded.ptr = off
|
|
127
|
+
@header.decode(self)
|
|
128
|
+
if @header.opthdr != 0
|
|
129
|
+
@optheader = OptHeader.decode(self)
|
|
130
|
+
end
|
|
131
|
+
@header.nsec.times { @sections << Section.decode(self) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def decode
|
|
135
|
+
decode_header
|
|
136
|
+
@sections.each { |s| s.encoded = @encoded[s.scnptr, s.size] }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def encode
|
|
140
|
+
@encoded = EncodedData.new
|
|
141
|
+
@encoded << @header.encode(self)
|
|
142
|
+
@encoded << @optheader.encode(self) if @optheader
|
|
143
|
+
@sections.each { |s| @encoded << s.encode(self) }
|
|
144
|
+
va = @encoded.size
|
|
145
|
+
binding = {}
|
|
146
|
+
@sections.each { |s|
|
|
147
|
+
if s.scnptr.kind_of? ::String
|
|
148
|
+
binding[s.scnptr] = @encoded.size
|
|
149
|
+
else
|
|
150
|
+
raise 'scnptr too low' if @encoded.virtsize > s.scnptr
|
|
151
|
+
@encoded.virtsize = s.scnptr
|
|
152
|
+
end
|
|
153
|
+
va = (va + 4096 - 1)/4096*4096
|
|
154
|
+
if s.vaddr.kind_of? ::String
|
|
155
|
+
binding[s.vaddr] = va
|
|
156
|
+
else
|
|
157
|
+
va = s.vaddr
|
|
158
|
+
end
|
|
159
|
+
binding.update s.encoded.binding(va)
|
|
160
|
+
va += s.encoded.size
|
|
161
|
+
@encoded << s.encoded
|
|
162
|
+
}
|
|
163
|
+
@encoded.fixup!(binding)
|
|
164
|
+
@encoded.data
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
data/lib/metasm/gui.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
backend = case ENV['METASM_GUI']
|
|
2
|
+
when 'gtk'; 'gtk'
|
|
3
|
+
when 'qt'; 'qt'
|
|
4
|
+
when 'win32'; 'win32'
|
|
5
|
+
else
|
|
6
|
+
puts "Unsupported METASM_GUI #{ENV['METASM_GUI'].inspect}" if $VERBOSE and ENV['METASM_GUI']
|
|
7
|
+
if RUBY_PLATFORM =~ /(i.86|x(86_)?64)-(mswin|mingw|cygwin)/i
|
|
8
|
+
'win32'
|
|
9
|
+
else
|
|
10
|
+
begin
|
|
11
|
+
require 'gtk2'
|
|
12
|
+
'gtk'
|
|
13
|
+
rescue LoadError
|
|
14
|
+
#begin
|
|
15
|
+
# require 'Qt4'
|
|
16
|
+
# 'qt'
|
|
17
|
+
#rescue LoadError
|
|
18
|
+
raise LoadError, 'No GUI ruby binding installed - please install libgtk2-ruby'
|
|
19
|
+
#end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
require "metasm/gui/#{backend}"
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
module Metasm
|
|
7
|
+
module Gui
|
|
8
|
+
class CStructWidget < DrawableWidget
|
|
9
|
+
attr_accessor :dasm, :view_x, :view_y
|
|
10
|
+
|
|
11
|
+
def initialize_widget(dasm, parent_widget)
|
|
12
|
+
@dasm = dasm
|
|
13
|
+
@parent_widget = parent_widget
|
|
14
|
+
|
|
15
|
+
@line_text_col = [] # each line is [[:col, 'text'], [:col, 'text']]
|
|
16
|
+
@line_text = []
|
|
17
|
+
@line_dereference = [] # linenr => [addr, struct] (args to focus_addr)
|
|
18
|
+
@curaddr = nil
|
|
19
|
+
@curstruct = nil
|
|
20
|
+
@tabwidth = 8
|
|
21
|
+
@view_x = @view_y = 0
|
|
22
|
+
@caret_x = @caret_y = 0
|
|
23
|
+
@cwidth = @cheight = 1 # widget size in chars
|
|
24
|
+
@structdepth = 2
|
|
25
|
+
|
|
26
|
+
@default_color_association = { :text => :black, :keyword => :blue, :caret => :black,
|
|
27
|
+
:background => :white, :hl_word => :palered, :comment => :darkblue }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def click(x, y)
|
|
31
|
+
@caret_x = (x-1).to_i / @font_width + @view_x
|
|
32
|
+
@caret_y = y.to_i / @font_height + @view_y
|
|
33
|
+
update_caret
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def rightclick(x, y)
|
|
37
|
+
click(x, y)
|
|
38
|
+
@parent_widget.clone_window(@hl_word) if @hl_word
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def doubleclick(x, y)
|
|
42
|
+
click(x, y)
|
|
43
|
+
keypress(:enter)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def mouse_wheel(dir, x, y)
|
|
47
|
+
case dir
|
|
48
|
+
when :up
|
|
49
|
+
if @caret_y > 0
|
|
50
|
+
@view_y -= 4
|
|
51
|
+
@view_y = 0 if @view_y < 0
|
|
52
|
+
@caret_y -= 4
|
|
53
|
+
@caret_y = 0 if @caret_y < 0
|
|
54
|
+
end
|
|
55
|
+
when :down
|
|
56
|
+
if @caret_y < @line_text.length - 1
|
|
57
|
+
@view_y += 4
|
|
58
|
+
@caret_y += 4
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
redraw
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def paint
|
|
65
|
+
@cwidth = width/@font_width
|
|
66
|
+
@cheight = height/@font_height
|
|
67
|
+
|
|
68
|
+
# adjust viewport to cursor
|
|
69
|
+
sz_x = @line_text.map { |l| l.length }.max.to_i + 1
|
|
70
|
+
sz_y = @line_text.length.to_i + 1
|
|
71
|
+
@view_x = @caret_x - @cwidth + 1 if @caret_x > @view_x + @cwidth - 1
|
|
72
|
+
@view_x = @caret_x if @caret_x < @view_x
|
|
73
|
+
@view_x = sz_x - @cwidth - 1 if @view_x >= sz_x - @cwidth
|
|
74
|
+
@view_x = 0 if @view_x < 0
|
|
75
|
+
|
|
76
|
+
@view_y = @caret_y - @cheight + 1 if @caret_y > @view_y + @cheight - 1
|
|
77
|
+
@view_y = @caret_y if @caret_y < @view_y
|
|
78
|
+
@view_y = sz_y - @cheight - 1 if @view_y >= sz_y - @cheight
|
|
79
|
+
@view_y = 0 if @view_y < 0
|
|
80
|
+
|
|
81
|
+
# current cursor position
|
|
82
|
+
x = 1
|
|
83
|
+
y = 0
|
|
84
|
+
|
|
85
|
+
@line_text_col[@view_y, @cheight + 1].each { |l|
|
|
86
|
+
cx = 0
|
|
87
|
+
l.each { |c, t|
|
|
88
|
+
cx += t.length
|
|
89
|
+
if cx-t.length > @view_x + @cwidth + 1
|
|
90
|
+
elsif cx < @view_x
|
|
91
|
+
else
|
|
92
|
+
t = t[(@view_x - cx + t.length)..-1] if cx-t.length < @view_x
|
|
93
|
+
if @hl_word
|
|
94
|
+
stmp = t
|
|
95
|
+
pre_x = 0
|
|
96
|
+
while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
|
|
97
|
+
s1, s2 = $1, $2
|
|
98
|
+
pre_x += s1.length*@font_width
|
|
99
|
+
hl_w = s2.length*@font_width
|
|
100
|
+
draw_rectangle_color(:hl_word, x+pre_x, y, hl_w, @font_height)
|
|
101
|
+
pre_x += hl_w
|
|
102
|
+
stmp = stmp[s1.length+s2.length..-1]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
draw_string_color(c, x, y, t)
|
|
106
|
+
x += t.length * @font_width
|
|
107
|
+
end
|
|
108
|
+
}
|
|
109
|
+
x = 1
|
|
110
|
+
y += @font_height
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if focus?
|
|
114
|
+
# draw caret
|
|
115
|
+
cx = (@caret_x-@view_x)*@font_width+1
|
|
116
|
+
cy = (@caret_y-@view_y)*@font_height
|
|
117
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
@oldcaret_x, @oldcaret_y = @caret_x, @caret_y
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def keypress(key)
|
|
124
|
+
case key
|
|
125
|
+
when :left
|
|
126
|
+
if @caret_x >= 1
|
|
127
|
+
@caret_x -= 1
|
|
128
|
+
update_caret
|
|
129
|
+
end
|
|
130
|
+
when :up
|
|
131
|
+
if @caret_y > 0
|
|
132
|
+
@caret_y -= 1
|
|
133
|
+
update_caret
|
|
134
|
+
end
|
|
135
|
+
when :right
|
|
136
|
+
if @caret_x < @line_text[@caret_y].to_s.length
|
|
137
|
+
@caret_x += 1
|
|
138
|
+
update_caret
|
|
139
|
+
end
|
|
140
|
+
when :down
|
|
141
|
+
if @caret_y < @line_text.length
|
|
142
|
+
@caret_y += 1
|
|
143
|
+
update_caret
|
|
144
|
+
end
|
|
145
|
+
when :home
|
|
146
|
+
@caret_x = @line_text[@caret_y].to_s[/^\s*/].length
|
|
147
|
+
update_caret
|
|
148
|
+
when :end
|
|
149
|
+
@caret_x = @line_text[@caret_y].to_s.length
|
|
150
|
+
update_caret
|
|
151
|
+
when :pgup
|
|
152
|
+
@caret_y -= @cheight/2
|
|
153
|
+
@caret_y = 0 if @caret_y < 0
|
|
154
|
+
update_caret
|
|
155
|
+
when :pgdown
|
|
156
|
+
@caret_y += @cheight/2
|
|
157
|
+
@caret_y = @line_text.length if @caret_y > @line_text.length
|
|
158
|
+
update_caret
|
|
159
|
+
when :enter
|
|
160
|
+
if l = @line_dereference[@caret_y]
|
|
161
|
+
if @parent_widget
|
|
162
|
+
@parent_widget.focus_addr(l[0], :cstruct, false, l[1])
|
|
163
|
+
else
|
|
164
|
+
focus_addr(l[0], l[1])
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
when ?+
|
|
168
|
+
@structdepth += 1
|
|
169
|
+
gui_update
|
|
170
|
+
when ?-
|
|
171
|
+
@structdepth -= 1
|
|
172
|
+
gui_update
|
|
173
|
+
when ?/
|
|
174
|
+
@structdepth = 1
|
|
175
|
+
gui_update
|
|
176
|
+
when ?*
|
|
177
|
+
@structdepth = 50
|
|
178
|
+
gui_update
|
|
179
|
+
when ?l
|
|
180
|
+
liststructs
|
|
181
|
+
when ?t
|
|
182
|
+
inputbox('new struct name to use', :text => (@curstruct.name rescue '')) { |n|
|
|
183
|
+
lst = @dasm.c_parser.toplevel.struct.keys.grep(String)
|
|
184
|
+
if fn = lst.find { |ln| ln == n } || lst.find { |ln| ln.downcase == n.downcase }
|
|
185
|
+
focus_addr(@curaddr, @dasm.c_parser.toplevel.struct[fn])
|
|
186
|
+
else
|
|
187
|
+
lst = @dasm.c_parser.toplevel.symbol.keys.grep(String).find_all { |ln|
|
|
188
|
+
s = @dasm.c_parser.toplevel.symbol[ln]
|
|
189
|
+
s.kind_of?(C::TypeDef) and s.untypedef.kind_of?(C::Union)
|
|
190
|
+
}
|
|
191
|
+
if fn = lst.find { |ln| ln == n } || lst.find { |ln| ln.downcase == n.downcase }
|
|
192
|
+
focus_addr(@curaddr, @dasm.c_parser.toplevel.symbol[fn].untypedef)
|
|
193
|
+
else
|
|
194
|
+
liststructs(n)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
}
|
|
198
|
+
else return false
|
|
199
|
+
end
|
|
200
|
+
true
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def liststructs(partname=nil)
|
|
204
|
+
tl = @dasm.c_parser.toplevel
|
|
205
|
+
list = [['name', 'size']]
|
|
206
|
+
list += tl.struct.keys.grep(String).sort.map { |stn|
|
|
207
|
+
next if partname and stn !~ /#{partname}/i
|
|
208
|
+
st = tl.struct[stn]
|
|
209
|
+
[stn, @dasm.c_parser.sizeof(st)] if st.members
|
|
210
|
+
}.compact
|
|
211
|
+
list += tl.symbol.keys.grep(String).sort.map { |stn|
|
|
212
|
+
next if partname and stn !~ /#{partname}/i
|
|
213
|
+
st = tl.symbol[stn]
|
|
214
|
+
next unless st.kind_of?(C::TypeDef) and st.untypedef.kind_of?(C::Union)
|
|
215
|
+
[stn, @dasm.c_parser.sizeof(st)] if st.untypedef.members
|
|
216
|
+
}.compact
|
|
217
|
+
|
|
218
|
+
if partname and list.length == 2
|
|
219
|
+
focus_addr(@curaddr, tl.struct[list[1][0]] || tl.symbol[list[1][0]].untypedef)
|
|
220
|
+
return
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
listwindow('structs', list) { |stn|
|
|
224
|
+
focus_addr(@curaddr, tl.struct[stn[0]] || tl.symbol[stn[0]].untypedef)
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def get_cursor_pos
|
|
229
|
+
[@curaddr, @curstruct, @caret_x, @caret_y, @view_y]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def set_cursor_pos(p)
|
|
233
|
+
focus_addr p[0], p[1]
|
|
234
|
+
@caret_x, @caret_y, @view_y = p[2, 3]
|
|
235
|
+
update_caret
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# hint that the caret moved
|
|
239
|
+
# redraws the caret, change the hilighted word, redraw if needed
|
|
240
|
+
def update_caret
|
|
241
|
+
if @caret_x < @view_x or @caret_x >= @view_x + @cwidth or @caret_y < @view_y or @caret_y >= @view_y + @cheight
|
|
242
|
+
redraw
|
|
243
|
+
elsif update_hl_word(@line_text[@caret_y], @caret_x)
|
|
244
|
+
redraw
|
|
245
|
+
else
|
|
246
|
+
invalidate_caret(@oldcaret_x-@view_x, @oldcaret_y-@view_y)
|
|
247
|
+
invalidate_caret(@caret_x-@view_x, @caret_y-@view_y)
|
|
248
|
+
end
|
|
249
|
+
@oldcaret_x, @oldcaret_y = @caret_x, @caret_y
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# focus on addr
|
|
253
|
+
# returns true on success
|
|
254
|
+
def focus_addr(addr, struct=@curstruct)
|
|
255
|
+
return if @parent_widget and not addr = @parent_widget.normalize(addr)
|
|
256
|
+
@curaddr = addr
|
|
257
|
+
@curstruct = struct
|
|
258
|
+
@caret_x = @caret_y = 0
|
|
259
|
+
gui_update
|
|
260
|
+
true
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# returns the address of the data under the cursor
|
|
264
|
+
def current_address
|
|
265
|
+
@curaddr
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def render_struct(obj=nil, off=nil, maxdepth=@structdepth)
|
|
269
|
+
render = lambda { |str, col|
|
|
270
|
+
if @line_text_col.last[0] == col
|
|
271
|
+
@line_text_col.last[1] << str
|
|
272
|
+
else
|
|
273
|
+
@line_text_col.last << [col, str]
|
|
274
|
+
end
|
|
275
|
+
}
|
|
276
|
+
indent = ' ' * @tabwidth
|
|
277
|
+
nl = lambda {
|
|
278
|
+
@line_text_col << []
|
|
279
|
+
render[indent * [@structdepth - maxdepth, 0].max, :text]
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if not obj
|
|
283
|
+
@line_text_col = [[]]
|
|
284
|
+
@line_dereference = []
|
|
285
|
+
|
|
286
|
+
struct = @curstruct
|
|
287
|
+
if str = @dasm.get_section_at(@curaddr)
|
|
288
|
+
obj = @dasm.c_parser.decode_c_struct(struct, str[0].read(@dasm.c_parser.sizeof(struct)))
|
|
289
|
+
else
|
|
290
|
+
render["/* unmapped area #{Expression[@curaddr]} */", :text]
|
|
291
|
+
return
|
|
292
|
+
end
|
|
293
|
+
else
|
|
294
|
+
struct = obj.struct
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
if maxdepth <= 0
|
|
298
|
+
render['{ /* type "+" to expand */ }', :text]
|
|
299
|
+
return
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# from AllocCStruct#to_s
|
|
303
|
+
if struct.kind_of?(C::Array)
|
|
304
|
+
render["#{struct.type} ar_#{Expression[@curaddr]}[#{struct.length}] = ", :text] if not off
|
|
305
|
+
mlist = (0...struct.length)
|
|
306
|
+
el = @dasm.c_parser.sizeof(struct.type)
|
|
307
|
+
fldoff = mlist.inject({}) { |h, i| h.update i => i*el }
|
|
308
|
+
elsif struct.kind_of?(C::Struct)
|
|
309
|
+
render["struct #{struct.name || '_'} st_#{Expression[@curaddr]} = ", :text] if not off
|
|
310
|
+
fldoff = struct.fldoffset
|
|
311
|
+
fbo = struct.fldbitoffset || {}
|
|
312
|
+
else
|
|
313
|
+
render["union #{struct.name || '_'} un_#{Expression[@curaddr]} = ", :text] if not off
|
|
314
|
+
end
|
|
315
|
+
mlist ||= struct.members
|
|
316
|
+
render['{', :text]
|
|
317
|
+
mlist.each { |k|
|
|
318
|
+
if k.kind_of? C::Variable
|
|
319
|
+
ct = k.type
|
|
320
|
+
curoff = off.to_i + (fldoff && k.name ? fldoff[k.name].to_i : struct.offsetof(@dasm.c_parser, k))
|
|
321
|
+
val = obj[k]
|
|
322
|
+
else
|
|
323
|
+
ct = struct.type
|
|
324
|
+
curoff = off.to_i + fldoff[k].to_i
|
|
325
|
+
val = obj[k]
|
|
326
|
+
end
|
|
327
|
+
nl[]
|
|
328
|
+
render[indent, :text]
|
|
329
|
+
render[k.kind_of?(Integer) ? "[#{k}]" : ".#{k.name || '?'}", :text]
|
|
330
|
+
render[' = ', :text]
|
|
331
|
+
if val.kind_of?(Integer)
|
|
332
|
+
if ct.pointer? and ct.pointed.untypedef.kind_of?(C::Union)
|
|
333
|
+
@line_dereference[@line_text_col.length-1] = [val, ct.pointed.untypedef]
|
|
334
|
+
end
|
|
335
|
+
if val >= 0x100
|
|
336
|
+
val = '0x%X' % val
|
|
337
|
+
elsif val <= -0x100
|
|
338
|
+
val = '-0x%X' % -val
|
|
339
|
+
else
|
|
340
|
+
val = val.to_s
|
|
341
|
+
end
|
|
342
|
+
elsif val.kind_of?(C::AllocCStruct)
|
|
343
|
+
render_struct(val, curoff, maxdepth-1)
|
|
344
|
+
next
|
|
345
|
+
elsif not val
|
|
346
|
+
val = 'NULL' # pointer with NULL value
|
|
347
|
+
else
|
|
348
|
+
raise "unknown value #{val.inspect}"
|
|
349
|
+
end
|
|
350
|
+
render[val, :text]
|
|
351
|
+
render[',', :text]
|
|
352
|
+
render[' // +%x' % curoff, :comment]
|
|
353
|
+
}
|
|
354
|
+
nl[]
|
|
355
|
+
render['}', :text]
|
|
356
|
+
render[(off ? ',' : ';'), :text]
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def gui_update
|
|
361
|
+
if @curstruct
|
|
362
|
+
render_struct
|
|
363
|
+
else
|
|
364
|
+
@line_text_col = [[[:text, '/* no struct selected (list with "l") */']]]
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
@line_text = @line_text_col.map { |l| l.map { |c, s| s }.join }
|
|
368
|
+
update_caret
|
|
369
|
+
redraw
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|