idb 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +65 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/idb +5 -0
- data/idb.gemspec +41 -0
- data/lib/LICENSE +20 -0
- data/lib/README.md +54 -0
- data/lib/config/.dummy +0 -0
- data/lib/config/settings.yml +8 -0
- data/lib/gui/app_binary_tab_widget.rb +45 -0
- data/lib/gui/app_details_group_box.rb +213 -0
- data/lib/gui/app_list_dialog.rb +67 -0
- data/lib/gui/app_list_widget_item.rb +9 -0
- data/lib/gui/binary_strings_widget.rb +33 -0
- data/lib/gui/browse_filesystem_widget.rb +4 -0
- data/lib/gui/ca_manager_dialog.rb +137 -0
- data/lib/gui/cache_db_widget.rb +61 -0
- data/lib/gui/certificate_item.rb +5 -0
- data/lib/gui/console_widget.rb +163 -0
- data/lib/gui/cycript_console_widget.rb +68 -0
- data/lib/gui/cycript_thread.rb +81 -0
- data/lib/gui/device_info_group_box.rb +55 -0
- data/lib/gui/device_status_dialog.rb +351 -0
- data/lib/gui/file_system_events_widget.rb +4 -0
- data/lib/gui/fs_viewer_tab_widget.rb +245 -0
- data/lib/gui/i_device_syslog_thread.rb +47 -0
- data/lib/gui/images/check.png +0 -0
- data/lib/gui/images/folder.ico +0 -0
- data/lib/gui/images/iphone.ico +0 -0
- data/lib/gui/images/screenshot.png +0 -0
- data/lib/gui/key_chain_widget.rb +86 -0
- data/lib/gui/local_storage_tab_widget.rb +37 -0
- data/lib/gui/log_plain_text_edit.rb +18 -0
- data/lib/gui/log_widget.rb +71 -0
- data/lib/gui/main_tab_widget.rb +179 -0
- data/lib/gui/pasteboard_monitor_widget.rb +116 -0
- data/lib/gui/path_list_widget_item.rb +5 -0
- data/lib/gui/pb_watcher_thread.rb +63 -0
- data/lib/gui/plist_file_widget.rb +66 -0
- data/lib/gui/qt_ruby_variant.rb +16 -0
- data/lib/gui/screenshot_wizard.rb +169 -0
- data/lib/gui/settings_dialog.rb +69 -0
- data/lib/gui/settings_tab_widget.rb +149 -0
- data/lib/gui/shared_libraries_widget.rb +47 -0
- data/lib/gui/snoop_it_fs_events_widget.rb +150 -0
- data/lib/gui/snoop_it_keychain_widget.rb +172 -0
- data/lib/gui/snoop_it_sensitive_api_widget.rb +128 -0
- data/lib/gui/snoop_it_tab_widget.rb +27 -0
- data/lib/gui/snoop_it_update_thread.rb +48 -0
- data/lib/gui/sqlite_widget.rb +73 -0
- data/lib/gui/ssh_port_forward_tab_widget.rb +209 -0
- data/lib/gui/tool_widget.rb +94 -0
- data/lib/gui/url_handler_widget.rb +26 -0
- data/lib/gui/url_scheme_fuzz_widget.rb +103 -0
- data/lib/gui/url_scheme_widget.rb +60 -0
- data/lib/gui/weak_class_dump_widget.rb +89 -0
- data/lib/helper/ssh_port_forwarder.rb +72 -0
- data/lib/idb.rb +295 -0
- data/lib/idb/version.rb +3 -0
- data/lib/lib/CgBI.rb +153 -0
- data/lib/lib/abstract_device.rb +31 -0
- data/lib/lib/app.rb +286 -0
- data/lib/lib/app_binary.rb +57 -0
- data/lib/lib/ca_interface.rb +151 -0
- data/lib/lib/configuration.rb +0 -0
- data/lib/lib/console_launcher.rb +24 -0
- data/lib/lib/device.rb +438 -0
- data/lib/lib/device_ca_interface.rb +36 -0
- data/lib/lib/host_file_wrapper.rb +27 -0
- data/lib/lib/i_device_diagnostics_wrapper.rb +90 -0
- data/lib/lib/keychain_plist_parser.rb +15 -0
- data/lib/lib/local_operations.rb +67 -0
- data/lib/lib/otool_wrapper.rb +116 -0
- data/lib/lib/plist_util.rb +72 -0
- data/lib/lib/qt_thread_fix.rb +29 -0
- data/lib/lib/rsync_git_manager.rb +81 -0
- data/lib/lib/screen_shot_util.rb +59 -0
- data/lib/lib/settings.rb +67 -0
- data/lib/lib/simulator.rb +60 -0
- data/lib/lib/simulator_ca_interface.rb +16 -0
- data/lib/lib/snoop_it_wrapper.rb +80 -0
- data/lib/lib/ssh_operations.rb +136 -0
- data/lib/lib/ssh_port_forwarder.rb +43 -0
- data/lib/lib/tools.rb +11 -0
- data/lib/lib/url_scheme_fuzzer.rb +98 -0
- data/lib/lib/usb_muxd_wrapper.rb +32 -0
- data/lib/lib/weak_class_dump_wrapper.rb +62 -0
- data/lib/utils/dumpdecrypted/README +4 -0
- data/lib/utils/dumpdecrypted/dumpdecrypted_armv6.dylib +0 -0
- data/lib/utils/dumpdecrypted/dumpdecrypted_armv7.dylib +0 -0
- data/lib/utils/ios-ssl-kill-switch/com.isecpartners.nabla.sslkillswitch_v0.5-iOS_6.1.deb +0 -0
- data/lib/utils/keychain_dump/README +2 -0
- data/lib/utils/keychain_dump/keychain_dump +0 -0
- data/lib/utils/pbwatcher/pbwatcher +0 -0
- data/lib/utils/pcviewer/protectionclassviewer +0 -0
- data/lib/utils/weak_class_dump/README +5 -0
- data/lib/utils/weak_class_dump/weak_classdump.cy +726 -0
- metadata +412 -0
data/lib/idb/version.rb
ADDED
data/lib/lib/CgBI.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
# By Jeff Jarmoc (jeff@jarmoc.com)
|
4
|
+
|
5
|
+
# Simplified usage
|
6
|
+
# ---------------
|
7
|
+
# require './CGBI.rb'
|
8
|
+
# CGBI.from_file('./test.png').to_png_file('./outfile.png')
|
9
|
+
|
10
|
+
module Idb
|
11
|
+
class CGBI
|
12
|
+
# http://iphonedevwiki.net/index.php/CgBI_file_format
|
13
|
+
PNGHEADER = "\x89PNG\r\n\x1A\n".force_encoding('ASCII-8BIT')
|
14
|
+
|
15
|
+
# Places to hold data
|
16
|
+
@cgbi
|
17
|
+
@png
|
18
|
+
|
19
|
+
# Places to store header info..
|
20
|
+
attr_accessor :width, :height, :depth, :filter
|
21
|
+
|
22
|
+
#Stores the input format
|
23
|
+
@orig_format
|
24
|
+
|
25
|
+
def initialize(string)
|
26
|
+
unless string[0,8] == PNGHEADER
|
27
|
+
raise "Not a PNG"
|
28
|
+
end
|
29
|
+
|
30
|
+
if string.match(/CgBI/)
|
31
|
+
#puts "Input is CGBI"
|
32
|
+
@cgbi = string
|
33
|
+
@orig_format = "CGBI"
|
34
|
+
else
|
35
|
+
#puts "Input is PNG"
|
36
|
+
@png = string
|
37
|
+
@orig_format = "PNG"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_file(file_path)
|
42
|
+
self.new(File.open(file_path, 'rb') {|f| f.read})
|
43
|
+
end
|
44
|
+
|
45
|
+
def png
|
46
|
+
return @png if @png
|
47
|
+
|
48
|
+
#Convert from cgbi, set instance var, return
|
49
|
+
data = @cgbi.dup
|
50
|
+
png = ""
|
51
|
+
png = data.slice!(0,8) #Copy header
|
52
|
+
|
53
|
+
loop do
|
54
|
+
# Parse chunks... fixup as needed.
|
55
|
+
chunk = {}
|
56
|
+
chunk[:length] = data.slice!(0,4).unpack("N")[0]
|
57
|
+
chunk[:type] = data.slice!(0,4)
|
58
|
+
|
59
|
+
chunk[:data] = data.slice!(0,chunk[:length] || nil)
|
60
|
+
chunk[:crc] = data.slice!(0,4)
|
61
|
+
|
62
|
+
#puts "#{chunk[:length]} : #{chunk[:type]} : #{chunk[:crc].inspect}"
|
63
|
+
|
64
|
+
case chunk[:type]
|
65
|
+
when "CgBI"
|
66
|
+
#puts "Skipping CgBI Chunk"
|
67
|
+
|
68
|
+
when "IHDR"
|
69
|
+
self.width = chunk[:data][0, 4].unpack("L>").first
|
70
|
+
self.height = chunk[:data][4, 4].unpack("L>").first
|
71
|
+
self.depth = chunk[:data][8, 1].unpack("C").first
|
72
|
+
self.filter = chunk[:data][11, 1].unpack("C").first
|
73
|
+
|
74
|
+
#puts "Image: #{width}x#{height} #{depth}bit - Filter: #{filter}"
|
75
|
+
|
76
|
+
png << [chunk[:length]].pack("N")
|
77
|
+
png << chunk[:type]
|
78
|
+
png << chunk[:data]
|
79
|
+
png << chunk[:crc]
|
80
|
+
|
81
|
+
when "IDAT"
|
82
|
+
#Inflate the IDAT chunk
|
83
|
+
inflate = Zlib::Inflate.new(-15)
|
84
|
+
decompressed = inflate.inflate(chunk[:data])
|
85
|
+
|
86
|
+
# Re-order pixels to RGBA
|
87
|
+
chunk[:data] = ""
|
88
|
+
while (decompressed) do
|
89
|
+
#(1..@height).each do |y|
|
90
|
+
# Copy over the filter type byte on each line
|
91
|
+
# TODO: Might not be necessary for all filter types
|
92
|
+
chunk[:data] << decompressed.slice!(0,1)
|
93
|
+
(1..@width).each do |x|
|
94
|
+
# BGRA => RGBA
|
95
|
+
b,g,r,a = decompressed.unpack("CCCC")
|
96
|
+
decompressed.slice!(0,4).split(//)
|
97
|
+
|
98
|
+
begin
|
99
|
+
chunk[:data] += [r,g,b,a].pack("CCCC")
|
100
|
+
decompressed = nil if decompressed.length == 0
|
101
|
+
rescue => e
|
102
|
+
puts "Left: #{decompressed.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Deflate the IDAT chunk
|
108
|
+
chunk[:type] = "IDAT"
|
109
|
+
chunk[:data] = Zlib::Deflate.deflate(chunk[:data])
|
110
|
+
chunk[:length] = [chunk[:data].length].pack("N")
|
111
|
+
chunk[:crc] = [Zlib::crc32('IDAT' + chunk[:data])].pack("N")
|
112
|
+
|
113
|
+
# store it away
|
114
|
+
png << chunk[:length]
|
115
|
+
png << chunk[:type]
|
116
|
+
png << chunk[:data]
|
117
|
+
png << chunk[:crc]
|
118
|
+
|
119
|
+
when "IEND"
|
120
|
+
raise "Data after IEND" unless data.empty?
|
121
|
+
png << [chunk[:length]].pack("N")
|
122
|
+
png << chunk[:type]
|
123
|
+
png << chunk[:data]
|
124
|
+
png << chunk[:crc]
|
125
|
+
# do stuff
|
126
|
+
#puts "#{chunk[:length]} : #{chunk[:type]} : #{chunk[:crc].inspect}"
|
127
|
+
break
|
128
|
+
else
|
129
|
+
# For any chunk we don't modify, copy it through.
|
130
|
+
png << [chunk[:length]].pack("N")
|
131
|
+
png << chunk[:type]
|
132
|
+
png << chunk[:data]
|
133
|
+
png << chunk[:crc]
|
134
|
+
end
|
135
|
+
######
|
136
|
+
#binding.pry
|
137
|
+
end
|
138
|
+
|
139
|
+
@png = png
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_png_file(file)
|
143
|
+
File.open(file, 'w') { |f| f.write(self.png)}
|
144
|
+
end
|
145
|
+
|
146
|
+
def cgbi
|
147
|
+
return @cgbi
|
148
|
+
|
149
|
+
#TODO: Convert from png, set local var, return
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Idb
|
2
|
+
class AbstractDevice
|
3
|
+
attr_accessor :apps_dir
|
4
|
+
attr_accessor :ops
|
5
|
+
|
6
|
+
|
7
|
+
def get_app_uuids
|
8
|
+
if not @ops.file_exists? @apps_dir
|
9
|
+
puts "Application directory #{@apps_dir} not found."
|
10
|
+
raise "Application directory #{@apps_dir} not found."
|
11
|
+
end
|
12
|
+
|
13
|
+
puts '[*] Retrieving list of applications...'
|
14
|
+
|
15
|
+
dirs = @ops.list_dir "#{@apps_dir}"
|
16
|
+
dirs.select! { |x| x != "." and x != ".." }
|
17
|
+
|
18
|
+
if dirs.length == 0
|
19
|
+
puts "No applications found in #{@apps_dir}."
|
20
|
+
raise "No applications found in #{@apps_dir}."
|
21
|
+
end
|
22
|
+
return dirs
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/lib/app.rb
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
require_relative 'plist_util'
|
2
|
+
require_relative 'app_binary'
|
3
|
+
require_relative 'CgBI'
|
4
|
+
|
5
|
+
module Idb
|
6
|
+
class App
|
7
|
+
attr_accessor :uuid, :app_dir, :binary, :cache_dir
|
8
|
+
|
9
|
+
|
10
|
+
def initialize uuid
|
11
|
+
@uuid = uuid
|
12
|
+
@app_dir = "#{$device.apps_dir}/#{@uuid}"
|
13
|
+
@cache_dir = "#{$tmp_path}/#{uuid}"
|
14
|
+
FileUtils.mkdir_p @cache_dir unless Dir.exist? @cache_dir
|
15
|
+
parse_info_plist
|
16
|
+
end
|
17
|
+
|
18
|
+
def analyze
|
19
|
+
local_binary_path = cache_file binary_path
|
20
|
+
@binary = AppBinary.new local_binary_path
|
21
|
+
if @binary.is_encrypted?
|
22
|
+
$log.info "Binary is encrypted. Decrypting for further analysis."
|
23
|
+
decrypt_binary!
|
24
|
+
else
|
25
|
+
$log.info "Binary is not encrypted."
|
26
|
+
@local_decrypted_binary = local_binary_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def strings
|
31
|
+
data = `strings '#{@local_decrypted_binary}'`
|
32
|
+
end
|
33
|
+
|
34
|
+
def decrypt_binary!
|
35
|
+
unless $device.dumpdecrypted_installed?
|
36
|
+
$log.error "dumpdecrypted not installed."
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
dylib = "dumpdecrypted_#{$device.arch}.dylib"
|
41
|
+
|
42
|
+
$log.info "Running '#{binary_path}'"
|
43
|
+
full_remote_path = binary_path
|
44
|
+
decrypted_path = "/var/root/#{File.basename full_remote_path}.decrypted"
|
45
|
+
|
46
|
+
$device.ops.execute "cd /var/root/"
|
47
|
+
$device.ops.execute "DYLD_INSERT_LIBRARIES=dumpdecrypted_armv7.dylib \"#{full_remote_path}\""
|
48
|
+
$log.info "Checking if decrypted file #{decrypted_path} was created..."
|
49
|
+
if not $device.ops.file_exists? decrypted_path
|
50
|
+
$log.error "Decryption failed. Trying armv6 build for iOS 6 and earlier..."
|
51
|
+
$device.ops.execute "DYLD_INSERT_LIBRARIES=dumpdecrypted_armv6.dylib \"#{full_remote_path}\""
|
52
|
+
$log.info "Checking if decrypted file #{decrypted_path} was created..."
|
53
|
+
end
|
54
|
+
|
55
|
+
if not $device.ops.file_exists? decrypted_path
|
56
|
+
$log.error "Decryption failed. File may not be encrypted."
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
$log.info "Decrypted file found. Downloading..."
|
61
|
+
|
62
|
+
@local_decrypted_binary = "#{cache_dir}/#{File.basename full_remote_path}.decrypted"
|
63
|
+
@binary.setDecryptedPath @local_decrypted_binary
|
64
|
+
|
65
|
+
local_path = $device.ops.download decrypted_path, @local_decrypted_binary
|
66
|
+
|
67
|
+
$log.info "Decrypted binary downloaded to #{@local_decrypted_binary}"
|
68
|
+
@local_decrypted_binary
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def decrypt_binary_clutch!
|
73
|
+
# not in use since Stefan Esser updated dumpdecrypted
|
74
|
+
unless $device.clutch_installed?
|
75
|
+
$log.error "clutch not installed."
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
$log.info "Binary name is: '#{binary_name}'"
|
80
|
+
|
81
|
+
$device.ops.execute "cd /var/root/"
|
82
|
+
output = $device.ops.execute "#{$device.clutch_path} #{binary_name}"
|
83
|
+
puts output
|
84
|
+
|
85
|
+
decrypted_path = "/var/root/Documents/Cracked/#{binary_name}*.ipa"
|
86
|
+
$log.info "Checking if decrypted file #{decrypted_path} was created..."
|
87
|
+
if not $device.ops.file_exists? decrypted_path
|
88
|
+
$log.error "Decryption / Cracking failed."
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
$log.info "Decrypted file found. Downloading..."
|
93
|
+
|
94
|
+
@local_decrypted_binary = "#{cache_dir}/#{File.basename full_remote_path}.decrypted"
|
95
|
+
@binary.setDecryptedPath @local_decrypted_binary
|
96
|
+
|
97
|
+
local_path = $device.ops.download decrypted_path, @local_decrypted_binary
|
98
|
+
|
99
|
+
$log.info "Decrypted binary downloaded to #{@local_decrypted_binary}"
|
100
|
+
@local_decrypted_binary
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
def get_raw_plist_value val
|
108
|
+
begin
|
109
|
+
@info_plist.plist_data[val]
|
110
|
+
rescue
|
111
|
+
"[error]"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def icon_path
|
117
|
+
icon_name = get_raw_plist_value('CFBundleIconFile')
|
118
|
+
|
119
|
+
unless icon_name
|
120
|
+
# If there's no icon specified, grab the first one from the array.
|
121
|
+
# If it's large, this could make the app nonresponsive.
|
122
|
+
icon_name = get_raw_plist_value('CFBundleIconFiles').first
|
123
|
+
# NOTE: This still fails to find the icon for some apps that store it in odd places,
|
124
|
+
# Like Netflix' plist_data['CFBundleIcons~ipad']['CFBundlePrimaryIcon']['CFBundleIconFiles']
|
125
|
+
end
|
126
|
+
|
127
|
+
app_dir = Shellwords.escape(@app_dir)
|
128
|
+
|
129
|
+
unless (icon_name[-4,4] == ".png")
|
130
|
+
$log.debug "Appending extension to #{icon_name}"
|
131
|
+
icon_name += ".png"
|
132
|
+
$log.debug "Now: #{icon_name}"
|
133
|
+
end
|
134
|
+
|
135
|
+
icon_file = $device.ops.execute("ls #{app_dir}/*app/#{icon_name}").strip
|
136
|
+
|
137
|
+
if not $device.ops.file_exists? icon_file
|
138
|
+
$log.warn "Icon not found: #{icon_file}"
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
$log.info "Icon found at #{icon_file}"
|
142
|
+
return icon_file
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_icon_file
|
146
|
+
path = icon_path
|
147
|
+
|
148
|
+
unless path.nil?
|
149
|
+
local_path = cache_file path
|
150
|
+
new_local_path = "#{local_path}.png"
|
151
|
+
CGBI.from_file(local_path).to_png_file(new_local_path)
|
152
|
+
new_local_path
|
153
|
+
else
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def bundle_name
|
159
|
+
get_raw_plist_value 'CFBundleDisplayName'
|
160
|
+
end
|
161
|
+
|
162
|
+
def platform_version
|
163
|
+
get_raw_plist_value 'DTPlatformVersion'
|
164
|
+
end
|
165
|
+
|
166
|
+
def sdk_version
|
167
|
+
get_raw_plist_value 'DTSDKName'
|
168
|
+
end
|
169
|
+
|
170
|
+
def minimum_os_version
|
171
|
+
get_raw_plist_value 'MinimumOSVersion'
|
172
|
+
end
|
173
|
+
|
174
|
+
def bundle_id
|
175
|
+
begin
|
176
|
+
@info_plist.bundle_identifier
|
177
|
+
rescue
|
178
|
+
"[error]"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def launch
|
183
|
+
$device.app_launch self
|
184
|
+
end
|
185
|
+
|
186
|
+
def binary_path
|
187
|
+
$log.info "Locating application binary..."
|
188
|
+
dirs = $device.ops.dir_glob("#{@app_dir}/","**")
|
189
|
+
dirs.select! { |f|
|
190
|
+
$device.ops.file_exists? "#{f}/#{binary_name}"
|
191
|
+
}
|
192
|
+
|
193
|
+
"#{dirs.first}/#{binary_name}"
|
194
|
+
end
|
195
|
+
|
196
|
+
def binary_name
|
197
|
+
begin
|
198
|
+
@info_plist.binary_name
|
199
|
+
rescue
|
200
|
+
"[error]"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def sync_app_dir
|
205
|
+
`#{rsync} avc -e ssh TKTK #{} `
|
206
|
+
end
|
207
|
+
|
208
|
+
def find_plist_files
|
209
|
+
$log.info "Looking for plist files..."
|
210
|
+
$device.ops.dir_glob(@app_dir, "**/*plist")
|
211
|
+
end
|
212
|
+
|
213
|
+
def find_sqlite_dbs
|
214
|
+
$log.info "Looking for sqlite files..."
|
215
|
+
$device.ops.dir_glob(@app_dir, "**/*sql**")
|
216
|
+
end
|
217
|
+
|
218
|
+
def find_cache_dbs
|
219
|
+
$log.info "Looking for Cache.db files..."
|
220
|
+
$device.ops.dir_glob(@app_dir, "**/Cache.db")
|
221
|
+
end
|
222
|
+
|
223
|
+
def get_url_handlers
|
224
|
+
@info_plist.schemas
|
225
|
+
end
|
226
|
+
|
227
|
+
def cache_dir
|
228
|
+
"#{$tmp_path}/#{@uuid}/"
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
def cache_file f
|
233
|
+
relative_file = f.sub(@app_dir,'')
|
234
|
+
relative_dir = File.dirname relative_file
|
235
|
+
cache_dir = "#{$tmp_path}/#{@uuid}/#{relative_dir}"
|
236
|
+
FileUtils.mkdir_p(cache_dir) unless Dir.exist?(cache_dir)
|
237
|
+
cached_file_path = "#{cache_dir}/#{File.basename(f)}"
|
238
|
+
|
239
|
+
if $device.ops.download f, cached_file_path
|
240
|
+
return cached_file_path
|
241
|
+
else
|
242
|
+
return nil
|
243
|
+
end
|
244
|
+
end
|
245
|
+
private
|
246
|
+
|
247
|
+
def parse_info_plist
|
248
|
+
begin
|
249
|
+
plist_file = cache_file(info_plist_path)
|
250
|
+
rescue Exception => ex
|
251
|
+
$log.error "Error getting plist file #{info_plist_path}."
|
252
|
+
$log.debug "Exception Details: #{ex.message}."
|
253
|
+
$log.debug "Backtrace: #{ex.backtrace.join("\n")}."
|
254
|
+
return
|
255
|
+
end
|
256
|
+
|
257
|
+
begin
|
258
|
+
@info_plist = PlistUtil.new plist_file
|
259
|
+
@info_plist.parse_info_plist
|
260
|
+
rescue Exception => ex
|
261
|
+
$log.error "Error parsing plist file #{plist_file}."
|
262
|
+
$log.debug "Exception Details: #{ex.message}."
|
263
|
+
$log.debug "Backtrace: #{ex.backtrace.join("\n")}."
|
264
|
+
return
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
def info_plist_path
|
272
|
+
app_dir = Shellwords.escape(@app_dir)
|
273
|
+
plist_file = $device.ops.execute("ls #{app_dir}/*app/Info.plist").strip
|
274
|
+
|
275
|
+
# the following works but is terribly slow.
|
276
|
+
#plist_file = (@if.ops.dir_glob "#{@app_dir}/","*app/Info.plist").first
|
277
|
+
|
278
|
+
if not $device.ops.file_exists? plist_file
|
279
|
+
$log.error "Info.plist not found."
|
280
|
+
return nil
|
281
|
+
end
|
282
|
+
$log.info "Info.plist found at #{plist_file}"
|
283
|
+
return plist_file
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|