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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,314 @@
|
|
|
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/ia32/opcodes'
|
|
8
|
+
require 'metasm/encode'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class Ia32
|
|
12
|
+
class InvalidModRM < Exception ; end
|
|
13
|
+
class ModRM
|
|
14
|
+
# returns the byte representing the register encoded as modrm
|
|
15
|
+
# works with Reg/SimdReg
|
|
16
|
+
def self.encode_reg(reg, mregval = 0)
|
|
17
|
+
0xc0 | (mregval << 3) | reg.val
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# The argument is an integer representing the 'reg' field of the mrm
|
|
21
|
+
#
|
|
22
|
+
# caller is responsible for setting the adsz
|
|
23
|
+
# returns an array, 1 element per possible immediate size (for un-reduce()able Expression)
|
|
24
|
+
def encode(reg = 0, endianness = :little)
|
|
25
|
+
reg = reg.val if reg.kind_of? Argument
|
|
26
|
+
case @adsz
|
|
27
|
+
when 16; encode16(reg, endianness)
|
|
28
|
+
when 32; encode32(reg, endianness)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
def encode16(reg, endianness)
|
|
34
|
+
if not b
|
|
35
|
+
# imm only
|
|
36
|
+
return [EncodedData.new << (6 | (reg << 3)) << @imm.encode(:u16, endianness)]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
imm = @imm.reduce if self.imm
|
|
40
|
+
imm = nil if imm == 0
|
|
41
|
+
ret = EncodedData.new
|
|
42
|
+
ret <<
|
|
43
|
+
case [@b.val, (@i.val if i)]
|
|
44
|
+
when [3, 6], [6, 3]; 0
|
|
45
|
+
when [3, 7], [7, 3]; 1
|
|
46
|
+
when [5, 6], [6, 5]; 2
|
|
47
|
+
when [5, 7], [7, 5]; 3
|
|
48
|
+
when [6, nil]; 4
|
|
49
|
+
when [7, nil]; 5
|
|
50
|
+
when [5, nil]
|
|
51
|
+
imm ||= 0
|
|
52
|
+
6
|
|
53
|
+
when [3, nil]; 7
|
|
54
|
+
else raise InvalidModRM, 'invalid modrm16'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# add bits in the first octet of ret.data (1.9 compatibility layer)
|
|
58
|
+
or_bits = lambda { |v| # rape me
|
|
59
|
+
if ret.data[0].kind_of? Integer
|
|
60
|
+
ret.data[0] |= v
|
|
61
|
+
else
|
|
62
|
+
ret.data[0] = (ret.data[0].unpack('C').first | v).chr
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
or_bits[reg << 3]
|
|
67
|
+
|
|
68
|
+
if imm
|
|
69
|
+
case Expression.in_range?(imm, :i8)
|
|
70
|
+
when true
|
|
71
|
+
or_bits[1 << 6]
|
|
72
|
+
[ret << Expression.encode_imm(imm, :i8, endianness)]
|
|
73
|
+
when false
|
|
74
|
+
or_bits[2 << 6]
|
|
75
|
+
[ret << Expression.encode_imm(imm, :a16, endianness)]
|
|
76
|
+
when nil
|
|
77
|
+
rets = ret.dup
|
|
78
|
+
or_bits[1<<6]
|
|
79
|
+
ret << @imm.encode(:i8, endianness)
|
|
80
|
+
ret, rets = rets, ret # or_bits uses ret
|
|
81
|
+
or_bits[2<<6]
|
|
82
|
+
ret << @imm.encode(:a16, endianness)
|
|
83
|
+
[ret, rets]
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
[ret]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def encode32(reg, endianness)
|
|
91
|
+
# 0 => [ [0 ], [1 ], [2 ], [3 ], [:sib ], [:i32 ], [6 ], [7 ] ], \
|
|
92
|
+
# 1 => [ [0, :i8 ], [1, :i8 ], [2, :i8 ], [3, :i8 ], [:sib, :i8 ], [5, :i8 ], [6, :i8 ], [7, :i8 ] ], \
|
|
93
|
+
# 2 => [ [0, :i32], [1, :i32], [2, :i32], [3, :i32], [:sib, :i32], [5, :i32], [6, :i32], [7, :i32] ]
|
|
94
|
+
#
|
|
95
|
+
# b => 0 1 2 3 4 5+i|i 6 7
|
|
96
|
+
# i => 0 1 2 3 nil 5 6 7
|
|
97
|
+
|
|
98
|
+
ret = EncodedData.new << (reg << 3)
|
|
99
|
+
|
|
100
|
+
# add bits in the first octet of ret.data (1.9 compatibility layer)
|
|
101
|
+
or_bits = lambda { |v| # rape me
|
|
102
|
+
if ret.data[0].kind_of? Integer
|
|
103
|
+
ret.data[0] |= v
|
|
104
|
+
else
|
|
105
|
+
ret.data[0] = (ret.data[0].unpack('C').first | v).chr
|
|
106
|
+
end
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if not self.b and not self.i
|
|
110
|
+
or_bits[5]
|
|
111
|
+
[ret << @imm.encode(:a32, endianness)]
|
|
112
|
+
|
|
113
|
+
elsif not self.b and self.s != 1
|
|
114
|
+
# sib with no b
|
|
115
|
+
raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4
|
|
116
|
+
or_bits[4]
|
|
117
|
+
s = {8=>3, 4=>2, 2=>1}[@s]
|
|
118
|
+
imm = self.imm || Expression[0]
|
|
119
|
+
fu = (s << 6) | (@i.val << 3) | 5
|
|
120
|
+
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
121
|
+
[ret << fu << imm.encode(:a32, endianness)]
|
|
122
|
+
else
|
|
123
|
+
imm = @imm.reduce if self.imm
|
|
124
|
+
imm = nil if imm == 0
|
|
125
|
+
|
|
126
|
+
if not self.i or (not self.b and self.s == 1)
|
|
127
|
+
# no sib byte (except for [esp])
|
|
128
|
+
b = self.b || self.i
|
|
129
|
+
|
|
130
|
+
or_bits[b.val]
|
|
131
|
+
ret << 0x24 if b.val == 4
|
|
132
|
+
else
|
|
133
|
+
# sib
|
|
134
|
+
or_bits[4]
|
|
135
|
+
|
|
136
|
+
i, b = @i, @b
|
|
137
|
+
b, i = i, b if @s == 1 and (i.val == 4 or b.val == 5)
|
|
138
|
+
|
|
139
|
+
raise EncodeError, "Invalid ModRM #{self}" if i.val == 4
|
|
140
|
+
|
|
141
|
+
s = {8=>3, 4=>2, 2=>1, 1=>0}[@s]
|
|
142
|
+
fu = (s << 6) | (i.val << 3) | b.val
|
|
143
|
+
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
144
|
+
ret << fu
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
imm ||= 0 if b.val == 5
|
|
148
|
+
if imm
|
|
149
|
+
case Expression.in_range?(imm, :i8)
|
|
150
|
+
when true
|
|
151
|
+
or_bits[1<<6]
|
|
152
|
+
[ret << Expression.encode_imm(imm, :i8, endianness)]
|
|
153
|
+
when false
|
|
154
|
+
or_bits[2<<6]
|
|
155
|
+
[ret << Expression.encode_imm(imm, :a32, endianness)]
|
|
156
|
+
when nil
|
|
157
|
+
rets = ret.dup
|
|
158
|
+
or_bits[1<<6]
|
|
159
|
+
ret << @imm.encode(:i8, endianness)
|
|
160
|
+
rets, ret = ret, rets # or_bits[] modifies ret directly
|
|
161
|
+
or_bits[2<<6]
|
|
162
|
+
ret << @imm.encode(:a32, endianness)
|
|
163
|
+
[ret, rets]
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
[ret]
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class Farptr
|
|
173
|
+
def encode(endianness, atype)
|
|
174
|
+
@addr.encode(atype, endianness) << @seg.encode(:u16, endianness)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# returns all forms of the encoding of instruction i using opcode op
|
|
179
|
+
# program may be used to create a new label for relative jump/call
|
|
180
|
+
def encode_instr_op(program, i, op)
|
|
181
|
+
base = op.bin.dup
|
|
182
|
+
oi = op.args.zip(i.args)
|
|
183
|
+
set_field = lambda { |f, v|
|
|
184
|
+
v ||= 0 # ST => ST(0)
|
|
185
|
+
fld = op.fields[f]
|
|
186
|
+
base[fld[0]] |= v << fld[1]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
size = i.prefix[:sz] || @size
|
|
190
|
+
|
|
191
|
+
#
|
|
192
|
+
# handle prefixes and bit fields
|
|
193
|
+
#
|
|
194
|
+
pfx = i.prefix.map { |k, v|
|
|
195
|
+
case k
|
|
196
|
+
when :jmp; {:jmp => 0x3e, :nojmp => 0x2e}[v]
|
|
197
|
+
when :lock; 0xf0
|
|
198
|
+
when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v] # TODO
|
|
199
|
+
end
|
|
200
|
+
}.compact.pack 'C*'
|
|
201
|
+
pfx << op.props[:needpfx] if op.props[:needpfx]
|
|
202
|
+
|
|
203
|
+
if op.name == 'movsx' or op.name == 'movzx'
|
|
204
|
+
pfx << 0x66 if size == 48-i.args[0].sz
|
|
205
|
+
else
|
|
206
|
+
opsz = op.props[:argsz]
|
|
207
|
+
oi.each { |oa, ia|
|
|
208
|
+
case oa
|
|
209
|
+
when :reg, :reg_eax, :modrm, :modrmA, :mrm_imm
|
|
210
|
+
raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
|
|
211
|
+
opsz = ia.sz
|
|
212
|
+
end
|
|
213
|
+
}
|
|
214
|
+
pfx << 0x66 if (not op.props[:argsz] or opsz != op.props[:argsz]) and (
|
|
215
|
+
(opsz and size == 48 - opsz) or (op.props[:opsz] and op.props[:opsz] != size))
|
|
216
|
+
if op.props[:opsz] and size == 48 - op.props[:opsz]
|
|
217
|
+
opsz = op.props[:opsz]
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
opsz ||= size
|
|
221
|
+
|
|
222
|
+
if op.props[:adsz] and size == 48 - op.props[:adsz]
|
|
223
|
+
pfx << 0x67
|
|
224
|
+
adsz = 48 - size
|
|
225
|
+
end
|
|
226
|
+
adsz ||= size
|
|
227
|
+
# addrsize override / segment override
|
|
228
|
+
if mrm = i.args.grep(ModRM).first
|
|
229
|
+
if not op.props[:adsz] and ((mrm.b and mrm.b.sz != adsz) or (mrm.i and mrm.i.sz != adsz))
|
|
230
|
+
pfx << 0x67
|
|
231
|
+
adsz = 48 - adsz
|
|
232
|
+
end
|
|
233
|
+
pfx << [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][mrm.seg.val] if mrm.seg
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
#
|
|
238
|
+
# encode embedded arguments
|
|
239
|
+
#
|
|
240
|
+
postponed = []
|
|
241
|
+
oi.each { |oa, ia|
|
|
242
|
+
case oa
|
|
243
|
+
when :reg, :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :regfp, :regmmx, :regxmm
|
|
244
|
+
# field arg
|
|
245
|
+
set_field[oa, ia.val]
|
|
246
|
+
pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
|
|
247
|
+
when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
|
|
248
|
+
# implicit
|
|
249
|
+
else
|
|
250
|
+
postponed << [oa, ia]
|
|
251
|
+
end
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if !(op.args & [:modrm, :modrmA, :modrmxmm, :modrmmmx]).empty?
|
|
255
|
+
# reg field of modrm
|
|
256
|
+
regval = (base[-1] >> 3) & 7
|
|
257
|
+
base.pop
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# convert label name for jmp/call/loop to relative offset
|
|
261
|
+
if op.props[:setip] and op.name[0, 3] != 'ret' and i.args.first.kind_of? Expression
|
|
262
|
+
postlabel = program.new_label('post'+op.name)
|
|
263
|
+
target = postponed.first[1]
|
|
264
|
+
target = target.rexpr if target.kind_of? Expression and target.op == :+ and not target.lexpr
|
|
265
|
+
postponed.first[1] = Expression[target, :-, postlabel]
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
#
|
|
269
|
+
# append other arguments
|
|
270
|
+
#
|
|
271
|
+
ret = EncodedData.new(pfx + base.pack('C*'))
|
|
272
|
+
|
|
273
|
+
postponed.each { |oa, ia|
|
|
274
|
+
case oa
|
|
275
|
+
when :farptr; ed = ia.encode(@endianness, "a#{opsz}".to_sym)
|
|
276
|
+
when :modrm, :modrmA, :modrmmmx, :modrmxmm
|
|
277
|
+
if ia.kind_of? ModRM
|
|
278
|
+
ed = ia.encode(regval, @endianness)
|
|
279
|
+
if ed.kind_of?(::Array)
|
|
280
|
+
if ed.length > 1
|
|
281
|
+
# we know that no opcode can have more than 1 modrm
|
|
282
|
+
ary = []
|
|
283
|
+
ed.each { |m|
|
|
284
|
+
ary << (ret.dup << m)
|
|
285
|
+
}
|
|
286
|
+
ret = ary
|
|
287
|
+
next
|
|
288
|
+
else
|
|
289
|
+
ed = ed.first
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
else
|
|
293
|
+
ed = ModRM.encode_reg(ia, regval)
|
|
294
|
+
end
|
|
295
|
+
when :mrm_imm; ed = ia.imm.encode("a#{adsz}".to_sym, @endianness)
|
|
296
|
+
when :i8, :u8, :u16; ed = ia.encode(oa, @endianness)
|
|
297
|
+
when :i; ed = ia.encode("a#{opsz}".to_sym, @endianness)
|
|
298
|
+
else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
if ret.kind_of?(::Array)
|
|
302
|
+
ret.each { |e| e << ed }
|
|
303
|
+
else
|
|
304
|
+
ret << ed
|
|
305
|
+
end
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# we know that no opcode with setip accept both modrm and immediate arg, so ret is not an ::Array
|
|
309
|
+
ret.add_export(postlabel, ret.virtsize) if postlabel
|
|
310
|
+
|
|
311
|
+
ret
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
@@ -0,0 +1,233 @@
|
|
|
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
|
+
|
|
9
|
+
module Metasm
|
|
10
|
+
|
|
11
|
+
# The ia32 aka x86 CPU
|
|
12
|
+
# currently limited to 16 and 32bit modes
|
|
13
|
+
class Ia32 < CPU
|
|
14
|
+
|
|
15
|
+
# some ruby magic to declare classes with index -> name association (registers)
|
|
16
|
+
class Argument
|
|
17
|
+
class << self
|
|
18
|
+
# for subclasses
|
|
19
|
+
attr_accessor :i_to_s, :s_to_i
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
# index -> name, name -> index
|
|
24
|
+
def self.simple_map(a)
|
|
25
|
+
# { 1 => 'dr1' }
|
|
26
|
+
@i_to_s = Hash[*a.flatten]
|
|
27
|
+
# { 'dr1' => 1 }
|
|
28
|
+
@s_to_i = @i_to_s.invert
|
|
29
|
+
|
|
30
|
+
class_eval {
|
|
31
|
+
attr_accessor :val
|
|
32
|
+
def initialize(v)
|
|
33
|
+
raise Exception, "invalid #{self.class} #{v}" unless self.class.i_to_s[v]
|
|
34
|
+
@val = v
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.from_str(s) new(@s_to_i[s]) end
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# size -> (index -> name), name -> [index, size]
|
|
42
|
+
def self.double_map(h)
|
|
43
|
+
# { 32 => { 1 => 'ecx' } }
|
|
44
|
+
@i_to_s = h
|
|
45
|
+
# { 'ecx' => [1, 32] }
|
|
46
|
+
@s_to_i = {} ; @i_to_s.each { |sz, hh| hh.each_with_index { |r, i| @s_to_i[r] = [i, sz] } }
|
|
47
|
+
|
|
48
|
+
class_eval {
|
|
49
|
+
attr_accessor :val, :sz
|
|
50
|
+
def initialize(v, sz)
|
|
51
|
+
raise Exception, "invalid #{self.class} #{sz}/#{v}" unless self.class.i_to_s[sz] and self.class.i_to_s[sz][v]
|
|
52
|
+
@val = v
|
|
53
|
+
@sz = sz
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.from_str(s)
|
|
57
|
+
raise "Bad #{name} #{s.inspect}" if not x = @s_to_i[s]
|
|
58
|
+
new(*x)
|
|
59
|
+
end
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# segment register: es, cs, ss, ds, fs, gs and the theoretical segr6/7
|
|
67
|
+
class SegReg < Argument
|
|
68
|
+
simple_map((0..7).zip(%w(es cs ss ds fs gs segr6 segr7)))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# debug register (dr0..dr3, dr6, dr7), and theoretical dr4/5
|
|
72
|
+
class DbgReg < Argument
|
|
73
|
+
simple_map((0..7).map { |i| [i, "dr#{i}"] })
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# control register (cr0, cr2, cr3, cr4) and theoretical cr1/5/6/7
|
|
77
|
+
class CtrlReg < Argument
|
|
78
|
+
simple_map((0..7).map { |i| [i, "cr#{i}"] })
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# floating point registers
|
|
82
|
+
class FpReg < Argument
|
|
83
|
+
simple_map((0..7).map { |i| [i, "ST(#{i})"] } << [nil, 'ST'])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# a single operation multiple data register (mm0..mm7, xmm0..xmm7)
|
|
87
|
+
class SimdReg < Argument
|
|
88
|
+
double_map 64 => (0..7).map { |n| "mm#{n}" },
|
|
89
|
+
128 => (0..7).map { |n| "xmm#{n}" }
|
|
90
|
+
def symbolic(di=nil) ; to_s.to_sym end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# general purpose registers, all sizes
|
|
94
|
+
class Reg < Argument
|
|
95
|
+
double_map 8 => %w{ al cl dl bl ah ch dh bh},
|
|
96
|
+
16 => %w{ ax cx dx bx sp bp si di},
|
|
97
|
+
32 => %w{eax ecx edx ebx esp ebp esi edi}
|
|
98
|
+
|
|
99
|
+
Sym = @i_to_s[32].map { |s| s.to_sym }
|
|
100
|
+
|
|
101
|
+
# returns a symbolic representation of the register:
|
|
102
|
+
# eax => :eax
|
|
103
|
+
# cx => :ecx & 0xffff
|
|
104
|
+
# ah => (:eax >> 8) & 0xff
|
|
105
|
+
def symbolic(di=nil)
|
|
106
|
+
s = Sym[@val]
|
|
107
|
+
if @sz == 8 and to_s[-1] == ?h
|
|
108
|
+
Expression[[Sym[@val-4], :>>, 8], :&, 0xff]
|
|
109
|
+
elsif @sz == 8
|
|
110
|
+
Expression[s, :&, 0xff]
|
|
111
|
+
elsif @sz == 16
|
|
112
|
+
Expression[s, :&, 0xffff]
|
|
113
|
+
else
|
|
114
|
+
s
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# checks if two registers have bits in common
|
|
119
|
+
def share?(other)
|
|
120
|
+
other.val % (other.sz >> 1) == @val % (@sz >> 1) and (other.sz != @sz or @sz != 8 or other.val == @val)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# a far pointer
|
|
125
|
+
# an immediate (numeric) pointer and an immediate segment selector
|
|
126
|
+
class Farptr < Argument
|
|
127
|
+
attr_accessor :seg, :addr
|
|
128
|
+
def initialize(seg, addr)
|
|
129
|
+
@seg, @addr = seg, addr
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# ModRM represents indirections in x86 (eg dword ptr [eax+4*ebx+12h])
|
|
134
|
+
class ModRM < Argument
|
|
135
|
+
# valid combinaisons for a modrm
|
|
136
|
+
# ints are reg indexes, symbols are immediates, except :sib
|
|
137
|
+
Sum = {
|
|
138
|
+
16 => {
|
|
139
|
+
0 => [ [3, 6], [3, 7], [5, 6], [5, 7], [6], [7], [:i16], [3] ],
|
|
140
|
+
1 => [ [3, 6, :i8 ], [3, 7, :i8 ], [5, 6, :i8 ], [5, 7, :i8 ], [6, :i8 ], [7, :i8 ], [5, :i8 ], [3, :i8 ] ],
|
|
141
|
+
2 => [ [3, 6, :i16], [3, 7, :i16], [5, 6, :i16], [5, 7, :i16], [6, :i16], [7, :i16], [5, :i16], [3, :i16] ]
|
|
142
|
+
},
|
|
143
|
+
32 => {
|
|
144
|
+
0 => [ [0], [1], [2], [3], [:sib], [:i32], [6], [7] ],
|
|
145
|
+
1 => [ [0, :i8 ], [1, :i8 ], [2, :i8 ], [3, :i8 ], [:sib, :i8 ], [5, :i8 ], [6, :i8 ], [7, :i8 ] ],
|
|
146
|
+
2 => [ [0, :i32], [1, :i32], [2, :i32], [3, :i32], [:sib, :i32], [5, :i32], [6, :i32], [7, :i32] ]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
attr_accessor :adsz, :sz
|
|
152
|
+
attr_accessor :seg
|
|
153
|
+
attr_accessor :s, :i, :b, :imm
|
|
154
|
+
|
|
155
|
+
# creates a new ModRM with the specified attributes:
|
|
156
|
+
# - adsz (16/32), sz (8/16/32: byte ptr, word ptr, dword ptr)
|
|
157
|
+
# - s, i, b, imm
|
|
158
|
+
# - segment selector override
|
|
159
|
+
def initialize(adsz, sz, s, i, b, imm, seg = nil)
|
|
160
|
+
@adsz, @sz = adsz, sz
|
|
161
|
+
@s, @i = s, i if i
|
|
162
|
+
@b = b if b
|
|
163
|
+
@imm = imm if imm
|
|
164
|
+
@seg = seg if seg
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# returns the symbolic representation of the ModRM (ie an Indirection)
|
|
168
|
+
# segment selectors are represented as eg "segment_base_fs"
|
|
169
|
+
# not present when same as implicit (ds:edx, ss:esp)
|
|
170
|
+
def symbolic(di=nil)
|
|
171
|
+
p = nil
|
|
172
|
+
p = Expression[p, :+, @b.symbolic(di)] if b
|
|
173
|
+
p = Expression[p, :+, [@s, :*, @i.symbolic(di)]] if i
|
|
174
|
+
p = Expression[p, :+, @imm] if imm
|
|
175
|
+
p = Expression["segment_base_#@seg", :+, p] if seg and seg.val != ((b && (@b.val == 4 || @b.val == 5)) ? 2 : 3)
|
|
176
|
+
Indirection[p.reduce, @sz/8, (di.address if di)]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Create a new instance of an Ia32 cpu
|
|
182
|
+
# arguments (any order)
|
|
183
|
+
# - size in bits (16, 32) [32]
|
|
184
|
+
# - instruction set (386, 486, pentium...) [latest]
|
|
185
|
+
# - endianness [:little]
|
|
186
|
+
def initialize(*a)
|
|
187
|
+
super()
|
|
188
|
+
@size = (a & [16, 32]).first || 32
|
|
189
|
+
a.delete @size
|
|
190
|
+
@endianness = (a & [:big, :little]).first || :little
|
|
191
|
+
a.delete @endianness
|
|
192
|
+
@family = a.pop || :latest
|
|
193
|
+
raise "Invalid arguments #{a.inspect}" if not a.empty?
|
|
194
|
+
raise "Invalid Ia32 family #{@family.inspect}" if not respond_to?("init_#@family")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# wrapper to transparently forward Ia32.new(64) to X86_64.new
|
|
198
|
+
def self.new(*a)
|
|
199
|
+
return X86_64.new(*a) if a.include? 64 and self == Ia32
|
|
200
|
+
super(*a)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# initializes the @opcode_list according to @family
|
|
204
|
+
def init_opcode_list
|
|
205
|
+
send("init_#@family")
|
|
206
|
+
@opcode_list
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# defines some preprocessor macros to say who we are:
|
|
210
|
+
# _M_IX86 = 500, _X86_, __i386__
|
|
211
|
+
# pass any value in nodefine to just call super w/o defining anything of our own
|
|
212
|
+
def tune_prepro(pp, nodefine = false)
|
|
213
|
+
super(pp)
|
|
214
|
+
return if nodefine
|
|
215
|
+
pp.define_weak('_M_IX86', 500)
|
|
216
|
+
pp.define_weak('_X86_')
|
|
217
|
+
pp.define_weak('__i386__')
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# returns a Reg object if the arg is a valid register (eg 'ax' => Reg.new(0, 16))
|
|
221
|
+
# returns nil if str is invalid
|
|
222
|
+
def str_to_reg(str)
|
|
223
|
+
Reg.from_str(str) if Reg.s_to_i.has_key? str
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def shortname
|
|
227
|
+
"ia32#{'_16' if @size == 16}#{'_be' if @endianness == :big}"
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
X86 = Ia32
|
|
232
|
+
|
|
233
|
+
end
|