metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|