dex-oracle 1.0.4 → 1.0.5
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.
- checksums.yaml +4 -4
- data/Gemfile +6 -1
- data/Gemfile.lock +36 -27
- data/LICENSE.txt +1 -1
- data/README.md +43 -8
- data/bin/dex-oracle +1 -1
- data/dex-oracle.gemspec +1 -1
- data/driver/src/main/java/org/cf/oracle/Driver.java +2 -3
- data/lib/dex-oracle/driver.rb +119 -78
- data/lib/dex-oracle/plugin.rb +7 -7
- data/lib/dex-oracle/plugins/bitwise_antiskid.rb +61 -0
- data/lib/dex-oracle/plugins/string_decryptor.rb +6 -6
- data/lib/dex-oracle/plugins/undexguard.rb +28 -23
- data/lib/dex-oracle/plugins/unreflector.rb +25 -28
- data/lib/dex-oracle/resources.rb +4 -2
- data/lib/dex-oracle/smali_file.rb +30 -8
- data/lib/dex-oracle/smali_input.rb +27 -21
- data/lib/dex-oracle/version.rb +1 -1
- data/lib/oracle.rb +21 -4
- data/res/driver.dex +0 -0
- data/res/dx.jar +0 -0
- data/spec/data/plugins/clinit.smali +14 -0
- data/spec/dex-oracle/driver_spec.rb +1 -2
- data/spec/dex-oracle/plugins/string_decryptor_spec.rb +11 -0
- data/spec/dex-oracle/plugins/unreflector_spec.rb +1 -1
- data/spec/dex-oracle/smali_file_spec.rb +2 -1
- data/spec/dex-oracle/smali_input_spec.rb +12 -3
- data/spec/spec_helper.rb +3 -0
- data/update_driver +7 -1
- metadata +9 -4
data/lib/dex-oracle/plugin.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
class Plugin
|
2
2
|
module CommonRegex
|
3
|
-
CONST_NUMBER = 'const(?:\/\d+) [vp]\d+, (-?0x[a-f\d]+)'
|
4
|
-
ESCAPE_STRING = '"(.*?)(?<!\\\\)"'
|
5
|
-
CONST_STRING = 'const-string [vp]\d+, ' << ESCAPE_STRING << '.*'
|
6
|
-
MOVE_RESULT_OBJECT = 'move-result-object ([vp]\d+)'
|
3
|
+
CONST_NUMBER = 'const(?:\/\d+) [vp]\d+, (-?0x[a-f\d]+)'.freeze
|
4
|
+
ESCAPE_STRING = '"(.*?)(?<!\\\\)"'.freeze
|
5
|
+
CONST_STRING = 'const-string(?:/jumbo)? [vp]\d+, ' << ESCAPE_STRING << '.*'.freeze
|
6
|
+
MOVE_RESULT_OBJECT = 'move-result-object ([vp]\d+)'.freeze
|
7
7
|
end
|
8
8
|
|
9
9
|
@plugins = []
|
@@ -16,7 +16,7 @@ class Plugin
|
|
16
16
|
Dir["#{File.dirname(__FILE__)}/plugins/*.rb"].each { |f| require f }
|
17
17
|
classes = []
|
18
18
|
Object.constants.each do |klass|
|
19
|
-
const = Kernel.const_get(klass)
|
19
|
+
const = Kernel.const_get(klass) unless klass == :TimeoutError
|
20
20
|
next unless const.respond_to?(:superclass) && const.superclass == Plugin
|
21
21
|
classes << const
|
22
22
|
end
|
@@ -29,11 +29,11 @@ class Plugin
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def process
|
32
|
-
|
32
|
+
raise 'process not implemented'
|
33
33
|
end
|
34
34
|
|
35
35
|
def optimizations
|
36
|
-
|
36
|
+
raise 'optimizations not implemented'
|
37
37
|
end
|
38
38
|
|
39
39
|
# method_to_target_to_context -> { method: [target_to_context] }
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative '../logging'
|
2
|
+
require_relative '../utility'
|
3
|
+
|
4
|
+
# Sample: 0e18bbf2a3539e5669be76ed4c468257ecfd3d36
|
5
|
+
class BitwiseAntiSkid < Plugin
|
6
|
+
attr_reader :optimizations
|
7
|
+
|
8
|
+
include Logging
|
9
|
+
include CommonRegex
|
10
|
+
|
11
|
+
STRING_DECRYPT = Regexp.new(
|
12
|
+
'^[ \t]*(' +
|
13
|
+
CONST_STRING + '\s+' + \
|
14
|
+
CONST_NUMBER + '\s+' \
|
15
|
+
'invoke-static \{[vp]\d+, [vp]\d+\}, L([^;]+);->(Go_Learn_Something\(Ljava/lang/String;I\))Ljava/lang/String;' \
|
16
|
+
'\s+' + \
|
17
|
+
MOVE_RESULT_OBJECT + ')'
|
18
|
+
)
|
19
|
+
|
20
|
+
MODIFIER = -> (_, output, out_reg) { "const-string #{out_reg}, \"#{output.split('').collect { |e| e.inspect[1..-2] }.join}\"" }
|
21
|
+
|
22
|
+
def initialize(driver, smali_files, methods)
|
23
|
+
@driver = driver
|
24
|
+
@smali_files = smali_files
|
25
|
+
@methods = methods
|
26
|
+
@optimizations = Hash.new(0)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process
|
30
|
+
method_to_target_to_contexts = {}
|
31
|
+
@methods.each do |method|
|
32
|
+
logger.info("Decrypting Bitwise Anti-Skid #{method.descriptor}")
|
33
|
+
target_to_contexts = {}
|
34
|
+
target_to_contexts.merge!(decrypt_strings(method))
|
35
|
+
target_to_contexts.map { |_, v| v.uniq! }
|
36
|
+
method_to_target_to_contexts[method] = target_to_contexts unless target_to_contexts.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
made_changes = false
|
40
|
+
made_changes |= Plugin.apply_batch(@driver, method_to_target_to_contexts, MODIFIER)
|
41
|
+
|
42
|
+
made_changes
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def decrypt_strings(method)
|
48
|
+
target_to_contexts = {}
|
49
|
+
matches = method.body.scan(STRING_DECRYPT)
|
50
|
+
@optimizations[:string_decrypts] += matches.size if matches
|
51
|
+
matches.each do |original, encrypted, number, class_name, method_signature, out_reg|
|
52
|
+
target = @driver.make_target(
|
53
|
+
class_name, method_signature, encrypted, number.to_i(16)
|
54
|
+
)
|
55
|
+
target_to_contexts[target] = [] unless target_to_contexts.key?(target)
|
56
|
+
target_to_contexts[target] << [original, out_reg]
|
57
|
+
end
|
58
|
+
|
59
|
+
target_to_contexts
|
60
|
+
end
|
61
|
+
end
|
@@ -2,13 +2,17 @@ require_relative '../logging'
|
|
2
2
|
require_relative '../utility'
|
3
3
|
|
4
4
|
class StringDecryptor < Plugin
|
5
|
+
attr_reader :optimizations
|
6
|
+
|
5
7
|
include Logging
|
6
8
|
include CommonRegex
|
7
9
|
|
8
10
|
STRING_DECRYPT = Regexp.new(
|
9
|
-
'^[ \t]*('
|
11
|
+
'^[ \t]*(' +
|
12
|
+
CONST_STRING + '\s+' \
|
10
13
|
'invoke-static \{[vp]\d+\}, L([^;]+);->([^\(]+\(Ljava/lang/String;\))Ljava/lang/String;' \
|
11
|
-
'\s+'
|
14
|
+
'\s+' +
|
15
|
+
MOVE_RESULT_OBJECT + ')'
|
12
16
|
)
|
13
17
|
|
14
18
|
MODIFIER = -> (_, output, out_reg) { "const-string #{out_reg}, \"#{output.split('').collect { |e| e.inspect[1..-2] }.join}\"" }
|
@@ -36,10 +40,6 @@ class StringDecryptor < Plugin
|
|
36
40
|
made_changes
|
37
41
|
end
|
38
42
|
|
39
|
-
def optimizations
|
40
|
-
@optimizations
|
41
|
-
end
|
42
|
-
|
43
43
|
private
|
44
44
|
|
45
45
|
def decrypt_strings(method)
|
@@ -2,36 +2,45 @@ require_relative '../logging'
|
|
2
2
|
require_relative '../utility'
|
3
3
|
|
4
4
|
class Undexguard < Plugin
|
5
|
+
attr_reader :optimizations
|
6
|
+
|
5
7
|
include Logging
|
6
8
|
include CommonRegex
|
7
9
|
|
8
10
|
STRING_LOOKUP_3INT = Regexp.new(
|
9
|
-
'^[ \t]*('
|
11
|
+
'^[ \t]*(' +
|
12
|
+
((CONST_NUMBER + '\s+') * 3) +
|
10
13
|
'invoke-static \{[vp]\d+, [vp]\d+, [vp]\d+\}, L([^;]+);->([^\(]+\(III\))Ljava/lang/String;' \
|
11
|
-
'\s+'
|
14
|
+
'\s+' +
|
15
|
+
MOVE_RESULT_OBJECT + ')',
|
12
16
|
Regexp::MULTILINE
|
13
17
|
)
|
14
18
|
|
15
19
|
STRING_LOOKUP_1INT = Regexp.new(
|
16
|
-
'^[ \t]*('
|
20
|
+
'^[ \t]*(' +
|
21
|
+
CONST_NUMBER + '\s+' \
|
17
22
|
'invoke-static \{[vp]\d+\}, L([^;]+);->([^\(]+\(I\))Ljava/lang/String;' \
|
18
|
-
'\s+'
|
23
|
+
'\s+' +
|
24
|
+
MOVE_RESULT_OBJECT + ')'
|
19
25
|
)
|
20
26
|
|
21
27
|
BYTES_DECRYPT = Regexp.new(
|
22
|
-
'^[ \t]*('
|
28
|
+
'^[ \t]*(' +
|
29
|
+
CONST_STRING + '\s+' \
|
23
30
|
'invoke-virtual \{[vp]\d+\}, Ljava\/lang\/String;->getBytes\(\)\[B\s+' \
|
24
31
|
'move-result-object [vp]\d+\s+' \
|
25
32
|
'invoke-static \{[vp]\d+\}, L([^;]+);->([^\(]+\(\[B\))Ljava/lang/String;' \
|
26
|
-
'\s+'
|
33
|
+
'\s+' +
|
34
|
+
MOVE_RESULT_OBJECT + ')'
|
27
35
|
)
|
28
36
|
|
29
37
|
MULTI_BYTES_DECRYPT = Regexp.new(
|
30
|
-
'^[ \t]*('
|
38
|
+
'^[ \t]*(' +
|
39
|
+
CONST_STRING + '\s+' \
|
31
40
|
'new-instance ([vp]\d+), L[^;]+;\s+' \
|
32
41
|
'invoke-static \{[vp]\d+\}, L([^;]+);->([^\(]+\(Ljava/lang/String;\))\[B\s+' \
|
33
|
-
'move-result-object [vp]\d+\s+'
|
34
|
-
CONST_STRING
|
42
|
+
'move-result-object [vp]\d+\s+' +
|
43
|
+
CONST_STRING + '\s+' \
|
35
44
|
'invoke-static \{[vp]\d+, [vp]\d+\}, L([^;]+);->([^\(]+\(\[BLjava/lang/String;\))\[B\s+' \
|
36
45
|
'move-result-object [vp]\d+\s+' \
|
37
46
|
'invoke-static \{[vp]\d+\}, L([^;]+);->([^\(]+\(\[B\))\[B\s+' \
|
@@ -71,21 +80,8 @@ class Undexguard < Plugin
|
|
71
80
|
made_changes
|
72
81
|
end
|
73
82
|
|
74
|
-
def optimizations
|
75
|
-
@optimizations
|
76
|
-
end
|
77
|
-
|
78
83
|
private
|
79
84
|
|
80
|
-
def self.array_string_to_array(str)
|
81
|
-
if str =~ /\A\[(?:\d+(?:,\d+)*)?\]\z/
|
82
|
-
str = eval(str)
|
83
|
-
else
|
84
|
-
fail "Output is not in byte format; this frightens me: #{str}"
|
85
|
-
end
|
86
|
-
str
|
87
|
-
end
|
88
|
-
|
89
85
|
def lookup_strings_3int(method)
|
90
86
|
target_to_contexts = {}
|
91
87
|
matches = method.body.scan(STRING_LOOKUP_3INT)
|
@@ -140,7 +136,7 @@ class Undexguard < Plugin
|
|
140
136
|
iv_bytes = @driver.run(iv_class_name, iv_method_signature, iv_str)
|
141
137
|
enc_bytes = @driver.run(iv2_class_name, iv2_method_signature, iv_bytes, iv2_str)
|
142
138
|
dec_bytes = @driver.run(dec_class_name, dec_method_signature, enc_bytes)
|
143
|
-
dec_array =
|
139
|
+
dec_array = array_string_to_array(dec_bytes)
|
144
140
|
dec_string = dec_array.pack('U*')
|
145
141
|
|
146
142
|
target = { id: Digest::SHA256.hexdigest(original) }
|
@@ -152,4 +148,13 @@ class Undexguard < Plugin
|
|
152
148
|
method_to_target_to_contexts = { method => target_to_contexts }
|
153
149
|
Plugin.apply_outputs(target_id_to_output, method_to_target_to_contexts, MODIFIER)
|
154
150
|
end
|
151
|
+
|
152
|
+
def array_string_to_array(str)
|
153
|
+
if str =~ /\A\[(?:\d+(?:,\d+)*)?\]\z/
|
154
|
+
str = eval(str)
|
155
|
+
else
|
156
|
+
raise "Output is not in byte format; this frightens me: #{str}"
|
157
|
+
end
|
158
|
+
str
|
159
|
+
end
|
155
160
|
end
|
@@ -2,42 +2,43 @@ require 'digest'
|
|
2
2
|
require_relative '../logging'
|
3
3
|
|
4
4
|
class Unreflector < Plugin
|
5
|
+
attr_reader :optimizations
|
6
|
+
|
5
7
|
include Logging
|
6
8
|
include CommonRegex
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
CLASS_FOR_NAME = 'invoke-static \{[vp]\d+\}, Ljava\/lang\/Class;->forName\(Ljava\/lang\/String;\)Ljava\/lang\/Class;'
|
10
|
+
CLASS_FOR_NAME = 'invoke-static \{[vp]\d+\}, Ljava\/lang\/Class;->forName\(Ljava\/lang\/String;\)Ljava\/lang\/Class;'.freeze
|
11
11
|
|
12
12
|
CONST_CLASS_REGEX = Regexp.new(
|
13
|
-
'^[ \t]*('
|
14
|
-
|
15
|
-
|
13
|
+
'^[ \t]*(' +
|
14
|
+
CONST_STRING + '\s+' +
|
15
|
+
CLASS_FOR_NAME + '\s+' +
|
16
|
+
MOVE_RESULT_OBJECT + ')'
|
16
17
|
)
|
17
18
|
|
18
19
|
VIRTUAL_FIELD_LOOKUP = Regexp.new(
|
19
|
-
'^[ \t]*('
|
20
|
-
CONST_STRING
|
21
|
-
'invoke-static \{[vp]\d+\}, Ljava\/lang\/Class;->forName\(Ljava\/lang\/String;\)Ljava\/lang\/Class;\s+'
|
22
|
-
MOVE_RESULT_OBJECT
|
23
|
-
CONST_STRING
|
24
|
-
'invoke-virtual \{[vp]\d+, [vp]\d+\}, Ljava\/lang\/Class;->getField\(Ljava\/lang\/String;\)Ljava\/lang\/reflect\/Field;\s+'
|
25
|
-
MOVE_RESULT_OBJECT
|
26
|
-
'invoke-virtual \{[vp]\d+, ([vp]\d+)\}, Ljava\/lang\/reflect\/Field;->get\(Ljava\/lang\/Object;\)Ljava\/lang\/Object;\s+'
|
27
|
-
MOVE_RESULT_OBJECT
|
20
|
+
'^[ \t]*(' +
|
21
|
+
CONST_STRING + '\s+' \
|
22
|
+
'invoke-static \{[vp]\d+\}, Ljava\/lang\/Class;->forName\(Ljava\/lang\/String;\)Ljava\/lang\/Class;\s+' +
|
23
|
+
MOVE_RESULT_OBJECT + '\s+' +
|
24
|
+
CONST_STRING + '\s+' \
|
25
|
+
'invoke-virtual \{[vp]\d+, [vp]\d+\}, Ljava\/lang\/Class;->getField\(Ljava\/lang\/String;\)Ljava\/lang\/reflect\/Field;\s+' +
|
26
|
+
MOVE_RESULT_OBJECT + '\s+' \
|
27
|
+
'invoke-virtual \{[vp]\d+, ([vp]\d+)\}, Ljava\/lang\/reflect\/Field;->get\(Ljava\/lang\/Object;\)Ljava\/lang\/Object;\s+' +
|
28
|
+
MOVE_RESULT_OBJECT + ')'
|
28
29
|
)
|
29
30
|
|
30
31
|
STATIC_FIELD_LOOKUP = Regexp.new(
|
31
|
-
'^[ \t]*('
|
32
|
-
CONST_STRING
|
33
|
-
CLASS_FOR_NAME
|
34
|
-
MOVE_RESULT_OBJECT
|
35
|
-
CONST_STRING
|
36
|
-
'invoke-virtual \{[vp]\d+, [vp]\d+\}, Ljava\/lang\/Class;->getField\(Ljava\/lang\/String;\)Ljava\/lang\/reflect\/Field;\s+'
|
37
|
-
MOVE_RESULT_OBJECT
|
32
|
+
'^[ \t]*(' +
|
33
|
+
CONST_STRING + '\s+' +
|
34
|
+
CLASS_FOR_NAME + '\s+' +
|
35
|
+
MOVE_RESULT_OBJECT + '\s+' +
|
36
|
+
CONST_STRING +
|
37
|
+
'invoke-virtual \{[vp]\d+, [vp]\d+\}, Ljava\/lang\/Class;->getField\(Ljava\/lang\/String;\)Ljava\/lang\/reflect\/Field;\s+' +
|
38
|
+
MOVE_RESULT_OBJECT + '\s+' \
|
38
39
|
'const/4 [vp]\d+, 0x0\s+' \
|
39
|
-
'invoke-virtual \{[vp]\d+, ([vp]\d+)\}, Ljava\/lang\/reflect\/Field;->get\(Ljava\/lang\/Object;\)Ljava\/lang\/Object;\s+'
|
40
|
-
MOVE_RESULT_OBJECT
|
40
|
+
'invoke-virtual \{[vp]\d+, ([vp]\d+)\}, Ljava\/lang\/reflect\/Field;->get\(Ljava\/lang\/Object;\)Ljava\/lang\/Object;\s+' +
|
41
|
+
MOVE_RESULT_OBJECT +
|
41
42
|
')'
|
42
43
|
)
|
43
44
|
|
@@ -60,10 +61,6 @@ class Unreflector < Plugin
|
|
60
61
|
made_changes
|
61
62
|
end
|
62
63
|
|
63
|
-
def optimizations
|
64
|
-
@optimizations
|
65
|
-
end
|
66
|
-
|
67
64
|
private
|
68
65
|
|
69
66
|
def lookup_classes(method)
|
data/lib/dex-oracle/resources.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
require_relative 'logging'
|
2
|
+
|
1
3
|
class Resources
|
2
4
|
include Logging
|
3
5
|
|
4
6
|
PATH = File.join(File.dirname(File.expand_path(__FILE__)), '../../res')
|
5
7
|
|
6
8
|
def self.dx
|
7
|
-
|
9
|
+
"#{PATH}/dx.jar"
|
8
10
|
end
|
9
11
|
|
10
12
|
def self.driver_dex
|
11
|
-
|
13
|
+
"#{PATH}/driver.dex"
|
12
14
|
end
|
13
15
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require_relative 'smali_field'
|
2
3
|
require_relative 'smali_method'
|
3
4
|
require_relative 'logging'
|
@@ -7,13 +8,13 @@ class SmaliFile
|
|
7
8
|
|
8
9
|
include Logging
|
9
10
|
|
10
|
-
ACCESSOR = /(?:
|
11
|
+
ACCESSOR = /(?:abstract|annotation|constructor|enum|final|interface|native|private|protected|public|static|strictfp|synchronized|synthetic|transient|volatile)/
|
11
12
|
TYPE = /(?:[IJFDZBCV]|L[^;]+;)/
|
12
13
|
CLASS = /^\.class (?:#{ACCESSOR} )+(L[^;]+;)/
|
13
14
|
SUPER = /^\.super (L[^;]+;)/
|
14
15
|
INTERFACE = /^\.implements (L[^;]+;)/
|
15
16
|
FIELD = /^\.field (?:#{ACCESSOR} )+([^\s]+)$/
|
16
|
-
METHOD =
|
17
|
+
METHOD = /^\.method (?:#{ACCESSOR} )+([^\s]+)$/
|
17
18
|
|
18
19
|
def initialize(file_path)
|
19
20
|
@file_path = file_path
|
@@ -38,23 +39,44 @@ class SmaliFile
|
|
38
39
|
private
|
39
40
|
|
40
41
|
def parse(file_path)
|
41
|
-
|
42
|
+
logger.debug("Parsing: #{file_path} ...")
|
43
|
+
@content = File.open(file_path, 'r:UTF-8', &:read)
|
42
44
|
@class = @content[CLASS, 1]
|
43
45
|
@super = @content[SUPER, 1]
|
44
46
|
@interfaces = []
|
45
47
|
@content.scan(INTERFACE).each { |m| @interfaces << m.first }
|
46
48
|
@fields = []
|
47
49
|
@content.scan(FIELD).each { |m| @fields << SmaliField.new(@class, m.first) }
|
50
|
+
parse_methods
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_methods
|
48
54
|
@methods = []
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
method_signature = nil
|
56
|
+
in_method = false
|
57
|
+
body = nil
|
58
|
+
@content.each_line do |line|
|
59
|
+
if in_method
|
60
|
+
if /^\.end method$/ =~ line
|
61
|
+
in_method = false
|
62
|
+
@methods << SmaliMethod.new(@class, method_signature, body)
|
63
|
+
next
|
64
|
+
end
|
65
|
+
body << line
|
66
|
+
else
|
67
|
+
next unless line.include?('.method ')
|
68
|
+
m = METHOD.match(line)
|
69
|
+
next unless m
|
70
|
+
|
71
|
+
in_method = true
|
72
|
+
method_signature = m.captures.first
|
73
|
+
body = "\n"
|
74
|
+
end
|
53
75
|
end
|
54
76
|
end
|
55
77
|
|
56
78
|
def build_method_regex(method_signature)
|
57
|
-
|
79
|
+
/^\.method (?:#{ACCESSOR} )+#{Regexp.escape(method_signature)}(.+?)^\.end method$/m
|
58
80
|
end
|
59
81
|
|
60
82
|
def update_method(method)
|
@@ -5,8 +5,10 @@ require_relative 'utility'
|
|
5
5
|
class SmaliInput
|
6
6
|
attr_reader :dir, :out_apk, :out_dex, :temp_dir, :temp_dex
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
DEX_MAGIC = [0x64, 0x65, 0x78].freeze
|
11
|
+
PK_ZIP_MAGIC = [0x50, 0x4b, 0x3].freeze
|
10
12
|
|
11
13
|
def initialize(input)
|
12
14
|
prepare(input)
|
@@ -14,19 +16,18 @@ class SmaliInput
|
|
14
16
|
|
15
17
|
def finish
|
16
18
|
SmaliInput.update_apk(dir, @out_apk) if @out_apk
|
17
|
-
SmaliInput.compile(dir, @out_dex) if @out_dex
|
19
|
+
SmaliInput.compile(dir, @out_dex) if @out_dex && !@out_apk
|
18
20
|
FileUtils.rm_rf(@dir) if @temp_dir
|
19
21
|
FileUtils.rm_rf(@out_dex) if @temp_dex
|
20
22
|
end
|
21
23
|
|
22
|
-
private
|
23
|
-
|
24
24
|
def self.compile(dir, out_dex = nil)
|
25
|
-
|
26
|
-
out_dex = Tempfile.new(
|
25
|
+
raise 'Smali could not be found on the path.' if Utility.which('smali').nil?
|
26
|
+
out_dex = Tempfile.new(%w(oracle .dex)) if out_dex.nil?
|
27
|
+
logger.info("Compiling DEX #{out_dex.path} ...")
|
27
28
|
exit_code = SmaliInput.exec("smali #{dir} -o #{out_dex.path}")
|
28
29
|
# Remember kids, if you make a CLI, exit with non-zero status for failures
|
29
|
-
|
30
|
+
raise 'Crap, smali compilation failed.' if $CHILD_STATUS.exitstatus != 0
|
30
31
|
out_dex
|
31
32
|
end
|
32
33
|
|
@@ -39,6 +40,20 @@ class SmaliInput
|
|
39
40
|
Utility.extract_file(apk, 'classes.dex', out_dex)
|
40
41
|
end
|
41
42
|
|
43
|
+
def self.exec(cmd)
|
44
|
+
`#{cmd}`
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def baksmali(input)
|
50
|
+
logger.debug("Disassembling #{input} ...")
|
51
|
+
raise 'Baksmali could not be found on the path.' if Utility.which('baksmali').nil?
|
52
|
+
@dir = Dir.mktmpdir
|
53
|
+
cmd = "baksmali #{input} -o #{@dir}"
|
54
|
+
SmaliInput.exec(cmd)
|
55
|
+
end
|
56
|
+
|
42
57
|
def prepare(input)
|
43
58
|
if File.directory?(input)
|
44
59
|
@temp_dir = false
|
@@ -54,7 +69,7 @@ class SmaliInput
|
|
54
69
|
@temp_dex = true
|
55
70
|
@temp_dir = true
|
56
71
|
@out_apk = "#{File.basename(input, '.*')}_oracle#{File.extname(input)}"
|
57
|
-
@out_dex = Tempfile.new(
|
72
|
+
@out_dex = Tempfile.new(%w(oracle .dex))
|
58
73
|
FileUtils.cp(input, @out_apk)
|
59
74
|
SmaliInput.extract_dex(@out_apk, @out_dex)
|
60
75
|
baksmali(input)
|
@@ -62,20 +77,11 @@ class SmaliInput
|
|
62
77
|
@temp_dex = false
|
63
78
|
@temp_dir = true
|
64
79
|
@out_dex = "#{File.basename(input, '.*')}_oracle#{File.extname(input)}"
|
80
|
+
FileUtils.cp(input, @out_dex)
|
81
|
+
@out_dex = File.new(@out_dex)
|
65
82
|
baksmali(input)
|
66
83
|
else
|
67
|
-
|
84
|
+
raise "Unrecognized file type for: #{input}, magic=#{magic.inspect}"
|
68
85
|
end
|
69
86
|
end
|
70
|
-
|
71
|
-
def baksmali(input)
|
72
|
-
fail 'Baksmali could not be found on the path.' if Utility.which('baksmali').nil?
|
73
|
-
@dir = Dir.mktmpdir
|
74
|
-
cmd = "baksmali #{input} -o #{@dir}"
|
75
|
-
SmaliInput.exec(cmd)
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.exec(cmd)
|
79
|
-
`#{cmd}`
|
80
|
-
end
|
81
87
|
end
|