metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. 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
@@ -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