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,583 @@
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
+ # this is a rubstop-api compatible Gdb stub
8
+ # it can connect to a gdb server and interface with the lindebug frontend
9
+ # linux/x86 only
10
+ #
11
+
12
+ require 'socket'
13
+ require 'metasm'
14
+
15
+ class GdbRemoteString < Metasm::VirtualString
16
+ attr_accessor :gdbg
17
+
18
+ def initialize(gdbg, addr_start=0, length=0xffff_ffff)
19
+ @gdbg = gdbg
20
+ @pagelength = 512
21
+ super(addr_start, length)
22
+ end
23
+
24
+ def dup(addr=@addr_start, len=@length)
25
+ self.class.new(@gdbg, addr, len)
26
+ end
27
+
28
+ def rewrite_at(addr, data)
29
+ len = data.length
30
+ off = 0
31
+ while len > @pagelength
32
+ @gdbg.setmem(addr+off, data[off, @pagelength])
33
+ off += @pagelength
34
+ len -= @pagelength
35
+ end
36
+ @gdbg.setmem(addr+off, data[off, len])
37
+ end
38
+
39
+ def get_page(addr)
40
+ @gdbg.getmem(addr, @pagelength)
41
+ end
42
+ end
43
+
44
+ class Rubstop
45
+ EFLAGS = {0 => 'c', 2 => 'p', 4 => 'a', 6 => 'z', 7 => 's', 9 => 'i', 10 => 'd', 11 => 'o'}
46
+ GDBREGS = %w[eax ecx edx ebx esp ebp esi edi eip eflags cs ss ds es fs gs] # XXX [77] = 'orig_eax'
47
+ # define accessors for registers
48
+ GDBREGS.compact.each { |reg|
49
+ define_method(reg) { regs_cache[reg] }
50
+ define_method(reg + '=') { |v| regs_cache[reg] = v ; regs_dirty }
51
+ }
52
+
53
+ # compute the hex checksum used in gdb protocol
54
+ def gdb_csum(buf)
55
+ '%02x' % (buf.unpack('C*').inject(0) { |cs, c| cs + c } & 0xff)
56
+ end
57
+
58
+ # send the buffer, waits ack
59
+ # return true on success
60
+ def gdb_send(cmd, buf='')
61
+ buf = cmd + buf
62
+ buf = '$' << buf << '#' << gdb_csum(buf)
63
+ log "gdb_send(#{buf[0, 32].inspect}#{'...' if buf.length > 32})" if $DEBUG
64
+
65
+ 5.times {
66
+ @io.write buf
67
+ loop do
68
+ if not IO.select([@io], nil, nil, 1)
69
+ break
70
+ end
71
+ raise Errno::EPIPE if not ack = @io.read(1)
72
+ case ack
73
+ when '+'
74
+ return true
75
+ when '-'
76
+ log "gdb_send: ack neg" if $DEBUG
77
+ break
78
+ when nil; return
79
+ end
80
+ end
81
+ }
82
+ log "send error #{cmd.inspect} (no ack)"
83
+ false
84
+ end
85
+
86
+ # return buf, or nil on error / csum error
87
+ def gdb_readresp
88
+ state = :nosync
89
+ buf = ''
90
+ cs = ''
91
+ while state != :done
92
+ # XXX timeout etc
93
+ raise Errno::EPIPE if not c = @io.read(1)
94
+ case state
95
+ when :nosync
96
+ if c == '$'
97
+ state = :data
98
+ end
99
+ when :data
100
+ if c == '#'
101
+ state = :csum1
102
+ else
103
+ buf << c
104
+ end
105
+ when :csum1
106
+ cs << c
107
+ state = :csum2
108
+ when :csum2
109
+ cs << c
110
+ state = :done
111
+ if cs.downcase != gdb_csum(buf).downcase
112
+ log "transmit error"
113
+ @io.write '-'
114
+ return
115
+ end
116
+ end
117
+ end
118
+ @io.write '+'
119
+
120
+ if buf =~ /^E(..)$/
121
+ e = $1.to_i(16)
122
+ log "error #{e} (#{Metasm::PTrace::ERRNO.index(e)})"
123
+ return
124
+ end
125
+ log "gdb_readresp: got #{buf[0, 64].inspect}#{'...' if buf.length > 64}" if $DEBUG
126
+
127
+ buf
128
+ end
129
+
130
+ def gdb_msg(*a)
131
+ if gdb_send(*a)
132
+ gdb_readresp
133
+ end
134
+ end
135
+
136
+ # rle: build the regexp that will match repetitions of a character, skipping counts leading to invalid char
137
+ rng = [3..(125-29)]
138
+ [?+, ?-, ?#, ?$].sort.each { |invalid|
139
+ invalid -= 29
140
+ rng.each_with_index { |r, i|
141
+ if r.include? invalid
142
+ replace = [r.begin..invalid-1, invalid+1..r.end]
143
+ replace.delete_if { |r_| r_.begin > r_.end }
144
+ rng[i, 1] = replace
145
+ end
146
+ }
147
+ }
148
+ repet = rng.reverse.map { |r| "\\1{#{r.begin},#{r.end}}" }.join('|')
149
+ RLE_RE = /(.)(#{repet})/
150
+
151
+ # rle-compress a buffer
152
+ # a character followed by '*' followed by 'x' is asc(x)-28 repetitions of the char
153
+ # eg '0* ' => '0' * (asc(' ') - 28) = '0000'
154
+ # for the count character, it must be 32 <= char < 126 and not be '+' '-' '#' or '$'
155
+ def rle(buf)
156
+ buf.gsub(RLE_RE) {
157
+ chr, len = $1, $2.length+1
158
+ chr + '*' + (len+28).chr
159
+ }
160
+ end
161
+ # decompress rle-encoded data
162
+ def unrle(buf) buf.gsub(/(.)\*(.)/) { $1 * ($2[0]-28) } end
163
+ # send an integer as a long hex packed with leading 0 stripped
164
+ def hexl(int) [int].pack('N').unpack('H*').first.gsub(/^0+(.)/, '\1') end
165
+ # send a binary buffer as a rle hex-encoded
166
+ def hex(buf) buf.unpack('H*').first end
167
+ # decode an rle hex-encoded buffer
168
+ def unhex(buf)
169
+ buf = buf[/^[a-fA-F0-9]*/]
170
+ buf = '0' + buf if buf.length % 1 == 1
171
+ [buf].pack('H*')
172
+ end
173
+
174
+ # on-demand local cache of registers
175
+ def regs_cache
176
+ readregs if @regs_cache.empty?
177
+ @regs_cache
178
+ end
179
+
180
+ # retrieve remote regs
181
+ def readregs
182
+ sync_regs
183
+ if buf = gdb_msg('g')
184
+ regs = unhex(unrle(buf))
185
+ if regs.length < GDBREGS.length*4
186
+ # retry once, was probably a response to something else
187
+ puts "bad regs size!" if $DEBUG
188
+ buf = gdb_msg('g')
189
+ regs = unhex(unrle(buf)) if buf
190
+ if not buf or regs.length < GDBREGS.length*4
191
+ raise "regs buffer recv is too short !"
192
+ end
193
+ end
194
+ @regs_dirty = false
195
+ @regs_cache = Hash[GDBREGS.zip(regs.unpack('L*'))]
196
+ end
197
+ @curinstr = nil if @regs_cache['eip'] != @oldregs['eip']
198
+ end
199
+
200
+ # mark local cache of regs as modified, need to send it before continuing execution
201
+ def regs_dirty
202
+ @regs_dirty = true
203
+ end
204
+
205
+ # send the local copy of regs if dirty
206
+ def sync_regs
207
+ if not @regs_cache.empty? and @regs_dirty
208
+ send_regs
209
+ end
210
+ end
211
+
212
+ # send the local copy of regs
213
+ def send_regs
214
+ return if @regs_cache.empty?
215
+ regs = @regs_cache.values_at(*GDBREGS)
216
+ @regs_dirty = false
217
+ gdb_msg('G', hex(regs.pack('L*')))
218
+ end
219
+
220
+ # read memory (small blocks prefered)
221
+ def getmem(addr, len)
222
+ return '' if len == 0
223
+ if mem = gdb_msg('m', hexl(addr) << ',' << hexl(len))
224
+ unhex(unrle(mem))
225
+ end
226
+ end
227
+
228
+ # write memory (small blocks prefered)
229
+ def setmem(addr, data)
230
+ len = data.length
231
+ return if len == 0
232
+ raise 'writemem error' if not gdb_msg('M', hexl(addr) << ',' << hexl(len) << ':' << rle(hex(data)))
233
+ end
234
+
235
+ # read arbitrary blocks of memory (chunks to getmem)
236
+ def [](addr, len)
237
+ @pgm.encoded[addr, len].data rescue ''
238
+ end
239
+
240
+ # write arbitrary blocks of memory (chunks to getmem)
241
+ def []=(addr, len, str)
242
+ @pgm.encoded[addr, len] = str
243
+ end
244
+
245
+ def curinstr
246
+ @curinstr ||= mnemonic_di
247
+ end
248
+
249
+ def mnemonic_di(addr = eip)
250
+ @pgm.encoded.ptr = addr
251
+ di = @pgm.cpu.decode_instruction(@pgm.encoded, addr)
252
+ @curinstr = di if addr == @regs_cache['eip']
253
+ di
254
+ end
255
+
256
+ def mnemonic(addr = eip)
257
+ mnemonic_di(addr).instruction
258
+ end
259
+
260
+ def pre_run
261
+ @oldregs = regs_cache.dup
262
+ sync_regs
263
+ end
264
+
265
+ def post_run
266
+ @regs_cache.clear
267
+ @curinstr = nil
268
+ @mem.invalidate
269
+ end
270
+
271
+ def quiet
272
+ @quiet = true
273
+ begin
274
+ yield
275
+ ensure
276
+ @quiet = false
277
+ end
278
+ end
279
+
280
+ def log_stopped(msg)
281
+ return if @quiet ||= false
282
+ case msg[0]
283
+ when ?T
284
+ sig = [msg[1, 2]].pack('H*')[0]
285
+ misc = msg[3..-1].split(';').inject({}) { |h, s| k, v = s.split(':', 2) ; h.update k => (v || true) }
286
+ str = "stopped by signal #{sig}"
287
+ str = "thread #{[misc['thread']].pack('H*').unpack('N').first} #{str}" if misc['thread']
288
+ log str
289
+ when ?S
290
+ sig = [msg[1, 2]].pack('H*')[0]
291
+ log "stopped by signal #{sig}"
292
+ end
293
+ end
294
+
295
+ def cont
296
+ pre_run
297
+ do_singlestep if @wantbp
298
+ rmsg = gdb_msg('c')
299
+ post_run
300
+ ccaddr = eip-1
301
+ if @breakpoints[ccaddr] and self[ccaddr, 1] == "\xcc"
302
+ self[ccaddr, 1] = @breakpoints.delete ccaddr
303
+ mem.invalidate
304
+ self.eip = ccaddr
305
+ @wantbp = ccaddr if not @singleshot.delete ccaddr
306
+ sync_regs
307
+ end
308
+ log_stopped rmsg
309
+ end
310
+
311
+ def singlestep
312
+ pre_run
313
+ do_singlestep
314
+ post_run
315
+ end
316
+
317
+ def do_singlestep
318
+ gdb_msg('s')
319
+ if @wantbp
320
+ self[@wantbp, 1] = "\xcc"
321
+ @wantbp = nil
322
+ end
323
+ end
324
+
325
+ def stepover
326
+ i = curinstr.instruction if curinstr
327
+ if i and (i.opname == 'call' or (i.prefix and i.prefix[:rep]))
328
+ eaddr = eip + curinstr.bin_length
329
+ bpx eaddr, true
330
+ quiet { cont }
331
+ else
332
+ singlestep
333
+ end
334
+ end
335
+
336
+ def stepout
337
+ stepover until curinstr and curinstr.opcode.name == 'ret'
338
+ singlestep
339
+ rescue Interrupt
340
+ log 'interrupted'
341
+ end
342
+
343
+ def bpx(addr, singleshot=false)
344
+ return if @breakpoints[addr]
345
+ @singleshot[addr] = true if singleshot
346
+ @breakpoints[addr] = self[addr, 1]
347
+ self[addr, 1] = "\xcc"
348
+ end
349
+
350
+
351
+ def kill
352
+ gdb_send('k')
353
+ end
354
+
355
+ def detach
356
+ # TODO clear breakpoints
357
+ gdb_send('D')
358
+ end
359
+
360
+ attr_accessor :pgm, :breakpoints, :singleshot, :wantbp,
361
+ :symbols, :symbols_len, :filemap, :oldregs, :io, :mem
362
+ def initialize(io)
363
+ case io
364
+ when IO; @io = io
365
+ when /^udp:([^:]*):(\d+)$/; @io = UDPSocket.new ; @io.connect($1, $2)
366
+ when /^(?:tcp:)?([^:]*):(\d+)$/; @io = TCPSocket.open($1, $2)
367
+ else raise "unknown target #{io.inspect}"
368
+ end
369
+ @pgm = Metasm::ExeFormat.new Metasm::Ia32.new
370
+ @mem = GdbRemoteString.new self
371
+ @pgm.encoded = Metasm::EncodedData.new @mem
372
+ @regs_cache = {}
373
+ @regs_dirty = nil
374
+ @oldregs = {}
375
+ @breakpoints = {}
376
+ @singleshot = {}
377
+ @wantbp = nil
378
+ @symbols = {}
379
+ @symbols_len = {}
380
+ @filemap = {}
381
+
382
+ gdb_setup
383
+ end
384
+
385
+ def gdb_setup
386
+ #gdb_msg('q', 'Supported')
387
+ #gdb_msg('Hc', '-1')
388
+ #gdb_msg('qC')
389
+ if not gdb_msg('?')
390
+ log "nobody on the line, waiting for someone to wake up"
391
+ IO.select([@io], nil, nil, nil)
392
+ log "who's there ?"
393
+ end
394
+ end
395
+
396
+ def set_hwbp(type, addr, len=1, set=true)
397
+ set = (set ? 'Z' : 'z')
398
+ type = { 'r' => '3', 'w' => '2', 'x' => '1', 's' => '0' }[type] || raise("invalid hwbp type #{type}")
399
+ gdb_msg(set, type << ',' << hexl(addr) << ',' << hexl(len))
400
+ true
401
+ end
402
+
403
+ def unset_hwbp(type, addr, len=1)
404
+ set_hwbp(type, addr, len, false)
405
+ end
406
+
407
+
408
+ def findfilemap(s)
409
+ @filemap.keys.find { |k| @filemap[k][0] <= s and @filemap[k][1] > s } || '???'
410
+ end
411
+
412
+ def findsymbol(k)
413
+ file = findfilemap(k) + '!'
414
+ if s = @symbols[k] ? k : @symbols.keys.find { |s_| s_ < k and s_ + @symbols_len[s_].to_i > k }
415
+ file + @symbols[s] + (s == k ? '' : "+#{(k-s).to_s(16)}")
416
+ else
417
+ file + ('%08x' % k)
418
+ end
419
+ end
420
+
421
+ def loadsyms(baseaddr, name)
422
+ @loadedsyms ||= {}
423
+ return if @loadedsyms[name] or self[baseaddr, 4] != "\x7fELF"
424
+ @loadedsyms[name] = true
425
+
426
+ set_status " loading symbols from #{name}..."
427
+ e = Metasm::LoadedELF.load self[baseaddr, 0x100_0000]
428
+ e.load_address = baseaddr
429
+ begin
430
+ e.decode
431
+ #e = Metasm::ELF.decode_file name rescue return # read from disk
432
+ rescue
433
+ log "failed to load symbols from #{name}: #$!"
434
+ ($!.backtrace - caller).each { |l| log l.chomp }
435
+ @filemap[baseaddr.to_s(16)] = [baseaddr, baseaddr+0x1000]
436
+ return
437
+ rescue Interrupt
438
+ log "interrupted"
439
+ end
440
+
441
+ if e.tag['SONAME']
442
+ name = e.tag['SONAME']
443
+ return if name and @loadedsyms[name]
444
+ @loadedsyms[name] = true
445
+ end
446
+
447
+ last_s = e.segments.reverse.find { |s| s.type == 'LOAD' }
448
+ vlen = last_s.vaddr + last_s.memsz
449
+ vlen -= baseaddr if e.header.type == 'EXEC'
450
+ @filemap[name] = [baseaddr, baseaddr + vlen]
451
+
452
+ oldsyms = @symbols.length
453
+ e.symbols.each { |s|
454
+ next if not s.name or s.shndx == 'UNDEF'
455
+ sname = s.name
456
+ sname = 'weak_'+sname if s.bind == 'WEAK'
457
+ sname = 'local_'+sname if s.bind == 'LOCAL'
458
+ v = s.value
459
+ v = baseaddr + v if v < baseaddr
460
+ @symbols[v] = sname
461
+ @symbols_len[v] = s.size
462
+ }
463
+ if e.header.type == 'EXEC' and e.header.entry >= baseaddr and e.header.entry < baseaddr + vlen
464
+ @symbols[e.header.entry] = 'entrypoint'
465
+ end
466
+ set_status nil
467
+ log "loaded #{@symbols.length-oldsyms} symbols from #{name} at #{'%08x' % baseaddr}"
468
+ end
469
+
470
+ # scan val at the beginning of each page (custom gdb msg)
471
+ def pageheadsearch(val)
472
+ resp = gdb_msg('qy', hexl(val))
473
+ unhex(resp).unpack('L*')
474
+ end
475
+
476
+ def scansyms
477
+ # TODO use qSymbol or something
478
+ pageheadsearch("\x7fELF".unpack('L').first).each { |addr| loadsyms(addr, '%08x'%addr) }
479
+ end
480
+
481
+ # use qSymbol to retrieve a symbol value (uint)
482
+ def request_symbol(name)
483
+ resp = gdb_msg('qSymbol:', hex(name))
484
+ if resp and a = resp.split(':')[1]
485
+ unhex(a).unpack('N').first
486
+ end
487
+ end
488
+
489
+ def loadallsyms
490
+ # kgdb: read kernel symbols from 'module_list'
491
+ # too bad module_list is not in ksyms
492
+ if mod = request_symbol('module_list')
493
+ int_at = lambda { |addr, off| @mem[addr+off, 4].unpack('L').first }
494
+ mod_size = lambda { int_at[mod, 0] }
495
+ mod_next = lambda { int_at[mod, 4] }
496
+ mod_nsym = lambda { int_at[mod, 0x18] } # most portable. yes.
497
+ mod_syms = lambda { int_at[mod, 0x20] }
498
+
499
+ read_strz = lambda { |addr|
500
+ if i = @mem.index(?\0, addr)
501
+ @mem[addr...i]
502
+ end
503
+ }
504
+
505
+ while mod != 0
506
+ symtab = [[]]
507
+
508
+ @mem[mod_syms[], mod_nsym[]*8].to_str.unpack('L*').each { |i|
509
+ # make a list of couples
510
+ if symtab.last.length < 2
511
+ symtab.last << i
512
+ else
513
+ symtab << [i]
514
+ end
515
+ }
516
+
517
+ symtab.each { |v, n|
518
+ n = read_strz[n]
519
+ # ||= to keep symbol precedence order (1st match wins)
520
+ @symbols[v] ||= n
521
+ }
522
+
523
+ mod = mod_next[]
524
+ end
525
+ end
526
+ end
527
+
528
+ def loadmap(mapfile)
529
+ # file fmt: addr type name eg 'c01001ba t setup_idt'
530
+ minaddr = maxaddr = nil
531
+ File.read(mapfile).each { |l|
532
+ addr, type, name = l.chomp.split
533
+ addr = addr.to_i(16)
534
+ minaddr = addr if not minaddr or minaddr > addr
535
+ maxaddr = addr if not maxaddr or maxaddr < addr
536
+ @symbols[addr] = name
537
+ }
538
+ if minaddr
539
+ @filemap[minaddr.to_s(16)] = [minaddr, maxaddr+1]
540
+ end
541
+ end
542
+
543
+ def backtrace
544
+ s = findsymbol(eip)
545
+ if block_given?
546
+ yield s
547
+ else
548
+ bt = []
549
+ bt << s
550
+ end
551
+ fp = ebp
552
+ while fp >= esp and fp <= esp+0x100000
553
+ s = findsymbol(self[fp+4, 4].unpack('L').first)
554
+ if block_given?
555
+ yield s
556
+ else
557
+ bt << s
558
+ end
559
+ fp = self[fp, 4].unpack('L').first
560
+ end
561
+ bt
562
+ end
563
+
564
+ attr_accessor :logger
565
+ def log(s)
566
+ @logger ||= $stdout
567
+ @logger.puts s
568
+ end
569
+
570
+ # set a temporary status info (nil for default value)
571
+ def set_status(s)
572
+ @logger ||= $stdout
573
+ if @logger != $stdout
574
+ @logger.statusline = s
575
+ else
576
+ s ||= ' '*72
577
+ @logger.print s + "\r"
578
+ @logger.flush
579
+ end
580
+ end
581
+
582
+ def checkbp ; end
583
+ end