Class: Driver

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/dex-oracle/driver.rb

Constant Summary collapse

UNESCAPES =
{
  'a' => "\x07", 'b' => "\x08", 't' => "\x09",
  'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c",
  'r' => "\x0d", 'e' => "\x1b", '\\' => "\x5c",
  '"' => "\x22", "'" => "\x27"
}
UNESCAPE_REGEX =
/\\(?:([#{UNESCAPES.keys.join}])|u([\da-fA-F]{4}))|\\0?x([\da-fA-F]{2})/
OUTPUT_HEADER =
'===ORACLE DRIVER OUTPUT==='
DRIVER_DIR =
'/data/local'
DRIVER_CLASS =
'org.cf.oracle.Driver'

Instance Method Summary collapse

Methods included from Logging

included, logger, #logger, logger=

Constructor Details

#initialize(device_id, timeout = 60) ⇒ Driver

Returns a new instance of Driver.



24
25
26
27
28
29
30
31
32
33
# File 'lib/dex-oracle/driver.rb', line 24

def initialize(device_id, timeout = 60)
  @device_id = device_id
  @timeout = timeout

  device_str = device_id.empty? ? '' : "-s #{@device_id} "
  @adb_base = "adb #{device_str}%s"
  @cmd_stub = "export CLASSPATH=#{DRIVER_DIR}/od.zip; app_process /system/bin #{DRIVER_CLASS}"

  @cache = {}
end

Instance Method Details

#install(dex) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/dex-oracle/driver.rb', line 35

def install(dex)
  fail 'Unable to find Java on the path.' unless Utility.which('java')

  begin
    # Merge driver and target dex file
    # Congratulations. You're now one of the 5 people who've used this tool explicitly.
    logger.debug("Merging #{dex.path} and driver dex ...")
    fail "#{Resources.dx} does not exist and is required for DexMerger" unless File.exist?(Resources.dx)
    fail "#{Resources.driver_dex} does not exist" unless File.exist?(Resources.driver_dex)
    tf = Tempfile.new(['oracle-driver', '.dex'])
    cmd = "java -cp #{Resources.dx} com.android.dx.merge.DexMerger #{tf.path} #{dex.path} #{Resources.driver_dex}"
    exec("#{cmd}")

    # Zip merged dex and push to device
    logger.debug('Pushing merged driver to device ...')
    tz = Tempfile.new(['oracle-driver', '.zip'])
    Utility.create_zip(tz.path, { 'classes.dex' => tf })
    adb("push #{tz.path} #{DRIVER_DIR}/od.zip")
  rescue => e
    puts "Error installing the driver: #{e}"
  ensure
    tf.close
    tf.unlink
    tz.close
    tz.unlink
  end
end

#make_target(class_name, signature, *args) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/dex-oracle/driver.rb', line 105

def make_target(class_name, signature, *args)
  method = SmaliMethod.new(class_name, signature)
  target = {
    className: method.class.tr('/', '.'),
    methodName: method.name,
    arguments: Driver.build_arguments(method.parameters, args)
  }
  # Identifiers are used to map individual inputs to outputs
  target[:id] = Digest::SHA256.hexdigest(target.to_json)

  target
end

#run(class_name, signature, *args) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/dex-oracle/driver.rb', line 63

def run(class_name, signature, *args)
  method = SmaliMethod.new(class_name, signature)
  cmd = build_command(method.class, method.name, method.parameters, args)
  output = nil
  retries = 1
  begin
    output = drive(cmd)
  rescue => e
    # If you slam an emulator or device with too many app_process commands,
    # it eventually gets angry and segmentation faults. No idea why.
    # This took many frustrating hours to figure out.
    if retries <= 3
      logger.debug("Driver execution failed. Taking a quick nap and retrying, Zzzzz ##{retries} / 3 ...")
      sleep 5
      retries += 1
      retry
    else
      raise e
    end
  end
  output
end

#run_batch(batch) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/dex-oracle/driver.rb', line 86

def run_batch(batch)
  push_batch_targets(batch)
  retries = 1
  begin
    drive("#{@cmd_stub} @#{DRIVER_DIR}/od-targets.json", true)
  rescue => e
    if retries <= 3 && e.message.include?('Segmentation fault')
      # Maybe we just need to retry
      logger.debug("Driver execution segfaulted. Taking a quick nap and retrying, Zzzzz ##{retries} / 3 ...")
      sleep 5
      retries += 1
      retry
    else
      raise e
    end
  end
  pull_batch_outputs
end