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,59 @@
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/opcodes'
8
+
9
+ module Metasm
10
+ class X86_64
11
+ def dbg_register_pc
12
+ @dbg_register_pc ||= :rip
13
+ end
14
+ def dbg_register_flags
15
+ @dbg_register_flags ||= :rflags
16
+ end
17
+
18
+ def dbg_register_list
19
+ @dbg_register_list ||= [:rax, :rbx, :rcx, :rdx, :rsi, :rdi, :rbp, :rsp, :r8, :r9, :r10, :r11, :r12, :r13, :r14, :r15, :rip]
20
+ end
21
+
22
+ def dbg_register_size
23
+ @dbg_register_size ||= Hash.new(64).update(:cs => 16, :ds => 16, :es => 16, :fs => 16, :gs => 16)
24
+ end
25
+
26
+ def dbg_func_arg(dbg, argnr)
27
+ if dbg.class.name =~ /win/i
28
+ list = [:rcx, :rdx, :r8, :r9]
29
+ off = 0x20
30
+ else
31
+ list = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
32
+ off = 0
33
+ end
34
+ if r = list[argnr]
35
+ dbg.get_reg_value(r)
36
+ else
37
+ argnr -= list.length
38
+ dbg.memory_read_int(Expression[:esp, :+, off + 8 + 8*argnr])
39
+ end
40
+ end
41
+ def dbg_func_arg_set(dbg, argnr, arg)
42
+ if dbg.class.name =~ /win/i
43
+ list = []
44
+ off = 0x20
45
+ else
46
+ list = []
47
+ off = 0
48
+ end
49
+ if r = list[argnr]
50
+ dbg.set_reg_value(r, arg)
51
+ else
52
+ argnr -= list.length
53
+ dbg.memory_write_int(Expression[:esp, :+, off + 8 + 8*argnr], arg)
54
+ end
55
+ end
56
+
57
+ # what's left is inherited from Ia32
58
+ end
59
+ end
@@ -0,0 +1,268 @@
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/opcodes'
8
+ require 'metasm/decode'
9
+
10
+ module Metasm
11
+ class X86_64
12
+ class ModRM
13
+ def self.decode(edata, byte, endianness, adsz, opsz, seg=nil, regclass=Reg, pfx={})
14
+ m = (byte >> 6) & 3
15
+ rm = byte & 7
16
+
17
+ if m == 3
18
+ rm |= 8 if pfx[:rex_b]
19
+ return regclass.new(rm, opsz)
20
+ end
21
+
22
+ adsz ||= 64
23
+
24
+ # mod 0/1/2 m 4 => sib
25
+ # mod 0 m 5 => rip+imm
26
+ # sib: i 4 => no index, b 5 => no base
27
+
28
+ s = i = b = imm = nil
29
+ if rm == 4
30
+ sib = edata.get_byte.to_i
31
+
32
+ ii = (sib >> 3) & 7
33
+ ii |= 8 if pfx[:rex_x]
34
+ if ii != 4
35
+ s = 1 << ((sib >> 6) & 3)
36
+ i = Reg.new(ii, adsz)
37
+ end
38
+
39
+ bb = sib & 7
40
+ if bb == 5 and m == 0
41
+ m = 2 # :i32 follows
42
+ else
43
+ bb |= 8 if pfx[:rex_b]
44
+ b = Reg.new(bb, adsz)
45
+ end
46
+ elsif rm == 5 and m == 0
47
+ b = Reg.new(16, adsz)
48
+ m = 2 # :i32 follows
49
+ else
50
+ rm |= 8 if pfx[:rex_b]
51
+ b = Reg.new(rm, adsz)
52
+ end
53
+
54
+ case m
55
+ when 1; itype = :i8
56
+ when 2; itype = :i32
57
+ end
58
+ imm = Expression[edata.decode_imm(itype, endianness)] if itype
59
+
60
+ if imm and imm.reduce.kind_of? Integer and imm.reduce < -0x100_0000
61
+ # probably a base address -> unsigned
62
+ imm = Expression[imm.reduce & ((1 << adsz) - 1)]
63
+ end
64
+
65
+ new adsz, opsz, s, i, b, imm, seg
66
+ end
67
+ end
68
+
69
+ def decode_prefix(instr, byte)
70
+ x = super(instr, byte)
71
+ if instr.prefix.delete :rex
72
+ # rex ignored if not last
73
+ instr.prefix.delete :rex_b
74
+ instr.prefix.delete :rex_x
75
+ instr.prefix.delete :rex_r
76
+ instr.prefix.delete :rex_w
77
+ end
78
+ if byte & 0xf0 == 0x40
79
+ x = instr.prefix[:rex] = byte
80
+ instr.prefix[:rex_b] = 1 if byte & 1 > 0
81
+ instr.prefix[:rex_x] = 1 if byte & 2 > 0
82
+ instr.prefix[:rex_r] = 1 if byte & 4 > 0
83
+ instr.prefix[:rex_w] = 1 if byte & 8 > 0
84
+ end
85
+ x
86
+ end
87
+
88
+ def decode_instr_op(edata, di)
89
+ before_ptr = edata.ptr
90
+ op = di.opcode
91
+ di.instruction.opname = op.name
92
+ bseq = edata.read(op.bin.length).unpack('C*') # decode_findopcode ensures that data >= op.length
93
+ pfx = di.instruction.prefix || {}
94
+
95
+ field_val = lambda { |f|
96
+ if fld = op.fields[f]
97
+ (bseq[fld[0]] >> fld[1]) & @fields_mask[f]
98
+ end
99
+ }
100
+ field_val_r = lambda { |f|
101
+ v = field_val[f]
102
+ v |= 8 if v and (op.fields[f][1] == 3 ? pfx[:rex_r] : pfx[:rex_b]) # gruick ?
103
+ v
104
+ }
105
+
106
+ opsz = op.props[:argsz] || (pfx[:rex_w] ? 64 : (pfx[:opsz] ? 16 : (op.props[:auto64] ? 64 : 32)))
107
+ adsz = pfx[:adsz] ? 32 : 64
108
+ mmxsz = (op.props[:xmmx] && pfx[:opsz]) ? 128 : 64
109
+
110
+ op.args.each { |a|
111
+ di.instruction.args << case a
112
+ when :reg; Reg.new field_val_r[a], opsz
113
+ when :eeec; CtrlReg.new field_val_r[a]
114
+ when :eeed; DbgReg.new field_val_r[a]
115
+ when :seg2, :seg2A, :seg3, :seg3A; SegReg.new field_val[a]
116
+ when :regmmx; SimdReg.new field_val_r[a], mmxsz
117
+ when :regxmm; SimdReg.new field_val_r[a], 128
118
+
119
+ when :farptr; Farptr.decode edata, @endianness, opsz
120
+ when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64; Expression[edata.decode_imm(a, @endianness)]
121
+ when :i # 64bit constants are sign-extended from :i32
122
+ type = (opsz == 64 ? op.props[:imm64] ? :a64 : :i32 : "#{op.props[:unsigned_imm] ? 'a' : 'i'}#{opsz}".to_sym )
123
+ v = edata.decode_imm(type, @endianness)
124
+ v &= 0xffff_ffff_ffff_ffff if opsz == 64 and op.props[:unsigned_imm] and v.kind_of? Integer
125
+ Expression[v]
126
+
127
+ when :mrm_imm; ModRM.new(adsz, opsz, nil, nil, nil, Expression[edata.decode_imm("a#{adsz}".to_sym, @endianness)], pfx[:seg])
128
+ when :modrm, :modrmA; ModRM.decode edata, field_val[a], @endianness, adsz, opsz, pfx[:seg], Reg, pfx
129
+ when :modrmmmx; ModRM.decode edata, field_val[:modrm], @endianness, adsz, mmxsz, pfx[:seg], SimdReg, pfx
130
+ when :modrmxmm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 128, pfx[:seg], SimdReg, pfx
131
+
132
+ when :regfp; FpReg.new field_val[a]
133
+ when :imm_val1; Expression[1]
134
+ when :imm_val3; Expression[3]
135
+ when :reg_cl; Reg.new 1, 8
136
+ when :reg_eax; Reg.new 0, opsz
137
+ when :reg_dx; Reg.new 2, 16
138
+ when :regfp0; FpReg.new nil
139
+ else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
140
+ end
141
+ }
142
+
143
+ di.bin_length += edata.ptr - before_ptr
144
+
145
+ if op.name == 'movsx' or op.name == 'movzx' or op.name == 'movsxd'
146
+ if op.name == 'movsxd'
147
+ di.instruction.args[1].sz = 32
148
+ elsif opsz == 8
149
+ di.instruction.args[1].sz = 8
150
+ else
151
+ di.instruction.args[1].sz = 16
152
+ end
153
+ if pfx[:rex_w]
154
+ di.instruction.args[0].sz = 64
155
+ elsif pfx[:opsz]
156
+ di.instruction.args[0].sz = 16
157
+ else
158
+ di.instruction.args[0].sz = 32
159
+ end
160
+ end
161
+
162
+ # sil => bh
163
+ di.instruction.args.each { |a| a.val += 12 if a.kind_of? Reg and a.sz == 8 and not pfx[:rex] and a.val >= 4 and a.val <= 8 }
164
+
165
+ pfx.delete :seg
166
+ case pfx.delete(:rep)
167
+ when :nz
168
+ if di.opcode.props[:strop]
169
+ pfx[:rep] = 'rep'
170
+ elsif di.opcode.props[:stropz]
171
+ pfx[:rep] = 'repnz'
172
+ end
173
+ when :z
174
+ if di.opcode.props[:strop]
175
+ pfx[:rep] = 'rep'
176
+ elsif di.opcode.props[:stropz]
177
+ pfx[:rep] = 'repz'
178
+ end
179
+ end
180
+
181
+ di
182
+ end
183
+
184
+ def decode_instr_interpret(di, addr)
185
+ super(di, addr)
186
+
187
+ # [rip + 42] => [rip - addr + foo]
188
+ if m = di.instruction.args.grep(ModRM).first and
189
+ ((m.b and m.b.val == 16) or (m.i and m.i.val == 16)) and
190
+ m.imm and m.imm.reduce.kind_of?(Integer)
191
+ m.imm = Expression[[:-, di.address + di.bin_length], :+, di.address+di.bin_length+m.imm.reduce]
192
+ end
193
+
194
+ di
195
+ end
196
+
197
+ def opsz(di)
198
+ if di and di.instruction.prefix and di.instruction.prefix[:rex_w]; 64
199
+ elsif di and di.instruction.prefix and di.instruction.prefix[:opsz]; 16
200
+ elsif di and di.opcode.props[:auto64]; 64
201
+ else 32
202
+ end
203
+ end
204
+
205
+ def register_symbols
206
+ [:rax, :rcx, :rdx, :rbx, :rsp, :rbp, :rsi, :rdi, :r8, :r9, :r10, :r11, :r12, :r13, :r14, :r15]
207
+ end
208
+
209
+ # returns a DecodedFunction from a parsed C function prototype
210
+ def decode_c_function_prototype(cp, sym, orig=nil)
211
+ sym = cp.toplevel.symbol[sym] if sym.kind_of?(::String)
212
+ df = DecodedFunction.new
213
+ orig ||= Expression[sym.name]
214
+
215
+ new_bt = lambda { |expr, rlen|
216
+ df.backtracked_for << BacktraceTrace.new(expr, orig, expr, rlen ? :r : :x, rlen)
217
+ }
218
+
219
+ # return instr emulation
220
+ if sym.has_attribute 'noreturn' or sym.has_attribute '__noreturn__'
221
+ df.noreturn = true
222
+ else
223
+ new_bt[Indirection[:rsp, @size/8, orig], nil]
224
+ end
225
+
226
+ # register dirty (MS standard ABI)
227
+ [:rax, :rcx, :rdx, :r8, :r9, :r10, :r11].each { |r|
228
+ df.backtrace_binding.update r => Expression::Unknown
229
+ }
230
+
231
+ if cp.lexer.definition['__MS_X86_64_ABI__']
232
+ reg_args = [:rcx, :rdx, :r8, :r9]
233
+ else
234
+ reg_args = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
235
+ end
236
+
237
+ al = cp.typesize[:ptr]
238
+ df.backtrace_binding[:rsp] = Expression[:rsp, :+, al]
239
+
240
+ # scan args for function pointers
241
+ # TODO walk structs/unions..
242
+ stackoff = al
243
+ sym.type.args.to_a.zip(reg_args).each { |a, r|
244
+ if not r
245
+ r = Indirection[[:rsp, :+, stackoff], al, orig]
246
+ stackoff += (cp.sizeof(a) + al - 1) / al * al
247
+ end
248
+ if a.type.untypedef.kind_of? C::Pointer
249
+ pt = a.type.untypedef.type.untypedef
250
+ if pt.kind_of? C::Function
251
+ new_bt[r, nil]
252
+ df.backtracked_for.last.detached = true
253
+ elsif pt.kind_of? C::Struct
254
+ new_bt[r, al]
255
+ else
256
+ new_bt[r, cp.sizeof(nil, pt)]
257
+ end
258
+ end
259
+ }
260
+
261
+ df
262
+ end
263
+
264
+ def backtrace_update_function_binding_check(dasm, faddr, f, b)
265
+ # TODO save regs according to ABI
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,264 @@
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/opcodes'
8
+ require 'metasm/encode'
9
+
10
+ module Metasm
11
+ class X86_64
12
+ class ModRM
13
+ def self.encode_reg(reg, mregval = 0)
14
+ v = reg.kind_of?(Reg) ? reg.val_enc : reg.val & 7
15
+ 0xc0 | (mregval << 3) | v
16
+ end
17
+
18
+ def encode(reg = 0, endianness = :little)
19
+ reg = reg.val if reg.kind_of? Ia32::Argument
20
+
21
+ ret = EncodedData.new << (reg << 3)
22
+
23
+ # add bits in the first octet of ret.data (1.9 compatibility layer)
24
+ or_bits = lambda { |v| # rape me
25
+ if ret.data[0].kind_of? Integer
26
+ ret.data[0] |= v
27
+ else
28
+ ret.data[0] = (ret.data[0].unpack('C').first | v).chr
29
+ end
30
+ }
31
+
32
+ if not self.b and not self.i
33
+ # imm only, use sib
34
+ or_bits[4]
35
+ imm = self.imm || Expression[0]
36
+ [ret << ((4 << 3) | 5) << imm.encode(:i32, endianness)]
37
+
38
+ elsif (self.b and self.b.val == 16) or (self.i and self.i.val == 16) # rip+imm (rip == addr of the octet after the current instr)
39
+ # should have been filtered by #parse, but just in case
40
+ raise "invalid rip addressing #{self}" if (self.i and self.b) or (self.s and self.s != 1)
41
+ or_bits[5]
42
+ imm = self.imm || Expression[0]
43
+ [ret << imm.encode(:i32, endianness)]
44
+
45
+ elsif not self.b and self.s != 1
46
+ # sib with no b
47
+ raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4 # XXX 12 ?
48
+ or_bits[4]
49
+ s = {8=>3, 4=>2, 2=>1}[@s]
50
+ imm = self.imm || Expression[0]
51
+ fu = (s << 6) | (@i.val_enc << 3) | 5
52
+ fu = fu.chr if s >= 2 # rb1.9 encoding fix
53
+ [ret << fu << imm.encode(:i32, endianness)]
54
+ else
55
+ imm = @imm.reduce if self.imm
56
+ imm = nil if imm == 0
57
+
58
+ if not self.i or (not self.b and self.s == 1)
59
+ # no sib byte (except for [esp])
60
+ @s, @i, @b = nil, nil, @s if not self.b
61
+ or_bits[@b.val_enc]
62
+ ret << 0x24 if @b.val_enc == 4 # XXX val_enc ?
63
+ else
64
+ # sib
65
+ or_bits[4]
66
+
67
+ @b, @i = @i, @b if @s == 1 and (@i.val_enc == 4 or @b.val_enc == 5)
68
+
69
+ raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4
70
+
71
+ s = {8=>3, 4=>2, 2=>1, 1=>0}[@s]
72
+ fu = (s << 6) | (@i.val_enc << 3) | @b.val_enc
73
+ fu = fu.chr if s >= 2 # rb1.9 encoding fix
74
+ ret << fu
75
+ end
76
+
77
+ imm ||= 0 if @b.val_enc == 5
78
+ if imm
79
+ case Expression.in_range?(imm, :i8)
80
+ when true
81
+ or_bits[1<<6]
82
+ [ret << Expression.encode_imm(imm, :i8, endianness)]
83
+ when false
84
+ or_bits[2<<6]
85
+ [ret << Expression.encode_imm(imm, :a32, endianness)]
86
+ when nil
87
+ rets = ret.dup
88
+ or_bits[1<<6]
89
+ ret << @imm.encode(:i8, endianness)
90
+ rets, ret = ret, rets # or_bits[] modifies ret directly
91
+ or_bits[2<<6]
92
+ ret << @imm.encode(:a32, endianness)
93
+ [ret, rets]
94
+ end
95
+ else
96
+ [ret]
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ # returns all forms of the encoding of instruction i using opcode op
103
+ # program may be used to create a new label for relative jump/call
104
+ def encode_instr_op(program, i, op)
105
+ base = op.bin.dup
106
+ oi = op.args.zip(i.args)
107
+ set_field = lambda { |f, v|
108
+ fld = op.fields[f]
109
+ base[fld[0]] |= v << fld[1]
110
+ }
111
+
112
+ #
113
+ # handle prefixes and bit fields
114
+ #
115
+ pfx = i.prefix.map { |k, v|
116
+ case k
117
+ when :jmp; {:jmp => 0x3e, :nojmp => 0x2e}[v]
118
+ when :lock; 0xf0
119
+ when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v] # TODO
120
+ end
121
+ }.compact.pack 'C*'
122
+ pfx << op.props[:needpfx] if op.props[:needpfx]
123
+
124
+ rex_w = rex_r = rex_x = rex_b = nil
125
+ if op.name == 'movsx' or op.name == 'movzx' or op.name == 'movsxd'
126
+ case i.args[0].sz
127
+ when 64; rex_w = 1
128
+ when 32
129
+ when 16; pfx << 0x66
130
+ end
131
+ else
132
+ opsz = op.props[:argsz] || i.prefix[:sz]
133
+ oi.each { |oa, ia|
134
+ case oa
135
+ when :reg, :reg_eax, :modrm, :modrmA, :mrm_imm
136
+ raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
137
+ opsz = ia.sz
138
+ end
139
+ }
140
+ opsz ||= 64 if op.props[:auto64]
141
+ opsz = op.props[:opsz] if op.props[:opsz] # XXX ?
142
+ case opsz
143
+ when 64; rex_w = 1 if not op.props[:auto64]
144
+ when 32; raise EncodeError, "Incompatible arg size in #{i}" if op.props[:auto64]
145
+ when 16; pfx << 0x66
146
+ end
147
+ end
148
+ opsz ||= @size
149
+
150
+ # addrsize override / segment override / rex_bx
151
+ if mrm = i.args.grep(ModRM).first
152
+ mrm.encode(0, @endianness) if mrm.b or mrm.i # may reorder b/i, which must be correct for rex
153
+ rex_b = 1 if mrm.b and mrm.b.val_rex.to_i > 0
154
+ rex_x = 1 if mrm.i and mrm.i.val_rex.to_i > 0
155
+ pfx << 0x67 if (mrm.b and mrm.b.sz == 32) or (mrm.i and mrm.i.sz == 32) or op.props[:adsz] == 32
156
+ pfx << [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][mrm.seg.val] if mrm.seg
157
+ elsif op.props[:adsz] == 32
158
+ pfx << 0x67
159
+ end
160
+
161
+
162
+ #
163
+ # encode embedded arguments
164
+ #
165
+ postponed = []
166
+ oi.each { |oa, ia|
167
+ case oa
168
+ when :reg
169
+ set_field[oa, ia.val_enc]
170
+ if op.fields[:reg][1] == 3
171
+ rex_r = ia.val_rex
172
+ else
173
+ rex_b = ia.val_rex
174
+ end
175
+ when :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :regfp, :regxmm, :regmmx
176
+ set_field[oa, ia.val & 7]
177
+ rex_r = 1 if ia.val > 7
178
+ pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
179
+ when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
180
+ # implicit
181
+ when :modrm, :modrmA, :modrmmmx, :modrmxmm
182
+ # postpone, but we must set rex now
183
+ case ia
184
+ when ModRM
185
+ ia.encode(0, @endianness) # could swap b/i
186
+ rex_x = ia.i.val_rex if ia.i
187
+ rex_b = ia.b.val_rex if ia.b
188
+ when Reg
189
+ rex_b = ia.val_rex
190
+ else
191
+ rex_b = ia.val >> 3
192
+ end
193
+ postponed << [oa, ia]
194
+ else
195
+ postponed << [oa, ia]
196
+ end
197
+ }
198
+
199
+ if !(op.args & [:modrm, :modrmA, :modrmxmm, :modrmmmx]).empty?
200
+ # reg field of modrm
201
+ regval = (base[-1] >> 3) & 7
202
+ base.pop
203
+ end
204
+
205
+ # convert label name for jmp/call/loop to relative offset
206
+ if op.props[:setip] and op.name[0, 3] != 'ret' and i.args.first.kind_of? Expression
207
+ postlabel = program.new_label('post'+op.name)
208
+ target = postponed.first[1]
209
+ target = target.rexpr if target.kind_of? Expression and target.op == :+ and not target.lexpr
210
+ postponed.first[1] = Expression[target, :-, postlabel]
211
+ end
212
+
213
+ if rex_w == 1 or rex_r == 1 or rex_b == 1 or rex_x == 1 or i.args.grep(Reg).find { |r| r.sz == 8 and r.val >= 4 and r.val < 8 }
214
+ rex ||= 0x40
215
+ rex |= 1 if rex_b.to_i > 0
216
+ rex |= 2 if rex_x.to_i > 0
217
+ rex |= 4 if rex_r.to_i > 0
218
+ rex |= 8 if rex_w.to_i > 0
219
+ end
220
+ pfx << rex if rex
221
+ ret = EncodedData.new(pfx + base.pack('C*'))
222
+
223
+ postponed.each { |oa, ia|
224
+ case oa
225
+ when :farptr; ed = ia.encode(@endianness, "a#{opsz}".to_sym)
226
+ when :modrm, :modrmA, :modrmmmx, :modrmxmm
227
+ if ia.kind_of? ModRM
228
+ ed = ia.encode(regval, @endianness)
229
+ if ed.kind_of?(::Array)
230
+ if ed.length > 1
231
+ # we know that no opcode can have more than 1 modrm
232
+ ary = []
233
+ ed.each { |m| ary << (ret.dup << m) }
234
+ ret = ary
235
+ next
236
+ else
237
+ ed = ed.first
238
+ end
239
+ end
240
+ else
241
+ ed = ModRM.encode_reg(ia, regval)
242
+ end
243
+ when :mrm_imm; ed = ia.imm.encode("a#{op.props[:adsz] || 64}".to_sym, @endianness)
244
+ when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64; ed = ia.encode(oa, @endianness)
245
+ when :i
246
+ type = (opsz == 64 ? op.props[:imm64] ? :a64 : :i32 : "#{op.props[:unsigned_imm] ? 'a' : 'i'}#{opsz}".to_sym)
247
+ ed = ia.encode(type, @endianness)
248
+ else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
249
+ end
250
+
251
+ if ret.kind_of?(::Array)
252
+ ret.each { |e| e << ed }
253
+ else
254
+ ret << ed
255
+ end
256
+ }
257
+
258
+ # we know that no opcode with setip accept both modrm and immediate arg, so ret is not an ::Array
259
+ ret.add_export(postlabel, ret.virtsize) if postlabel
260
+
261
+ ret
262
+ end
263
+ end
264
+ end