metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -1,52 +0,0 @@
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/ppc/opcodes'
8
- require 'metasm/parse'
9
-
10
- module Metasm
11
- class PowerPC
12
- # TODO
13
- def parse_arg_valid?(op, sym, arg)
14
- # special case for lw reg, imm32(reg) ? (pseudo-instr, need to convert to 'lui t0, up imm32 ori t0 down imm32 add t0, reg lw reg, 0(t0)
15
- case sym
16
- when :rs, :rt, :rd; arg.kind_of? Reg
17
- when :sa, :i16, :i20, :i26; arg.kind_of? Expression
18
- when :rs_i16; arg.kind_of? Memref
19
- when :ft; arg.kind_of? FpReg
20
- else raise "internal error: mips arg #{sym.inspect}"
21
- end
22
- end
23
-
24
- def parse_argument(pgm)
25
- pgm.skip_space
26
- return if not tok = pgm.nexttok
27
- if tok.type == :string and Reg.s_to_i[tok.raw]
28
- pgm.readtok
29
- arg = Reg.new Reg.s_to_i[tok.raw]
30
- elsif tok.type == :string and FpReg.s_to_i[tok.raw]
31
- pgm.readtok
32
- arg = FpReg.new FpReg.s_to_i[tok.raw]
33
- else
34
- arg = Expression.parse pgm
35
- pgm.skip_space
36
- # check memory indirection: 'off(base reg)' # XXX scaled index ?
37
- if arg and pgm.nexttok and pgm.nexttok.type == :punct and pgm.nexttok.raw == '('
38
- pgm.readtok
39
- pgm.skip_space_eol
40
- ntok = pgm.readtok
41
- raise tok, "Invalid base #{ntok}" unless ntok and ntok.type == :string and Reg.s_to_i[ntok.raw]
42
- base = Reg.new Reg.s_to_i[ntok.raw]
43
- pgm.skip_space_eol
44
- ntok = pgm.readtok
45
- raise tok, "Invalid memory reference, ')' expected" if not ntok or ntok.type != :punct or ntok.raw != ')'
46
- arg = Memref.new base, arg
47
- end
48
- end
49
- arg
50
- end
51
- end
52
- end
@@ -1,12 +0,0 @@
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/main'
8
- require 'metasm/x86_64/parse'
9
- require 'metasm/x86_64/encode'
10
- require 'metasm/x86_64/decode'
11
- require 'metasm/x86_64/debug'
12
- require 'metasm/x86_64/compile_c'
@@ -1,118 +0,0 @@
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/x86_64/main'
8
- require 'metasm/ia32/opcodes'
9
-
10
- module Metasm
11
- class X86_64
12
- def init_cpu_constants
13
- super()
14
- @valid_args.concat [:i32, :u32, :i64, :u64] - @valid_args
15
- end
16
-
17
- def init_386_common_only
18
- super()
19
- # :imm64 => accept a real int64 as :i argument
20
- # :auto64 => ignore rex_w, always 64-bit op
21
- # :op32no64 => if write to a 32-bit reg, dont zero the top 32-bits of dest
22
- @valid_props |= [:imm64, :auto64, :op32no64]
23
- @opcode_list.delete_if { |o| o.bin[0].to_i & 0xf0 == 0x40 } # now REX prefix
24
- @opcode_list.each { |o|
25
- o.props[:imm64] = true if o.bin == [0xB8] # mov reg, <true imm64>
26
- o.props[:auto64] = true if o.name =~ /^(j|loop|(call|enter|leave|lgdt|lidt|lldt|ltr|pop|push|ret)$)/
27
- #o.props[:op32no64] = true if o.name =~ // # TODO are there any instr here ?
28
- }
29
- addop 'movsxd', [0x63], :mrm
30
- end
31
-
32
- # all x86_64 cpu understand <= sse2 instrs
33
- def init_x8664_only
34
- init_386_common_only
35
- init_386_only
36
- init_387_only # 387 indeed
37
- init_486_only
38
- init_pentium_only
39
- init_p6_only
40
- init_sse_only
41
- init_sse2_only
42
-
43
- @opcode_list.delete_if { |o|
44
- o.args.include?(:seg2) or
45
- o.args.include?(:seg2A) or
46
- %w[aaa aad aam aas bound daa das
47
- lds les loadall arpl pusha pushad popa popad].include?(o.name)
48
- }
49
-
50
- addop 'swapgs', [0x0F, 0x01, 0xF8]
51
- end
52
-
53
- def init_sse3
54
- init_x8664_only
55
- init_sse3_only
56
- end
57
-
58
- def init_vmx
59
- init_sse3
60
- init_vmx_only
61
- end
62
-
63
- def init_all
64
- init_vmx
65
- init_sse42_only
66
- init_3dnow_only
67
- end
68
-
69
- alias init_latest init_all
70
-
71
-
72
- def addop_macrostr(name, bin, type)
73
- super(name, bin, type)
74
- bin = bin.dup
75
- bin[0] |= 1
76
- addop(name+'q', bin) { |o| o.props[:opsz] = 64 ; o.props[type] = true }
77
- end
78
-
79
- def addop_post(op)
80
- if op.fields[:d] or op.fields[:w] or op.fields[:s] or op.args.first == :regfp0
81
- return super(op)
82
- end
83
-
84
- dupe = lambda { |o|
85
- dop = Opcode.new o.name.dup, o.bin.dup
86
- dop.fields, dop.props, dop.args = o.fields.dup, o.props.dup, o.args.dup
87
- dop
88
- }
89
-
90
- @opcode_list << op
91
-
92
- if op.args == [:i] or op.args == [:farptr] or op.name[0, 3] == 'ret'
93
- # define opsz-override version for ambiguous opcodes
94
- op16 = dupe[op]
95
- op16.name << '.i16'
96
- op16.props[:opsz] = 16
97
- @opcode_list << op16
98
- # push call ret jz can't 32bit
99
- op64 = dupe[op]
100
- op64.name << '.i64'
101
- op64.props[:opsz] = 64
102
- @opcode_list << op64
103
- elsif op.props[:strop] or op.props[:stropz] or op.args.include? :mrm_imm or
104
- op.args.include? :modrm or op.args.include? :modrmA or op.name =~ /loop|xlat/
105
- # define adsz-override version for ambiguous opcodes (movsq)
106
- # XXX loop pfx 67 = rip+ecx, 66/rex ignored
107
- op32 = dupe[op]
108
- op32.name << '.a32'
109
- op32.props[:adsz] = 32
110
- @opcode_list << op32
111
- op64 = dupe[op]
112
- op64.name << '.a64'
113
- op64.props[:adsz] = 64
114
- @opcode_list << op64
115
- end
116
- end
117
- end
118
- end
@@ -1,583 +0,0 @@
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