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,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
|