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