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,199 @@
|
|
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 CoverageWidget < DrawableWidget
|
9
|
+
attr_accessor :dasm, :sections, :pixel_w, :pixel_h
|
10
|
+
|
11
|
+
# TODO wheel -> zoom, dragdrop -> scroll?(zoomed)
|
12
|
+
def initialize_widget(dasm, parent_widget)
|
13
|
+
@dasm = dasm
|
14
|
+
@parent_widget = parent_widget
|
15
|
+
|
16
|
+
@curaddr = 0
|
17
|
+
@pixel_w = @pixel_h = 2 # use a font ?
|
18
|
+
@sections = []
|
19
|
+
@section_x = []
|
20
|
+
@slave = nil # another dasmwidget whose curaddr is kept sync
|
21
|
+
|
22
|
+
@default_color_association = { :caret => :yellow, :caret_col => :darkyellow,
|
23
|
+
:background => :palegrey, :code => :red, :data => :blue }
|
24
|
+
end
|
25
|
+
|
26
|
+
def click(x, y)
|
27
|
+
x, y = x.to_i - 1, y.to_i
|
28
|
+
@sections.zip(@section_x).each { |(a, l, seq), (sx, sxe)|
|
29
|
+
if x >= sx and x < sxe+@pixel_w
|
30
|
+
@curaddr = a + (x-sx)/@pixel_w*@byte_per_col + (y/@pixel_h-@spacing)*@byte_per_col/@col_height
|
31
|
+
@slave.focus_addr(@curaddr) if @slave rescue @slave=nil
|
32
|
+
redraw
|
33
|
+
break
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def doubleclick(x, y)
|
39
|
+
click(x, y)
|
40
|
+
cw = @parent_widget.clone_window(@curaddr, :listing)
|
41
|
+
@slave = cw.dasm_widget
|
42
|
+
@slave.focus_changed_callback = lambda { redraw rescue @slave.focus_changed_callback = nil }
|
43
|
+
end
|
44
|
+
alias rightclick doubleclick
|
45
|
+
|
46
|
+
def mouse_wheel(dir, x, y)
|
47
|
+
# TODO zoom ?
|
48
|
+
case dir
|
49
|
+
when :up
|
50
|
+
when :down
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def paint
|
55
|
+
@curaddr = @slave.curaddr if @slave and @slave.curaddr rescue @slave=nil
|
56
|
+
|
57
|
+
@spacing = 4 # pixels left for borders / inter-section
|
58
|
+
|
59
|
+
@col_height = height/@pixel_h - 2*@spacing # pixels per column
|
60
|
+
@col_height = 1 if @col_height < 1
|
61
|
+
|
62
|
+
cols = width/@pixel_w - 2*@spacing
|
63
|
+
cols -= (@sections.length-1) * (@spacing+1) # space+1: last col of each section may be only 1byte long
|
64
|
+
cols = 64 if cols < 64
|
65
|
+
|
66
|
+
# find how much bytes we must stuff per pixel so that it fits in the window
|
67
|
+
bytes = @sections.map { |a, l, seq| l }.inject(0) { |a, b| a+b }
|
68
|
+
@byte_per_col = (bytes / cols + @col_height) / @col_height * @col_height
|
69
|
+
@byte_per_col = @col_height if @byte_per_col < @col_height
|
70
|
+
|
71
|
+
x = @spacing*@pixel_w
|
72
|
+
ybase = @spacing*@pixel_h
|
73
|
+
|
74
|
+
# draws a rectangle covering h1 to h2 in y, of width w
|
75
|
+
# advances x as needed
|
76
|
+
draw_rect = lambda { |h1, h2, rw|
|
77
|
+
h2 += 1
|
78
|
+
draw_rectangle(x, ybase+@pixel_h*h1, @pixel_w*rw, @pixel_h*(h2-h1))
|
79
|
+
rw -= 1 if h2 != @col_height
|
80
|
+
x += rw*@pixel_w
|
81
|
+
}
|
82
|
+
|
83
|
+
# draws rectangles to cover o1 to o2
|
84
|
+
draw = lambda { |o1, o2|
|
85
|
+
next if o1 > o2
|
86
|
+
o1_ = o1
|
87
|
+
|
88
|
+
o1 /= @byte_per_col / @col_height
|
89
|
+
o2 /= @byte_per_col / @col_height
|
90
|
+
|
91
|
+
o11 = o1 % @col_height
|
92
|
+
o12 = o1 / @col_height
|
93
|
+
o21 = o2 % @col_height
|
94
|
+
o22 = o2 / @col_height
|
95
|
+
|
96
|
+
p11 = (o1_ - 1) / (@byte_per_col / @col_height) % @col_height
|
97
|
+
x -= @pixel_w if o11 == @col_height-1 and o11 == p11
|
98
|
+
|
99
|
+
if o11 > 0
|
100
|
+
draw_rect[o11, (o12 == o22 ? o21 : @col_height-1), 1]
|
101
|
+
next if o12 == o22
|
102
|
+
o12 += 1
|
103
|
+
end
|
104
|
+
|
105
|
+
if o12 < o22
|
106
|
+
draw_rect[0, @col_height-1, o22-o12]
|
107
|
+
end
|
108
|
+
|
109
|
+
draw_rect[0, o21, 1]
|
110
|
+
}
|
111
|
+
|
112
|
+
@section_x = []
|
113
|
+
@sections.each { |a, l, seq|
|
114
|
+
curoff = 0
|
115
|
+
@section_x << [x]
|
116
|
+
seq += [[l, l-1]] if not seq[-1] or seq[-1][1] < l # to draw last data
|
117
|
+
seq.each { |o, oe|
|
118
|
+
draw_color :data
|
119
|
+
draw[curoff, o-1]
|
120
|
+
draw_color :code
|
121
|
+
draw[o, oe]
|
122
|
+
curoff = oe+1
|
123
|
+
}
|
124
|
+
@section_x.last << x
|
125
|
+
x += @spacing*@pixel_w
|
126
|
+
}
|
127
|
+
|
128
|
+
@sections.zip(@section_x).each { |(a, l, seq), (sx, sxe)|
|
129
|
+
next if @curaddr.kind_of? Integer and not a.kind_of? Integer
|
130
|
+
next if @curaddr.kind_of? Expression and not a.kind_of? Expression
|
131
|
+
co = @curaddr-a
|
132
|
+
if co >= 0 and co < l
|
133
|
+
draw_color :caret_col
|
134
|
+
x = sx + (co/@byte_per_col)*@pixel_w
|
135
|
+
draw_rect[-@spacing, -1, 1]
|
136
|
+
draw_rect[@col_height, @col_height+@spacing, 1]
|
137
|
+
draw_color :caret
|
138
|
+
y = (co*@col_height/@byte_per_col) % @col_height
|
139
|
+
y = (co % @byte_per_col) / (@byte_per_col/@col_height)
|
140
|
+
draw_rect[y, y, 1]
|
141
|
+
end
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_cursor_pos
|
146
|
+
@curaddr
|
147
|
+
end
|
148
|
+
|
149
|
+
def set_cursor_pos(p)
|
150
|
+
@curaddr = p
|
151
|
+
@slave.focus_addr(@curaddr) if @slave rescue @slave=nil
|
152
|
+
redraw
|
153
|
+
end
|
154
|
+
|
155
|
+
# focus on addr
|
156
|
+
# returns true on success (address exists)
|
157
|
+
def focus_addr(addr)
|
158
|
+
return if not addr = @parent_widget.normalize(addr) or not @dasm.get_section_at(addr)
|
159
|
+
@curaddr = addr
|
160
|
+
@slave.focus_addr(@curaddr) if @slave rescue @slave=nil
|
161
|
+
gui_update
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
# returns the address of the data under the cursor
|
166
|
+
def current_address
|
167
|
+
@curaddr
|
168
|
+
end
|
169
|
+
|
170
|
+
def gui_update
|
171
|
+
# ary of section [addr, len, codespan]
|
172
|
+
# codespan is an ary of [code_off_start, code_off_end] (sorted by off)
|
173
|
+
@sections = @dasm.sections.map { |a, ed|
|
174
|
+
a = Expression[a].reduce
|
175
|
+
l = ed.length
|
176
|
+
if a.kind_of? Integer
|
177
|
+
l += a % 32
|
178
|
+
a -= a % 32
|
179
|
+
end
|
180
|
+
acc = []
|
181
|
+
# stuff with addr-section_addr is to handle non-numeric section addrs (eg elf ET_REL)
|
182
|
+
@dasm.decoded.keys.map { |da| da-a rescue nil }.grep(Integer).grep(0..l).sort.each { |o|
|
183
|
+
next if not da = @dasm.di_at(a+o)
|
184
|
+
oe = o + da.bin_length
|
185
|
+
if acc[-1] and acc[-1][1] >= o
|
186
|
+
# handle di overlapping
|
187
|
+
acc[-1][1] = oe if acc[-1][1] < oe
|
188
|
+
else
|
189
|
+
acc << [o, oe]
|
190
|
+
end
|
191
|
+
}
|
192
|
+
[a, l, acc]
|
193
|
+
}
|
194
|
+
@sections = @sections.sort if @sections.all? { |a, l, s| a.kind_of? Integer }
|
195
|
+
redraw
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,369 @@
|
|
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 CdecompListingWidget < DrawableWidget
|
9
|
+
attr_accessor :dasm, :curaddr, :tabwidth
|
10
|
+
|
11
|
+
def initialize_widget(dasm, parent_widget)
|
12
|
+
@dasm = dasm
|
13
|
+
@parent_widget = parent_widget
|
14
|
+
|
15
|
+
@view_x = @view_y = 0 # coord of corner of view in characters
|
16
|
+
@cwidth = @cheight = 1 # widget size in chars
|
17
|
+
@line_text = []
|
18
|
+
@line_text_col = [] # each line is [[:col, 'text'], [:col, 'text']]
|
19
|
+
@curaddr = nil
|
20
|
+
@tabwidth = 8
|
21
|
+
|
22
|
+
@default_color_association = { :text => :black, :keyword => :blue, :caret => :black,
|
23
|
+
:background => :white, :hl_word => :palered, :localvar => :darkred,
|
24
|
+
:globalvar => :darkgreen, :intrinsic => :darkyellow }
|
25
|
+
end
|
26
|
+
|
27
|
+
def curfunc
|
28
|
+
@dasm.c_parser and (@dasm.c_parser.toplevel.symbol[@curaddr] or @dasm.c_parser.toplevel.struct[@curaddr])
|
29
|
+
end
|
30
|
+
|
31
|
+
def click(x, y)
|
32
|
+
@caret_x = (x-1).to_i / @font_width + @view_x
|
33
|
+
@caret_y = y.to_i / @font_height + @view_y
|
34
|
+
update_caret
|
35
|
+
end
|
36
|
+
|
37
|
+
def rightclick(x, y)
|
38
|
+
click(x, y)
|
39
|
+
if @dasm.c_parser and @dasm.c_parser.toplevel.symbol[@hl_word]
|
40
|
+
@parent_widget.clone_window(@hl_word, :decompile)
|
41
|
+
elsif @hl_word
|
42
|
+
@parent_widget.clone_window(@hl_word)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def doubleclick(x, y)
|
47
|
+
click(x, y)
|
48
|
+
@parent_widget.focus_addr(@hl_word)
|
49
|
+
end
|
50
|
+
|
51
|
+
def mouse_wheel(dir, x, y)
|
52
|
+
case dir
|
53
|
+
when :up
|
54
|
+
if @caret_y > 0
|
55
|
+
@view_y -= 4
|
56
|
+
@caret_y -= 4
|
57
|
+
@caret_y = 0 if @caret_y < 0
|
58
|
+
end
|
59
|
+
when :down
|
60
|
+
if @caret_y < @line_text.length - 1
|
61
|
+
@view_y += 4
|
62
|
+
@caret_y += 4
|
63
|
+
redraw
|
64
|
+
end
|
65
|
+
end
|
66
|
+
redraw
|
67
|
+
end
|
68
|
+
|
69
|
+
def paint
|
70
|
+
@cwidth = width/@font_width
|
71
|
+
@cheight = height/@font_height
|
72
|
+
|
73
|
+
# adjust viewport to cursor
|
74
|
+
sz_x = @line_text.map { |l| l.length }.max.to_i + 1
|
75
|
+
sz_y = @line_text.length.to_i + 1
|
76
|
+
@view_x = @caret_x - @cwidth + 1 if @caret_x > @view_x + @cwidth - 1
|
77
|
+
@view_x = @caret_x if @caret_x < @view_x
|
78
|
+
@view_x = sz_x - @cwidth - 1 if @view_x >= sz_x - @cwidth
|
79
|
+
@view_x = 0 if @view_x < 0
|
80
|
+
|
81
|
+
@view_y = @caret_y - @cheight + 1 if @caret_y > @view_y + @cheight - 1
|
82
|
+
@view_y = @caret_y if @caret_y < @view_y
|
83
|
+
@view_y = sz_y - @cheight - 1 if @view_y >= sz_y - @cheight
|
84
|
+
@view_y = 0 if @view_y < 0
|
85
|
+
|
86
|
+
# current cursor position
|
87
|
+
x = 1
|
88
|
+
y = 0
|
89
|
+
|
90
|
+
# renders a string at current cursor position with a color
|
91
|
+
# must not include newline
|
92
|
+
render = lambda { |str, color|
|
93
|
+
# function ends when we write under the bottom of the listing
|
94
|
+
if @hl_word
|
95
|
+
stmp = str
|
96
|
+
pre_x = 0
|
97
|
+
while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
|
98
|
+
s1, s2 = $1, $2
|
99
|
+
pre_x += s1.length*@font_width
|
100
|
+
hl_w = s2.length*@font_width
|
101
|
+
draw_rectangle_color(:hl_word, x+pre_x, y, hl_w, @font_height)
|
102
|
+
pre_x += hl_w
|
103
|
+
stmp = stmp[s1.length+s2.length..-1]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
draw_string_color(color, x, y, str)
|
107
|
+
x += str.length * @font_width
|
108
|
+
}
|
109
|
+
|
110
|
+
@line_text_col[@view_y, @cheight + 1].each { |l|
|
111
|
+
cx = 0
|
112
|
+
l.each { |c, t|
|
113
|
+
cx += t.length
|
114
|
+
if cx-t.length > @view_x + @cwidth + 1
|
115
|
+
elsif cx < @view_x
|
116
|
+
else
|
117
|
+
t = t[(@view_x - cx + t.length)..-1] if cx-t.length < @view_x
|
118
|
+
render[t, c]
|
119
|
+
end
|
120
|
+
}
|
121
|
+
x = 1
|
122
|
+
y += @font_height
|
123
|
+
}
|
124
|
+
|
125
|
+
if focus?
|
126
|
+
# draw caret
|
127
|
+
cx = (@caret_x-@view_x)*@font_width+1
|
128
|
+
cy = (@caret_y-@view_y)*@font_height
|
129
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
130
|
+
end
|
131
|
+
|
132
|
+
@oldcaret_x, @oldcaret_y = @caret_x, @caret_y
|
133
|
+
end
|
134
|
+
|
135
|
+
def keypress(key)
|
136
|
+
case key
|
137
|
+
when :left
|
138
|
+
if @caret_x >= 1
|
139
|
+
@caret_x -= 1
|
140
|
+
update_caret
|
141
|
+
end
|
142
|
+
when :up
|
143
|
+
if @caret_y > 0
|
144
|
+
@caret_y -= 1
|
145
|
+
update_caret
|
146
|
+
end
|
147
|
+
when :right
|
148
|
+
if @caret_x < @line_text[@caret_y].to_s.length
|
149
|
+
@caret_x += 1
|
150
|
+
update_caret
|
151
|
+
end
|
152
|
+
when :down
|
153
|
+
if @caret_y < @line_text.length
|
154
|
+
@caret_y += 1
|
155
|
+
update_caret
|
156
|
+
end
|
157
|
+
when :home
|
158
|
+
@caret_x = @line_text[@caret_y].to_s[/^\s*/].length
|
159
|
+
update_caret
|
160
|
+
when :end
|
161
|
+
@caret_x = @line_text[@caret_y].to_s.length
|
162
|
+
update_caret
|
163
|
+
when :pgup
|
164
|
+
@caret_y -= @cheight/2
|
165
|
+
@caret_y = 0 if @caret_y < 0
|
166
|
+
update_caret
|
167
|
+
when :pgdown
|
168
|
+
@caret_y += @cheight/2
|
169
|
+
@caret_y = @line_text.length if @caret_y > @line_text.length
|
170
|
+
update_caret
|
171
|
+
when ?n # rename local/global variable
|
172
|
+
f = curfunc.initializer if curfunc and curfunc.initializer.kind_of? C::Block
|
173
|
+
n = @hl_word
|
174
|
+
if (f and f.symbol[n]) or @dasm.c_parser.toplevel.symbol[n]
|
175
|
+
@parent_widget.inputbox("new name for #{n}", :text => n) { |v|
|
176
|
+
if v !~ /^[a-z_$][a-z_0-9$]*$/i
|
177
|
+
@parent_widget.messagebox("invalid name #{v.inspect} !")
|
178
|
+
next
|
179
|
+
end
|
180
|
+
if f and f.symbol[n]
|
181
|
+
# TODO add/update comment to the asm instrs
|
182
|
+
s = f.symbol[v] = f.symbol.delete(n)
|
183
|
+
s.name = v
|
184
|
+
f.decompdata[:stackoff_name][s.stackoff] = v if s.stackoff
|
185
|
+
elsif @dasm.c_parser.toplevel.symbol[n]
|
186
|
+
@dasm.rename_label(n, v)
|
187
|
+
@curaddr = v if @curaddr == n
|
188
|
+
end
|
189
|
+
gui_update
|
190
|
+
}
|
191
|
+
end
|
192
|
+
when ?r # redecompile
|
193
|
+
@parent_widget.decompile(@curaddr)
|
194
|
+
when ?t # change variable type (you'll want to redecompile after that)
|
195
|
+
f = curfunc.initializer if curfunc.kind_of? C::Variable and curfunc.initializer.kind_of? C::Block
|
196
|
+
n = @hl_word
|
197
|
+
cp = @dasm.c_parser
|
198
|
+
if (f and s = f.symbol[n]) or s = cp.toplevel.symbol[n] or s = cp.toplevel.symbol[@curaddr]
|
199
|
+
s_ = s.dup
|
200
|
+
s_.initializer = nil if s.kind_of? C::Variable # for static var, avoid dumping the initializer in the textbox
|
201
|
+
s_.attributes &= C::Attributes::DECLSPECS if s_.attributes
|
202
|
+
@parent_widget.inputbox("new type for #{s.name}", :text => s_.dump_def(cp.toplevel)[0].join(' ')) { |t|
|
203
|
+
if t == ''
|
204
|
+
if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
|
205
|
+
s.initializer.decompdata[:stackoff_type].clear
|
206
|
+
s.initializer.decompdata.delete :return_type
|
207
|
+
elsif s.kind_of? C::Variable and s.stackoff
|
208
|
+
f.decompdata[:stackoff_type].delete s.stackoff
|
209
|
+
end
|
210
|
+
next
|
211
|
+
end
|
212
|
+
begin
|
213
|
+
cp.lexer.feed(t)
|
214
|
+
raise 'bad type' if not v = C::Variable.parse_type(cp, cp.toplevel, true)
|
215
|
+
v.parse_declarator(cp, cp.toplevel)
|
216
|
+
if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
|
217
|
+
# updated type of a decompiled func: update stack
|
218
|
+
vt = v.type.untypedef
|
219
|
+
vt = vt.type.untypedef if vt.kind_of? C::Pointer
|
220
|
+
raise 'function forever !' if not vt.kind_of? C::Function
|
221
|
+
# TODO _declspec
|
222
|
+
ao = 1
|
223
|
+
vt.args.to_a.each { |a|
|
224
|
+
next if a.has_attribute_var('register')
|
225
|
+
ao = (ao + [cp.sizeof(a), cp.typesize[:ptr]].max - 1) / cp.typesize[:ptr] * cp.typesize[:ptr]
|
226
|
+
s.initializer.decompdata[:stackoff_name][ao] = a.name if a.name
|
227
|
+
s.initializer.decompdata[:stackoff_type][ao] = a.type
|
228
|
+
ao += cp.sizeof(a)
|
229
|
+
}
|
230
|
+
s.initializer.decompdata[:return_type] = vt.type
|
231
|
+
s.type = v.type
|
232
|
+
else
|
233
|
+
f.decompdata[:stackoff_type][s.stackoff] = v.type if f and s.kind_of? C::Variable and s.stackoff
|
234
|
+
s.type = v.type
|
235
|
+
end
|
236
|
+
gui_update
|
237
|
+
rescue Object
|
238
|
+
@parent_widget.messagebox([$!.message, $!.backtrace].join("\n"), "error")
|
239
|
+
end
|
240
|
+
cp.readtok until cp.eos?
|
241
|
+
}
|
242
|
+
end
|
243
|
+
else return false
|
244
|
+
end
|
245
|
+
true
|
246
|
+
end
|
247
|
+
|
248
|
+
def get_cursor_pos
|
249
|
+
[@curaddr, @caret_x, @caret_y, @view_y]
|
250
|
+
end
|
251
|
+
|
252
|
+
def set_cursor_pos(p)
|
253
|
+
focus_addr p[0]
|
254
|
+
@caret_x, @caret_y, @view_y = p[1, 3]
|
255
|
+
update_caret
|
256
|
+
end
|
257
|
+
|
258
|
+
# hint that the caret moved
|
259
|
+
# redraws the caret, change the hilighted word, redraw if needed
|
260
|
+
def update_caret
|
261
|
+
redraw if @caret_x < @view_x or @caret_x >= @view_x + @cwidth or @caret_y < @view_y or @caret_y >= @view_y + @cheight
|
262
|
+
|
263
|
+
invalidate_caret(@oldcaret_x-@view_x, @oldcaret_y-@view_y)
|
264
|
+
invalidate_caret(@caret_x-@view_x, @caret_y-@view_y)
|
265
|
+
@oldcaret_x, @oldcaret_y = @caret_x, @caret_y
|
266
|
+
|
267
|
+
redraw if update_hl_word(@line_text[@caret_y], @caret_x)
|
268
|
+
end
|
269
|
+
|
270
|
+
# focus on addr
|
271
|
+
# returns true on success (address exists & decompiled)
|
272
|
+
def focus_addr(addr)
|
273
|
+
if @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[addr] or @dasm.c_parser.toplevel.struct[addr])
|
274
|
+
@curaddr = addr
|
275
|
+
@caret_x = @caret_y = 0
|
276
|
+
gui_update
|
277
|
+
return true
|
278
|
+
end
|
279
|
+
|
280
|
+
return if not addr = @parent_widget.normalize(addr)
|
281
|
+
|
282
|
+
# scan up to func start/entrypoint
|
283
|
+
todo = [addr]
|
284
|
+
done = []
|
285
|
+
ep = @dasm.entrypoints.to_a.inject({}) { |h, e| h.update @dasm.normalize(e) => true }
|
286
|
+
while addr = todo.pop
|
287
|
+
next if not di = @dasm.di_at(addr)
|
288
|
+
addr = di.block.address
|
289
|
+
next if done.include?(addr) or not @dasm.di_at(addr)
|
290
|
+
done << addr
|
291
|
+
break if @dasm.function[addr] or ep[addr]
|
292
|
+
empty = true
|
293
|
+
@dasm.decoded[addr].block.each_from_samefunc(@dasm) { |na| empty = false ; todo << na }
|
294
|
+
break if empty
|
295
|
+
end
|
296
|
+
@dasm.auto_label_at(addr, 'loc') if @dasm.get_section_at(addr) and not @dasm.get_label_at(addr)
|
297
|
+
return if not l = @dasm.get_label_at(addr)
|
298
|
+
@curaddr = l
|
299
|
+
@caret_x = @caret_y = 0
|
300
|
+
gui_update
|
301
|
+
true
|
302
|
+
end
|
303
|
+
|
304
|
+
# returns the address of the data under the cursor
|
305
|
+
def current_address
|
306
|
+
@curaddr
|
307
|
+
end
|
308
|
+
|
309
|
+
def update_line_text
|
310
|
+
@line_text = curfunc.dump_def(@dasm.c_parser.toplevel)[0].map { |l| l.gsub("\t", ' '*@tabwidth) }
|
311
|
+
@line_text_col = []
|
312
|
+
|
313
|
+
if f = curfunc and f.kind_of? C::Variable and f.initializer.kind_of? C::Block
|
314
|
+
keyword_re = /\b(#{C::Keyword.keys.join('|')})\b/
|
315
|
+
intrinsic_re = /\b(intrinsic_\w+)\b/
|
316
|
+
lv = f.initializer.symbol.keys
|
317
|
+
lv << '00' if lv.empty?
|
318
|
+
localvar_re = /\b(#{lv.join('|')})\b/
|
319
|
+
globalvar_re = /\b(#{f.initializer.outer.symbol.keys.join('|')})\b/
|
320
|
+
end
|
321
|
+
|
322
|
+
@line_text.each { |l|
|
323
|
+
lc = []
|
324
|
+
if f
|
325
|
+
while l and l.length > 0
|
326
|
+
if (i_k = (l =~ keyword_re)) == 0
|
327
|
+
m = $1.length
|
328
|
+
col = :keyword
|
329
|
+
elsif (i_i = (l =~ intrinsic_re)) == 0
|
330
|
+
m = $1.length
|
331
|
+
col = :intrinsic
|
332
|
+
elsif (i_l = (l =~ localvar_re)) == 0
|
333
|
+
m = $1.length
|
334
|
+
col = :localvar
|
335
|
+
elsif (i_g = (l =~ globalvar_re)) == 0
|
336
|
+
m = $1.length
|
337
|
+
col = :globalvar
|
338
|
+
else
|
339
|
+
m = ([i_k, i_i, i_l, i_g, l.length] - [nil, false]).min
|
340
|
+
col = :text
|
341
|
+
end
|
342
|
+
lc << [col, l[0, m]]
|
343
|
+
l = l[m..-1]
|
344
|
+
end
|
345
|
+
else
|
346
|
+
lc << [:text, l]
|
347
|
+
end
|
348
|
+
@line_text_col << lc
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
def gui_update
|
353
|
+
if not curfunc and not @decompiling ||= false
|
354
|
+
@line_text = ['please wait']
|
355
|
+
@line_text_col = [[[:text, 'please wait']]]
|
356
|
+
redraw
|
357
|
+
@decompiling = true
|
358
|
+
@dasm.decompile_func(@curaddr)
|
359
|
+
@decompiling = false
|
360
|
+
end
|
361
|
+
if curfunc
|
362
|
+
update_line_text
|
363
|
+
update_caret
|
364
|
+
end
|
365
|
+
redraw
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|