metasm 1.0.1 → 1.0.2

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