idb 1.3.1
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 +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
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'net/ssh'
|
|
2
|
+
require 'log4r'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module Idb
|
|
6
|
+
class SSHPortForwarder
|
|
7
|
+
|
|
8
|
+
def initialize username, password, hostname, port
|
|
9
|
+
# initialize log
|
|
10
|
+
@log = Log4r::Logger.new 'port_forward'
|
|
11
|
+
outputter = Log4r::Outputter.stdout
|
|
12
|
+
outputter.formatter = Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %c :: %m")
|
|
13
|
+
|
|
14
|
+
@log.outputters = [ outputter ]
|
|
15
|
+
|
|
16
|
+
@log.info 'Establishing SSH port forwarding...'
|
|
17
|
+
@ssh = Net::SSH.start hostname, username, :password => password, :port => port
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_local_forward local_port, remote_host, remote_port
|
|
21
|
+
@log.info " - Forwarding local:#{local_port} -> #{remote_host}:#{remote_port}"
|
|
22
|
+
@ssh.forward.local local_port, remote_host, remote_port
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_remote_forward remote_port, local_host, local_port
|
|
26
|
+
@log.info " - Forwarding remote:#{remote_port} -> #{local_host}:#{local_port}"
|
|
27
|
+
@ssh.forward.remote_to local_port, local_host, remote_port
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def start
|
|
31
|
+
@ssh.loop {
|
|
32
|
+
true
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def stop
|
|
37
|
+
$log.info "Closing SSH connection."
|
|
38
|
+
@ssh.close
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/lib/tools.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# source: https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
|
2
|
+
def which(cmd)
|
|
3
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
|
4
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
|
5
|
+
exts.each { |ext|
|
|
6
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
|
7
|
+
return exe if File.executable? exe
|
|
8
|
+
}
|
|
9
|
+
end
|
|
10
|
+
return nil
|
|
11
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
|
|
3
|
+
module Idb
|
|
4
|
+
class URLSchemeFuzzer
|
|
5
|
+
def initialize
|
|
6
|
+
@crash_report_folder = "/var/mobile/Library/Logs/CrashReporter"
|
|
7
|
+
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def default_fuzz_strings
|
|
12
|
+
fuzz_inputs = [
|
|
13
|
+
"A" * 10,
|
|
14
|
+
"A" * 101,
|
|
15
|
+
"A" * 1001,
|
|
16
|
+
"\x0",
|
|
17
|
+
"'",
|
|
18
|
+
"%",
|
|
19
|
+
"%n",
|
|
20
|
+
"%@" * 20,
|
|
21
|
+
"%n%d" * 20,
|
|
22
|
+
"%s%p%x%d",
|
|
23
|
+
"%x%x%x%x",
|
|
24
|
+
"%#0123456x%08x%x%s%p%d%n%o%u%c%h%l%q%j%z%Z%t%i%e%g%f%a%C%S%08x%%",
|
|
25
|
+
# "100",
|
|
26
|
+
# "1000",
|
|
27
|
+
# "3fffffff",
|
|
28
|
+
# "7ffffffe",
|
|
29
|
+
# "7fffffff",
|
|
30
|
+
# "80000000",
|
|
31
|
+
# "fffffffe",
|
|
32
|
+
# "ffffffff",
|
|
33
|
+
# "10000",
|
|
34
|
+
# "100000",
|
|
35
|
+
"0",
|
|
36
|
+
"-1",
|
|
37
|
+
"1",
|
|
38
|
+
]
|
|
39
|
+
fuzz_inputs
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def delete_old_reports
|
|
43
|
+
#remove old crash reports
|
|
44
|
+
crashes = $device.ops.dir_glob @crash_report_folder, "*"
|
|
45
|
+
crashes.each { |x|
|
|
46
|
+
if x.include? $selected_app.binary_name
|
|
47
|
+
$log.info "Deleting old log #{x}"
|
|
48
|
+
$device.ops.execute ("rm -f '#{x}' ")
|
|
49
|
+
end
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def generate_inputs url, fuzz_inputs
|
|
56
|
+
inputs = Array.new
|
|
57
|
+
|
|
58
|
+
# count fuzz locations
|
|
59
|
+
locs = url.scan(/\$@\$/)
|
|
60
|
+
|
|
61
|
+
# generate input combinations
|
|
62
|
+
combs = fuzz_inputs.combination(locs.size).to_a
|
|
63
|
+
|
|
64
|
+
# generate test instance for each combination
|
|
65
|
+
for c in combs do
|
|
66
|
+
inputs << url.dup.gsub!(/\$@\$/) { |x|
|
|
67
|
+
x = URI::encode(c.pop) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return inputs
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def execute url
|
|
74
|
+
$log.info "Fuzzing: #{url}"
|
|
75
|
+
$device.open_url url
|
|
76
|
+
sleep 2
|
|
77
|
+
|
|
78
|
+
$log.info "Killing processes names #{$selected_app.binary_name}"
|
|
79
|
+
$device.kill_by_name $selected_app.binary_name
|
|
80
|
+
|
|
81
|
+
crashed?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def crashed?
|
|
85
|
+
crashes = $device.ops.dir_glob @crash_report_folder, "*"
|
|
86
|
+
crashed = false
|
|
87
|
+
crashes.each { |x|
|
|
88
|
+
if x.include? $selected_app.binary_name
|
|
89
|
+
crashed = true
|
|
90
|
+
end
|
|
91
|
+
}
|
|
92
|
+
crashed
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'awesome_print'
|
|
3
|
+
|
|
4
|
+
module Idb
|
|
5
|
+
class USBMuxdWrapper
|
|
6
|
+
def initialize
|
|
7
|
+
@proxy_pids = Array.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def find_available_port
|
|
11
|
+
x = TCPServer.new("127.0.0.1",0)
|
|
12
|
+
@port= x.addr[1]
|
|
13
|
+
x.close
|
|
14
|
+
@port
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def proxy local_port, remote_port
|
|
18
|
+
$log.info "Launching SSH proxy on port #{local_port}"
|
|
19
|
+
@proxy_pids << Process.spawn("iproxy #{local_port} #{remote_port}")
|
|
20
|
+
@proxy_pids.last
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def stop_all
|
|
24
|
+
@proxy_pids.each { |pid|
|
|
25
|
+
$log.info "Terminating proxy with pid #{pid}"
|
|
26
|
+
Process.kill("INT", pid)
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Idb
|
|
2
|
+
class WeakClassDumpWrapper
|
|
3
|
+
|
|
4
|
+
def initialize local_header_dir
|
|
5
|
+
|
|
6
|
+
@remote_header_dir_base = "/tmp/weak_class_dump_"
|
|
7
|
+
@remote_header_dir = @remote_header_dir_base + $selected_app.uuid
|
|
8
|
+
|
|
9
|
+
@local_header_dir = local_header_dir
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute_cycript
|
|
14
|
+
unless $device.cycript_installed?
|
|
15
|
+
$log.error "Cycript not found, aborting."
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# ensure cycript script is installed.
|
|
20
|
+
# originally from: https://github.com/limneos/weak_classdump
|
|
21
|
+
|
|
22
|
+
wc_file = "/var/root/weak_classdump.cy"
|
|
23
|
+
unless $device.ops.file_exists? wc_file
|
|
24
|
+
$log.info "weak_classdump not found, Installing onto device."
|
|
25
|
+
$device.ops.upload("utils/weak_class_dump/weak_classdump.cy", wc_file)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
local_instructions_file = "#{$tmp_path}/weak_classdump_instructions.cy"
|
|
29
|
+
remote_instructions_file = "/var/root/weak_classdump_instructions.cy"
|
|
30
|
+
File.open(local_instructions_file,"w") { |x|
|
|
31
|
+
x.puts("weak_classdump_bundle([NSBundle mainBundle],\"#{@remote_header_dir}\")")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$device.ops.upload local_instructions_file, remote_instructions_file
|
|
35
|
+
|
|
36
|
+
$log.info "Launching app..."
|
|
37
|
+
$selected_app.launch
|
|
38
|
+
|
|
39
|
+
cmd = "cycript -p '#{$selected_app.binary_name}' #{wc_file}"
|
|
40
|
+
$log.info "Injecting: #{cmd}"
|
|
41
|
+
$device.ops.execute cmd
|
|
42
|
+
|
|
43
|
+
$log.info "Running cycript using weak_classdump."
|
|
44
|
+
cmd = "cycript -p '#{$selected_app.binary_name}' #{remote_instructions_file}"
|
|
45
|
+
$log.info "Running: #{cmd}"
|
|
46
|
+
$device.ops.execute cmd
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_header_files
|
|
51
|
+
Dir.entries(@local_header_dir).reject{|entry| entry == "." || entry == ".."}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def pull_header_files
|
|
55
|
+
$log.info "Downloading header files from #{@remote_header_dir} to #{@local_header_dir}"
|
|
56
|
+
$device.ops.download_recursive(@remote_header_dir, @local_header_dir)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
|
|
2
|
+
function commonTypes(type){
|
|
3
|
+
|
|
4
|
+
isPointer=NO;
|
|
5
|
+
if ([type containsSubstring:@"^"]){
|
|
6
|
+
isPointer=YES;
|
|
7
|
+
type=[type stringByReplacingOccurrencesOfString:@"^" withString:""];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
switch (type.toString()){
|
|
11
|
+
|
|
12
|
+
case "d": type = "double"; break;
|
|
13
|
+
case "i": type = "int"; break;
|
|
14
|
+
case "f": type = "float"; break;
|
|
15
|
+
case "c": type = "BOOL"; break;
|
|
16
|
+
case "s": type = "short"; break;
|
|
17
|
+
case "I": type = "unsigned"; break;
|
|
18
|
+
case "l": type = "long"; break;
|
|
19
|
+
case "q": type = "long long"; break;
|
|
20
|
+
case "L": type = "unsigned long"; break;
|
|
21
|
+
case "C": type = "unsigned char"; break;
|
|
22
|
+
case "S": type = "unsigned short"; break;
|
|
23
|
+
case "Q": type = "unsigned long long"; break;
|
|
24
|
+
case "B": type = "_Bool"; break;
|
|
25
|
+
case "v": type = "void"; break;
|
|
26
|
+
case "*": type = "char*"; break;
|
|
27
|
+
case ":": type = "SEL"; break;
|
|
28
|
+
case "#": type = "Class"; break;
|
|
29
|
+
case "@": type = "id"; break;
|
|
30
|
+
case "@?": type = "id"; break;
|
|
31
|
+
case "Vv": type = "void"; break;
|
|
32
|
+
case "rv": type = "const void*"; break;
|
|
33
|
+
default: type = type;
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return isPointer ? type.toString()+"*" : type.toString();
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getProtocolLines(protocol){
|
|
42
|
+
|
|
43
|
+
var protocolsMethodsString="";
|
|
44
|
+
currentProtocol=protocol;
|
|
45
|
+
|
|
46
|
+
protocolName=protocol_getName(currentProtocol);
|
|
47
|
+
protocolsMethodsString=protocolsMethodsString.toString()+"\n@protocol "+protocolName.toString()+"\n";
|
|
48
|
+
|
|
49
|
+
protPropertiesString="";
|
|
50
|
+
protPropertiesCount=new int;
|
|
51
|
+
protPropertyList=protocol_copyPropertyList(currentProtocol,protPropertiesCount);
|
|
52
|
+
for (xi=0; xi<*protPropertiesCount; xi++){
|
|
53
|
+
|
|
54
|
+
propname=property_getName(protPropertyList[xi]);
|
|
55
|
+
attrs=property_getAttributes(protPropertyList[xi]);
|
|
56
|
+
newString=propertyLineGenerator(attrs,propname).toString();
|
|
57
|
+
if (![protPropertiesString containsSubstring:newString]){
|
|
58
|
+
protPropertiesString=protPropertiesString.toString()+newString.toString();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
protocolsMethodsString=protocolsMethodsString.toString()+protPropertiesString;
|
|
62
|
+
free(protPropertyList);
|
|
63
|
+
|
|
64
|
+
for (acase=0; acase<5; acase++){
|
|
65
|
+
|
|
66
|
+
protocolMethodsCount=new int;
|
|
67
|
+
isRequiredMethod=acase<2 ? NO : YES;
|
|
68
|
+
isInstanceMethod=(acase==0 || acase==2) ? NO : YES;
|
|
69
|
+
|
|
70
|
+
protMeths=protocol_copyMethodDescriptionList(currentProtocol, isRequiredMethod, isInstanceMethod, protocolMethodsCount);
|
|
71
|
+
for (gg=0; gg<*protocolMethodsCount; gg++){
|
|
72
|
+
if (acase<2 && ![[NSString stringWithString:protocolsMethodsString] containsSubstring:@"@optional"]){
|
|
73
|
+
protocolsMethodsString=protocolsMethodsString.toString()+"@optional\n";
|
|
74
|
+
}
|
|
75
|
+
if (acase>1 && ![[NSString stringWithString:protocolsMethodsString] containsSubstring:@"@required"]){
|
|
76
|
+
protocolsMethodsString=protocolsMethodsString.toString()+"@required\n";
|
|
77
|
+
}
|
|
78
|
+
startSign=isInstanceMethod==NO ? "+" : "-";
|
|
79
|
+
protSelector=protMeths[gg][0].toString();
|
|
80
|
+
protTypes=protMeths[gg][1];
|
|
81
|
+
protTypes=[protTypes stringByRemovingCharactersFromSet: [NSCharacterSet decimalDigitCharacterSet ]];
|
|
82
|
+
protTypes=[[NSString stringWithString:protTypes] stringByReplacingOccurrencesOfString:@"@:" withString:""];
|
|
83
|
+
returnType=[protTypes substringToIndex: 1];
|
|
84
|
+
returnType=commonTypes(returnType);
|
|
85
|
+
finString="";
|
|
86
|
+
if ([protTypes length]>1){
|
|
87
|
+
selectorsArray=[[NSString stringWithString:protSelector] componentsSeparatedByString:@":"];
|
|
88
|
+
typesArray=[NSMutableArray array];
|
|
89
|
+
for (typ=0; typ<[selectorsArray count]-1; typ++){
|
|
90
|
+
newobject=[NSString stringWithString:commonTypes([protTypes substringWithRange: [typ+1,1]])];
|
|
91
|
+
[typesArray addObject:newobject];
|
|
92
|
+
}
|
|
93
|
+
for (ad=0;ad<[typesArray count]; ad++){
|
|
94
|
+
argCount=ad+1;
|
|
95
|
+
finString=finString.toString()+selectorsArray[ad].toString()+"("+typesArray[ad].toString()+")"+":arg"+argCount.toString()+" ";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else{
|
|
99
|
+
finString=protSelector.toString();
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
finString=finString.toString()+";";
|
|
104
|
+
protocolsMethodsString=protocolsMethodsString.toString()+startSign.toString()+"("+returnType.toString()+")"+finString.toString()+"\n";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return protocolsMethodsString.toString()+"@end\n";
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function constructTypeAndName(aType,IvarName,isIvar){
|
|
112
|
+
|
|
113
|
+
NSNotFound=2147483647;
|
|
114
|
+
|
|
115
|
+
compareString1=[NSString stringWithString:aType];
|
|
116
|
+
compareString2=[[[NSString stringWithString:aType] stringByReplacingOccurrencesOfString:"^" withString:""] stringByAppendingString:@"*"];
|
|
117
|
+
|
|
118
|
+
if (![[NSString stringWithString:commonTypes(aType)] isEqual:compareString1] && ![[NSString stringWithString:commonTypes(aType)] isEqual:compareString2]){
|
|
119
|
+
|
|
120
|
+
return commonTypes(aType).toString()+" "+IvarName.toString();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
charSet=[NSCharacterSet characterSetWithCharactersInString:"@^\"{}="];
|
|
124
|
+
structCharSet=[NSCharacterSet characterSetWithCharactersInString:"?:{}="];
|
|
125
|
+
|
|
126
|
+
if ([aType rangeOfString:"]"].location!=NSNotFound && [aType rangeOfString:"^{"].location==NSNotFound){
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
aType=[aType stringByRemovingCharactersFromSet: [NSCharacterSet punctuationCharacterSet ]];
|
|
132
|
+
arrayCount=[[aType copy] stringByRemovingCharactersFromSet: [NSCharacterSet letterCharacterSet ]];
|
|
133
|
+
arrayType=[aType stringByRemovingCharactersFromSet: [NSCharacterSet decimalDigitCharacterSet ]];
|
|
134
|
+
return commonTypes(arrayType).toString()+"["+arrayCount.toString()+"]"+" "+IvarName.toString();
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if ([aType rangeOfString:"{?"].length>0 && isIvar){
|
|
139
|
+
|
|
140
|
+
aType=[aType stringByRemovingCharactersFromSet:structCharSet];
|
|
141
|
+
structValues=[aType componentsSeparatedByString:@"\""];
|
|
142
|
+
structValues =[NSMutableArray arrayWithArray:structValues ];
|
|
143
|
+
firstEntry=[structValues removeObjectAtIndex:0];
|
|
144
|
+
[structValues removeObject:firstEntry];
|
|
145
|
+
newString=[NSString stringWithString:"struct {\n"];
|
|
146
|
+
namesArray=[NSMutableArray array];
|
|
147
|
+
typesArray=[NSMutableArray array];
|
|
148
|
+
|
|
149
|
+
for (d=0; d<[structValues count] ; d++){
|
|
150
|
+
|
|
151
|
+
if ((d % 2)==0){
|
|
152
|
+
[namesArray addObject:structValues[d]];
|
|
153
|
+
}
|
|
154
|
+
else{
|
|
155
|
+
[typesArray addObject:structValues[d]];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (e=0; e<[typesArray count]; e++){
|
|
160
|
+
newString=newString.toString()+"\t\t"+constructTypeAndName(typesArray[e],namesArray[e],0).toString()+";\n";
|
|
161
|
+
}
|
|
162
|
+
return newString.toString()+"\t} "+IvarName.toString();
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if ([aType rangeOfString:"{"].length>0){
|
|
167
|
+
|
|
168
|
+
returnValue="struct ";
|
|
169
|
+
range=[aType rangeOfString:@"="];
|
|
170
|
+
|
|
171
|
+
if ([aType containsSubstring:@"^{?="]){
|
|
172
|
+
|
|
173
|
+
aStruct=aType;
|
|
174
|
+
structString="";
|
|
175
|
+
someType="";
|
|
176
|
+
aStruct=[NSString stringWithString:aStruct];
|
|
177
|
+
aStruct=[aStruct stringByReplacingOccurrencesOfString:@"^{?=" withString:""];
|
|
178
|
+
aStruct=[aStruct stringByReplacingOccurrencesOfString:@"}" withString:""];
|
|
179
|
+
|
|
180
|
+
for (var f=0; f<[aStruct length]; f++){
|
|
181
|
+
currentLetter=[aStruct substringWithRange:[f,1]];
|
|
182
|
+
someType=constructTypeAndName(currentLetter,"",0);
|
|
183
|
+
someType=[someType stringByRemovingWhitespace];
|
|
184
|
+
structString=structString.toString()+"\t"+someType.toString()+" value"+(f+1).toString()+";\n";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
structName="WCStruct_"+aStruct.toString();
|
|
188
|
+
|
|
189
|
+
if (![structsString containsSubstring:structName]){
|
|
190
|
+
structString="typedef struct{\n"+structString.toString();
|
|
191
|
+
structString=structString.toString()+"} "+structName.toString()+";\n\n";
|
|
192
|
+
structsString=structsString.toString()+structString.toString();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
structName=structName.toString()+"*";
|
|
196
|
+
|
|
197
|
+
return structName+" "+IvarName.toString();
|
|
198
|
+
}
|
|
199
|
+
aType=[aType stringByReplacingCharactersInRange:[range.location,aType.length-range.location] withString:"" ];
|
|
200
|
+
returnValue=returnValue.toString()+[aType stringByRemovingCharactersFromSet:structCharSet].toString();
|
|
201
|
+
if ([returnValue containsSubstring:@"GSEvent"] || [returnValue containsSubstring:@"CTCall"]){
|
|
202
|
+
returnValue=[returnValue stringByReplacingOccurrencesOfString:"__" withString:""];
|
|
203
|
+
returnValue=[returnValue stringByReplacingOccurrencesOfString:"struct " withString:""];
|
|
204
|
+
returnValue=[returnValue stringByReplacingOccurrencesOfString:"^" withString:""];
|
|
205
|
+
returnValue=[returnValue stringByAppendingString:@"Ref"];
|
|
206
|
+
|
|
207
|
+
}
|
|
208
|
+
if ([returnValue containsSubstring:@"NSZone"]){
|
|
209
|
+
returnValue="NSZone*";
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if ([returnValue containsSubstring:@"CGPoint"] || [returnValue containsSubstring:@"CGRect"] || [returnValue containsSubstring:@"CGSize"] ){
|
|
213
|
+
returnValue=[returnValue stringByReplacingOccurrencesOfString:@"struct " withString:@""];
|
|
214
|
+
}
|
|
215
|
+
return commonTypes(returnValue).toString()+" "+IvarName.toString();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if ([aType rangeOfString:@"^"].length>0){
|
|
220
|
+
|
|
221
|
+
range=[aType rangeOfString:"^" options: NULL range: [2,aType.length-2]];
|
|
222
|
+
if (range.length>0){
|
|
223
|
+
aType=[aType stringByReplacingCharactersInRange:[range.location-1,aType.length-range.location+1] withString:"" ];
|
|
224
|
+
}
|
|
225
|
+
aType=[aType stringByRemovingCharactersFromSet:charSet];
|
|
226
|
+
//aType=[aType stringByReplacingOccurrencesOfString:@"__" withString:""];
|
|
227
|
+
return aType.toString()+"* "+IvarName.toString();
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if ([aType rangeOfString:@"@\""].length>0){
|
|
234
|
+
if ([aType rangeOfString:"<"].location==2){
|
|
235
|
+
aType="id"+aType.toString();
|
|
236
|
+
return [aType stringByRemovingCharactersFromSet:charSet].toString()+" "+IvarName.toString();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
strippedString=[aType stringByRemovingCharactersFromSet:charSet];
|
|
240
|
+
return strippedString.toString()+ "* "+IvarName.toString();
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if ([aType rangeOfString:@"b"].length>0 && [aType rangeOfString:":{"].length<1){
|
|
245
|
+
string=[aType stringByReplacingOccurrencesOfString:@"b" withString:""];
|
|
246
|
+
return "unsigned int "+IvarName.toString()+":"+string.toString();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return aType.toString() + " " + IvarName.toString();
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
function propertyLineGenerator(attributes,name){
|
|
256
|
+
|
|
257
|
+
parSet=[NSCharacterSet characterSetWithCharactersInString:@"()"];
|
|
258
|
+
attributes=[attributes stringByRemovingCharactersFromSet:parSet];
|
|
259
|
+
attrArr=[attributes componentsSeparatedByString:@","];
|
|
260
|
+
|
|
261
|
+
type=attrArr[0];
|
|
262
|
+
type=[type stringByReplacingCharactersInRange:[0,1] withString:""];
|
|
263
|
+
type=constructTypeAndName(type,"",0);
|
|
264
|
+
type=[type stringByRemovingWhitespace];
|
|
265
|
+
attrArr=[NSMutableArray arrayWithArray:attrArr];
|
|
266
|
+
[attrArr removeObjectAtIndex:0];
|
|
267
|
+
|
|
268
|
+
propertyString="@property ";
|
|
269
|
+
|
|
270
|
+
newPropsArray=[NSMutableArray array];
|
|
271
|
+
synthesize=[NSString stringWithString:""];
|
|
272
|
+
for each (attr in attrArr){
|
|
273
|
+
|
|
274
|
+
vToClear=nil;
|
|
275
|
+
|
|
276
|
+
if ([attr rangeOfString:@"V_"].location==0){
|
|
277
|
+
vToClear=attr;
|
|
278
|
+
attr=[attr stringByReplacingCharactersInRange:[0,2] withString:""];
|
|
279
|
+
synthesize="\t\t\t\t//@synthesize "+attr.toString()+"=_"+attr.toString()+" - In the implementation block";
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if ([attr length]==1){
|
|
283
|
+
|
|
284
|
+
switch (attr.toString()){
|
|
285
|
+
case "R" : translatedProperty = "readonly";
|
|
286
|
+
case "C" : translatedProperty = "copy"; break;
|
|
287
|
+
case "&" : translatedProperty = "retain"; break;
|
|
288
|
+
case "N" : translatedProperty = "nonatomic"; break;
|
|
289
|
+
case "D" : translatedProperty = "@dynamic"; break;
|
|
290
|
+
case "W" : translatedProperty = "__weak"; break;
|
|
291
|
+
case "P" : translatedProperty = "t<encoding>"; break;
|
|
292
|
+
default: translatedProperty = attr;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
[newPropsArray addObject:translatedProperty];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if ([attr rangeOfString:@"G"].location==0){
|
|
299
|
+
attr=[attr stringByReplacingCharactersInRange:[0,1] withString:""];
|
|
300
|
+
attr="getter="+attr.toString();
|
|
301
|
+
[newPropsArray addObject:attr];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if ([attr rangeOfString:@"S"].location==0){
|
|
305
|
+
attr=[attr stringByReplacingCharactersInRange:[0,1] withString:""];
|
|
306
|
+
attr="setter="+attr.toString();
|
|
307
|
+
[newPropsArray addObject:attr];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if ([newPropsArray containsObject:@"nonatomic"] && ![newPropsArray containsObject:@"assign"] && ![newPropsArray containsObject:@"readonly"] && ![newPropsArray containsObject:@"copy"] && ![newPropsArray containsObject:@"retain"]){
|
|
313
|
+
[newPropsArray addObject:@"assign"];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
newPropsArray=[newPropsArray reversedArray];
|
|
317
|
+
|
|
318
|
+
rebuiltString=[newPropsArray componentsJoinedByString:","];
|
|
319
|
+
attrString=newPropsArray.length>0 ? "("+rebuiltString.toString()+")" : "(assign)";
|
|
320
|
+
|
|
321
|
+
propertyString=propertyString.toString()+attrString.toString()+" "+type.toString()+" "+name.toString()+"; "+synthesize.toString()+"\n";
|
|
322
|
+
return propertyString;
|
|
323
|
+
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function methodLinesGenerator(methodList,methodsCount,isClassMethod){
|
|
327
|
+
methodLines="";
|
|
328
|
+
for (n=0; n<*methodsCount;n++){
|
|
329
|
+
method=methodList[n];
|
|
330
|
+
methodName=_method_getName(method);
|
|
331
|
+
returnType=_method_copyReturnType(method);
|
|
332
|
+
returnType=[constructTypeAndName([NSString stringWithString:returnType],[NSString stringWithString:""],0) stringByRemovingWhitespace];
|
|
333
|
+
argNum=_method_getNumberOfArguments(method);
|
|
334
|
+
methodBrokenDown=[methodName componentsSeparatedByString:@":"];
|
|
335
|
+
methodString=[NSString stringWithString:""];
|
|
336
|
+
if ([methodBrokenDown count]>1){
|
|
337
|
+
for (x=0; x<[methodBrokenDown count]-1; x++){
|
|
338
|
+
anIndex=x+2;
|
|
339
|
+
argumentType=_method_copyArgumentType(method,anIndex);
|
|
340
|
+
if (!argumentType){
|
|
341
|
+
argumentType="id";
|
|
342
|
+
}
|
|
343
|
+
typeName=constructTypeAndName([NSString stringWithString:argumentType],[NSString stringWithString:""],0);
|
|
344
|
+
typeName=[typeName stringByTrimmingLastCharacter];
|
|
345
|
+
|
|
346
|
+
methodString=methodString.toString()+methodBrokenDown[x].toString()+":("+typeName.toString()+")arg"+(x+1)+" ";
|
|
347
|
+
|
|
348
|
+
}
|
|
349
|
+
methodString=[methodString stringByTrimmingLastCharacter];
|
|
350
|
+
}
|
|
351
|
+
else{
|
|
352
|
+
methodString=methodName;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
symbol=isClassMethod ? "+" : "-"; symbol=[NSString stringWithString:symbol];
|
|
356
|
+
newMethod=symbol.toString()+"("+returnType.toString()+")"+methodString.toString()+";\n";
|
|
357
|
+
cappedMethod=[methodName capitalizedString];
|
|
358
|
+
setterMethod="set"+cappedMethod.toString();
|
|
359
|
+
if (![methodsArray containsObject:newMethod] && ![propertiesString containsSubstring:methodName] && ![methodsString containsSubstring:setterMethod]){
|
|
360
|
+
[methodsArray addObject:newMethod];
|
|
361
|
+
methodLines=methodLines.toString()+newMethod.toString();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
}
|
|
365
|
+
return methodLines;
|
|
366
|
+
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function weak_classdump(classname,alsoDumpSuperclasses,outputdir){
|
|
370
|
+
|
|
371
|
+
//NSLog(@"weak_classdump: Dumping class %@",classname);
|
|
372
|
+
if (!classname){
|
|
373
|
+
return "Cannot find class";
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (typeof(alsoDumpSuperclasses) == 'undefined' || !alsoDumpSuperclasses){
|
|
377
|
+
alsoDumpSuperclasses=0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
structsString="";
|
|
383
|
+
interfaceString="";
|
|
384
|
+
version = [NSProcessInfo processInfo ].operatingSystemVersionString;
|
|
385
|
+
loc=[NSLocale localeWithLocaleIdentifier: "en-us"];
|
|
386
|
+
date=[NSDate.date descriptionWithLocale: loc];
|
|
387
|
+
classString = "/*\n * This header is generated by weak_classdump 0.2\n * on "+date.toString()+"\n * Operating System: "+version.toString()+"\n * weak_classdump is Freeware by Elias Limneos.\n *\n */\n\n";
|
|
388
|
+
if ( [[classname description] containsSubstring:@"<Protocol:"]){
|
|
389
|
+
|
|
390
|
+
classString=classString.toString()+getProtocolLines(classname).toString();
|
|
391
|
+
|
|
392
|
+
if (typeof(outputdir) == 'undefined'){
|
|
393
|
+
outputdir = "/tmp";
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (typeof(alsoDumpSuperclasses) == 'string'){
|
|
397
|
+
outputdir=alsoDumpSuperclasses;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
outputdir=outputdir.toString()+"/";
|
|
402
|
+
|
|
403
|
+
if (![NSFileManager.defaultManager fileExistsAtPath:outputdir]){
|
|
404
|
+
try{
|
|
405
|
+
[NSFileManager.defaultManager createDirectoryAtPath:outputdir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
406
|
+
}catch(e){}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
classString = [NSString stringWithString:classString ];
|
|
410
|
+
if ([classString writeToFile:outputdir.toString()+classname.toString()+".h" atomically:YES]){
|
|
411
|
+
return "Wrote /PROTOCOL/ header file to "+outputdir.toString()+protocol_getName(classname).toString()+".h";
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
NSSearchPathForDirectoriesInDomains=new Functor(dlsym(RTLD_DEFAULT,"NSSearchPathForDirectoriesInDomains"),"@ccc");
|
|
415
|
+
writeableDir=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
416
|
+
writeableDir=writeableDir[0];
|
|
417
|
+
return "Failed to write to "+outputdir.toString()+protocol_getName(classname).toString()+".h - Check file path and permissions? Suggested writeable directory: "+writeableDir.toString();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
}
|
|
421
|
+
_class_copyIvarList = new Functor(dlsym(RTLD_DEFAULT,"class_copyIvarList"),"^^{objc_ivar=}#^I");
|
|
422
|
+
_class_copyProtocolList=new Functor(dlsym(RTLD_DEFAULT,"class_copyProtocolList"),"^@#^I");
|
|
423
|
+
_class_conformsToProtocol=new Functor(dlsym(RTLD_DEFAULT,"class_conformsToProtocol"),"B#@");
|
|
424
|
+
_class_copyMethodList=new Functor(dlsym(RTLD_DEFAULT,"class_copyMethodList"),"^^{objc_method=}#^I");
|
|
425
|
+
_class_copyPropertyList=new Functor(dlsym(RTLD_DEFAULT,"class_copyPropertyList"),"^^{objc_property=}#^I");
|
|
426
|
+
_method_getName=new Functor(dlsym(RTLD_DEFAULT,"method_getName"),"*^{objc_method=}");
|
|
427
|
+
_method_getNumberOfArguments=new Functor(dlsym(RTLD_DEFAULT,"method_getNumberOfArguments"),"I^{objc_method=}");
|
|
428
|
+
_method_copyReturnType=new Functor(dlsym(RTLD_DEFAULT,"method_copyReturnType"),"*^{objc_method=}");
|
|
429
|
+
_method_copyArgumentType=new Functor(dlsym(RTLD_DEFAULT,"method_copyArgumentType"),"*^{objc_method=}I");
|
|
430
|
+
_property_getAttributes=new Functor(dlsym(RTLD_DEFAULT,"property_getAttributes"),"*^{objc_property=}");
|
|
431
|
+
_property_getName=new Functor(dlsym(RTLD_DEFAULT,"property_getName"),"*^{objc_property=}");
|
|
432
|
+
_ivar_getName = new Functor(dlsym(RTLD_DEFAULT,"ivar_getName"),"*^?");
|
|
433
|
+
_ivar_getTypeEncoding = new Functor(dlsym(RTLD_DEFAULT,"ivar_getTypeEncoding"),"*^{objc_ivar=}");
|
|
434
|
+
_protocol_getName=new Functor(dlsym(RTLD_DEFAULT,"protocol_getName"),"*@");
|
|
435
|
+
_objc_getClassList=new Functor(dlsym(RTLD_DEFAULT,"objc_getClassList"),"i^#");
|
|
436
|
+
methodsArray=[NSMutableArray array];
|
|
437
|
+
propertiesString=@"";
|
|
438
|
+
methodsString=@"";
|
|
439
|
+
ivarsString=@"";
|
|
440
|
+
classMethodsString=@"";
|
|
441
|
+
|
|
442
|
+
startingClassname=classname;
|
|
443
|
+
superclass=classname.superclass;
|
|
444
|
+
|
|
445
|
+
protocolsCount=new int;
|
|
446
|
+
protocolArray=_class_copyProtocolList(classname,protocolsCount);
|
|
447
|
+
|
|
448
|
+
var protocolsMethodsString=classString;
|
|
449
|
+
var protocolName;
|
|
450
|
+
allProtocols="";
|
|
451
|
+
if (*protocolsCount>0){
|
|
452
|
+
for (iter=0; iter<*protocolsCount; iter++){
|
|
453
|
+
if (_class_conformsToProtocol(classname,protocolArray[iter])){
|
|
454
|
+
protocolName=protocol_getName(protocolArray[iter]);
|
|
455
|
+
protocolsMethodsString=protocolsMethodsString.toString()+getProtocolLines(protocolArray[iter]).toString();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
protocolsMethodsString = [NSString stringWithString:protocolsMethodsString ];
|
|
459
|
+
|
|
460
|
+
classString=classString.toString()+"#import <"+protocolName.toString()+".h>\n";
|
|
461
|
+
|
|
462
|
+
if (typeof(outputdir)=="undefined" || outputdir==null){
|
|
463
|
+
outputdir="/tmp";
|
|
464
|
+
}
|
|
465
|
+
outputdir=outputdir+"/";
|
|
466
|
+
allProtocols=allProtocols.toString()+", "+outputdir.toString()+protocolName.toString()+".h";
|
|
467
|
+
if ([protocolsMethodsString writeToFile:outputdir.toString()+protocolName.toString()+".h" atomically:YES]){
|
|
468
|
+
//NSLog(@"Found Protocol %@, wrote to %@%@.h",protocolName,outputdir,protocolName);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
protocolsString="";
|
|
475
|
+
if (*protocolsCount>0){
|
|
476
|
+
protocolsString=@" <".toString();
|
|
477
|
+
for (i=0; i<*protocolsCount; i++){
|
|
478
|
+
if (_class_conformsToProtocol(classname,protocolArray[i])){
|
|
479
|
+
comma=@"".toString();
|
|
480
|
+
if (i<*protocolsCount-1){
|
|
481
|
+
comma=@", ".toString();
|
|
482
|
+
}
|
|
483
|
+
protocolsString=protocolsString.toString()+_protocol_getName(protocolArray[i]).toString()+comma;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
protocolsString=protocolsString+@">".toString();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (classname.superclass!=nil && classname.superclass!="nil"){
|
|
490
|
+
interfaceString = [NSString stringWithString:@"\n@interface "+classname.toString()+" : "+classname.superclass.toString()].toString() + protocolsString.toString();
|
|
491
|
+
}
|
|
492
|
+
else{
|
|
493
|
+
interfaceString = [NSString stringWithString:@"\n@interface "+classname.toString()].toString() + protocolsString.toString();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
while (classname!=NSObject && (classname.superclass!="nil" && classname.superclass!=NSObject) ) {
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
// Get Ivars
|
|
501
|
+
classIvarCount=new int;
|
|
502
|
+
superclassIvarCount=new int;
|
|
503
|
+
list=_class_copyIvarList(classname,classIvarCount);
|
|
504
|
+
superlist=_class_copyIvarList(superclass,superclassIvarCount);
|
|
505
|
+
superClassIvars=[NSMutableArray array];
|
|
506
|
+
for (i=0; i<*superclassIvarCount;i++){
|
|
507
|
+
if (_ivar_getName(superlist[i])){
|
|
508
|
+
[superClassIvars addObject:_ivar_getName(superlist[i])];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
}
|
|
512
|
+
free(superlist);
|
|
513
|
+
|
|
514
|
+
for (i=0; i<*classIvarCount;i++){
|
|
515
|
+
classIvar=_ivar_getName(list[i]);
|
|
516
|
+
appendString="";
|
|
517
|
+
if (classIvar && ![superClassIvars containsObject:classIvar]){
|
|
518
|
+
ivarType=ivar_getTypeEncoding(list[i]).toString();
|
|
519
|
+
ivar=constructTypeAndName([NSString stringWithString:ivarType],[NSString stringWithString:_ivar_getName(list[i])],1);
|
|
520
|
+
newString="\n\t"+ivar.toString()+"; ";
|
|
521
|
+
if (![ivarsString containsSubstring:newString]){
|
|
522
|
+
ivarsString=ivarsString.toString()+newString.toString();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
free(list);
|
|
528
|
+
|
|
529
|
+
// Get Properties
|
|
530
|
+
propertiesCount=new int;
|
|
531
|
+
propertyList=_class_copyPropertyList(classname,propertiesCount);
|
|
532
|
+
for (i=0; i<*propertiesCount; i++){
|
|
533
|
+
|
|
534
|
+
propname=_property_getName(propertyList[i]);
|
|
535
|
+
attrs=_property_getAttributes(propertyList[i]);
|
|
536
|
+
newString=propertyLineGenerator(attrs,propname).toString();
|
|
537
|
+
if (![propertiesString containsSubstring:newString]){
|
|
538
|
+
propertiesString=propertiesString.toString()+newString.toString();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
free(propertyList);
|
|
542
|
+
|
|
543
|
+
// Get Methods
|
|
544
|
+
|
|
545
|
+
methodsCount=new int;
|
|
546
|
+
classMethodsCount=new int;
|
|
547
|
+
classMethodList=_class_copyMethodList(object_getClass(classname),classMethodsCount);
|
|
548
|
+
methodList=_class_copyMethodList(classname,methodsCount);
|
|
549
|
+
classMethodsString=classMethodsString.toString()+methodLinesGenerator(classMethodList,classMethodsCount,1).toString();
|
|
550
|
+
methodsString=methodsString.toString()+methodLinesGenerator(methodList,methodsCount,0).toString();
|
|
551
|
+
free(methodList);
|
|
552
|
+
free(classMethodList);
|
|
553
|
+
|
|
554
|
+
if (!alsoDumpSuperclasses)
|
|
555
|
+
break;
|
|
556
|
+
classname=classname.superclass ? classname.superclass : NSObject;
|
|
557
|
+
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
classString= classString.toString()+structsString.toString()+interfaceString.toString();
|
|
561
|
+
classString = classString.toString()+" {"+ivarsString.toString()+"\n}\n"+propertiesString.toString()+classMethodsString.toString()+methodsString.toString();
|
|
562
|
+
classString = classString.toString()+"@end";
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
if (typeof(outputdir) == 'undefined'){
|
|
566
|
+
outputdir = "/tmp";
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (typeof(alsoDumpSuperclasses) == 'string'){
|
|
570
|
+
outputdir=alsoDumpSuperclasses;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
isDir= new boolean;
|
|
574
|
+
dirExists=[[NSFileManager defaultManager ] fileExistsAtPath:outputdir isDirectory: isDir] ;
|
|
575
|
+
if (!dirExists || !isDir){
|
|
576
|
+
createDirSucceeded = [[NSFileManager defaultManager ] createDirectoryAtPath:outputdir attributes: nil];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
outputdir=outputdir.toString()+"/";
|
|
580
|
+
|
|
581
|
+
if (![NSFileManager.defaultManager fileExistsAtPath:outputdir]){
|
|
582
|
+
try{
|
|
583
|
+
[NSFileManager.defaultManager createDirectoryAtPath:outputdir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
584
|
+
}catch(e){}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
classString = [NSString stringWithString:classString ];
|
|
588
|
+
if ([classString writeToFile:outputdir.toString()+startingClassname.toString()+".h" atomically:YES]){
|
|
589
|
+
return "Wrote file to "+outputdir.toString()+startingClassname.toString()+".h"+allProtocols.toString();
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
NSSearchPathForDirectoriesInDomains=new Functor(dlsym(RTLD_DEFAULT,"NSSearchPathForDirectoriesInDomains"),"@ccc");
|
|
593
|
+
writeableDir=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
594
|
+
writeableDir=writeableDir[0];
|
|
595
|
+
return "Failed to write to "+outputdir.toString()+startingClassname.toString()+".h - Check file path and permissions? Suggested writeable directory: "+writeableDir.toString();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
function writeToSylogFromBgThread(string){
|
|
601
|
+
//NSLog(string);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function mweak_loadwdc_class(){
|
|
605
|
+
|
|
606
|
+
@implementation WCDBundleDumper : NSObject {}
|
|
607
|
+
+(id)dumpBundle:(id)infoDictionary{
|
|
608
|
+
var bundle=[infoDictionary objectForKey:@"bundle"];
|
|
609
|
+
var outputdir=[infoDictionary objectForKey:@"outputdir"];
|
|
610
|
+
writeToSylogFromBgThread(@"weak_classdump: Gathering all classes...please wait...");
|
|
611
|
+
var permittedNames = [ObjectiveC.classes allKeys].filter( function (name) { if ([name rangeOfString:"LA"].location!=0){ return [[NSBundle bundleForClass:objc_getClass(name.toString())] isEqual:bundle]; } else{ return NO; } } );
|
|
612
|
+
writeToSylogFromBgThread(@"weak_classdump: Found " + [permittedNames count].toString() +" classes matching your bundle. Starting dump...");
|
|
613
|
+
var results = [];
|
|
614
|
+
for (var i = 0; i < permittedNames.length; i++) {
|
|
615
|
+
try {
|
|
616
|
+
results.push(weak_classdump(objc_getClass(objc_getClass([[permittedNames[i] description] UTF8String])), false, outputdir));
|
|
617
|
+
} catch (e) {
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
[UIDevice.currentDevice _playSystemSound:1100]; // comment out to not produce any sound on finish
|
|
622
|
+
writeToSylogFromBgThread([NSString stringWithFormat:@"weak_classdump: Finished dumping bundle %@. Check output dir %@",bundle,outputdir]);
|
|
623
|
+
|
|
624
|
+
}
|
|
625
|
+
@end
|
|
626
|
+
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function weak_classdump_bundle(bundle, outputdir) {
|
|
630
|
+
|
|
631
|
+
if (typeof(bundle)=="undefined"){
|
|
632
|
+
bundle=[NSBundle mainBundle];
|
|
633
|
+
}
|
|
634
|
+
if (![bundle isLoaded]){
|
|
635
|
+
//NSLog(@"weak_classdump: Bundle %@ is not loaded,attempting to load it",bundle);
|
|
636
|
+
[bundle load];
|
|
637
|
+
}
|
|
638
|
+
if (typeof(outputdir)=="undefined"){
|
|
639
|
+
outputdir="/tmp/";
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
var infoDict=[NSMutableDictionary dictionary];
|
|
644
|
+
[ infoDict setObject:bundle forKey:@"bundle"];
|
|
645
|
+
[ infoDict setObject:outputdir forKey:@"outputdir"];
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
[WCDBundleDumper class];
|
|
649
|
+
} catch (e){
|
|
650
|
+
mweak_loadwdc_class();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
[objc_getClass("WCDBundleDumper") performSelectorInBackground:@selector(dumpBundle:) withObject:infoDict ];
|
|
654
|
+
return "Dumping bundle... Check syslog. Will play lock sound when done."
|
|
655
|
+
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
if ( ! [NSString instancesRespondToSelector:@selector(stringByRemovingCharactersFromSet:)] ){
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@implementation NSString (weakclassdump_compatibility)
|
|
665
|
+
- (void)removeCharactersInSet:(id)set{
|
|
666
|
+
|
|
667
|
+
length = [this length];
|
|
668
|
+
matchRange = [this rangeOfCharacterFromSet:set options:2 range:[0, length]];
|
|
669
|
+
while(matchRange.length > 0){
|
|
670
|
+
replaceRange = matchRange;
|
|
671
|
+
searchRange=[0,0];
|
|
672
|
+
searchRange.location = replaceRange.location + replaceRange.length;
|
|
673
|
+
searchRange.length = length - searchRange.location;
|
|
674
|
+
for(;;){
|
|
675
|
+
matchRange = [this rangeOfCharacterFromSet:set options:2 range:searchRange];
|
|
676
|
+
if((matchRange.length == 0) || (matchRange.location != searchRange.location))
|
|
677
|
+
break;
|
|
678
|
+
replaceRange.length += matchRange.length;
|
|
679
|
+
searchRange.length -= matchRange.length;
|
|
680
|
+
searchRange.location += matchRange.length;
|
|
681
|
+
}
|
|
682
|
+
[this deleteCharactersInRange:replaceRange];
|
|
683
|
+
matchRange.location -= replaceRange.length;
|
|
684
|
+
length -= replaceRange.length;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
- (id)stringByRemovingCharactersFromSet:(id)set{
|
|
690
|
+
|
|
691
|
+
if([this rangeOfCharacterFromSet:set options:2].length == 0)
|
|
692
|
+
return this;
|
|
693
|
+
temp = [[this mutableCopyWithZone:[this zone]] autorelease];
|
|
694
|
+
[temp removeCharactersInSet:set];
|
|
695
|
+
temp=[temp stringByReplacingOccurrencesOfString: @"\"" withString: @""];
|
|
696
|
+
return temp;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
@end
|
|
700
|
+
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if ( ! [NSString instancesRespondToSelector:@selector(stringByRemovingWhitespace)] ){
|
|
704
|
+
|
|
705
|
+
@implementation NSString (weakclassdump_compatibility)
|
|
706
|
+
-(id)stringByRemovingWhitespace{
|
|
707
|
+
return [this stringByRemovingCharactersFromSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
708
|
+
}
|
|
709
|
+
@end
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
//NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
|
|
713
|
+
//NSLog = function() { var types = 'v', args = [], count = arguments.length; for (var i = 0; i != count; ++i) { types += '@'; args.push(arguments[i]); } new Functor(NSLog_, types).apply(null, args); }
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
// Usage example : weak_classdump(SBAwayController);
|
|
717
|
+
// (will write to default path "/tmp/SBAwayController.h"
|
|
718
|
+
// example 2: weak_classdump(UIApplication,"/var/mobile/");
|
|
719
|
+
// will write to "/var/mobile/UIApplication.h"
|
|
720
|
+
// example 3: weak_classdump_bundle([NSBundle bundleWithPath:"/System/Library/Frameworks/iAd.framework"]);
|
|
721
|
+
// will dump all classes in the defined bundle to default dir "/tmp"
|
|
722
|
+
// example 4: weak_classdump_bundle([NSBundle bundleWithPath:"/System/Library/Frameworks/iAd.framework"],"/tmp/iAD.framework/Headers/");
|
|
723
|
+
// will dump all classes in the defined bundle to "/tmp/iAD.framework/"
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
"Added weak_classdump to \""+NSProcessInfo.processInfo .processName.toString()+"\" ("+NSProcessInfo.processInfo .processIdentifier.toString()+")";
|