mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
bring metasm to tip
git-svn-id: file:///home/svn/framework3/trunk@10600 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
d8b9cf5cac
commit
7f9fe3b527
@ -47,8 +47,7 @@ module Metasm
|
||||
'X86_64' => 'x86_64', 'Sh4' => 'sh4', 'Dalvik' => 'dalvik',
|
||||
'C' => ['parse_c', 'compile_c'],
|
||||
'MZ' => 'exe_format/mz', 'PE' => 'exe_format/pe',
|
||||
'ELF' => ['exe_format/elf_encode', 'exe_format/elf_decode'],
|
||||
'COFF' => ['exe_format/coff_encode', 'exe_format/coff_decode'],
|
||||
'ELF' => 'exe_format/elf', 'COFF' => 'exe_format/coff',
|
||||
'Shellcode' => 'exe_format/shellcode', 'AutoExe' => 'exe_format/autoexe',
|
||||
'AOut' => 'exe_format/a_out', 'MachO' => 'exe_format/macho',
|
||||
'DEX' => 'exe_format/dex',
|
||||
@ -76,7 +75,6 @@ def self.autorequire_const_missing(c)
|
||||
const_get c
|
||||
end
|
||||
|
||||
|
||||
def self.require(f)
|
||||
# temporarily put the current file directory in the ruby include path
|
||||
if not $:.include? Metasmdir
|
||||
@ -121,6 +119,10 @@ require 'metasm/os/main'
|
||||
|
||||
# remove an 1.9 warning, couldn't find a compatible way...
|
||||
if {}.respond_to? :key
|
||||
puts "using ruby1.9 workaround for Hash.index" if $DEBUG
|
||||
class Hash ; alias index key end
|
||||
puts "using ruby1.9 workaround for Hash#index warning" if $DEBUG
|
||||
class Hash
|
||||
alias index_premetasm index rescue nil
|
||||
undef index rescue nil
|
||||
alias index key
|
||||
end
|
||||
end
|
||||
|
@ -204,6 +204,9 @@ class Decompiler
|
||||
def new_global_var(addr, type, scope=nil)
|
||||
addr = @dasm.normalize(addr)
|
||||
|
||||
# (almost) NULL ptr
|
||||
return if addr.kind_of? Fixnum and addr >= 0 and addr < 32
|
||||
|
||||
# check preceding structure we're hitting
|
||||
# TODO check what we step over when defining a new static struct
|
||||
0x100.times { |i_|
|
||||
@ -485,7 +488,7 @@ class Decompiler
|
||||
when C::Goto
|
||||
if jumpto[s.target]
|
||||
r = jumpto[s.target].dup
|
||||
r.value = C::CExpression[r.value.reduce(@c_parser)] if r.kind_of? C::Return and r.value # deep_dup
|
||||
r.value = r.value.deep_dup if r.kind_of? C::Return and r.value.kind_of? C::CExpression
|
||||
r
|
||||
end
|
||||
when C::Return
|
||||
|
@ -657,6 +657,8 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
||||
split_block(di.block, di.address) if not di.block_head? # this updates di.block
|
||||
di.block.add_from(from, from_subfuncret ? :subfuncret : :normal) if from and from != :default
|
||||
bf = di.block
|
||||
elsif di == true
|
||||
bf = @function[addr]
|
||||
end
|
||||
elsif bf = @function[addr]
|
||||
detect_function_thunk_noreturn(from) if bf.noreturn
|
||||
@ -1943,20 +1945,23 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
||||
vals = []
|
||||
edata.ptr = off
|
||||
dups = dumplen/elemlen
|
||||
elemsym = "u#{elemlen*8}".to_sym
|
||||
while edata.ptr < edata.data.length
|
||||
if vals.length > dups and vals.uniq.length > 1
|
||||
if vals.length > dups and vals.last != vals.first
|
||||
# we have a dup(), unread the last element which is different
|
||||
vals.pop
|
||||
addr = Expression[addr, :-, elemlen].reduce
|
||||
edata.ptr -= elemlen
|
||||
break
|
||||
end
|
||||
break if vals.length == dups and vals.uniq.length > 1
|
||||
vals << edata.decode_imm("u#{elemlen*8}".to_sym, @cpu.endianness)
|
||||
vals << edata.decode_imm(elemsym, @cpu.endianness)
|
||||
addr += elemlen
|
||||
if i = (1-elemlen..0).find { |i_|
|
||||
t = addr + i_
|
||||
@xrefs[t] or @decoded[t] or edata.reloc[edata.ptr+i_] or edata.inv_export[edata.ptr+i_]
|
||||
}
|
||||
# i < 0
|
||||
edata.ptr += i
|
||||
addr += i
|
||||
break
|
||||
|
@ -700,18 +700,11 @@ class Disassembler
|
||||
|
||||
found = []
|
||||
@sections.each { |sec_addr, e|
|
||||
chunkoff = 0
|
||||
while chunkoff < e.data.length
|
||||
chunk = e.data[chunkoff, chunksz+margin].to_str
|
||||
off = 0
|
||||
while match_off = (chunk[off..-1] =~ pat)
|
||||
break if off+match_off >= chunksz
|
||||
match_addr = sec_addr + chunkoff + off + match_off
|
||||
e.pattern_scan(pat, chunksz, margin) { |eo|
|
||||
match_addr = sec_addr + eo
|
||||
found << match_addr if not block_given? or yield(match_addr)
|
||||
off += match_off + 1
|
||||
end
|
||||
chunkoff += chunksz
|
||||
end
|
||||
false
|
||||
}
|
||||
}
|
||||
found
|
||||
end
|
||||
|
@ -52,7 +52,12 @@ extern VALUE *rb_eArgError __attribute__((import));
|
||||
#define Qtrue ((VALUE)2)
|
||||
#define Qnil ((VALUE)4)
|
||||
|
||||
#if #{RUBY_VERSION >= '1.9' ? 1 : 0}
|
||||
// allows generating a ruby1.9 dynldr.so from ruby1.8
|
||||
#ifndef DYNLDR_RUBY_19
|
||||
#define DYNLDR_RUBY_19 #{RUBY_VERSION >= '1.9' ? 1 : 0}
|
||||
#endif
|
||||
|
||||
#if DYNLDR_RUBY_19
|
||||
#define T_STRING 0x05
|
||||
#define T_ARRAY 0x07
|
||||
#define T_FIXNUM 0x15
|
||||
@ -78,7 +83,7 @@ extern VALUE *rb_eArgError __attribute__((import));
|
||||
VALUE rb_uint2inum(VALUE);
|
||||
VALUE rb_ull2inum(unsigned long long);
|
||||
VALUE rb_num2ulong(VALUE);
|
||||
VALUE rb_str_new(const char* ptr, unsigned long len); // alloc + memcpy + 0term
|
||||
VALUE rb_str_new(const char* ptr, long len); // alloc + memcpy + 0term
|
||||
VALUE rb_ary_new2(int len);
|
||||
VALUE rb_float_new(double);
|
||||
|
||||
@ -128,7 +133,7 @@ static VALUE dynldr;
|
||||
|
||||
static VALUE memory_read(VALUE self, VALUE addr, VALUE len)
|
||||
{
|
||||
return rb_str_new((char*)VAL2INT(addr), (unsigned long)VAL2INT(len));
|
||||
return rb_str_new((char*)VAL2INT(addr), (long)VAL2INT(len));
|
||||
}
|
||||
|
||||
static VALUE memory_read_int(VALUE self, VALUE addr)
|
||||
@ -162,18 +167,34 @@ static VALUE str_ptr(VALUE self, VALUE str)
|
||||
return INT2VAL((uintptr_t)STR_PTR(str));
|
||||
}
|
||||
|
||||
// return the VALUE of an object (different of .object_id for Symbols, maybe others)
|
||||
static VALUE rb_obj_to_value(VALUE self, VALUE obj)
|
||||
{
|
||||
return INT2VAL((uintptr_t)obj);
|
||||
}
|
||||
|
||||
// return the ruby object at VALUE
|
||||
// USE WITH CAUTION, passing invalid values will segfault the interpreter/GC
|
||||
static VALUE rb_value_to_obj(VALUE self, VALUE val)
|
||||
{
|
||||
return VAL2INT(val);
|
||||
}
|
||||
|
||||
// load a symbol from a lib byname, byordinal if integral
|
||||
static VALUE sym_addr(VALUE self, VALUE lib, VALUE func)
|
||||
{
|
||||
uintptr_t h, p;
|
||||
|
||||
if (TYPE(lib) != T_STRING)
|
||||
if (TYPE(lib) == T_STRING)
|
||||
h = os_load_lib(STR_PTR(lib));
|
||||
else if (TYPE(lib) == T_FIXNUM)
|
||||
h = VAL2INT(lib);
|
||||
else
|
||||
rb_raise(*rb_eArgError, "Invalid lib");
|
||||
|
||||
if (TYPE(func) != T_STRING && TYPE(func) != T_FIXNUM)
|
||||
rb_raise(*rb_eArgError, "Invalid func");
|
||||
|
||||
h = os_load_lib(STR_PTR(lib));
|
||||
|
||||
if (TYPE(func) == T_FIXNUM)
|
||||
p = os_load_sym_ord(h, VAL2INT(func));
|
||||
else
|
||||
@ -322,6 +343,8 @@ int Init_dynldr(void) __attribute__((export_as(Init_<insertfilenamehere>))) // t
|
||||
rb_define_singleton_method(dynldr, "memory_write", memory_write, 2);
|
||||
rb_define_singleton_method(dynldr, "memory_write_int", memory_write_int, 2);
|
||||
rb_define_singleton_method(dynldr, "str_ptr", str_ptr, 1);
|
||||
rb_define_singleton_method(dynldr, "rb_obj_to_value", rb_obj_to_value, 1);
|
||||
rb_define_singleton_method(dynldr, "rb_value_to_obj", rb_value_to_obj, 1);
|
||||
rb_define_singleton_method(dynldr, "sym_addr", sym_addr, 2);
|
||||
rb_define_singleton_method(dynldr, "raw_invoke", invoke, 3);
|
||||
rb_define_const(dynldr, "CALLBACK_TARGET", INT2VAL((VALUE)&callback_handler));
|
||||
@ -462,13 +485,9 @@ do_invoke_fastcall:
|
||||
add eax, 8
|
||||
mov [ebp+16], eax
|
||||
|
||||
mov eax,[ebp+12]
|
||||
test eax, eax
|
||||
jz _do_invoke_call
|
||||
dec eax
|
||||
test eax, eax
|
||||
jz _do_invoke_call
|
||||
dec eax
|
||||
mov eax, [ebp+12]
|
||||
sub eax, 2
|
||||
jb _do_invoke_call
|
||||
jmp _do_invoke_copy
|
||||
|
||||
do_invoke:
|
||||
@ -583,7 +602,7 @@ EOS
|
||||
def self.compile_binary_module_hack(bin)
|
||||
# this is a hack
|
||||
# we need the module to use ruby symbols
|
||||
# but we don't know the actual lib filename (depends on ruby version,
|
||||
# but we don't know the actual ruby lib filename (depends on ruby version,
|
||||
# platform, ...)
|
||||
case bin.class.name.gsub(/.*::/, '')
|
||||
when 'ELF'
|
||||
@ -626,7 +645,7 @@ EOS
|
||||
bin.arch_encode_thunk(text, i) # encode a jmp [importtable]
|
||||
end
|
||||
|
||||
# update to the offset table
|
||||
# update the offset table
|
||||
asm_table << "#{sym} #{dd} #{str_label} - ruby_import_table"
|
||||
}
|
||||
# dont forget the final 0
|
||||
@ -701,13 +720,12 @@ EOS
|
||||
|
||||
# parse a C string into the @cp parser, create it if needed
|
||||
def self.parse_c(src)
|
||||
@cp ||= C::Parser.new(host_exe.new(host_cpu))
|
||||
@cp.parse(src)
|
||||
cp.parse(src)
|
||||
end
|
||||
|
||||
# compile a C fragment into a Shellcode, honors the host ABI
|
||||
def self.compile_c(src)
|
||||
# XXX could we reuse @cp ? (for its macros etc)
|
||||
# XXX could we reuse self.cp ? (for its macros etc)
|
||||
cp = C::Parser.new(host_exe.new(host_cpu))
|
||||
cp.parse(src)
|
||||
sc = Shellcode.new(host_cpu)
|
||||
@ -733,9 +751,9 @@ EOS
|
||||
proto += "\n;" # allow 'int foo()' and '#include <bar>'
|
||||
parse_c(proto)
|
||||
|
||||
@cp.toplevel.symbol.dup.each_value { |v|
|
||||
cp.toplevel.symbol.dup.each_value { |v|
|
||||
next if not v.kind_of? C::Variable # enums
|
||||
@cp.toplevel.symbol.delete v.name
|
||||
cp.toplevel.symbol.delete v.name
|
||||
lib = fromlib || lib_from_sym(v.name)
|
||||
addr = sym_addr(lib, v.name)
|
||||
if addr == 0 or addr == -1 or addr == 0xffff_ffff or addr == 0xffffffff_ffffffff
|
||||
@ -756,7 +774,7 @@ EOS
|
||||
}
|
||||
|
||||
# constant definition from macro/enum
|
||||
@cp.numeric_constants.each { |k, v|
|
||||
cp.numeric_constants.each { |k, v|
|
||||
n = k.upcase
|
||||
n = "C#{n}" if n !~ /^[A-Z]/
|
||||
const_set(n, v) if v.kind_of? Integer and not constants.map { |c| c.to_s }.include?(n)
|
||||
@ -773,7 +791,7 @@ EOS
|
||||
flags = 0
|
||||
flags |= 1 if proto.has_attribute('stdcall')
|
||||
flags |= 2 if proto.has_attribute('fastcall')
|
||||
flags |= 4 if proto.type.type.integral? and @cp.sizeof(nil, proto.type.type) == 8
|
||||
flags |= 4 if proto.type.type.integral? and cp.sizeof(nil, proto.type.type) == 8
|
||||
flags |= 8 if proto.type.type.float?
|
||||
class << self ; self ; end.send(:define_method, name) { |*a|
|
||||
raise ArgumentError, "bad arg count for #{name}: #{a.length} for #{proto.type.args.length}" if a.length != proto.type.args.length and not proto.type.varargs
|
||||
@ -792,10 +810,10 @@ EOS
|
||||
when String; str_ptr(val)
|
||||
when Proc; cb = callback_alloc_cobj(formal, val) ; (opts[:cb_list] ||= []) << cb ; cb
|
||||
# TODO when Hash, Array; if formal.type.pointed.kind_of? C::Struct; yadda yadda ; end
|
||||
else val.to_i
|
||||
else val.to_i rescue 0 # NaN, Infinity, etc
|
||||
end
|
||||
|
||||
if opts[:expand_i64] and formal and formal.type.integral? and @cp.sizeof(formal) == 8 and host_cpu.size == 32
|
||||
if opts[:expand_i64] and formal and formal.type.integral? and cp.sizeof(formal) == 8 and host_cpu.size == 32
|
||||
val = [val & 0xffff_ffff, (val >> 32) & 0xffff_ffff]
|
||||
val.reverse! if host_cpu.endianness != :little
|
||||
end
|
||||
@ -822,7 +840,7 @@ EOS
|
||||
# C raw cb arg -> ruby object
|
||||
def self.convert_arg_c2rb(formal, rawargs)
|
||||
val = rawargs.shift
|
||||
if formal.type.integral? and @cp.sizeof(formal) == 64 and host_cpu.size == 32
|
||||
if formal.type.integral? and cp.sizeof(formal) == 64 and host_cpu.size == 32
|
||||
if host.cpu.endianness == :little
|
||||
val |= rawargs.shift << 32
|
||||
else
|
||||
@ -841,7 +859,7 @@ EOS
|
||||
ret
|
||||
end
|
||||
|
||||
def self.cp; @cp ||= nil ; end
|
||||
def self.cp ; @cp ||= C::Parser.new(host_exe.new(host_cpu)) ; end
|
||||
def self.cp=(c); @cp = c ; end
|
||||
|
||||
# allocate a callback for a given C prototype (string)
|
||||
@ -849,10 +867,10 @@ EOS
|
||||
def self.callback_alloc_c(proto, &b)
|
||||
proto += ';' # allow 'int foo()'
|
||||
parse_c(proto)
|
||||
v = @cp.toplevel.symbol.values.find_all { |v_| v_.kind_of? C::Variable and v_.type.kind_of? C::Function }.first
|
||||
if (v and v.initializer) or @cp.toplevel.statements.find { |st| st.kind_of? C::Asm }
|
||||
@cp.toplevel.statements.delete_if { |st| st.kind_of? C::Asm }
|
||||
@cp.toplevel.symbol.delete v.name if v
|
||||
v = cp.toplevel.symbol.values.find_all { |v_| v_.kind_of? C::Variable and v_.type.kind_of? C::Function }.first
|
||||
if (v and v.initializer) or cp.toplevel.statements.find { |st| st.kind_of? C::Asm }
|
||||
cp.toplevel.statements.delete_if { |st| st.kind_of? C::Asm }
|
||||
cp.toplevel.symbol.delete v.name if v
|
||||
sc = compile_c(proto)
|
||||
ptr = memory_alloc(sc.encoded.length)
|
||||
sc.base_addr = ptr
|
||||
@ -863,7 +881,7 @@ EOS
|
||||
elsif not v
|
||||
raise 'empty prototype'
|
||||
else
|
||||
@cp.toplevel.symbol.delete v.name
|
||||
cp.toplevel.symbol.delete v.name
|
||||
callback_alloc_cobj(v, b)
|
||||
end
|
||||
end
|
||||
@ -878,8 +896,8 @@ EOS
|
||||
cb[:id] = id
|
||||
cb[:proc] = b
|
||||
cb[:proto] = proto
|
||||
cb[:abi_stackfix] = proto.args.inject(0) { |s, a| s + [@cp.sizeof(a), @cp.typesize[:ptr]].max } if ori and ori.has_attribute('stdcall')
|
||||
cb[:abi_stackfix] = proto.args[2..-1].to_a.inject(0) { |s, a| s + [@cp.sizeof(a), @cp.typesize[:ptr]].max } if ori and ori.has_attribute('fastcall') # supercedes stdcall
|
||||
cb[:abi_stackfix] = proto.args.inject(0) { |s, a| s + [cp.sizeof(a), cp.typesize[:ptr]].max } if ori and ori.has_attribute('stdcall')
|
||||
cb[:abi_stackfix] = proto.args[2..-1].to_a.inject(0) { |s, a| s + [cp.sizeof(a), cp.typesize[:ptr]].max } if ori and ori.has_attribute('fastcall') # supercedes stdcall
|
||||
@@callback_table[id] = cb
|
||||
id
|
||||
end
|
||||
@ -914,19 +932,21 @@ EOS
|
||||
|
||||
# compile a bunch of C functions, defines methods in this module to call them
|
||||
# returns the raw pointer to the code page
|
||||
# if given a block, run the block and then undefine all the C functions
|
||||
# if given a block, run the block and then undefine all the C functions & free memory
|
||||
def self.new_func_c(src)
|
||||
sc = compile_c(src)
|
||||
ptr = memory_alloc(sc.encoded.length)
|
||||
sc.base_addr = ptr
|
||||
# TODO fixup external calls - this will need OS ABI compat (eg win64)
|
||||
bd = sc.encoded.binding(ptr)
|
||||
sc.encoded.reloc_externals.uniq.each { |ext| bd[ext] = sym_addr(lib_from_sym(ext), ext) or raise "unknown symbol #{ext}" }
|
||||
sc.encoded.fixup(bd)
|
||||
memory_write ptr, sc.encode_string
|
||||
memory_perm ptr, sc.encoded.length, 'rwx'
|
||||
parse_c(src) # XXX the Shellcode parser may have defined stuff / interpreted C another way...
|
||||
defs = []
|
||||
@cp.toplevel.symbol.dup.each_value { |v|
|
||||
cp.toplevel.symbol.dup.each_value { |v|
|
||||
next if not v.kind_of? C::Variable
|
||||
@cp.toplevel.symbol.delete v.name
|
||||
cp.toplevel.symbol.delete v.name
|
||||
next if not v.type.kind_of? C::Function or not v.initializer
|
||||
next if not off = sc.encoded.export[v.name]
|
||||
new_caller_for(v, v.name, ptr+off)
|
||||
@ -1116,6 +1136,9 @@ EOS
|
||||
# on PaX-enabled systems, this may need a non-mprotect-restricted ruby interpreter
|
||||
def self.memory_perm(addr, len, perm)
|
||||
perm = perm.to_s.downcase
|
||||
len += (addr & 0xfff) + 0xfff
|
||||
len &= ~0xfff
|
||||
addr &= ~0xfff
|
||||
p = 0
|
||||
p |= PROT_READ if perm.include? 'r'
|
||||
p |= PROT_WRITE if perm.include? 'w'
|
||||
|
@ -409,3 +409,6 @@ class COFFArchive < ExeFormat
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'metasm/exe_format/coff_encode'
|
||||
require 'metasm/exe_format/coff_decode'
|
||||
|
@ -76,7 +76,7 @@ class COFF
|
||||
if coff.sect_at_rva(@func_p)
|
||||
@exports = []
|
||||
addrs = []
|
||||
@num_exports.times { |i| addrs << coff.decode_word }
|
||||
@num_exports.times { addrs << coff.decode_word }
|
||||
@num_exports.times { |i|
|
||||
e = Export.new
|
||||
e.ordinal = i + @ordinal_base
|
||||
|
@ -439,11 +439,11 @@ class COFF
|
||||
|
||||
# encodes a thunk to imported function
|
||||
def arch_encode_thunk(edata, import)
|
||||
case @cpu
|
||||
when Ia32
|
||||
case @cpu.shortname
|
||||
when 'ia32', 'x64'
|
||||
shellcode = lambda { |c| Shellcode.new(@cpu).share_namespace(self).assemble(c).encoded }
|
||||
if @cpu.generate_PIC
|
||||
if @cpu.size == 64
|
||||
if @cpu.shortname == 'x64'
|
||||
edata << shellcode["#{import.thunk}: jmp [rip-$_+#{import.target}]"]
|
||||
return
|
||||
end
|
||||
|
@ -736,6 +736,9 @@ class FatELF < ExeFormat
|
||||
end
|
||||
end
|
||||
|
||||
require 'metasm/exe_format/elf_encode'
|
||||
require 'metasm/exe_format/elf_decode'
|
||||
|
||||
# TODO symbol version info
|
||||
__END__
|
||||
/*
|
||||
@ -906,5 +909,3 @@ typedef struct {
|
||||
#define SYMINFO_CURRENT 1
|
||||
#define SYMINFO_NUM 2
|
||||
|
||||
P
|
||||
|
||||
|
@ -860,8 +860,13 @@ class ELF
|
||||
def init_disassembler
|
||||
d = super()
|
||||
d.backtrace_maxblocks_data = 4
|
||||
case @cpu
|
||||
when Ia32
|
||||
if d.get_section_at(0)
|
||||
# fixes call [constructor] => 0
|
||||
d.decoded[0] = true
|
||||
d.function[0] = @cpu.disassembler_default_func
|
||||
end
|
||||
case @cpu.shortname
|
||||
when 'ia32', 'x64'
|
||||
old_cp = d.c_parser
|
||||
d.c_parser = nil
|
||||
d.parse_c <<EOC
|
||||
@ -884,7 +889,7 @@ EOC
|
||||
dls.btbind_callback = lambda { |dasm, bind, funcaddr, calladdr, expr, origin, maxdepth|
|
||||
sz = @cpu.size/8
|
||||
raise 'dlsym call error' if not dasm.decoded[calladdr]
|
||||
if @cpu.kind_of? X86_64
|
||||
if @cpu.shortname == 'x64'
|
||||
arg2 = :rsi
|
||||
else
|
||||
arg2 = Indirection.new(Expression[:esp, :+, 2*sz], sz, calladdr)
|
||||
@ -898,7 +903,7 @@ EOC
|
||||
df = d.function[:default] = @cpu.disassembler_default_func
|
||||
df.backtrace_binding[@cpu.register_symbols[4]] = Expression[@cpu.register_symbols[4], :+, @cpu.size/8]
|
||||
df.btbind_callback = nil
|
||||
when MIPS
|
||||
when 'mips'
|
||||
(d.address_binding[@header.entry] ||= {})[:$t9] ||= Expression[@header.entry]
|
||||
@symbols.each { |s|
|
||||
next if s.shndx == 'UNDEF' or s.type != 'FUNC'
|
||||
|
@ -9,6 +9,7 @@ require 'metasm/parse'
|
||||
require 'metasm/encode'
|
||||
require 'metasm/decode'
|
||||
require 'metasm/exe_format/serialstruct'
|
||||
require 'metasm/os/main' # VirtualFile
|
||||
|
||||
module Metasm
|
||||
class ExeFormat
|
||||
|
@ -6,8 +6,7 @@
|
||||
|
||||
require 'metasm/exe_format/main'
|
||||
require 'metasm/exe_format/mz'
|
||||
require 'metasm/exe_format/coff_encode'
|
||||
require 'metasm/exe_format/coff_decode'
|
||||
require 'metasm/exe_format/coff'
|
||||
|
||||
module Metasm
|
||||
class PE < COFF
|
||||
@ -216,7 +215,7 @@ EOS
|
||||
# TODO seh prototype (args => context)
|
||||
# TODO hook on (non)resolution of :w xref
|
||||
def get_xrefs_x(dasm, di)
|
||||
if @cpu.kind_of? Ia32 and a = di.instruction.args.first and a.kind_of? Ia32::ModRM and a.seg and a.seg.val == 4 and
|
||||
if @cpu.shortname =~ /ia32|x64/ and a = di.instruction.args.first and a.kind_of? Ia32::ModRM and a.seg and a.seg.val == 4 and
|
||||
w = get_xrefs_rw(dasm, di).find { |type, ptr, len| type == :w and ptr.externals.include? 'segment_base_fs' } and
|
||||
dasm.backtrace(Expression[w[1], :-, 'segment_base_fs'], di.address) == [Expression[0]]
|
||||
sehptr = w[1]
|
||||
@ -239,8 +238,8 @@ EOS
|
||||
def init_disassembler
|
||||
d = super()
|
||||
d.backtrace_maxblocks_data = 4
|
||||
case @cpu
|
||||
when Ia32
|
||||
case @cpu.shortname
|
||||
when 'ia32', 'x64'
|
||||
old_cp = d.c_parser
|
||||
d.c_parser = nil
|
||||
d.parse_c '__stdcall void *GetProcAddress(int, char *);'
|
||||
|
@ -85,9 +85,20 @@ class Graph
|
||||
@madetree = false
|
||||
end
|
||||
|
||||
# gives a text representation of the current graph state
|
||||
def dump_layout(groups=@groups)
|
||||
groups.map { |g| "#{groups.index(g)} -> #{g.to.map { |t| groups.index(t) }.sort.inspect}" }
|
||||
end
|
||||
|
||||
def auto_arrange_step
|
||||
# TODO fix
|
||||
# 0->[1, 2] 1->[3] 2->[3, 4] 3->[] 4->[1]
|
||||
# push 0 jz l3 push 1 jz l4 push 2 l3: push 3 l4: hlt
|
||||
# and more generally all non-looping graphs where this algo creates backward links
|
||||
|
||||
groups = @groups
|
||||
return if groups.length <= 1
|
||||
|
||||
maketree = lambda { |roots|
|
||||
next if @madetree
|
||||
@madetree = true
|
||||
@ -116,7 +127,7 @@ class Graph
|
||||
g.to.each { |gg| walk[gg] }
|
||||
}
|
||||
|
||||
roots.each { |g| trim[g, g.from] }
|
||||
roots.each { |g| trim[g, g.from] unless g.from.empty? }
|
||||
roots.each { |g| walk[g] }
|
||||
|
||||
# handle loops now (unmarked nodes)
|
||||
@ -319,7 +330,7 @@ class Graph
|
||||
|
||||
# unknown pattern, group as we can..
|
||||
group_other = lambda {
|
||||
puts 'graph arrange: unknown configuration', groups.map { |g| "#{groups.index(g)} -> #{g.to.map { |t| groups.index(t) }.inspect}" }
|
||||
puts 'graph arrange: unknown configuration', dump_layout
|
||||
g1 = groups.find_all { |g| g.from.empty? }
|
||||
g1 << groups[rand(groups.length)] if g1.empty?
|
||||
g2 = g1.map { |g| g.to }.flatten.uniq - g1
|
||||
@ -408,8 +419,28 @@ puts 'graph arrange: unknown configuration', groups.map { |g| "#{groups.index(g)
|
||||
end
|
||||
}
|
||||
|
||||
boxxy = @box.sort_by { |bb| bb.y }
|
||||
# fill gaps that we created
|
||||
@box.each { |b|
|
||||
bottom = b.y+b.h
|
||||
next if not follower = boxxy.find { |bb| bb.y+bb.h > bottom }
|
||||
|
||||
# preserve line[] constructs margins
|
||||
gap = follower.y-16*follower.from.length - (bottom+16*b.to.length)
|
||||
next if gap <= 0
|
||||
|
||||
@box.each { |bb|
|
||||
if bb.y+bb.h <= bottom
|
||||
bb.y += gap/2
|
||||
else
|
||||
bb.y -= gap/2
|
||||
end
|
||||
}
|
||||
boxxy = @box.sort_by { |bb| bb.y }
|
||||
}
|
||||
|
||||
@box[0,0].each { |b|
|
||||
# TODO elastic positionning (ignore up arrows ?) & collision detection (box vs box and box vs arrow)
|
||||
# TODO elastic positionning (ignore up arrows ?) & collision detection (box/box + box/arrow)
|
||||
f = b.from[0]
|
||||
t = b.to[0]
|
||||
if b.to.length == 1 and b.from.length == 1 and b.y+b.h<t.y and b.y>f.y+f.h
|
||||
@ -672,21 +703,32 @@ class GraphViewWidget < DrawableWidget
|
||||
def paint_arrow(b1, b2)
|
||||
x1, y1 = b1.x+b1.w/2-@curcontext.view_x, b1.y+b1.h-@curcontext.view_y
|
||||
x2, y2 = b2.x+b2.w/2-@curcontext.view_x, b2.y-1-@curcontext.view_y
|
||||
x1o, x2o = x1, x2
|
||||
margin = @margin
|
||||
x1 += (-(b1.to.length-1)/2 + b1.to.index(b2)) * margin/2
|
||||
x2 += (-(b2.from.length-1)/2 + b2.from.index(b1)) * margin/2
|
||||
return if (y1+margin < 0 and y2 < 0) or (y1 > height/@zoom and y2-margin > height/@zoom) # just clip on y
|
||||
margin, x1, y1, x2, y2, b1w, b2w = [margin, x1, y1, x2, y2, b1.w, b2.w].map { |v| v*@zoom }
|
||||
margin, x1, y1, x2, y2, b1w, b2w, x1o, x2o = [margin, x1, y1, x2, y2, b1.w, b2.w, x1o, x2o].map { |v| v*@zoom }
|
||||
|
||||
|
||||
# gtk wraps coords around 0x8000
|
||||
# XXX gtk wraps coords around 0x8000
|
||||
if x1.abs > 0x7000 ; y1 /= x1.abs/0x7000 ; x1 /= x1.abs/0x7000 ; end
|
||||
if y1.abs > 0x7000 ; x1 /= y1.abs/0x7000 ; y1 /= y1.abs/0x7000 ; end
|
||||
if x2.abs > 0x7000 ; y2 /= x2.abs/0x7000 ; x2 /= x2.abs/0x7000 ; end
|
||||
if y2.abs > 0x7000 ; x2 /= y2.abs/0x7000 ; y2 /= y2.abs/0x7000 ; end
|
||||
|
||||
# straighten vertical arrows if possible
|
||||
if y2 > y1 and (x1-x2).abs <= margin
|
||||
if b1.to.length == 1
|
||||
x1 = x2
|
||||
elsif b2.from.length == 1
|
||||
x2 = x1
|
||||
end
|
||||
end
|
||||
|
||||
set_color_arrow(b1, b2)
|
||||
if margin > 1
|
||||
# draw arrow tip
|
||||
draw_line(x1, y1, x1, y1+margin)
|
||||
draw_line(x2, y2-margin+1, x2, y2)
|
||||
draw_line(x2-margin/2, y2-margin/2, x2, y2)
|
||||
@ -695,23 +737,26 @@ class GraphViewWidget < DrawableWidget
|
||||
y2 -= margin-1
|
||||
end
|
||||
if y2+margin >= y1-margin-1
|
||||
# straight vertical down arrow
|
||||
draw_line(x1, y1, x2, y2) if x1 != y1 or x2 != y2
|
||||
elsif x1-b1w/2-margin >= x2+b2w/2+margin # z
|
||||
draw_line(x1, y1, x1-b1w/2-margin, y1)
|
||||
draw_line(x1-b1w/2-margin, y1, x2+b2w/2+margin, y2)
|
||||
draw_line(x2+b2w/2+margin, y2, x2, y2)
|
||||
draw_line(x1, y1+1, x1-b1w/2-margin, y1+1) # double
|
||||
draw_line(x1-b1w/2-margin+1, y1, x2+b2w/2+margin+1, y2)
|
||||
draw_line(x2+b2w/2+margin, y2+1, x2, y2+1)
|
||||
|
||||
# else arrow up, need to sneak around boxes
|
||||
elsif x1o-b1w/2-margin >= x2o+b2w/2+margin # z
|
||||
draw_line(x1, y1, x1o-b1w/2-margin, y1)
|
||||
draw_line(x1o-b1w/2-margin, y1, x2o+b2w/2+margin, y2)
|
||||
draw_line(x2o+b2w/2+margin, y2, x2, y2)
|
||||
draw_line(x1, y1+1, x1o-b1w/2-margin, y1+1) # double
|
||||
draw_line(x1o-b1w/2-margin+1, y1, x2o+b2w/2+margin+1, y2)
|
||||
draw_line(x2o+b2w/2+margin, y2+1, x2, y2+1)
|
||||
elsif x1+b1w/2+margin <= x2-b2w/2-margin # invert z
|
||||
draw_line(x1, y1, x1+b1w/2+margin, y1)
|
||||
draw_line(x1+b1w/2+margin, y1, x2-b2w/2-margin, y2)
|
||||
draw_line(x2-b2w/2-margin, y2, x2, y2)
|
||||
draw_line(x1, y1, x1o+b1w/2+margin, y1)
|
||||
draw_line(x1o+b1w/2+margin, y1, x2o-b2w/2-margin, y2)
|
||||
draw_line(x2o-b2w/2-margin, y2, x2, y2)
|
||||
draw_line(x1, y1+1, x1+b1w/2+margin, y1+1) # double
|
||||
draw_line(x1+b1w/2+margin+1, y1, x2-b2w/2-margin+1, y2)
|
||||
draw_line(x2-b2w/2-margin, y2+1, x2, y2+1)
|
||||
draw_line(x1o+b1w/2+margin+1, y1, x2o-b2w/2-margin+1, y2)
|
||||
draw_line(x2o-b2w/2-margin, y2+1, x2, y2+1)
|
||||
else # turn around
|
||||
x = (x1 <= x2 ? [x1-b1w/2-margin, x2-b2w/2-margin].min : [x1+b1w/2+margin, x2+b2w/2+margin].max)
|
||||
x = (x1 <= x2 ? [x1o-b1w/2-margin, x2o-b2w/2-margin].min : [x1o+b1w/2+margin, x2o+b2w/2+margin].max)
|
||||
draw_line(x1, y1, x, y1)
|
||||
draw_line(x, y1, x, y2)
|
||||
draw_line(x, y2, x2, y2)
|
||||
@ -936,8 +981,8 @@ class GraphViewWidget < DrawableWidget
|
||||
|
||||
def keypress_ctrl(key)
|
||||
case key
|
||||
when ?f
|
||||
@parent_widget.inputbox('text to search (regex)') { |pat|
|
||||
when ?F
|
||||
@parent_widget.inputbox('text to search in curfunc (regex)') { |pat|
|
||||
re = /#{pat}/i
|
||||
list = [['addr', 'instr']]
|
||||
@curcontext.box.each { |b|
|
||||
@ -1089,41 +1134,57 @@ class GraphViewWidget < DrawableWidget
|
||||
puts 'autoarrange done'
|
||||
when ?u
|
||||
gui_update
|
||||
|
||||
when ?R
|
||||
load __FILE__
|
||||
when ?S
|
||||
when ?S # reset
|
||||
@curcontext.auto_arrange_init(@selected_boxes.empty? ? @curcontext.box : @selected_boxes)
|
||||
puts 'reset', @curcontext.dump_layout, ''
|
||||
zoom_all
|
||||
redraw
|
||||
when ?T
|
||||
when ?T # step auto_arrange
|
||||
@curcontext.auto_arrange_step
|
||||
puts @curcontext.dump_layout, ''
|
||||
zoom_all
|
||||
redraw
|
||||
when ?L
|
||||
when ?L # post auto_arrange
|
||||
@curcontext.auto_arrange_post
|
||||
zoom_all
|
||||
redraw
|
||||
when ?V
|
||||
when ?V # shrink
|
||||
@selected_boxes.each { |b_|
|
||||
dx = (b_.from+b_.to).map { |bb| bb.x+bb.w/2 - b_.x-b_.w/2 }
|
||||
dx = (b_.from + b_.to).map { |bb| bb.x+bb.w/2 - b_.x-b_.w/2 }
|
||||
dx = dx.inject(0) { |s, xx| s+xx }/dx.length
|
||||
if dx > 0
|
||||
xmax = b_.from.map { |bb| bb.x if b_.from.find { |bbb|
|
||||
bbb.x+bbb.w/2 < bb.x+bb.w/2 and bbb.y+bbb.h < bb.y
|
||||
} }.compact.min
|
||||
bx = b_.x+dx
|
||||
bx = [bx, xmax-b_.w/2-@margin].min if xmax
|
||||
b_.x = bx if bx > b_.x
|
||||
else
|
||||
xmin = b_.from.map { |bb| bb.x+bb.w if b_.from.find { |bbb|
|
||||
bbb.x+bbb.w/2 < bb.x+bb.w/2 and bbb.y+bbb.h < bb.y
|
||||
} }.compact.max
|
||||
bx = b_.x+dx
|
||||
bx = [bx, xmin+b_.w/2+@margin].max if xmin
|
||||
b_.x = bx if bx < b_.x
|
||||
end
|
||||
b_.x += dx
|
||||
}
|
||||
redraw
|
||||
when ?I # create arbitrary boxes/links
|
||||
if @selected_boxes.empty?
|
||||
@fakebox ||= 0
|
||||
b = @curcontext.new_box "id_#@fakebox",
|
||||
:addresses => [], :line_address => [],
|
||||
:line_text_col => [[[" blublu #@fakebox", :text]]]
|
||||
b.w = @font_width * 15
|
||||
b.h = @font_height * 2
|
||||
b.x = rand(200) - 100
|
||||
b.y = rand(200) - 100
|
||||
|
||||
@fakebox += 1
|
||||
else
|
||||
b1, *bl = @selected_boxes
|
||||
bl = [b1] if bl.empty? # loop
|
||||
bl.each { |b2|
|
||||
if b1.to.include? b2
|
||||
b1.to.delete b2
|
||||
b2.from.delete b1
|
||||
else
|
||||
b1.to << b2
|
||||
b2.from << b1
|
||||
end
|
||||
}
|
||||
end
|
||||
redraw
|
||||
|
||||
when ?1 # (numeric) zoom to 1:1
|
||||
if @zoom == 1.0
|
||||
zoom_all
|
||||
|
@ -572,6 +572,9 @@ class DisasmWidget < ContainerChoiceWidget
|
||||
return if not popup = DasmWindow.new
|
||||
popup.display(@dasm, @entrypoints)
|
||||
w = popup.dasm_widget
|
||||
w.bg_color_callback = @bg_color_callback if bg_color_callback
|
||||
w.keyboard_callback = @keyboard_callback
|
||||
w.keyboard_callback_ctrl = @keyboard_callback_ctrl
|
||||
w.clones = @clones.concat w.clones
|
||||
w.focus_addr(*focus)
|
||||
popup
|
||||
|
@ -161,6 +161,7 @@ class DrawableWidget < Gtk::DrawingArea
|
||||
key = {
|
||||
:page_up => :pgup, :page_down => :pgdown, :next => :pgdown,
|
||||
:escape => :esc, :return => :enter, :l1 => :f11, :l2 => :f12,
|
||||
:prior => :pgup,
|
||||
|
||||
:space => ?\ ,
|
||||
:asciitilde => ?~, :quoteleft => ?`,
|
||||
|
@ -907,7 +907,7 @@ class CCompiler < C::Compiler
|
||||
ptr = make_volatile(ptr, expr.lexpr.type) if ptr.kind_of? Address
|
||||
instr 'call', ptr
|
||||
f = expr.lexpr
|
||||
f = f.rexpr while f.kind_of? C::CExpression and not f.op and f.type == f.rexpr.type
|
||||
f = f.rexpr while f.kind_of? C::CExpression and not f.op and f.rexpr.kind_of? C::Typed and f.type == f.rexpr.type
|
||||
if not f.type.attributes.to_a.include? 'stdcall' and (not f.kind_of?(C::Variable) or not f.attributes.to_a.include? 'stdcall')
|
||||
al = typesize[:ptr]
|
||||
argsz = expr.rexpr.inject(0) { |sum, a| sum + (sizeof(a) + al - 1) / al * al }
|
||||
|
@ -181,6 +181,7 @@ class Ia32
|
||||
base = op.bin.dup
|
||||
oi = op.args.zip(i.args)
|
||||
set_field = lambda { |f, v|
|
||||
v ||= 0 # ST => ST(0)
|
||||
fld = op.fields[f]
|
||||
base[fld[0]] |= v << fld[1]
|
||||
}
|
||||
|
@ -176,6 +176,7 @@ class Ia32
|
||||
addop('mov', [0x8C], 0, {:d => [0, 1], :seg3 => [1, 3]}, :seg3) { |op| op.args.reverse! }
|
||||
addop 'out', [0xE6], nil, {:w => [0, 0]}, :reg_eax, :u8
|
||||
addop 'out', [0xE6], nil, {:w => [0, 0]}, :u8
|
||||
addop 'out', [0xEE], nil, {:w => [0, 0]}, :reg_dx, :reg_eax
|
||||
addop 'out', [0xEE], nil, {:w => [0, 0]}, :reg_eax, :reg_dx
|
||||
addop 'out', [0xEE], nil, {:w => [0, 0]}, :reg_eax # implicit arguments
|
||||
addop 'out', [0xEE], nil, {:w => [0, 0]}
|
||||
|
@ -168,6 +168,8 @@ end
|
||||
|
||||
# parses an arbitrary ia32 instruction argument
|
||||
def parse_argument(lexer)
|
||||
lexer = AsmPreprocessor.new(lexer) if lexer.kind_of? String
|
||||
|
||||
# reserved names (registers/segments etc)
|
||||
@args_token ||= [Reg, SimdReg, SegReg, DbgReg, CtrlReg, FpReg].map { |a| a.s_to_i.keys }.flatten.inject({}) { |h, e| h.update e => true }
|
||||
|
||||
@ -238,7 +240,7 @@ end
|
||||
|
||||
cond = true
|
||||
if s = o.props[:argsz] and (arg.kind_of? Reg or arg.kind_of? ModRM)
|
||||
cond = (!arg.sz or arg.sz == s)
|
||||
cond = (!arg.sz or arg.sz == s or spec == :reg_dx)
|
||||
end
|
||||
|
||||
cond and
|
||||
|
@ -341,18 +341,30 @@ class Expression < ExpressionType
|
||||
# in operands order, and allows nesting using sub-arrays
|
||||
# ex: Expression[[:-, 42], :*, [1, :+, [4, :*, 7]]]
|
||||
# with a single argument, return it if already an Expression, else construct a new one (using unary +/-)
|
||||
def self.[](l, op = nil, r = nil)
|
||||
raise ArgumentError, 'invalid Expression[nil]' if not l and not r and not op
|
||||
return l if l.kind_of? Expression and not op
|
||||
l, op, r = nil, :-, -l if not op and l.kind_of? ::Numeric and l < 0
|
||||
l, op, r = nil, :+, l if not op
|
||||
l, op, r = nil, l, op if not r
|
||||
def self.[](l, op=nil, r=nil)
|
||||
if not r # need to shift args
|
||||
if not op
|
||||
raise ArgumentError, 'invalid Expression[nil]' if not l
|
||||
return l if l.kind_of? Expression
|
||||
if l.kind_of? ::Numeric and l < 0
|
||||
r = -l
|
||||
op = :'-'
|
||||
else
|
||||
r = l
|
||||
op = :'+'
|
||||
end
|
||||
else
|
||||
r = op
|
||||
op = l
|
||||
end
|
||||
l = nil
|
||||
else
|
||||
l = self[*l] if l.kind_of? ::Array
|
||||
end
|
||||
r = self[*r] if r.kind_of? ::Array
|
||||
new(op, r, l)
|
||||
end
|
||||
|
||||
|
||||
# checks if a given Expression/Integer is in the type range
|
||||
# returns true if it is, false if it overflows, and nil if cannot be determined (eg unresolved variable)
|
||||
def self.in_range?(val, type)
|
||||
@ -391,7 +403,7 @@ class Expression < ExpressionType
|
||||
# will not match 1+2 and 2+1
|
||||
def ==(o)
|
||||
# shortcircuit recursion
|
||||
o.object_id == object_id or (o.class == self.class and @op == o.op and @lexpr == o.lexpr and @rexpr == o.rexpr)
|
||||
o.object_id == object_id or (o.kind_of?(Expression) and @op == o.op and @lexpr == o.lexpr and @rexpr == o.rexpr)
|
||||
end
|
||||
|
||||
# make it useable as Hash key (see +==+)
|
||||
@ -517,18 +529,8 @@ class Expression < ExpressionType
|
||||
0
|
||||
elsif l == 1
|
||||
Expression[r, :'!=', 0].reduce_rec
|
||||
elsif r == 0 # (no sideeffects) && 0 => 0
|
||||
sideeffect = lambda { |e|
|
||||
if e.kind_of? Expression
|
||||
not [:+, :-, :*, :/, :&, :|, :^, :>, :<, :>>, :<<, :'==', :'!=', :<=, :>=, :'&&', :'||'].include?(e.op) or
|
||||
sideeffect[e.lexpr] or sideeffect[e.rexpr]
|
||||
elsif e.kind_of? ExpressionType
|
||||
true # fail safe
|
||||
else
|
||||
false
|
||||
end
|
||||
}
|
||||
0 if not sideeffect[l]
|
||||
elsif r == 0
|
||||
0 # XXX l could be a special ExprType with sideeffects ?
|
||||
end
|
||||
elsif @op == :'||'
|
||||
if l.kind_of? ::Numeric and l != 0 # shortcircuit eval
|
||||
@ -599,26 +601,7 @@ class Expression < ExpressionType
|
||||
Expression[[l.lexpr, :&, r], l.op, [l.rexpr, :&, r]].reduce_rec
|
||||
# rol/ror composition
|
||||
elsif r.kind_of? ::Integer and l.kind_of? Expression and l.op == :|
|
||||
m = Expression[[['var', :sh_op, 'amt'], :|, ['var', :inv_sh_op, 'inv_amt']], :&, 'mask']
|
||||
if vars = Expression[l, :&, r].match(m, 'var', :sh_op, 'amt', :inv_sh_op, 'inv_amt', 'mask') and vars[:sh_op] == {:>> => :<<, :<< => :>>}[ vars[:inv_sh_op]] and
|
||||
((vars['amt'].kind_of?(::Integer) and vars['inv_amt'].kind_of?(::Integer) and ampl = vars['amt'] + vars['inv_amt']) or
|
||||
(vars['amt'].kind_of? Expression and vars['amt'].op == :% and vars['amt'].rexpr.kind_of? ::Integer and
|
||||
vars['inv_amt'].kind_of? Expression and vars['inv_amt'].op == :% and vars['amt'].rexpr == vars['inv_amt'].rexpr and ampl = vars['amt'].rexpr)) and
|
||||
vars['mask'].kind_of?(::Integer) and vars['mask'] == (1<<ampl)-1 and vars['var'].kind_of? Expression and # it's a rotation
|
||||
ivars = vars['var'].match(m, 'var', :sh_op, 'amt', :inv_sh_op, 'inv_amt', 'mask') and ivars[:sh_op] == {:>> => :<<, :<< => :>>}[ivars[:inv_sh_op]] and
|
||||
((ivars['amt'].kind_of?(::Integer) and ivars['inv_amt'].kind_of?(::Integer) and ampl = ivars['amt'] + ivars['inv_amt']) or
|
||||
(ivars['amt'].kind_of? Expression and ivars['amt'].op == :% and ivars['amt'].rexpr.kind_of? ::Integer and
|
||||
ivars['inv_amt'].kind_of? Expression and ivars['inv_amt'].op == :% and ivars['amt'].rexpr == ivars['inv_amt'].rexpr and ampl = ivars['amt'].rexpr)) and
|
||||
ivars['mask'].kind_of?(::Integer) and ivars['mask'] == (1<<ampl)-1 and ivars['mask'] == vars['mask'] # it's a composed rotation
|
||||
if ivars[:sh_op] != vars[:sh_op]
|
||||
# ensure the rotations are the same orientation
|
||||
ivars[:sh_op], ivars[:inv_sh_op] = ivars[:inv_sh_op], ivars[:sh_op]
|
||||
ivars['amt'], ivars['inv_amt'] = ivars['inv_amt'], ivars['amt']
|
||||
end
|
||||
amt = Expression[[vars['amt'], :+, ivars['amt']], :%, ampl]
|
||||
invamt = Expression[[vars['inv_amt'], :+, ivars['inv_amt']], :%, ampl]
|
||||
Expression[[[ivars['var'], vars[:sh_op], amt], :|, [ivars['var'], vars[:inv_sh_op], invamt]], :&, vars['mask']].reduce_rec
|
||||
end
|
||||
reduce_rec_composerol r, l
|
||||
end
|
||||
elsif @op == :|
|
||||
if l == 0; r
|
||||
@ -675,28 +658,7 @@ class Expression < ExpressionType
|
||||
elsif l.kind_of? Expression and r.kind_of? Expression and l.op == :% and r.op == :% and l.rexpr.kind_of?(::Integer) and l.rexpr == r.rexpr
|
||||
Expression[[l.lexpr, :+, r.lexpr], :%, l.rexpr].reduce_rec
|
||||
else
|
||||
# a+(b+(c+(-a))) => b+c+0
|
||||
# a+((-a)+(b+c)) => 0+b+c
|
||||
neg_l = l.rexpr if l.kind_of? Expression and l.op == :-
|
||||
|
||||
# recursive search & replace -lexpr by 0
|
||||
simplifier = lambda { |cur|
|
||||
if (neg_l and neg_l == cur) or (cur.kind_of? Expression and cur.op == :- and not cur.lexpr and cur.rexpr == l)
|
||||
# -l found
|
||||
0
|
||||
else
|
||||
# recurse
|
||||
if cur.kind_of? Expression and cur.op == :+
|
||||
if newl = simplifier[cur.lexpr]
|
||||
Expression[newl, cur.op, cur.rexpr].reduce_rec
|
||||
elsif newr = simplifier[cur.rexpr]
|
||||
Expression[cur.lexpr, cur.op, newr].reduce_rec
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
simplifier[r]
|
||||
reduce_rec_add(l, r)
|
||||
end
|
||||
end
|
||||
|
||||
@ -719,6 +681,60 @@ class Expression < ExpressionType
|
||||
ret
|
||||
end
|
||||
|
||||
|
||||
# a+(b+(c+(-a))) => b+c+0
|
||||
# a+((-a)+(b+c)) => 0+b+c
|
||||
def reduce_rec_add(l, r)
|
||||
if l.kind_of? Expression and l.op == :- and not l.lexpr
|
||||
neg_l = l.rexpr
|
||||
else
|
||||
neg_l = Expression[:-, l]
|
||||
end
|
||||
|
||||
# recursive search & replace -lexpr by 0
|
||||
simplifier = lambda { |cur|
|
||||
if neg_l == cur
|
||||
# -l found
|
||||
0
|
||||
elsif cur.kind_of? Expression and cur.op == :+
|
||||
# recurse
|
||||
if newl = simplifier[cur.lexpr]
|
||||
Expression[newl, cur.op, cur.rexpr].reduce_rec
|
||||
elsif newr = simplifier[cur.rexpr]
|
||||
Expression[cur.lexpr, cur.op, newr].reduce_rec
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
simplifier[r]
|
||||
end
|
||||
|
||||
# a check to see if an Expr is the composition of two rotations (rol eax, 4 ; rol eax, 6 => rol eax, 10)
|
||||
# this is a bit too ugly to stay in the main reduce_rec body.
|
||||
def reduce_rec_composerol
|
||||
m = Expression[[['var', :sh_op, 'amt'], :|, ['var', :inv_sh_op, 'inv_amt']], :&, 'mask']
|
||||
if vars = Expression[l, :&, r].match(m, 'var', :sh_op, 'amt', :inv_sh_op, 'inv_amt', 'mask') and vars[:sh_op] == {:>> => :<<, :<< => :>>}[ vars[:inv_sh_op]] and
|
||||
((vars['amt'].kind_of?(::Integer) and vars['inv_amt'].kind_of?(::Integer) and ampl = vars['amt'] + vars['inv_amt']) or
|
||||
(vars['amt'].kind_of? Expression and vars['amt'].op == :% and vars['amt'].rexpr.kind_of? ::Integer and
|
||||
vars['inv_amt'].kind_of? Expression and vars['inv_amt'].op == :% and vars['amt'].rexpr == vars['inv_amt'].rexpr and ampl = vars['amt'].rexpr)) and
|
||||
vars['mask'].kind_of?(::Integer) and vars['mask'] == (1<<ampl)-1 and vars['var'].kind_of? Expression and # it's a rotation
|
||||
|
||||
ivars = vars['var'].match(m, 'var', :sh_op, 'amt', :inv_sh_op, 'inv_amt', 'mask') and ivars[:sh_op] == {:>> => :<<, :<< => :>>}[ivars[:inv_sh_op]] and
|
||||
((ivars['amt'].kind_of?(::Integer) and ivars['inv_amt'].kind_of?(::Integer) and ampl = ivars['amt'] + ivars['inv_amt']) or
|
||||
(ivars['amt'].kind_of? Expression and ivars['amt'].op == :% and ivars['amt'].rexpr.kind_of? ::Integer and
|
||||
ivars['inv_amt'].kind_of? Expression and ivars['inv_amt'].op == :% and ivars['amt'].rexpr == ivars['inv_amt'].rexpr and ampl = ivars['amt'].rexpr)) and
|
||||
ivars['mask'].kind_of?(::Integer) and ivars['mask'] == (1<<ampl)-1 and ivars['mask'] == vars['mask'] # it's a composed rotation
|
||||
if ivars[:sh_op] != vars[:sh_op]
|
||||
# ensure the rotations are the same orientation
|
||||
ivars[:sh_op], ivars[:inv_sh_op] = ivars[:inv_sh_op], ivars[:sh_op]
|
||||
ivars['amt'], ivars['inv_amt'] = ivars['inv_amt'], ivars['amt']
|
||||
end
|
||||
amt = Expression[[vars['amt'], :+, ivars['amt']], :%, ampl]
|
||||
invamt = Expression[[vars['inv_amt'], :+, ivars['inv_amt']], :%, ampl]
|
||||
Expression[[[ivars['var'], vars[:sh_op], amt], :|, [ivars['var'], vars[:inv_sh_op], invamt]], :&, vars['mask']].reduce_rec
|
||||
end
|
||||
end
|
||||
|
||||
# a pattern-matching method
|
||||
# Expression[42, :+, 28].match(Expression['any', :+, 28], 'any') => {'any' => 42}
|
||||
# Expression[42, :+, 28].match(Expression['any', :+, 'any'], 'any') => false
|
||||
@ -900,11 +916,11 @@ class EncodedData
|
||||
# base defaults to the first export name + its offset
|
||||
def binding(base = nil)
|
||||
if not base
|
||||
key = @export.keys.sort_by { |k| @export[k] }.first
|
||||
key = @export.index(@export.values.min)
|
||||
return {} if not key
|
||||
base = (@export[key] == 0 ? key : Expression[key, :-, @export[key]])
|
||||
end
|
||||
@export.inject({}) { |binding, (n, o)| binding.update n => Expression[base, :+, o] }
|
||||
@export.inject({}) { |binding, (n, o)| binding.update n => Expression.new(:+, o, base) }
|
||||
end
|
||||
|
||||
# returns an array of variables that needs to be defined for a complete #fixup
|
||||
@ -940,36 +956,35 @@ class EncodedData
|
||||
|
||||
# concatenation of another +EncodedData+ (or nil/Fixnum/anything supporting String#<<)
|
||||
def << other
|
||||
|
||||
|
||||
case other
|
||||
when nil
|
||||
when ::Fixnum
|
||||
fill
|
||||
@data = @data.realstring if defined? VirtualString and @data.kind_of? VirtualString
|
||||
@data = @data.to_str if not @data.kind_of? String
|
||||
@data << other
|
||||
@virtsize += 1
|
||||
when EncodedData
|
||||
fill if not other.data.empty?
|
||||
other.reloc.each { |k, v| @reloc[k + @virtsize] = v }
|
||||
cf = (other.export.keys & @export.keys).find_all { |k| other.export[k] != @export[k] - @virtsize }
|
||||
raise "edata merge: label conflict #{cf.inspect}" if not cf.empty?
|
||||
other.export.each { |k, v| @export[k] = v + @virtsize }
|
||||
other.reloc.each { |k, v| @reloc[k + @virtsize] = v } if not other.reloc.empty?
|
||||
if not other.export.empty?
|
||||
other.export.each { |k, v|
|
||||
if @export[k] and @export[k] != v + @virtsize
|
||||
cf = (other.export.keys & @export.keys).find_all { |k_| other.export[k_] != @export[k_] - @virtsize }
|
||||
raise "edata merge: label conflict #{cf.inspect}"
|
||||
end
|
||||
@export[k] = v + @virtsize
|
||||
}
|
||||
other.inv_export.each { |k, v| @inv_export[@virtsize + k] = v }
|
||||
if @data.empty?; @data = other.data.dup
|
||||
elsif defined? VirtualString and @data.kind_of? VirtualString; @data = @data.realstring << other.data
|
||||
else
|
||||
if(other.data.respond_to?('force_encoding'))
|
||||
other.data.force_encoding("binary")
|
||||
end
|
||||
|
||||
@data << other.data
|
||||
if @data.empty?; @data = other.data.dup
|
||||
elsif not @data.kind_of?(String); @data = @data.to_str << other.data
|
||||
else @data << other.data
|
||||
end
|
||||
@virtsize += other.virtsize
|
||||
else
|
||||
fill
|
||||
if @data.empty?; @data = other.dup
|
||||
elsif defined? VirtualString and @data.kind_of? VirtualString; @data = @data.realstring << other
|
||||
elsif not @data.kind_of?(String); @data = @data.to_str << other
|
||||
else @data << other
|
||||
end
|
||||
@virtsize += other.length
|
||||
@ -1095,5 +1110,31 @@ class EncodedData
|
||||
raise EncodeError, 'cannot patch data: new content too long' if to - from < content.length
|
||||
self[from, content.length] = content
|
||||
end
|
||||
|
||||
# returns a list of offsets where /pat/ can be found inside @data
|
||||
# scan is done per chunk of chunksz bytes, with a margin for chunk-overlapping patterns
|
||||
# yields each offset found, and only include it in the result if the block returns !false
|
||||
def pattern_scan(pat, chunksz=nil, margin=nil)
|
||||
chunksz ||= 4*1024*1024 # scan 4MB at a time
|
||||
margin ||= 65536 # add this much bytes at each chunk to find /pat/ over chunk boundaries
|
||||
pat = Regexp.new(Regexp.escape(pat)) if pat.kind_of? ::String
|
||||
|
||||
found = []
|
||||
chunkoff = 0
|
||||
while chunkoff < @data.length
|
||||
chunk = @data[chunkoff, chunksz+margin].to_str
|
||||
off = 0
|
||||
while match_off = (chunk[off..-1] =~ pat)
|
||||
break if off+match_off >= chunksz # match fully in margin
|
||||
match_addr = chunkoff + off + match_off
|
||||
found << match_addr if not block_given? or yield(match_addr)
|
||||
off += match_off + 1
|
||||
# XXX +1 or +lastmatch.length ?
|
||||
# 'aaaabc'.pattern_scan(/a*bc/) will match 5 times here
|
||||
end
|
||||
chunkoff += chunksz
|
||||
end
|
||||
found
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -227,10 +227,11 @@ class VirtualString
|
||||
# returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes
|
||||
def read_range(from, len)
|
||||
from += @addr_start
|
||||
base, page = cache_get_page(from)
|
||||
if not len
|
||||
base, page = cache_get_page(from)
|
||||
page[from - base]
|
||||
elsif len <= @pagelength
|
||||
base, page = cache_get_page(from)
|
||||
s = page[from - base, len]
|
||||
if from+len-base > @pagelength # request crosses a page boundary
|
||||
base, page = cache_get_page(from+len)
|
||||
@ -846,5 +847,18 @@ class Debugger
|
||||
|
||||
instance_eval File.read(plugin_filename)
|
||||
end
|
||||
|
||||
# see EData#pattern_scan
|
||||
# scans only mapped areas of @memory, using os_process.mappings
|
||||
def pattern_scan(pat)
|
||||
ret = []
|
||||
os_process.mappings.each { |a, l, *o|
|
||||
EncodedData.new(@memory[a, l]).pattern_scan(pat) { |o|
|
||||
o += a
|
||||
ret << o if not block_given? or yield(o)
|
||||
}
|
||||
}
|
||||
ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ typedef unsigned int UINT;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG, DWORD, *LPDWORD;
|
||||
typedef int BOOL;
|
||||
typedef unsigned long long DWORD64;
|
||||
typedef unsigned long long DWORD64, ULONGLONG;
|
||||
|
||||
typedef intptr_t INT_PTR, LONG_PTR;
|
||||
typedef uintptr_t UINT_PTR, ULONG_PTR, DWORD_PTR, SIZE_T;
|
||||
@ -665,6 +665,38 @@ Thread32Next(
|
||||
LPTHREADENTRY32 lpte
|
||||
);
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION32 {
|
||||
DWORD BaseAddress;
|
||||
DWORD AllocationBase;
|
||||
DWORD AllocationProtect; // initial (alloc time) prot
|
||||
DWORD RegionSize;
|
||||
DWORD State; // MEM_FREE/COMMIT/RESERVE
|
||||
DWORD Protect; // PAGE_EXECUTE_READWRITE etc
|
||||
DWORD Type; // MEM_IMAGE/MAPPED/PRIVATE
|
||||
} MEMORY_BASIC_INFORMATION32, *PMEMORY_BASIC_INFORMATION32;
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION64 {
|
||||
ULONGLONG BaseAddress;
|
||||
ULONGLONG AllocationBase;
|
||||
DWORD AllocationProtect;
|
||||
DWORD __alignment1;
|
||||
ULONGLONG RegionSize;
|
||||
DWORD State;
|
||||
DWORD Protect;
|
||||
DWORD Type;
|
||||
DWORD __alignment2;
|
||||
} MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;
|
||||
|
||||
SIZE_T
|
||||
WINAPI
|
||||
VirtualQueryEx(
|
||||
HANDLE hProcess,
|
||||
LPVOID lpAddress,
|
||||
PMEMORY_BASIC_INFORMATION32 lpBuffer,
|
||||
SIZE_T dwLength // sizeof lpBuffer
|
||||
);
|
||||
|
||||
|
||||
EOS
|
||||
|
||||
new_api_c <<EOS, 'advapi32'
|
||||
@ -860,6 +892,35 @@ class WinOS < OS
|
||||
WinAPI.closehandle(h)
|
||||
list
|
||||
end
|
||||
|
||||
# return a list of [addr_start, length, perms]
|
||||
def mappings
|
||||
addr = 0
|
||||
list = []
|
||||
info = WinAPI.alloc_c_struct("MEMORY_BASIC_INFORMATION#{addrsz}")
|
||||
|
||||
while WinAPI.virtualqueryex(handle, addr, info, info.length)
|
||||
addr += info[:regionsize]
|
||||
next unless info[:state] & WinAPI::MEM_COMMIT > 0
|
||||
|
||||
prot = {
|
||||
WinAPI::PAGE_NOACCESS => '---',
|
||||
WinAPI::PAGE_READONLY => 'r--',
|
||||
WinAPI::PAGE_READWRITE => 'rw-',
|
||||
WinAPI::PAGE_WRITECOPY => 'rw-',
|
||||
WinAPI::PAGE_EXECUTE => '--x',
|
||||
WinAPI::PAGE_EXECUTE_READ => 'r-x',
|
||||
WinAPI::PAGE_EXECUTE_READWRITE => 'rwx',
|
||||
WinAPI::PAGE_EXECUTE_WRITECOPY => 'rwx'
|
||||
}[info[:protect] & 0xff]
|
||||
prot << 'g' if info[:protect] & WinAPI::PAGE_GUARD > 0
|
||||
prot << 'p' if info[:type] & WinAPI::MEM_PRIVATE > 0
|
||||
|
||||
list << [info[:baseaddress], info[:regionsize], prot]
|
||||
end
|
||||
|
||||
list
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
@ -1005,12 +1066,6 @@ class WindowsRemoteString < VirtualString
|
||||
return if WinAPI.readprocessmemory(@handle, addr, page, len, 0) == 0
|
||||
page
|
||||
end
|
||||
|
||||
def realstring
|
||||
s = [0].pack('C') * @length
|
||||
WinAPI.readprocessmemory(@handle, @addr_start, s, @length, 0)
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
class WinDbgAPI
|
||||
|
@ -40,7 +40,7 @@ class CPU
|
||||
raise tok, 'invalid opcode' if not opcode_list_byname[tok.raw]
|
||||
|
||||
i.opname = tok.raw
|
||||
i.backtrace = tok.backtrace.dup
|
||||
i.backtrace = tok.backtrace
|
||||
lexer.skip_space
|
||||
|
||||
# find arguments list
|
||||
@ -317,7 +317,7 @@ class ExeFormat
|
||||
raise tok, "label redefinition" if new_label(lname) != lname
|
||||
end
|
||||
l = Label.new(lname)
|
||||
l.backtrace = tok.backtrace.dup
|
||||
l.backtrace = tok.backtrace
|
||||
@cursource << l
|
||||
lasteol = false
|
||||
else
|
||||
@ -331,7 +331,7 @@ class ExeFormat
|
||||
end
|
||||
if lname = @locallabels_fwd.delete('endinstr')
|
||||
l = Label.new(lname)
|
||||
l.backtrace = tok.backtrace.dup
|
||||
l.backtrace = tok.backtrace
|
||||
@cursource << l
|
||||
end
|
||||
end
|
||||
@ -377,7 +377,7 @@ class ExeFormat
|
||||
@lexer.unreadtok ntok
|
||||
end
|
||||
raise tok, 'syntax error' if ntok = @lexer.nexttok and ntok.type != :eol
|
||||
@cursource << Align.new(e, fillwith, tok.backtrace.dup)
|
||||
@cursource << Align.new(e, fillwith, tok.backtrace)
|
||||
|
||||
when '.pad'
|
||||
@lexer.skip_space
|
||||
@ -394,12 +394,12 @@ class ExeFormat
|
||||
@lexer.unreadtok ntok
|
||||
end
|
||||
raise tok, 'syntax error' if ntok = @lexer.nexttok and ntok.type != :eol
|
||||
@cursource << Padding.new(fillwith, tok.backtrace.dup)
|
||||
@cursource << Padding.new(fillwith, tok.backtrace)
|
||||
|
||||
when '.offset'
|
||||
e = Expression.parse(@lexer)
|
||||
raise tok, 'syntax error' if ntok = @lexer.nexttok and ntok.type != :eol
|
||||
@cursource << Offset.new(e, tok.backtrace.dup)
|
||||
@cursource << Offset.new(e, tok.backtrace)
|
||||
|
||||
when '.padto'
|
||||
e = Expression.parse(@lexer)
|
||||
@ -418,7 +418,7 @@ class ExeFormat
|
||||
@lexer.unreadtok ntok
|
||||
end
|
||||
raise tok, 'syntax error' if ntok = @lexer.nexttok and ntok.type != :eol
|
||||
@cursource << Padding.new(fillwith, tok.backtrace.dup) << Offset.new(e, tok.backtrace.dup)
|
||||
@cursource << Padding.new(fillwith, tok.backtrace) << Offset.new(e, tok.backtrace)
|
||||
|
||||
else
|
||||
@cpu.parse_parser_instruction(self, tok)
|
||||
@ -441,15 +441,15 @@ class ExeFormat
|
||||
break
|
||||
end
|
||||
end
|
||||
Data.new(type, arr, 1, tok.backtrace.dup)
|
||||
Data.new(type, arr, 1, tok.backtrace)
|
||||
end
|
||||
|
||||
def parse_data_data(type)
|
||||
raise ParseError, 'need data content' if not tok = @lexer.readtok
|
||||
if tok.type == :punct and tok.raw == '?'
|
||||
Data.new type, :uninitialized, 1, tok.backtrace.dup
|
||||
Data.new type, :uninitialized, 1, tok.backtrace
|
||||
elsif tok.type == :quoted
|
||||
Data.new type, tok.value, 1, tok.backtrace.dup
|
||||
Data.new type, tok.value, 1, tok.backtrace
|
||||
else
|
||||
@lexer.unreadtok tok
|
||||
raise tok, 'invalid data' if not i = Expression.parse(@lexer)
|
||||
@ -470,10 +470,10 @@ class ExeFormat
|
||||
end
|
||||
end
|
||||
raise ntok, 'syntax error, ) expected' if not ntok = @lexer.readtok or ntok.type != :punct or ntok.raw != ')'
|
||||
Data.new type, content, count, tok.backtrace.dup
|
||||
Data.new type, content, count, tok.backtrace
|
||||
else
|
||||
@lexer.unreadtok ntok
|
||||
Data.new type, i, 1, tok.backtrace.dup
|
||||
Data.new type, i, 1, tok.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -664,7 +664,7 @@ class Expression
|
||||
l = lexer.program.cursource.last
|
||||
if not l.kind_of? Label
|
||||
l = Label.new(lexer.program.new_label('instr_start'))
|
||||
l.backtrace = tok.backtrace.dup
|
||||
l.backtrace = tok.backtrace
|
||||
lexer.program.cursource << l
|
||||
end
|
||||
tok.value = l.name
|
||||
@ -672,7 +672,7 @@ class Expression
|
||||
l = lexer.program.cursource.first
|
||||
if not l.kind_of? Label
|
||||
l = Label.new(lexer.program.new_label('section_start'))
|
||||
l.backtrace = tok.backtrace.dup
|
||||
l.backtrace = tok.backtrace
|
||||
lexer.program.cursource.unshift l
|
||||
end
|
||||
tok.value = l.name
|
||||
|
@ -276,6 +276,7 @@ module C
|
||||
case tok.raw
|
||||
when ';'; break
|
||||
when ','
|
||||
when '}'; parser.unreadtok(tok); break
|
||||
else raise tok, '"," or ";" expected'
|
||||
end
|
||||
end
|
||||
@ -647,7 +648,7 @@ module C
|
||||
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
||||
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
||||
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
||||
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
parser.checkstatementend(tok)
|
||||
|
||||
new expr, body
|
||||
end
|
||||
@ -821,7 +822,7 @@ module C
|
||||
end
|
||||
raise tok || parser, '")" expected' if not tok or tok.type != :punct or tok.raw != ')'
|
||||
ret.parse_attributes(parser)
|
||||
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
parser.checkstatementend(tok)
|
||||
ret
|
||||
end
|
||||
end
|
||||
@ -1076,8 +1077,13 @@ module C
|
||||
@lexer.define_weak('__STDC__')
|
||||
@lexer.define_weak('__const', 'const')
|
||||
@lexer.define_weak('__signed', 'signed')
|
||||
@lexer.define_weak('__signed__', 'signed')
|
||||
@lexer.define_weak('__volatile', 'volatile')
|
||||
@lexer.nodefine_strong('__REDIRECT_NTH') # booh gnu
|
||||
if not @lexer.definition['__builtin_constant_p']
|
||||
# magic macro to check if its arg is an immediate value
|
||||
@lexer.define_weak('__builtin_constant_p', '0')
|
||||
@lexer.definition['__builtin_constant_p'].args = [Preprocessor::Token.new([])]
|
||||
end
|
||||
@lexer.nodefine_strong('alloca') # TODO __builtin_alloca
|
||||
@lexer.hooked_include['stddef.h'] = <<EOH
|
||||
/* simplified, define all at first invocation. may break things... */
|
||||
@ -1297,6 +1303,13 @@ EOH
|
||||
t
|
||||
end
|
||||
|
||||
# checks that we are at the end of a statement, ie an ';' character (consumed), or a '}' (not consumed)
|
||||
# otherwise, raise either the given token or self.
|
||||
def checkstatementend(tok=nil)
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != '}')
|
||||
unreadtok tok if tok.raw == '}'
|
||||
end
|
||||
|
||||
# returns the size of a type in bytes
|
||||
def sizeof(var, type=nil)
|
||||
var, type = nil, var if var.kind_of? Type and not type
|
||||
@ -1457,6 +1470,7 @@ EOH
|
||||
case tok.raw
|
||||
when ','; nofunc = true
|
||||
when ';'; break
|
||||
when '}'; unreadtok(tok); break
|
||||
else raise tok, '";" or "," expected'
|
||||
end
|
||||
end
|
||||
@ -1497,7 +1511,7 @@ EOH
|
||||
elsif tok.type != :string
|
||||
unreadtok tok
|
||||
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
|
||||
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
||||
puts tok.exception("statement with no effect : #{expr}").message
|
||||
@ -1519,7 +1533,7 @@ EOH
|
||||
when 'goto'
|
||||
raise tok || self, 'label expected' if not tok = skipspaces or tok.type != :string
|
||||
name = tok.raw
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
Goto.new name
|
||||
when 'return'
|
||||
expr = CExpression.parse(self, scope) # nil allowed
|
||||
@ -1528,7 +1542,7 @@ EOH
|
||||
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
||||
check_compatible_type(tok, (expr ? expr.type : BaseType.new(:void)), nest[0])
|
||||
end
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
Return.new expr
|
||||
when 'case'
|
||||
raise tok, 'case out of switch' if not nest.include? :switch
|
||||
@ -1538,11 +1552,11 @@ EOH
|
||||
raise tok, 'case out of switch' if not nest.include? :switch
|
||||
Case.new 'default', nil, parse_statement(scope, nest)
|
||||
when 'continue'
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
raise tok, 'continue out of loop' if not nest.include? :loop
|
||||
Continue.new
|
||||
when 'break'
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
raise tok, 'break out of loop' if not nest.include? :loop and not nest.include? :switch
|
||||
Break.new
|
||||
when 'asm', '__asm', '__asm__'
|
||||
@ -1559,7 +1573,7 @@ EOH
|
||||
unreadtok ntok
|
||||
unreadtok tok
|
||||
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
||||
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ';'
|
||||
checkstatementend(tok)
|
||||
|
||||
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
||||
puts tok.exception("statement with no effect : #{expr}").message
|
||||
@ -2095,7 +2109,7 @@ EOH
|
||||
|
||||
# overflow
|
||||
case t.name
|
||||
when :char, :short, :int, :long, :longlong, :__int8, :__int16, :__int32, :__int64
|
||||
when :char, :short, :int, :long, :ptr, :longlong, :__int8, :__int16, :__int32, :__int64
|
||||
max = 1 << (8*parser.typesize[t.name])
|
||||
ret = ret.to_i & (max-1)
|
||||
if t.specifier == :signed and (ret & (max >> 1)) > 0 # char == unsigned char
|
||||
@ -2707,11 +2721,11 @@ EOH
|
||||
r.join("\n")
|
||||
end
|
||||
|
||||
# returns a string containing the C definition of the toplevel function funcname, with its dependencies
|
||||
def dump_definition(funcname)
|
||||
# returns a string containing the C definition(s) of toplevel functions, with their dependencies
|
||||
def dump_definition(*funcnames)
|
||||
oldst = @toplevel.statements
|
||||
@toplevel.statements = []
|
||||
dump_definitions([@toplevel.symbol[funcname]])
|
||||
dump_definitions(funcnames.map { |f| @toplevel.symbol[f] })
|
||||
ensure
|
||||
@toplevel.statements = oldst
|
||||
end
|
||||
@ -3373,7 +3387,7 @@ EOH
|
||||
r, dep = @rexpr.dump(scope, r, dep)
|
||||
when Block
|
||||
r.last << '('
|
||||
r, dep = Statement.dump(scope, r, dep)
|
||||
r, dep = Statement.dump(@rexpr, scope, r, dep)
|
||||
r.last << ' )'
|
||||
when Label
|
||||
r.last << '&&' << @rexpr.name
|
||||
@ -3423,7 +3437,7 @@ EOH
|
||||
else
|
||||
r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op))
|
||||
r.last << ' ' << @op.to_s << ' '
|
||||
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op))
|
||||
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall))
|
||||
end
|
||||
end
|
||||
r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
|
||||
|
@ -551,6 +551,7 @@ class Preprocessor
|
||||
def ungetchar
|
||||
@pos = @ungetcharpos
|
||||
@lineno = @ungetcharlineno
|
||||
nil
|
||||
end
|
||||
|
||||
# returns true if no more data is available
|
||||
@ -562,6 +563,7 @@ class Preprocessor
|
||||
# lifo
|
||||
def unreadtok(tok)
|
||||
@queue << tok if tok
|
||||
nil
|
||||
end
|
||||
|
||||
# calls readtok_nopp and handles preprocessor directives
|
||||
@ -622,17 +624,88 @@ class Preprocessor
|
||||
def readtok_nopp
|
||||
return @queue.pop unless @queue.empty?
|
||||
|
||||
tok = Token.new((@backtrace.map { |bt| bt[0, 2] } + [@filename, @lineno]).flatten)
|
||||
nbt = []
|
||||
@backtrace.each { |bt| nbt << bt[0] << bt[1] }
|
||||
tok = Token.new(nbt << @filename << @lineno)
|
||||
|
||||
case c = getchar
|
||||
when nil
|
||||
return nil
|
||||
when ?', ?"
|
||||
# read quoted string value
|
||||
tok.type = :quoted
|
||||
delimiter = c
|
||||
readtok_nopp_str(tok, c)
|
||||
when ?a..?z, ?A..?Z, ?0..?9, ?$, ?_
|
||||
tok.type = :string
|
||||
raw = tok.raw << c
|
||||
loop do
|
||||
case c = getchar
|
||||
when nil; ungetchar; break # avoids 'no method "coerce" for nil' warning
|
||||
when ?a..?z, ?A..?Z, ?0..?9, ?$, ?_
|
||||
raw << c
|
||||
else ungetchar; break
|
||||
end
|
||||
end
|
||||
|
||||
when ?\ , ?\t, ?\r, ?\n, ?\f
|
||||
tok.type = ((c == ?\ || c == ?\t) ? :space : :eol)
|
||||
raw = tok.raw << c
|
||||
loop do
|
||||
case c = getchar
|
||||
when nil; break
|
||||
when ?\ , ?\t
|
||||
when ?\n, ?\f, ?\r; tok.type = :eol
|
||||
else break
|
||||
end
|
||||
raw << c
|
||||
end
|
||||
ungetchar
|
||||
|
||||
when ?/
|
||||
raw = tok.raw << c
|
||||
# comment
|
||||
case c = getchar
|
||||
when ?/
|
||||
# till eol
|
||||
tok.type = :eol
|
||||
raw << c
|
||||
while c = getchar
|
||||
raw << c
|
||||
break if c == ?\n
|
||||
end
|
||||
when ?*
|
||||
tok.type = :space
|
||||
raw << c
|
||||
seenstar = false
|
||||
loop do
|
||||
raise tok, 'unterminated c++ comment' if not c = getchar
|
||||
raw << c
|
||||
case c
|
||||
when ?*; seenstar = true
|
||||
when ?/; break if seenstar # no need to reset seenstar, already false
|
||||
else seenstar = false
|
||||
end
|
||||
end
|
||||
else
|
||||
# just a slash
|
||||
ungetchar
|
||||
tok.type = :punct
|
||||
end
|
||||
|
||||
else
|
||||
tok.type = :punct
|
||||
tok.raw << c
|
||||
end
|
||||
|
||||
tok
|
||||
end
|
||||
|
||||
# we just read a ' or a ", read until the end of the string
|
||||
# tok.value will contain the raw string (with escapes interpreted etc)
|
||||
def readtok_nopp_str(tok, delimiter)
|
||||
tok.type = :quoted
|
||||
tok.raw << delimiter
|
||||
tok.value = ''
|
||||
c = nil
|
||||
loop do
|
||||
raise tok, 'unterminated string' if not c = getchar
|
||||
tok.raw << c
|
||||
@ -685,72 +758,10 @@ class Preprocessor
|
||||
end
|
||||
end
|
||||
|
||||
when ?a..?z, ?A..?Z, ?0..?9, ?$, ?_
|
||||
tok.type = :string
|
||||
tok.raw << c
|
||||
loop do
|
||||
case c = getchar
|
||||
when nil; ungetchar; break # avoids 'no method "coerce" for nil' warning
|
||||
when ?a..?z, ?A..?Z, ?0..?9, ?$, ?_
|
||||
tok.raw << c
|
||||
else ungetchar; break
|
||||
end
|
||||
end
|
||||
|
||||
when ?\ , ?\t, ?\r, ?\n, ?\f
|
||||
tok.type = :space
|
||||
tok.raw << c
|
||||
loop do
|
||||
case c = getchar
|
||||
when nil; break
|
||||
when ?\ , ?\t
|
||||
when ?\n, ?\f, ?\r; tok.type = :eol
|
||||
else break
|
||||
end
|
||||
tok.raw << c
|
||||
end
|
||||
ungetchar
|
||||
tok.type = :eol if tok.raw.index(?\n) or tok.raw.index(?\f)
|
||||
|
||||
when ?/
|
||||
tok.raw << c
|
||||
# comment
|
||||
case c = getchar
|
||||
when ?/
|
||||
# till eol
|
||||
tok.type = :eol
|
||||
tok.raw << c
|
||||
while c = getchar
|
||||
tok.raw << c
|
||||
break if c == ?\n
|
||||
end
|
||||
when ?*
|
||||
tok.type = :space
|
||||
tok.raw << c
|
||||
seenstar = false
|
||||
loop do
|
||||
raise tok, 'unterminated c++ comment' if not c = getchar
|
||||
tok.raw << c
|
||||
case c
|
||||
when ?*; seenstar = true
|
||||
when ?/; break if seenstar # no need to reset seenstar, already false
|
||||
else seenstar = false
|
||||
end
|
||||
end
|
||||
else
|
||||
# just a slash
|
||||
ungetchar
|
||||
tok.type = :punct
|
||||
end
|
||||
|
||||
else
|
||||
tok.type = :punct
|
||||
tok.raw << c
|
||||
end
|
||||
|
||||
tok
|
||||
end
|
||||
|
||||
|
||||
# defines a simple preprocessor macro (expands to 0 or 1 token)
|
||||
# does not check overwriting
|
||||
def define(name, value=nil, from=caller.first)
|
||||
|
@ -65,9 +65,24 @@ end
|
||||
class Expression
|
||||
include Renderable
|
||||
attr_accessor :render_info
|
||||
def render
|
||||
l, r = [@lexpr, @rexpr].map { |e|
|
||||
if e.kind_of? Integer
|
||||
|
||||
# this is an accessor to @@render_int, the lambda used to render integers > 10
|
||||
# usage: Expression.render_int = lambda { |e| '0x%x' % e }
|
||||
# or Expression.render_int { |e| '0x%x' % e }
|
||||
# XXX the returned string should be suitable for inclusion in a label name etc
|
||||
def self.render_int(&b)
|
||||
if b
|
||||
@@render_int = b
|
||||
else
|
||||
@@render_int
|
||||
end
|
||||
end
|
||||
def self.render_int=(p)
|
||||
@@render_int = p
|
||||
end
|
||||
@@render_int = nil
|
||||
|
||||
def render_integer(e)
|
||||
if render_info and @render_info[:char]
|
||||
ee = e
|
||||
v = []
|
||||
@ -78,7 +93,7 @@ class Expression
|
||||
v.reverse! if @render_info[:char] == :big
|
||||
if not v.empty? and v.all? { |c| c < 0x7f }
|
||||
# XXX endianness
|
||||
next "'" + v.pack('C*').inspect.gsub("'") { '\\\'' }[1...-1] + "'"
|
||||
return "'" + v.pack('C*').inspect.gsub("'") { '\\\'' }[1...-1] + "'"
|
||||
end
|
||||
end
|
||||
if e < 0
|
||||
@ -86,18 +101,23 @@ class Expression
|
||||
e = -e
|
||||
end
|
||||
if e < 10; e = e.to_s
|
||||
elsif @@render_int
|
||||
e = @@render_int[e]
|
||||
else
|
||||
e = '%xh' % e
|
||||
e = '0' << e unless (?0..?9).include? e[0]
|
||||
end
|
||||
e = '-' << e if neg
|
||||
end
|
||||
e
|
||||
}
|
||||
nosq = {:* => [:*], :+ => [:+, :-, :*], :- => [:+, :-, :*]}
|
||||
l = ['(', l, ')'] if @lexpr.kind_of? Expression and not nosq[@op].to_a.include?(@lexpr.op)
|
||||
nosq[:-] = [:*]
|
||||
r = ['(', r, ')'] if @rexpr.kind_of? Expression and not nosq[@op].to_a.include?(@rexpr.op)
|
||||
end
|
||||
|
||||
NOSQ1 = NOSQ2 = {:* => [:*], :+ => [:+, :-, :*], :- => [:+, :-, :*]}
|
||||
NOSQ2[:-] = [:*]
|
||||
def render
|
||||
l = @lexpr.kind_of?(Integer) ? render_integer(@lexpr) : @lexpr
|
||||
r = @rexpr.kind_of?(Integer) ? render_integer(@rexpr) : @rexpr
|
||||
l = ['(', l, ')'] if @lexpr.kind_of? Expression and (not oa = NOSQ1[@op] or not oa.include?(@lexpr.op))
|
||||
r = ['(', r, ')'] if @rexpr.kind_of? Expression and (not oa = NOSQ2[@op] or not oa.include?(@rexpr.op))
|
||||
op = @op if l or @op != :+
|
||||
if op == :+
|
||||
r0 = [r].flatten.first
|
||||
|
@ -42,6 +42,7 @@ class X86_64
|
||||
@opcode_list.delete_if { |o|
|
||||
o.args.include? :modrmmmx or # mmx is dead!
|
||||
o.args.include? :regmmx or # movd
|
||||
o.args.include? :regfp or # no fpu beyond this line
|
||||
o.name == 'loadall' or
|
||||
o.name == 'arpl'
|
||||
}
|
||||
|
@ -4,34 +4,58 @@
|
||||
# Licence is LGPL, see LICENCE in the top-level directory
|
||||
|
||||
# A script to help finding performance bottlenecks:
|
||||
# ruby-prof myscript.rb
|
||||
#
|
||||
# $ ruby-prof myscript.rb
|
||||
# => String#+ gets called 50k times and takes 30s
|
||||
# LOGCALLER='String#+' ruby -r log_caller myscript.rb
|
||||
#
|
||||
# $ LOGCALLER='String#+' ruby -r bottleneck myscript.rb
|
||||
# => String#+ called 40k times from:
|
||||
# stuff.rb:42 in Myclass#uglymethod from
|
||||
# stuff.rb:32 in Myclass#initialize
|
||||
#
|
||||
# now you know what to rewrite
|
||||
|
||||
|
||||
def log_caller(cls, meth, histlen=-1)
|
||||
malias = meth.to_s.gsub(/[^a-z0-9_]/i, '') + '_log_caller'
|
||||
mcntr = '$' + meth.to_s.gsub(/[^a-z0-9_]/i, '') + '_counter'
|
||||
|
||||
def log_caller(cls, meth, singleton=false, histlen=nil)
|
||||
histlen ||= ENV.fetch('LOGCALLER_MAXHIST', 16).to_i
|
||||
dec_meth = 'm_' + meth.to_s.gsub(/[^\w]/) { |c| c.unpack('H*')[0] }
|
||||
malias = dec_meth + '_log_caller'
|
||||
mcntr = '$' + dec_meth + '_counter'
|
||||
eval <<EOS
|
||||
class #{cls}
|
||||
|
||||
#{cls.kind_of?(Class) ? 'class' : 'module'} #{cls}
|
||||
#{'class << self' if singleton}
|
||||
alias #{malias} #{meth}
|
||||
|
||||
def #{meth}(*a, &b)
|
||||
#{mcntr}[caller[0..#{histlen}]] += 1
|
||||
#{mcntr}[caller[0, #{histlen}]] += 1
|
||||
#{malias}(*a, &b)
|
||||
end
|
||||
|
||||
#{'end' if singleton}
|
||||
end
|
||||
|
||||
#{mcntr} = Hash.new(0)
|
||||
at_exit { puts " callers of #{cls} #{meth}:", #{mcntr}.sort_by { |k, v| -v }[0, 4].map { |k, v| ["\#{v} times from", k, ''] } }
|
||||
|
||||
at_exit {
|
||||
total = #{mcntr}.inject(0) { |a, (k, v)| a+v }
|
||||
puts "\#{total} callers of #{cls} #{meth}:"
|
||||
#{mcntr}.sort_by { |k, v|
|
||||
-v
|
||||
}[0, 4].each { |k, v|
|
||||
puts " \#{'%.2f%%' % (100.0*v/total)} - \#{v} times from", k, ''
|
||||
}
|
||||
}
|
||||
|
||||
EOS
|
||||
|
||||
end
|
||||
|
||||
if ENV['LOGCALLER'] =~ /^(.*)#(.*)$/
|
||||
cls, meth = $1, $2.to_sym
|
||||
cls = cls.split('::').inject(Object) { |o, cst| o.const_get(cst) }
|
||||
log_caller(cls, meth)
|
||||
end
|
||||
ENV['LOGCALLER'].to_s.split(';').map { |lc|
|
||||
next if not lc =~ /^(.*)([.#])(.*)$/
|
||||
cls, sg, meth = $1, $2, $3.to_sym
|
||||
sg = { '.' => true, '#' => false }[sg]
|
||||
cls = cls.split('::').inject(::Object) { |o, cst| o.const_get(cst) }
|
||||
log_caller(cls, meth, sg)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -258,7 +258,7 @@ class LinDebug
|
||||
text << ' '
|
||||
x += r.length + 11
|
||||
}
|
||||
text << (' '*(@console_width-x)) << "\n" << ' '
|
||||
text << (' '*([@console_width-x, 0].max)) << "\n" << ' '
|
||||
x = 1
|
||||
%w[esi edi ebp esp].each { |r|
|
||||
text << Color[:changed] if @rs.regs_cache[r] != @rs.oldregs[r]
|
||||
@ -281,7 +281,7 @@ class LinDebug
|
||||
text << ' '
|
||||
x += 2
|
||||
}
|
||||
text << (' '*(@console_width-x)) << "\n"
|
||||
text << (' '*([@console_width-x, 0].max)) << "\n"
|
||||
end
|
||||
|
||||
def updatecode
|
||||
@ -342,7 +342,7 @@ class LinDebug
|
||||
if di
|
||||
text <<
|
||||
if addr == @rs.regs_cache['eip']
|
||||
"*#{di.instruction}".ljust(@console_width-37)
|
||||
"*#{di.instruction}".ljust([@console_width-37, 0].max)
|
||||
else
|
||||
" #{di.instruction}" << Ansi::ClearLineAfter
|
||||
end
|
||||
@ -368,7 +368,7 @@ class LinDebug
|
||||
text << Color[:border]
|
||||
title = @rs.findsymbol(addr)
|
||||
pre = [@console_width-100, 6].max
|
||||
post = @console_width - (pre + title.length + 2)
|
||||
post = [@console_width - (pre + title.length + 2), 0].max
|
||||
text << Ansi.hline(pre) << ' ' << title << ' ' << Ansi.hline(post) << Color[:normal] << "\n"
|
||||
|
||||
cnt = @win_data_height
|
||||
|
@ -14,5 +14,16 @@ class TestDynldr < Test::Unit::TestCase
|
||||
d.new_api_c('int memcpy(char*, char*, int)')
|
||||
d.memcpy(str, "9999", 2)
|
||||
assert_equal('9934', str)
|
||||
|
||||
c_src = <<EOS
|
||||
int sprintf(char*, char*, ...);
|
||||
void fufu(int i, char* ptr)
|
||||
{
|
||||
sprintf(ptr, "lolzor %i\\n", i);
|
||||
}
|
||||
EOS
|
||||
buf = 'aaaaaaaaaaaaaaaaaa'
|
||||
d.new_func_c(c_src) { d.fufu(42, buf) }
|
||||
assert_equal("lolzor 42\n\000aaaaaaa", buf)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user