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