metasm/cpu/ia32/decompile.rb in metasm-1.0.2 vs metasm/cpu/ia32/decompile.rb in metasm-1.0.3
- old
+ new
@@ -9,33 +9,42 @@
module Metasm
class Ia32
# temporarily setup dasm.address_binding so that backtracking
# stack-related offsets resolve in :frameptr (relative to func start)
def decompile_makestackvars(dasm, funcstart, blocks)
+ esp = register_symbols[4]
oldfuncbd = dasm.address_binding[funcstart]
- dasm.address_binding[funcstart] = { :esp => :frameptr } # this would suffice, the rest here is just optimisation
-
+ dasm.address_binding[funcstart] = { esp => :frameptr }
patched_binding = [funcstart] # list of addresses to cleanup later
+
+ if blocks.length <= 12
+ blocks.each { |block| yield block }
+ return
+ end
+
+ # for large function, pre-trace and cache esp/ebp for every block start to improve decompilation time
+
+ ebp = register_symbols[5]
ebp_frame = true
# pretrace esp and ebp for each function block (cleared later)
# TODO with more than 1 unknown __stdcall ext func per path, esp -> unknown, which makes very ugly C (*esp-- = 12...); add heuristics ?
blocks.each { |block|
blockstart = block.address
if not dasm.address_binding[blockstart]
patched_binding << blockstart
dasm.address_binding[blockstart] = {}
- foo = dasm.backtrace(:esp, blockstart, :snapshot_addr => funcstart)
+ foo = dasm.backtrace(esp, blockstart, :snapshot_addr => funcstart)
if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
(ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
- dasm.address_binding[blockstart][:esp] = ee
+ dasm.address_binding[blockstart][esp] = ee
end
if ebp_frame
- foo = dasm.backtrace(:ebp, blockstart, :snapshot_addr => funcstart)
+ foo = dasm.backtrace(ebp, blockstart, :snapshot_addr => funcstart)
if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
(ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
- dasm.address_binding[blockstart][:ebp] = ee
+ dasm.address_binding[blockstart][ebp] = ee
else
ebp_frame = false # func does not use ebp as frame ptr, no need to bt for later blocks
end
end
end
@@ -46,10 +55,15 @@
ensure
patched_binding.each { |a| dasm.address_binding.delete a }
dasm.address_binding[funcstart] = oldfuncbd if oldfuncbd
end
+ # add di-specific registry written/accessed
+ def decompile_func_finddeps_di(dcmp, func, di, a, w)
+ a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
+ end
+
# list variable dependency for each block, remove useless writes
# returns { blockaddr => [list of vars that are needed by a following block] }
def decompile_func_finddeps(dcmp, blocks, func)
deps_r = {} ; deps_w = {} ; deps_to = {}
deps_subfunc = {} # things read/written by subfuncs
@@ -67,11 +81,11 @@
case k
when ::Symbol; w |= [k]
else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
end
}
- a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
+ decompile_func_finddeps_di(dcmp, func, di, a, w)
deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
}
stackoff = nil
@@ -118,10 +132,10 @@
case k
when ::Symbol; w |= [k]
else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
end
}
- a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
+ decompile_func_finddeps_di(dcmp, func, di, a, w)
next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
false
}