lib/oracle.rb
require_relative 'dex-oracle/logging'
require_relative 'dex-oracle/plugin'
require_relative 'dex-oracle/smali_file'
class Oracle
include Logging
def initialize(smali_dir, driver, include_types, exclude_types, disable_plugins)
@smali_files = Oracle.parse_smali(smali_dir)
@methods = Oracle.filter_methods(@smali_files, include_types, exclude_types)
Plugin.init_plugins(driver, @smali_files, @methods)
@disable_plugins = disable_plugins
logger.info("Disabled plugins: #{@disable_plugins * ','}") unless @disable_plugins.empty?
end
def divine
puts "Optimizing #{@methods.size} methods over #{@smali_files.size} Smali files."
made_changes = process_plugins
@smali_files.each(&:update) if made_changes
optimizations = {}
optimizations = Plugin.plugins.collect { |plugin| plugin.optimizations }
optimizations = optimizations.inject(Hash.new(0)) { |memo, subhash| subhash.each { |prod, value| memo[prod] += value } ; memo }
opt_str = optimizations.collect { |k, v| "#{k}=#{v}" } * ', '
puts "Optimizations: #{opt_str}"
end
def process_plugins
made_changes = false
loop do
sweep_changes = false
Plugin.plugins.each do |p|
next if @disable_plugins.include?(p.class.name.downcase)
sweep_changes |= p.process
end
made_changes |= sweep_changes
break unless sweep_changes
end
made_changes
end
def self.filter_methods(smali_files, include_types, exclude_types)
methods = []
smali_files.each do |smali_file|
smali_file.methods.each do |method|
if include_types
next if method.descriptor !~ include_types
elsif exclude_types && !(method.descriptor !~ exclude_types)
next
end
methods << method
end
end
methods
end
def self.enumerate_files(dir, ext)
# On Windows, filenames with unicode characters do not show up with Dir#glob or Dir#[]
# They do, however, show up with Dir.entries, which is fine because it seems to be
# the only Dir method that let's me set UTF-8 encoding. I must be missing something.
# OH WELL. Do it the hard way.
opts = { encoding: 'UTF-8' }
Dir.entries(dir, opts).collect do |entry|
next if entry == '.' or entry == '..'
full_path = "#{dir}/#{entry}"
if File.directory?(full_path)
Oracle.enumerate_files(full_path, ext)
else
full_path if entry.downcase.end_with?(ext)
end
end.flatten.compact
end
def self.parse_smali(smali_dir)
file_paths = Oracle.enumerate_files(smali_dir, '.smali')
smali_files = file_paths.collect { |path| SmaliFile.new(path) }
end
end