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