├── .rvmrc ├── .gitignore ├── Gemfile ├── .gitmodules ├── run_vbox.sh ├── test_ipsw_ext.rb ├── irecoverymode.rb ├── test_irestore.rb ├── plist_ext.rb ├── ideviceinfo.rb ├── test_irecoveryinfo.rb ├── main.rb ├── test_update_img3file.rb ├── test_plist_ext.rb ├── idevice.rb ├── cyg_irestoremode.rb ├── img3file.rb ├── irecoveryinfo.rb ├── osx_irestoremode.rb ├── ipsw_ext.rb ├── README.md ├── iactivate.rb ├── normalmode.rb ├── update_img3file.rb ├── devices.md ├── win32device.rb ├── irestore.rb └── iservice.rb /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm --create use 1.9.2@irestore-win32 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.swp 3 | *.0 4 | *.lock 5 | *.bak 6 | *.log 7 | *.o -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gem 'rake' 4 | gem 'bit-struct' 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ribusb"] 2 | path = ribusb 3 | url = git://github.com/libin/ribusb.git 4 | -------------------------------------------------------------------------------- /run_vbox.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | ### VirtualBox 4 | VBoxManage startvm "winxp" --type headless 5 | #VBoxHeadless --startvm --vrde=on 6 | 7 | ### Vmware Fusion 8 | #vmrun start /path/to/vmx nogui 9 | -------------------------------------------------------------------------------- /test_ipsw_ext.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'ipsw_ext' 8 | require 'pp' 9 | 10 | def test_get_iphone3gs_ios5 11 | pp get_ipsw_info("n88ap", "ios5_0") 12 | end 13 | 14 | def test_get_iphone3gs_ios4 15 | pp get_ipsw_info("n88ap", "ios4_3_5") 16 | end 17 | 18 | def test_get_iphone2g_ios3 19 | pp get_ipsw_info("m68ap", "ios3_1_3") 20 | end 21 | 22 | if __FILE__ == $0 23 | test_get_iphone3gs_ios5 24 | #test_get_iphone3gs_ios4 25 | #test_get_iphone2g_ios3 26 | end -------------------------------------------------------------------------------- /irecoverymode.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'iservice' 8 | require 'plist_ext' 9 | 10 | class InfoService < DeviceService 11 | 12 | def enter_recovery 13 | # obj = {"ProtocolVersion"=>"2", "Request" => "QueryType" } 14 | obj = {"Request" => "EnterRecovery" } 15 | write_plist(@socket, obj) 16 | p read_plist(@socket) 17 | end 18 | 19 | end 20 | 21 | def enter_recovery 22 | d = InfoService.new(PORT_RESTORE) 23 | d.enter_recovery 24 | end 25 | 26 | if __FILE__ == $0 27 | enter_recovery 28 | end 29 | -------------------------------------------------------------------------------- /test_irestore.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'openssl' 8 | require 'pp' 9 | 10 | def crc_sha1_hexdiget(data) 11 | digest = OpenSSL::Digest::Digest.new("SHA1") 12 | puts digest.update(data) 13 | #return "B65756F616988CFB171945E0302CBADD96C65C36" 14 | end 15 | 16 | def crc_sha1(data) 17 | OpenSSL::Digest::Digest.new("SHA1").digest(data) 18 | end 19 | 20 | def test_asr_checksum 21 | filename = "/home/dli/tools/iOS/ipsw/dmg/018-7873-736.dmg" 22 | data = open(filename).read(0x20000) 23 | pp crc_sha1(data) 24 | end 25 | 26 | if __FILE__ == $0 27 | test_asr_checksum 28 | end -------------------------------------------------------------------------------- /plist_ext.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | #$: << File.join(File.dirname(__FILE__), "./CFPropertyList/lib") 5 | 6 | require 'rubygems' 7 | require 'cfpropertylist' 8 | # require 'plist' 9 | # require 'Plist' # need ruby 1.9.2, not works on UTF8 10 | # require 'nokogiri-plist' # not works on cygwin 11 | # require 'plist4r' # not works on cygwin 12 | 13 | module PropertyList 14 | 15 | ## cfpropertylist 16 | def self.load(str) 17 | plist = CFPropertyList::List.new(:data => str) 18 | CFPropertyList.native_types(plist.value) 19 | end 20 | 21 | def self.dump(obj, fmt = nil) 22 | plist = CFPropertyList::List.new 23 | plist.value = CFPropertyList.guess(obj) 24 | plist.to_str(fmt == :xml1 ? CFPropertyList::List::FORMAT_XML : CFPropertyList::List::FORMAT_BINARY) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /ideviceinfo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'pp' 8 | require 'iservice' 9 | 10 | def getdeviceinfo 11 | l = DeviceRelay.new 12 | 13 | l.query_type 14 | 15 | # pub_key = l.get_value("DevicePublicKey").read 16 | pub_key = l.get_value("DevicePublicKey") 17 | p "pub_key:", pub_key 18 | # 19 | l.pair_device(pub_key) 20 | 21 | # l.validate_pair(pub_key) 22 | 23 | @session_id = l.start_session 24 | p "session_id:", @session_id 25 | 26 | # ssl_enable 27 | l.ssl_enable(true) 28 | d = l.get_value 29 | pp d 30 | l.ssl_enable(false) 31 | # 32 | l.stop_session(@session_id) 33 | d 34 | end 35 | 36 | if __FILE__ == $0 37 | getdeviceinfo 38 | end 39 | -------------------------------------------------------------------------------- /test_irecoveryinfo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'irecoveryinfo' 8 | require 'pp' 9 | 10 | def test_get_description 11 | puts "get_irecovery_info" 12 | desc = get_irecovery_description() 13 | puts desc 14 | end 15 | 16 | def test_get_ap_nonce 17 | puts "get_ap_nonce" 18 | desc = get_ap_nonce 19 | puts desc 20 | end 21 | 22 | def test_get_irecovery_info 23 | desc = get_irecovery_info() 24 | puts desc 25 | end 26 | 27 | def test_description_to_string 28 | desc = "abc efg" 29 | bytes = desc.unpack("C*") 30 | data = "\0"*8 + [desc.size, 0x03].pack("CC") + bytes.pack("S*") 31 | string = usb_scription_to_string(data) 32 | puts string 33 | end 34 | 35 | if __FILE__ == $0 36 | test_get_irecovery_info 37 | #test_description_to_string 38 | end -------------------------------------------------------------------------------- /main.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'ideviceinfo' 8 | require 'update_img3file' 9 | require 'irecoverymode' 10 | require 'iactivate' 11 | require 'irestore' 12 | require 'irecoveryinfo' 13 | 14 | p RUBY_PLATFORM 15 | 16 | if /darwin/ =~ RUBY_PLATFORM 17 | require 'osx_irestoremode' 18 | else 19 | require 'cyg_irestoremode' 20 | end 21 | 22 | if __FILE__ == $0 23 | dev_info=getdeviceinfo 24 | enter_recovery 25 | wait_for_reboot 26 | 27 | model=dev_info["HardwareModel"].downcase #"M68AP" 28 | p model 29 | ipsw_info = get_ipsw_info(model, IPSW_VERSION) 30 | unzip_ipsw ipsw_info 31 | 32 | dev_info = get_irecovery_info() 33 | update_img3file(dev_info, ipsw_info) unless model == "m68ap" 34 | enter_restore(ipsw_info) 35 | wait_for_reboot 36 | 37 | do_restore(ipsw_info) 38 | #do_activate(true) 39 | end -------------------------------------------------------------------------------- /test_update_img3file.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'update_img3file' 7 | 8 | def test_tss_response 9 | tssrqst_filename = "/home/dli/tools/iOS/ipsw/dmg_bak/tss-request.plist" 10 | payload = File.open(tssrqst_filename).read 11 | response = get_tss_response(payload) 12 | pp response.body.split("&REQUEST_STRING=")[1] 13 | end 14 | 15 | def test_update_apticket 16 | tssresp_filename = "/home/dli/tools/iOS/ipsw/dmg_bak/tss-response.plist" 17 | buffer = File.open(tssresp_filename).read 18 | obj = PropertyList.load(buffer) 19 | apticket_filename = "/home/dli/tools/iOS/ipsw/dmg/apticket.der" 20 | update_apticket(apticket_filename, obj) 21 | end 22 | 23 | def test_ap_nonce 24 | ap_nonce = "mZLyYI2NFgck+ZEbycwpiazVsi8=".unpack('m0')[0] 25 | pp ap_nonce 26 | 27 | # 4A 81 64 49 E2 9F 95 20 1C E3 52 7B 96 C1 91 08 7D 03 35 3C 28 | # F5 0D D0 F6 49 0F 39 E8 2A 0E B7 34 A8 86 E2 A0 99 E0 0D B5 29 | end 30 | 31 | def test_hexstring_to_binary 32 | hex_string = "7A2C0D05B3C9908A9F27100E04862E237FE0DA78" 33 | pp hex_string_to_binary(hex_string) 34 | end 35 | 36 | if __FILE__ == $0 37 | #test_tss_response() 38 | #test_update_apticket() 39 | test_ap_nonce 40 | end -------------------------------------------------------------------------------- /test_plist_ext.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'plist_ext' 8 | require 'pp' 9 | 10 | def test_xml_to_plist 11 | buffer = File.open("example.xml").read 12 | obj = PropertyList.load(buffer) 13 | puts PropertyList.dump(obj) 14 | end 15 | 16 | def test_plist_to_xml 17 | buffer = File.open("example.plist").read 18 | obj = PropertyList.load(buffer) 19 | puts PropertyList.dump(obj, :xml1) 20 | end 21 | 22 | def test_cfpropertylist 23 | # create a arbitrary data structure of basic data types 24 | data = { 25 | 'name' => 'John Doe', 26 | 'missing' => true, 27 | 'last_seen' => Time.now, 28 | 'friends' => ['Jane Doe','Julian Doe'], 29 | 'likes' => { 30 | 'me' => false 31 | } 32 | } 33 | 34 | # create CFPropertyList::List object 35 | plist = CFPropertyList::List.new 36 | 37 | # call CFPropertyList.guess() to create corresponding CFType values 38 | plist.value = CFPropertyList.guess(data) 39 | # write plist to file 40 | # plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY) 41 | # plist.save("example.xml", CFPropertyList::List::FORMAT_XML) 42 | 43 | # … later, read it again 44 | plist = CFPropertyList::List.new(:file => "example.plist") 45 | data = CFPropertyList.native_types(plist.value) 46 | end 47 | 48 | def test_array 49 | data = ['Jane Doe','Julian Doe']; 50 | plist = CFPropertyList::List.new 51 | plist.value = CFPropertyList.guess(data) 52 | pp plist.value 53 | data = CFPropertyList.native_types(plist.value) 54 | pp data 55 | end 56 | 57 | def test_hash 58 | data = {:item => 'Jane Doe', :value=>'Julian Doe'}; 59 | plist = CFPropertyList::List.new 60 | plist.value = CFPropertyList.guess(data) 61 | pp plist.value 62 | data = CFPropertyList.native_types(plist.value) 63 | pp data 64 | end 65 | 66 | def test_data 67 | require 'base64' 68 | filename = "/home/dli/tools/iOS/ipsw/dmg/ap-ticket.dat" 69 | f = File.open(filename) 70 | obj = {"RootTicketData"=> f} 71 | puts PropertyList.dump(obj, :xml1) 72 | end 73 | 74 | if __FILE__ == $0 75 | # test_array 76 | # test_hash 77 | # test_cfpropertylist 78 | #test_plist_to_xml 79 | # test_xml_to_plist 80 | test_data 81 | end -------------------------------------------------------------------------------- /idevice.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | $: << File.join(File.dirname(__FILE__), 'ribusb/lib') 6 | 7 | require 'rubygems' 8 | require 'ribusb' 9 | 10 | class AppleDevice 11 | def initialize 12 | @USB = RibUSB::Bus.new 13 | @device = @USB.find(:idVendor => 0x5ac, :idProduct => 0x1281).first 14 | end 15 | 16 | def open 17 | set_configuration(1) 18 | set_interface_alt_setting(0, 0) 19 | end 20 | 21 | def set_configuration(configuration) 22 | @device.configuration = configuration 23 | end 24 | 25 | def set_interface_alt_setting(interface, alt_setting) 26 | @device.claimInterface(interface) 27 | @device.setInterfaceAltSetting(interface, alt_setting) 28 | end 29 | 30 | def send_command(command) 31 | begin 32 | p "send_command #{command}.\n" 33 | @device.controlTransfer(:bmRequestType => 0x40, :bRequest => 0, :wValue => 0, :wIndex => 0, :dataOut => command + "\0") 34 | rescue 35 | p "====" 36 | end 37 | end 38 | 39 | def recv_command 40 | begin 41 | receive_buffer = "\x00" * 0x10 42 | size = @device.controlTransfer(:bmRequestType => 0xC0, :bRequest => 0, :wValue => 0, :wIndex => 0, :dataIn => receive_buffer, :timeout => 0) 43 | p size 44 | 45 | receive_buffer[0, size] 46 | rescue 47 | p "----" 48 | end 49 | end 50 | 51 | def send_file(filename, is_recovery_mode=true) 52 | if is_recovery_mode then 53 | @device.controlTransfer(:bmRequestType => 0x41, :bRequest => 0, :wValue => 0, :wIndex => 0) 54 | else 55 | @device.controlTransfer(:bmRequestType => 0x21, :bRequest => 4, :wValue => 0, :wIndex => 0); 56 | end 57 | packet_size = 0 58 | total_size = File.size(filename) 59 | 60 | File.open(filename) do |f| 61 | while buffer = f.read(0x800) do 62 | if is_recovery_mode then 63 | @device.bulkTransfer(:endpoint=>4, :dataOut => buffer) 64 | else 65 | @device.controlTransfer(:bmRequestType => 0x21, :bRequest => 1, :wValue => 0, :wIndex => 0, :dataOut => buffer); 66 | end 67 | 68 | packet_size += buffer.size 69 | puts "#{packet_size}/#{total_size}" 70 | end 71 | end 72 | 73 | buffer = "\x00" * 6 74 | end 75 | 76 | def close 77 | begin 78 | @device.releaseInterface(0) 79 | rescue 80 | p "reboot..." 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /cyg_irestoremode.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'ipsw_ext' 8 | require 'win32device' 9 | 10 | def send_apple_logo(dev, filename) 11 | dev.send_command("setenv auto-boot false") 12 | dev.send_command("saveenv") 13 | 14 | p "sending apple logo" 15 | puts filename 16 | dev.send_file(filename) 17 | 18 | dev.send_command("setpicture 0") 19 | dev.send_command("bgcolor 0 0 0") 20 | end 21 | 22 | def send_ramdisk(dev, filename) 23 | p "sending ramdisk" 24 | puts filename 25 | dev.send_file(filename) 26 | 27 | dev.send_command("ramdisk") 28 | end 29 | 30 | def send_device_tree(dev, filename) 31 | p "sending device tree" 32 | puts filename 33 | dev.send_file(filename) 34 | 35 | dev.send_command("devicetree") 36 | end 37 | 38 | def send_kernel_cache(dev, filename) 39 | p "sending kernel" 40 | puts filename 41 | dev.send_file(filename) 42 | 43 | p "booting" 44 | dev.send_command("setenv boot-args rd=md0 nand-enable-reformat=1 -progress") 45 | end 46 | 47 | def send_ticket(dev, filename) 48 | return unless IPSW_VERSION == "ios5_0" 49 | p "sending root ticket" #in iOS5 50 | puts filename 51 | dev.send_file(filename) 52 | # 21.0 CTL 40 00 00 00 00 00 07 00 VENDOR 196us 43.1.0 53 | # 21.0 USTS c0000004 stall pid 4.7ms 43.2.0 54 | dev.close 55 | dev.open 56 | dev.send_command("ticket") 57 | end 58 | 59 | def send_ibec(dev, filename) 60 | dev.send_command("setenv auto-boot false") 61 | dev.send_command("saveenv") 62 | 63 | puts "sending iBEC" 64 | puts filename 65 | dev.send_file(filename) 66 | end 67 | 68 | def send_ticket_and_ibec(ipsw_info) 69 | dev = Win32Device.new 70 | ret = dev.open 71 | return if ret < 0 72 | 73 | send_ticket(dev, FILE_AP_TICKET) 74 | send_ibec(dev, ipsw_info[:file_ibec]) 75 | dev.send_command("go", 0x1) 76 | 77 | dev.close 78 | end 79 | 80 | def send_ramdisk_and_kernel(ipsw_info) 81 | dev = Win32Device.new 82 | ret = dev.open 83 | return if ret < 0 84 | 85 | send_ticket(dev, FILE_AP_TICKET) 86 | send_apple_logo(dev, ipsw_info[:file_applelog]) 87 | send_ramdisk(dev, ipsw_info[:file_ramdisk]) 88 | send_device_tree(dev, ipsw_info[:file_devicetree]) 89 | send_kernel_cache(dev, ipsw_info[:file_kernelcache]) 90 | 91 | dev.send_command("bootx", 0x1) 92 | 93 | #dev.reset 94 | dev.close 95 | end 96 | 97 | def wait_for_reboot 98 | p "sleeping, press enter key to continue" 99 | gets 100 | end 101 | 102 | def enter_restore(ipsw_info) 103 | send_ticket_and_ibec(ipsw_info) 104 | wait_for_reboot() 105 | send_ramdisk_and_kernel(ipsw_info) 106 | end 107 | 108 | if __FILE__ == $0 109 | #ipsw_info = get_ipsw_info("m68ap", "ios3_1_3") 110 | ipsw_info = get_ipsw_info("n88ap", IPSW_VERSION) 111 | unzip_ipsw ipsw_info 112 | enter_restore(ipsw_info) 113 | end -------------------------------------------------------------------------------- /img3file.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'bit-struct' 8 | require 'pp' 9 | 10 | class Header < BitStruct 11 | default_options :endian=>:native 12 | unsigned :signature, 32, "signature" 13 | unsigned :full_size, 32, "full_size" 14 | unsigned :data_size, 32, "data_size" 15 | unsigned :shsh_offset, 32, "shsh_offset" 16 | unsigned :image_type, 32, "image_type" 17 | end 18 | 19 | class ElementHeader < BitStruct 20 | default_options :endian=>:native 21 | unsigned :signature, 32, "signature" 22 | unsigned :full_size, 32, "full_size" 23 | unsigned :data_size, 32, "data_size" 24 | end 25 | 26 | class Element < BitStruct 27 | default_options :endian=>:native 28 | nest :header, ElementHeader 29 | rest :data, "data" 30 | end 31 | 32 | class Img3File 33 | attr_accessor :header, :elements, :elementkeys 34 | 35 | def parse(input) 36 | data = input.read(Header.round_byte_length) 37 | @header = Header.new data 38 | @elements, @elementkeys = self.parse_elements(input, @header.full_size) 39 | end 40 | 41 | def parse_elements(input, total_len) 42 | elements = [] 43 | elementkeys = [] 44 | # 45 | pos = Header.round_byte_length 46 | while pos < total_len do 47 | #p "#{pos}/#{@header.full_size}" 48 | data = input.read(ElementHeader.round_byte_length) 49 | if data.nil? 50 | break 51 | end 52 | h = ElementHeader.new data 53 | elementkeys += [h.signature] 54 | element = Element.new 55 | element.header = h 56 | element.data = input.read(h.full_size-ElementHeader.round_byte_length) 57 | elements += [element] 58 | pos += h.full_size 59 | end 60 | # self 61 | [elements, elementkeys] 62 | end 63 | 64 | def update_elements(input, total_len) 65 | # parse 66 | elements, elementkeys = parse_elements(input, total_len) 67 | pp elementkeys 68 | # update keys 69 | tmp = [] 70 | @elements.each do |e| 71 | if not elementkeys.include?(e.header.signature) 72 | tmp += [e] 73 | end 74 | end 75 | # update 76 | @elements = tmp + elements 77 | @elementkeys = elementkeys 78 | end 79 | 80 | def to_s 81 | tmp = StringIO.new 82 | header = Header.new 83 | pos = 0 84 | @elements.each do |e| 85 | #p "#{pos}/#{@header.full_size} #{e.header.signature}" 86 | if e.header.signature == "SHSH".reverse.unpack("i*")[0] # 0x53485348, 1397248840 87 | # 53485348 88 | header.shsh_offset = pos 89 | end 90 | tmp << e.to_s 91 | pos += e.header.full_size 92 | end 93 | header.signature = @header.signature 94 | header.full_size = pos + Header.round_byte_length 95 | header.data_size = pos 96 | header.image_type = @header.image_type 97 | 98 | output = StringIO.new 99 | output << header.to_s 100 | output << tmp.string 101 | output.rewind 102 | output.read 103 | end 104 | 105 | end 106 | 107 | -------------------------------------------------------------------------------- /irecoveryinfo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'win32device' 8 | 9 | def get_usb_description(value, index, length) 10 | dev = Win32Device.new 11 | dev.open 12 | desc = dev.get_usb_description(value, index, length) 13 | dev.close 14 | desc 15 | end 16 | 17 | def usb_scription_to_string(data) 18 | #length, type = data.unpack("CC") 19 | data[8+2..-1].unpack("S*").pack("C*") 20 | end 21 | 22 | def get_irecovery_description 23 | #21.0 CTL 80 06 04 03 09 04 ff 00 GET DESCRIPTOR 51us 198.1.0(2) 24 | #21.0 IN b6 03 43 00 50 00 49 00 44 00 3a 00 38 00 39 00 ..C.P.I.D.:.8.9. 437us 198.2.0 25 | # 32 00 30 00 20 00 43 00 50 00 52 00 56 00 3a 00 2.0. .C.P.R.V.:. 198.2.16 26 | # 31 00 35 00 20 00 43 00 50 00 46 00 4d 00 3a 00 1.5. .C.P.F.M.:. 198.2.32 27 | # 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3a 00 0.3. .S.C.E.P.:. 198.2.48 28 | # 30 00 34 00 20 00 42 00 44 00 49 00 44 00 3a 00 0.4. .B.D.I.D.:. 198.2.64 29 | # 30 00 30 00 20 00 45 00 43 00 49 00 44 00 3a 00 0.0. .E.C.I.D.:. 198.2.80 30 | # 30 00 30 00 30 00 30 00 30 00 30 00 31 00 34 00 0.0.0.0.0.0.1.4. 198.2.96 31 | # 33 00 41 00 30 00 34 00 35 00 44 00 30 00 43 00 3.A.0.4.5.D.0.C. 198.2.112 32 | # 20 00 49 00 42 00 46 00 4c 00 3a 00 30 00 33 00 .I.B.F.L.:.0.3. 198.2.128 33 | # 20 00 53 00 52 00 4e 00 4d 00 3a 00 5b 00 38 00 .S.R.N.M.:.[.8. 198.2.144 34 | # 38 00 39 00 34 00 33 00 37 00 37 00 35 00 38 00 8.9.4.3.7.7.5.8. 198.2.160 35 | # 4d 00 38 00 5d 00 M.8.]. 198.2.176 36 | bytes = get_usb_description(0x0304, 0x0409, 0xFF) 37 | usb_scription_to_string(bytes) 38 | end 39 | 40 | def get_ap_nonce 41 | #21.0 CTL 80 06 01 03 09 04 00 01 GET DESCRIPTOR 75us 204.1.0(2) 42 | #21.0 IN 5e 03 20 00 4e 00 4f 00 4e 00 43 00 3a 00 32 00 ^. .N.O.N.C.:.2. 423us 204.2.0 43 | # 34 00 46 00 31 00 45 00 31 00 46 00 38 00 34 00 4.F.1.E.1.F.8.4. 204.2.16 44 | # 46 00 30 00 31 00 34 00 31 00 45 00 33 00 32 00 F.0.1.4.1.E.3.2. 204.2.32 45 | # 39 00 36 00 43 00 34 00 44 00 41 00 31 00 35 00 9.6.C.4.D.A.1.5. 204.2.48 46 | # 35 00 35 00 33 00 36 00 31 00 34 00 43 00 32 00 5.5.3.6.1.4.C.2. 204.2.64 47 | # 38 00 32 00 41 00 39 00 43 00 30 00 46 00 8.2.A.9.C.0.F. 204.2.80 48 | bytes = get_usb_description(0x0301, 0x0409, 0x100) 49 | usb_scription_to_string(bytes) 50 | end 51 | 52 | def get_irecovery_info 53 | string = get_irecovery_description() + get_ap_nonce() 54 | items = {} 55 | string.split(" ").each do | item | 56 | key = item.split(":")[0] 57 | value = item.split(":")[1] 58 | items[key] = value 59 | end 60 | items 61 | end 62 | 63 | -------------------------------------------------------------------------------- /osx_irestoremode.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'ipsw_ext' 8 | require 'idevice' 9 | 10 | def send_ibec(filename) 11 | dev = AppleDevice.new 12 | dev.open 13 | 14 | dev.send_command("setenv auto-boot false") 15 | dev.send_command("saveenv") 16 | 17 | p "sending iBEC" 18 | dev.send_file(filename) 19 | 20 | dev.send_command("go") 21 | 22 | dev.close 23 | end 24 | 25 | def send_kernel_cache(dev, filename) 26 | p "sending kernel" 27 | dev.send_file(filename) 28 | 29 | dev.send_command("setenv boot-args rd=md0 nand-enable-reformat=1 -progress") 30 | end 31 | 32 | def send_device_tree(dev, filename) 33 | p "sending device tree" 34 | dev.send_file(filename) 35 | 36 | dev.send_command("devicetree") 37 | end 38 | 39 | def send_ramdisk(dev, filename) 40 | p "sending ramdisk" 41 | dev.send_file(filename) 42 | 43 | dev.send_command("ramdisk") 44 | end 45 | 46 | def send_apple_logo(dev, filename) 47 | dev.send_command("setenv auto-boot false") 48 | dev.send_command("saveenv") 49 | 50 | p "sending apple logo" 51 | dev.send_file(filename) 52 | 53 | dev.send_command("setpicture 0") 54 | dev.send_command("bgcolor 0 0 0") 55 | end 56 | 57 | def send_ticket 58 | p "sending root ticket" #in iOS5 59 | #dev.send_command("ticket") 60 | 61 | #30 82 0a cb 30 0b 06 09 2a 86 48 86 f7 0d 01 01 0...0...*.H..... 3.6ms 218.1.0 62 | #05 31 82 02 d7 81 08 0c 5d 04 3a 14 00 00 00 82 .1......].:..... 218.1.16 63 | #04 20 89 00 00 83 04 00 00 00 00 84 04 01 00 00 . .............. 218.1.32 64 | #00 85 04 01 00 00 00 86 13 69 42 6f 6f 74 2d 31 .........iBoot-1 218.1.48 65 | #32 31 39 2e 34 33 2e 33 32 7e 31 35 87 14 2f a7 219.43.32.15../. 218.1.64 66 | 67 | #4d 8a ce cc dd f2 1a 08 c7 b6 fa 3f 80 14 13 9f M..........?.... 218.1.2688 68 | #f7 1b c2 5e 94 d2 b3 ba 97 db bc f2 00 e3 9e 01 ...^............ 218.1.2704 69 | #d3 81 c6 f7 26 cb 00 7a b6 59 7c 9f 93 93 26 1c ....&..z.Y....&. 218.1.2720 70 | #83 ac b9 8d 8a 27 d8 75 a9 26 c9 d9 4c 2a 78 d2 .....'.u.&..L*x. 218.1.2736 71 | #59 aa 6d 29 24 6e e6 55 35 e3 c5 b2 c2 bb 3d Y.m)$n.U5.....= 218.1.2752 72 | 73 | end 74 | 75 | def send_ramdisk_and_kernel(ipsw_info) 76 | 77 | dev = AppleDevice.new 78 | dev.open 79 | 80 | send_ticket() 81 | send_apple_logo(dev, ipsw_info[:file_applelog]) 82 | send_ramdisk(dev, ipsw_info[:file_ramdisk]) 83 | send_device_tree(dev, ipsw_info[:file_devicetree]) 84 | send_kernel_cache(dev, ipsw_info[:file_kernelcache]) 85 | 86 | p "booting" 87 | dev.send_command("bootx") 88 | 89 | dev.close() 90 | p "sleeping" 91 | end 92 | 93 | def enter_restore(ipsw_info) 94 | send_ibec(ipsw_info[:file_ibec]) 95 | send_ramdisk_and_kernel(ipsw_info) 96 | end 97 | 98 | if __FILE__ == $0 99 | ipsw_info = get_ipsw_info("m68ap", "ios3_1_3") 100 | unzip_ipsw ipsw_info 101 | enter_restore ipsw_info 102 | end -------------------------------------------------------------------------------- /ipsw_ext.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | 8 | PATH_BASE = File.expand_path("~/tools/iOS") 9 | PATH_DMG = File.join(PATH_BASE, "ipsw/dmg") 10 | PATH_DMG_NEW = PATH_DMG 11 | FILE_AP_TICKET = File.join(PATH_DMG_NEW, "apticket.der") 12 | IPSW_VERSION = "ios5_0" 13 | #IPSW_VERSION = "ios3_1_3" 14 | 15 | def unzip_ipsw(ipsw_info) 16 | system("mkdir -p #{PATH_DMG}") 17 | system("unzip -n -d #{PATH_DMG} #{ipsw_info[:file_ipsw]}") 18 | end 19 | 20 | def firmware_info(model, ipsw) 21 | get_firmware_files(model, ipsw) 22 | end 23 | 24 | def get_firmware_info_table 25 | { 26 | # iphone 2g 27 | "m68ap"=> { 28 | :file_manifest_plist => "BuildManifest.plist", 29 | :file_manifest => "Firmware/all_flash/all_flash.m68ap.production/manifest", 30 | 31 | :file_imgdir => "Firmware/all_flash/all_flash.m68ap.production", 32 | :file_kernelcache => "kernelcache.release.s5l8900x", 33 | :file_applelog => "Firmware/all_flash/all_flash.m68ap.production/applelogo.s5l8900x.img3", 34 | :file_ibec => "Firmware/dfu/iBEC.m68ap.RELEASE.dfu", 35 | :file_devicetree => "Firmware/all_flash/all_flash.m68ap.production/DeviceTree.m68ap.img3", 36 | :file_llb => "Firmware/all_flash/all_flash.m68ap.production/LLB.m68ap.RELEASE.img3", 37 | 38 | "ios3_1_3" => { 39 | :file_ipsw => "iPhone1,1_3.1.3_7E18_Restore.ipsw", 40 | :file_restoredmg => "018-6482-014.dmg", 41 | :file_ramdisk => "018-6494-014.dmg", 42 | }, 43 | }, 44 | # iphone 3gs 45 | "n88ap"=> { 46 | :file_manifest_plist => "BuildManifest.plist", 47 | :file_manifest => "Firmware/all_flash/all_flash.n88ap.production/manifest", 48 | 49 | :file_imgdir => "Firmware/all_flash/all_flash.n88ap.production", 50 | :file_kernelcache => "kernelcache.release.n88", 51 | :file_applelog => "Firmware/all_flash/all_flash.n88ap.production/applelogo.s5l8920x.img3", 52 | :file_ibec => "Firmware/dfu/iBEC.n88ap.RELEASE.dfu", 53 | :file_devicetree => "Firmware/all_flash/all_flash.n88ap.production/DeviceTree.n88ap.img3", 54 | :file_llb => "Firmware/all_flash/all_flash.n88ap.production/LLB.n88ap.RELEASE.img3", 55 | 56 | "ios4_3_5" => { 57 | :file_ipsw => "iPhone2,1_4.3.5_8L1_Restore.ipsw", 58 | :file_restoredmg => "038-2287-002.dmg", 59 | :file_ramdisk => "038-2257-002.dmg", 60 | }, 61 | 62 | "ios5_0" => { 63 | :file_ipsw => "iPhone2,1_5.0_9A334_Restore.ipsw", 64 | :file_restoredmg => "018-7873-736.dmg", 65 | :file_ramdisk => "018-7919-343.dmg", 66 | }, 67 | } 68 | } 69 | end 70 | 71 | def get_ipsw_info(model, ipsw_ver) 72 | info = get_firmware_info_table 73 | 74 | { 75 | :file_manifest_plist => File.join(PATH_DMG, info[model][:file_manifest_plist]), 76 | :file_manifest => File.join(PATH_DMG, info[model][:file_manifest]), 77 | 78 | :file_imgdir => File.join(PATH_DMG_NEW, info[model][:file_imgdir]), 79 | :file_kernelcache =>File.join(PATH_DMG_NEW, info[model][:file_kernelcache]), 80 | :file_applelog =>File.join(PATH_DMG_NEW, info[model][:file_applelog]), 81 | :file_ibec => File.join(PATH_DMG_NEW, info[model][:file_ibec]), 82 | :file_devicetree => File.join(PATH_DMG_NEW, info[model][:file_devicetree]), 83 | :file_llb => File.join(PATH_DMG_NEW, info[model][:file_llb]), 84 | 85 | :file_ipsw => File.join(PATH_BASE, info[model][ipsw_ver][:file_ipsw]), 86 | :file_restoredmg => File.join(PATH_DMG, info[model][ipsw_ver][:file_restoredmg]), 87 | :file_ramdisk => File.join(PATH_DMG_NEW, info[model][ipsw_ver][:file_ramdisk]), 88 | } 89 | end 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VBox on osx/linux 2 | ==== 3 | 4 | start vbox 5 | 6 | run "sh run_vbox.sh" 7 | after 30 seconds, use vrdp connect to 127.0.0.1:3389 8 | 9 | Prepare 10 | ==== 11 | 12 | download the ipsw from internet 13 | 14 | http://www.littleyu.com/article/soft_apple.html 15 | http://appldnld.apple.com/iPhone4/041-1921.20110715.ItuLh/iPhone2,1_4.3.4_8K2_Restore.ipsw 16 | 17 | Setup 18 | ==== 19 | 20 | osx 21 | ---- 22 | 23 | install libusb 24 | 25 | brew install libusb 26 | brew install socat 27 | socat -x -v tcp-l:27015,reuseaddr,fork unix:/var/run/usbmuxd & 28 | -v verbose data traffic, text 29 | -x verbose data traffic, hexadecimal 30 | 31 | cygwin 32 | ---- 33 | 34 | user 35 | 36 | mkpasswd -l > /etc/passwd 37 | mkgroup -l > /etc/group 38 | 39 | sshd 40 | 41 | ssh-host-config -y # tty ntsec 42 | ssh-user-config 43 | cygrunsrv -S sshd 44 | 45 | cygwin 46 | 47 | http://www.cygwin.com/ 48 | 49 | packages 50 | 51 | make automake cmake gcc gdb patch 52 | unzip curl ruby git wget subversion cvs coreutils binutils openssh 53 | libxml2-devel openssl-devel zlib-devel ncurses bison 54 | 55 | SSL certs 56 | 57 | mkdir -p /usr/ssl/certs 58 | cd /usr/ssl/certs 59 | curl http://curl.haxx.se/ca/cacert.pem | awk 'split_after==1{n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > "cert" n ".pem"}' 60 | c_rehash 61 | 62 | library 63 | ---- 64 | 65 | rubygems 66 | 67 | http://rubygems.org/pages/download 68 | ruby setup.rb install 69 | 70 | install rvm and gems 71 | 72 | bash < <(curl -k https://rvm.beginrescueend.com/install/rvm) 73 | source ~/.bashrc 74 | rvm install 1.9.2 75 | gem install rake 76 | gem install bundler 77 | gem install libxml-ruby 78 | gem install bit-struct 79 | gem install CFPropertyList 80 | 81 | FAQ 82 | ---- 83 | 84 | install gcc on cygwin 85 | 86 | extconf failure: need libm 87 | 88 | Run 89 | ---- 90 | 91 | put iphone ipsw under the path: ~/tools/iOS/ 92 | 93 | chmod +x s-irecovery.exe 94 | ruby main.rb 95 | 96 | and run 97 | 98 | Ref 99 | ---- 100 | 101 | irecovery 102 | 103 | http://github.com/iH8sn0w/syringe-irecovery 104 | 105 | ribusb 106 | 107 | http://github.com/libin/ribusb 108 | 109 | CFPropertyList 110 | 111 | http://github.com/ckruse/CFPropertyList 112 | 113 | iTools 114 | 115 | http://itools.hk/cms/?p=282 116 | 117 | win32_sms 118 | 119 | https://raw.github.com/gist/85729/135a0181467d2785a1d51a5a7551b7486f96c068/win32_sms.rb 120 | 121 | win32 tools 122 | 123 | Nirsoft DeviceIOView 124 | Fiddler 125 | 126 | log 127 | 128 | C:\Documents and Settings\dli\Application Data\Apple Computer\Logs 129 | C:\Documents and Settings\dli\Application Data\Apple Computer\iTunes\iPhone Updater Logs 130 | http://pastebin.com/7CtQSvDq 131 | 132 | apticket.der 133 | 134 | openssl asn1parse -in apticket.der -inform DER -dump > apticket.der.dump 135 | 136 | iphone-dataprotection 137 | 138 | http://code.google.com/p/iphone-dataprotection/wiki/EncryptionKeys 139 | http://code.google.com/p/iphone-dataprotection/wiki/EffaceableArea 140 | 141 | win32 142 | ---- 143 | 144 | ipsw 145 | C:\Documents and Settings\Administrator\Application Data\Apple Computer\iTunes\iPhone Software Updates 146 | 147 | unzip ipsw 148 | C:\Documents and Settings\All Users\Application Data\Apple Computer\iTunes 149 | 150 | AppleMobileDeviceSupport.msi 151 | C:\Documents and Settings\All Users\Application Data\Apple\Installer Cache 152 | 153 | amai 154 | C:\Documents and Settings\dli\Local Settings\Temp -------------------------------------------------------------------------------- /iactivate.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'optparse' 8 | require 'net/https' 9 | require 'rexml/document' 10 | require 'pp' 11 | require 'iservice' 12 | require 'plist_ext' 13 | 14 | class DeviceActivateRelay < DeviceRelay 15 | def activate 16 | dev = get_value("DeviceClass") 17 | if (dev == "iPhone") 18 | uid = get_value("UniqueDeviceID") 19 | imei = get_value("InternationalMobileEquipmentIdentity") 20 | iccid = get_value("IntegratedCircuitCardIdentity") 21 | sn = get_value("SerialNumber") 22 | imsi = get_value("InternationalMobileSubscriberIdentity") 23 | activation_info = get_value("ActivationInfo") 24 | end 25 | 26 | p "uid:#{uid} imei:#{imei} iccid:#{iccid} sn:#{sn} imsi:#{imsi}" 27 | 28 | p "activation_info:#{activation_info}" 29 | 30 | p "fetching activation_record..." 31 | # url = URI("https://albert.apple.com/WebObjects/ALActivation.woa/wa/iPhoneRegistration") 32 | url = URI("https://albert.apple.com/WebObjects/ALUnbrick.woa/wa/deviceActivation") 33 | http = Net::HTTP.new(url.host, url.port) 34 | http.use_ssl = true 35 | http.start do |h| 36 | req = Net::HTTP::Post.new(url.path, "User-Agent" => "iTunes/9.1 (Macintosh; U; Intel Mac OS X 10.5.6)") 37 | req.form_data = { 38 | "AppleSerialNumber" => sn, 39 | "IMSI" => imsi, 40 | "InStoreActivation" => "false", 41 | "machineName" => "macos", 42 | # "activation-info" => activation_info.to_plist, 43 | "activation-info" => PropertyList.dump(activation_info, :xml1), 44 | "ICCID" => iccid, 45 | "IMEI" => imei 46 | } 47 | #puts req.body 48 | result = h.request(req) 49 | puts result.body 50 | # 51 | # 52 | # 53 | # 54 | # 55 | # iphone-activation 56 | # 57 | # unbrick 58 | # 59 | # activation-record 60 | # 61 | # DeviceCertificate 62 | xmldoc = REXML::Document.new(result.body) 63 | buffer = REXML::XPath.first(xmldoc, "//plist").to_s 64 | obj = PropertyList.load(buffer) 65 | 66 | # "iphone-activation"=>{"show-settings"=>true, "ack-received"=>true} 67 | tmp = obj["iphone-activation"] 68 | if tmp.include?("activation-record") 69 | activation_record = tmp["activation-record"] 70 | pp activation_record 71 | p "activating..." 72 | # ssl_enabled 73 | obj = {"Request" => "Activate", "ActivationRecord" => activation_record} 74 | write_plist(@ssl, obj) 75 | read_plist(@ssl) 76 | end 77 | end 78 | end 79 | 80 | def deactivate 81 | # ssl_enabled 82 | obj = {"Request" => "Deactivate"} 83 | write_plist(@ssl, obj) 84 | read_plist(@ssl) 85 | end 86 | end 87 | 88 | def do_activate(is_activate) 89 | l = DeviceActivateRelay.new 90 | 91 | l.query_type 92 | 93 | # pub_key = l.get_value("DevicePublicKey").read 94 | pub_key = l.get_value("DevicePublicKey") 95 | p "pub_key:", pub_key 96 | # 97 | l.pair_device(pub_key) 98 | 99 | # l.validate_pair(pub_key) 100 | 101 | @session_id = l.start_session 102 | p "session_id:", @session_id 103 | 104 | # ssl_enable 105 | l.ssl_enable(true) 106 | is_activate ? l.activate : l.deactivate 107 | l.ssl_enable(false) 108 | # 109 | l.stop_session(@session_id) 110 | end 111 | 112 | if __FILE__ == $0 113 | do_activate(true) 114 | end 115 | 116 | 117 | -------------------------------------------------------------------------------- /normalmode.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'win32device' 8 | require 'ipsw_ext' 9 | 10 | def test_getdevpath 11 | dev = Win32Device.new 12 | puts dev.get_iboot 13 | puts dev.get_dfu 14 | end 15 | 16 | def test_reboot 17 | dev = Win32Device.new 18 | dev.open 19 | dev.send_command "reboot" 20 | dev.close 21 | end 22 | 23 | def test_return_normal_mode 24 | dev = Win32Device.new 25 | dev.open 26 | dev.send_command "setenv auto-boot true" 27 | dev.send_command "saveenv" 28 | dev.send_command "reboot" 29 | dev.close 30 | end 31 | 32 | def test_getenv 33 | dev = Win32Device.new 34 | dev.open 35 | cmd = "getenv build-version" 36 | dev.send_command(cmd) 37 | puts dev.recv_command 38 | dev.close 39 | end 40 | 41 | def test_set_interface 42 | dev = Win32Device.new 43 | dev.open 44 | dev.set_interface(1, 0) 45 | dev.close 46 | end 47 | 48 | def test_sleep 49 | dev = Win32Device.new 50 | puts Time.now 51 | dev.sleep(3) 52 | puts Time.now 53 | end 54 | 55 | def test_ibec(filename) 56 | dev = Win32Device.new 57 | dev.open 58 | 59 | dev.send_command("setenv auto-boot false") 60 | dev.send_command("saveenv") 61 | 62 | puts "sending iBEC" 63 | puts filename 64 | dev.send_file(filename) 65 | 66 | dev.send_command("go", 0x1) 67 | 68 | dev.reset 69 | dev.close 70 | end 71 | 72 | def test_enter_mode(ipsw_info) 73 | test_ticket 74 | test_applogo(ipsw_info[:file_applelog]) 75 | test_ramdisk(ipsw_info[:file_ramdisk]) 76 | test_devicetree(ipsw_info[:file_devicetree]) 77 | test_kernel(ipsw_info[:file_kernelcache]) 78 | end 79 | 80 | def test_ticket 81 | p "sending root ticket" #in iOS5 82 | #dev.send_command("ticket") 83 | 84 | #30 82 0a cb 30 0b 06 09 2a 86 48 86 f7 0d 01 01 0...0...*.H..... 3.6ms 218.1.0 85 | #05 31 82 02 d7 81 08 0c 5d 04 3a 14 00 00 00 82 .1......].:..... 218.1.16 86 | #04 20 89 00 00 83 04 00 00 00 00 84 04 01 00 00 . .............. 218.1.32 87 | #00 85 04 01 00 00 00 86 13 69 42 6f 6f 74 2d 31 .........iBoot-1 218.1.48 88 | #32 31 39 2e 34 33 2e 33 32 7e 31 35 87 14 2f a7 219.43.32.15../. 218.1.64 89 | 90 | #4d 8a ce cc dd f2 1a 08 c7 b6 fa 3f 80 14 13 9f M..........?.... 218.1.2688 91 | #f7 1b c2 5e 94 d2 b3 ba 97 db bc f2 00 e3 9e 01 ...^............ 218.1.2704 92 | #d3 81 c6 f7 26 cb 00 7a b6 59 7c 9f 93 93 26 1c ....&..z.Y....&. 218.1.2720 93 | #83 ac b9 8d 8a 27 d8 75 a9 26 c9 d9 4c 2a 78 d2 .....'.u.&..L*x. 218.1.2736 94 | #59 aa 6d 29 24 6e e6 55 35 e3 c5 b2 c2 bb 3d Y.m)$n.U5.....= 218.1.2752 95 | 96 | end 97 | 98 | def test_applogo(filename) 99 | dev = Win32Device.new 100 | dev.open 101 | 102 | dev.send_command("setenv auto-boot false") 103 | dev.send_command("saveenv") 104 | 105 | p "sending apple logo" 106 | puts filename 107 | dev.send_file(filename) 108 | 109 | dev.send_command("setpicture 0") 110 | dev.send_command("bgcolor 0 0 0") 111 | 112 | dev.reset 113 | dev.close 114 | end 115 | 116 | def test_ramdisk(filename) 117 | dev = Win32Device.new 118 | dev.open 119 | 120 | p "sending ramdisk" 121 | puts filename 122 | dev.send_file(filename) 123 | 124 | dev.send_command("ramdisk") 125 | 126 | dev.reset 127 | dev.close 128 | end 129 | 130 | def test_devicetree(filename) 131 | dev = Win32Device.new 132 | dev.open 133 | 134 | p "sending device tree" 135 | puts filename 136 | dev.send_file(filename) 137 | 138 | dev.send_command("devicetree") 139 | 140 | dev.reset 141 | dev.close 142 | end 143 | 144 | def test_kernel(filename) 145 | dev = Win32Device.new 146 | dev.open 147 | 148 | p "sending kernel" 149 | puts filename 150 | dev.send_file(filename) 151 | 152 | p "booting" 153 | dev.send_command("setenv boot-args rd=md0 nand-enable-reformat=1 -progress") 154 | dev.send_command("bootx", 0x1) 155 | 156 | dev.reset 157 | dev.close 158 | end 159 | 160 | if __FILE__ == $0 161 | #ipsw_info = get_ipsw_info("m68ap", "ios3_1_3") 162 | #ipsw_info = get_ipsw_info("n88ap", "ios5_0") 163 | #unzip_ipsw ipsw_info 164 | #test_sleep 165 | test_return_normal_mode 166 | #test_reboot 167 | #test_getdevpath 168 | #test_set_interface 169 | #test_getenv 170 | #test_ibec(ipsw_info[:file_ibec]) 171 | #test_enter_mode(ipsw_info) 172 | end 173 | -------------------------------------------------------------------------------- /update_img3file.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'net/https' 8 | require 'uri' 9 | require 'img3file' 10 | require 'fileutils' 11 | require 'pathname' 12 | require 'plist_ext' 13 | require 'ipsw_ext' 14 | require 'rexml/document' 15 | require 'irecoveryinfo' 16 | 17 | def hex_string_to_binary(hex_string) 18 | hex_string.scan(/.{2}/).map{ |x| x.hex.chr }.join 19 | end 20 | 21 | def get_tss_payload(dev_info, ipsw_info) 22 | buffer = File.open(ipsw_info[:file_manifest_plist]).read 23 | # p buffer 24 | obj = PropertyList.load(buffer) 25 | # pp obj["BuildIdentities"][0] 26 | 27 | #@APTicket 28 | #@BBTicket 29 | #@HostIpAddress192.168.1.101 30 | #@HostPlatformInfowindows 31 | #@UUID38E214DF-F33E-BA44-908C-39257A02784A 32 | #@VersionInfolibauthinstall-107.3 33 | #ApBoardID0 34 | #ApChipID35104 35 | #ApECID86872710412 36 | #ApNoncemZLyYI2NFgck+ZEbycwpiazVsi8= 37 | #ApProductionMode 38 | #ApSecurityDomain1 39 | 40 | #{"CPID"=>"8920", "CPRV"=>"15", "CPFM"=>"03", "SCEP"=>"04", "BDID"=>"00", 41 | #"ECID"=>"000000143A045D0C", "IBFL"=>"03", "SRNM"=>"[889437758M8]", 42 | #"NONC"=>"7A2C0D05B3C9908A9F27100E04862E237FE0DA78"} 43 | 44 | #ecid = dev_info["UniqueChipID"] #86872710412 45 | #ap_chip_id = 35104 #info[] 46 | #ap_nonce = "Y0rxrVEzJpYJhADmqCto+o1oCk0=".unpack('m0')[0] 47 | ap_nonce = hex_string_to_binary(dev_info["NONC"]) 48 | ap_nonce.blob=true 49 | 50 | ecid = dev_info["ECID"].to_i(16) 51 | ap_chip_id = dev_info["CPID"].to_i(16) 52 | ap_board_id = dev_info["BDID"].to_i(16) 53 | 54 | puts "ap_nonce", ap_nonce 55 | puts "ecid", ecid 56 | puts "ap_chip_id", ap_chip_id 57 | puts "ap_board_id", ap_board_id 58 | 59 | rqst_obj = { 60 | "@APTicket" => true, 61 | "@BBTicket" => true, 62 | "@HostIpAddress" => "192.168.1.101", 63 | "@HostPlatformInfo" => "windows", 64 | "@UUID" => "E6B885AE-227D-4D46-93BF-685F701313C5", 65 | "@VersionInfo" => "libauthinstall-107.3", 66 | "ApBoardID" => ap_board_id, 67 | "ApChipID" => ap_chip_id, 68 | "ApECID" => ecid, # 86872710412, # "UniqueChipID"=>86872710412, get from ideviceinfo.rb 69 | "ApNonce" => ap_nonce, # must set on iOS5 70 | "ApProductionMode" => true, 71 | "ApSecurityDomain" => 1, 72 | } 73 | 74 | tmp = obj["BuildIdentities"][0] 75 | manifest_info = {} 76 | 77 | tmp.each do |k, v| 78 | case k 79 | when "UniqueBuildID" 80 | v.blob = true 81 | rqst_obj[k] = v 82 | when "Manifest" 83 | hash = {} 84 | tmp["Manifest"].each do |mk, mv| 85 | #pp mk, mv 86 | unless mk =~ /Info|OS/ 87 | hash[mk] ={} 88 | mv.each do |vk, vv| 89 | #pp vk, vv 90 | case vk 91 | when "Info" 92 | manifest_info = manifest_info.merge({mk => vv["Path"]}) 93 | when "PartialDigest", "Digest" 94 | vv.blob = true 95 | hash[mk] = hash[mk].merge({vk => vv}) 96 | else 97 | hash[mk] = hash[mk].merge({vk => vv}) 98 | end 99 | end 100 | end 101 | end 102 | rqst_obj = rqst_obj.merge(hash) 103 | end 104 | end 105 | 106 | #pp manifest_info 107 | 108 | payload = PropertyList.dump(rqst_obj, :xml1) 109 | return manifest_info, payload 110 | end 111 | 112 | def get_tss_response(payload) 113 | uri_gs_serv = "http://gs.apple.com/TSS/controller?action=2" 114 | #uri_gs_serv = "http://cydia.saurik.com/TSS/controller?action=2" 115 | #uri_gs_serv = "http://127.0.0.1:8080/TSS/controller?action=2" 116 | uri = URI.parse(uri_gs_serv) 117 | http = Net::HTTP.new(uri.host, uri.port) 118 | request = Net::HTTP::Post.new(uri.request_uri) 119 | request["User-Agent"] = "InetURL/1.0" 120 | request["Content-Length"] = payload.length 121 | request["Content-Type"] = 'text/xml; charset="utf-8"' 122 | request.body = payload 123 | http.request(request) 124 | end 125 | 126 | def patch_img3_files(manifest_info, obj) 127 | manifest_info.each do |k, v| 128 | puts k 129 | if obj.include?(k) 130 | filename = File.join(PATH_DMG, v) 131 | img3 = Img3File.new 132 | data = File.open(filename, 'r').read 133 | img3.parse(StringIO.new(data)) 134 | 135 | blob = obj[k]["Blob"] 136 | img3.update_elements(StringIO.new(blob), blob.length) 137 | 138 | tmp_filename = File.join(PATH_DMG_NEW, v) 139 | puts "patch_img3_files", tmp_filename 140 | FileUtils.mkdir_p(Pathname.new(tmp_filename).dirname) 141 | f = File.open(tmp_filename, "wb") 142 | f.write(img3.to_s) 143 | f.close 144 | end 145 | end 146 | end 147 | 148 | def update_apticket(apticket_filename, obj) 149 | # 0x017 - 0x2e ap_ecid 150 | # 0C 5D 04 3A 14 00 00 00 151 | # 0x114 - 0x128 ap_nonce 152 | # 01 D5 23 60 13 D0 7E 34 31 96 4A 00 FE 4F 3F C0 7A 88 A2 6C 153 | # 0x21e - 0x232 154 | # 0x2ef - 0x36f 155 | puts "update_apticket", apticket_filename 156 | data = obj["APTicket"] 157 | f = File.open(apticket_filename, "wb+") 158 | f.write(data) 159 | f.close 160 | end 161 | 162 | def update_img3file(dev_info, ipsw_info) 163 | manifest_info, payload = get_tss_payload(dev_info, ipsw_info) 164 | response = get_tss_response(payload) 165 | 166 | if response.body.include?("STATUS=0&MESSAGE=") 167 | buffer = response.body.split("&REQUEST_STRING=")[1] 168 | obj = PropertyList.load(buffer) 169 | 170 | if not obj.nil? 171 | patch_img3_files(manifest_info, obj) 172 | update_apticket(FILE_AP_TICKET, obj) 173 | else 174 | puts "something wrong", buffer 175 | end 176 | else 177 | # STATUS=94&MESSAGE=This device isn't eligible for the requested build. 178 | puts response.body 179 | end 180 | 181 | end 182 | 183 | if __FILE__ == $0 184 | ipsw_info = get_ipsw_info("n88ap", "ios5_0") 185 | unzip_ipsw ipsw_info 186 | dev_info = get_irecovery_info() 187 | #update_img3file(4302652613389, ipsw_info) #4302652613389 188 | update_img3file(dev_info, ipsw_info) 189 | end 190 | 191 | -------------------------------------------------------------------------------- /devices.md: -------------------------------------------------------------------------------- 1 | # iphone 2g 3.1.3 2 | 3 | {"BasebandThumbprint"=>"571673BFEEE8E6AE1F6CFEFD1D6DBC9526B2A9BC", 4 | "FirmwareVersion"=>"iBoot-636.66.33", 5 | "MLBSerialNumber"=>"", 6 | "ProductVersion"=>"3.1.3", 7 | "TimeIntervalSince1970"=>1314024405, 8 | "WiFiAddress"=>"00:1c:b3:6e:ef:7f", 9 | "DeviceName"=>"iPhone", 10 | "ActivationPublicKey"=> 11 | "-----BEGIN RSA PUBLIC KEY----- 12 | MIGIAoGAb7hgZjno/6RK4zxH8C8rYWM90HuBDQAfkpB29b6KyuVd7RA1vSrFD6IH 13 | 7uYWZrg5z6/tn2mfkpsNziJN3a9p72LajozdBGDv5EMze4uPkQxUxr55Sl0CTbqF 14 | PdJCqY8vo25eBJZyZn1ncjOZY8+XPX+nzGc/Dvt0QHFV6loi8gECAwEAAQ== 15 | -----END RSA PUBLIC KEY----- 16 | ", 17 | "ProductType"=>"iPhone1,1", 18 | "ProtocolVersion"=>"2", 19 | "UniqueChipID"=>1394925108686, 20 | "BuildVersion"=>"7E18", 21 | "HardwareModel"=>"M68AP", 22 | "SBLockdownEverRegisteredKey"=>false, 23 | "TimeZone"=>"Asia/Harbin", 24 | "TrustedHostAttached"=>true, 25 | "BasebandBootloaderVersion"=>"3.9_M3S2", 26 | "DeviceClass"=>"iPhone", 27 | "DevicePublicKey"=> 28 | "-----BEGIN RSA PUBLIC KEY----- 29 | MIGIAoGAcPM982CxZraM4l1WVGY7EjaHRfAf379tnxdCuZsLuThmKPXIJcUz5UTX 30 | e3YXV4rFy2TmONP6hqODrRpNYaqPySxiAVqlhfodRc2BQLntY1pYVFdRaqV/mRvI 31 | 4T6hIgPp2SLFnKQX3J/KQAioEnRWAdbkiwC/wog03LaXUazC7scCAwEAAQ== 32 | -----END RSA PUBLIC KEY----- 33 | ", 34 | "BasebandMasterKeyHash"=>"E93B43F3EF6DAED516A2D4B9BAD5494DC81E92D3", 35 | "HostAttached"=>true, 36 | "SoftwareBehavior"=> 37 | "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", 38 | "TimeZoneOffsetFromUTC"=>28800.0, 39 | "InternationalMobileEquipmentIdentity"=>"011245007201252", 40 | "RegionInfo"=>"LL/A", 41 | "UniqueDeviceID"=>"4f098924a38e736de78eb524c2b7cfd7ccf23454", 42 | "ActivationState"=>"Unactivated", 43 | "BasebandVersion"=>"04.05.04_G", 44 | "CPUArchitecture"=>"armv6", 45 | "ProductionSOC"=>true, 46 | "SerialNumber"=>"7R7287VYWH8", 47 | "BluetoothAddress"=>"00:1c:b3:6e:ef:7e", 48 | "Uses24HourClock"=>false, 49 | "ModelNumber"=>"MA712", 50 | "SIMStatus"=>"kCTSIMSupportSIMStatusNotInserted", 51 | "SomebodySetTimeZone"=>true, 52 | "PasswordProtected"=>false} 53 | 54 | # iphone 3gs 5.0 55 | 56 | {"ActivationPublicKey"=> 57 | "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAO5n9l1MW3hcgbbgQbwCMz259CPVauaajxtUH0+ekxFM/zy2Aj2j22jp\nHwI3HOGHRZiFW9weOLlqBytBiBW2Hw9OmzbUlr8YU5CN7qDCjNQVnw5Xm9Li9BWS\nh1B73OjEHAjR52q400pjNpGj0i443T36ZV3pVFuLiREqgD8njCeRAgMBAAE=\n-----END RSA PUBLIC KEY-----\n", 58 | "ActivationState"=>"WildcardActivated", 59 | "ActivationStateAcknowledged"=>true, 60 | "ActivityURL"=> 61 | "https://nc-unbrick1.apple.com/WebObjects/ALUnbrick.woa/wa/activity", 62 | "BasebandBootloaderVersion"=>"6.4_M3S2", 63 | "BasebandMasterKeyHash"=>"E93B43F3EF6DAED516A2D4B9BAD5494DC81E92D3", 64 | "BasebandStatus"=>"BBInfoAvailable", 65 | "BasebandThumbprint"=>"CC87A49AC0E42EB03A723626764E44B2ABF289A5", 66 | "BasebandVersion"=>"05.16.05", 67 | "BluetoothAddress"=>"04:1e:64:8c:60:49", 68 | "BuildVersion"=>"9A334", 69 | "CPUArchitecture"=>"armv7", 70 | "DeviceCertificate"=> 71 | "-----BEGIN CERTIFICATE-----\nMIIC8zCCAlygAwIBAgIKA3BzBlXBjvgYlTANBgkqhkiG9w0BAQUFADBaMQswCQYD\nVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEVMBMGA1UECxMMQXBwbGUgaVBo\nb25lMR8wHQYDVQQDExZBcHBsZSBpUGhvbmUgRGV2aWNlIENBMB4XDTExMTAxMjE4\nMTExMVoXDTE0MTAxMjE4MTExMVowgYMxLTArBgNVBAMMJDQ0NThBMERCLUY3REIt\nNDdBQi1BQjBELUQ5MkFCN0JGNTc3NDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB\nMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4xDzANBgNV\nBAsMBmlQaG9uZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7mf2XUxbeFyB\ntuBBvAIzPbn0I9Vq5pqPG1QfT56TEUz/PLYCPaPbaOkfAjcc4YdFmIVb3B44uWoH\nK0GIFbYfD06bNtSWvxhTkI3uoMKM1BWfDleb0uL0FZKHUHvc6MQcCNHnarjTSmM2\nkaPSLjjdPfplXelUW4uJESqAPyeMJ5ECAwEAAaOBlTCBkjAfBgNVHSMEGDAWgBSy\n/iEjRIaVannVgSaOcxDYp0yOdDAdBgNVHQ4EFgQU7wSRTOp/BXq6DPOncuQ4ewH3\ncJcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYI\nKwYBBQUHAwEGCCsGAQUFBwMCMBAGCiqGSIb3Y2QGCgIEAgUAMA0GCSqGSIb3DQEB\nBQUAA4GBACi2Q7NA4VigpRiyNOK3+h5IVe3L2KKeN8eFpEpCg2DjJf36wmtlEQx5\nqq6bKKfrwiQi+hS6eG6vJKh6okVpcao6+5kYjjSxPU2IwIVrY09A74lIrAYqltlP\nFvbld57EuVqu9EbEW0Es4u7dPGe1tHdK7j/dDod6AZqitgq/h/dh\n-----END CERTIFICATE-----\n", 72 | "DeviceClass"=>"iPhone", 73 | "DeviceColor"=>"unknown", 74 | "DeviceName"=>"iPhone", 75 | "DevicePublicKey"=> 76 | "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBALU7robQr5VX9+bMCsxGpAiv4tsvsPlRU/Em/fVCwT3LqUZJffGwWru6\nddvNemxAY/R6BrkSJ1O4pPo/TL1tXIZ1hG9w4QqzKCL1WnubyZi0dx8dY9hAd1N5\nHEonVMh0DoCFsoftw5Ftl0+fztYMVJdR7XKKoqmgbVALhvox6VcpAgMBAAE=\n-----END RSA PUBLIC KEY-----\n", 77 | "DieID"=>3687662442428539344, 78 | "FirmwareVersion"=>"iBoot-1219.43.32", 79 | "HardwareModel"=>"N88AP", 80 | "HardwarePlatform"=>"s5l8920x", 81 | "HostAttached"=>true, 82 | "IMLockdownEverRegisteredKey"=>false, 83 | "IntegratedCircuitCardIdentity"=>"89860110983405218392", 84 | "InternationalMobileEquipmentIdentity"=>"012037007915703", 85 | "InternationalMobileSubscriberIdentity"=>"460012948902092", 86 | "MLBSerialNumber"=>"J59421U1T8P1A", 87 | "MobileSubscriberCountryCode"=>"460", 88 | "MobileSubscriberNetworkCode"=>"01", 89 | "ModelNumber"=>"MC179", 90 | "PartitionType"=>"", 91 | "PasswordProtected"=>false, 92 | "PhoneNumber"=>"+86 131-1500-0409", 93 | "ProductType"=>"iPhone2,1", 94 | "ProductVersion"=>"5.0", 95 | "ProductionSOC"=>true, 96 | "ProtocolVersion"=>"2", 97 | "ProximitySensorCalibration"=> 98 | "CM\x01\x00\f\x00\b\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 99 | "RegionInfo"=>"CH/A", 100 | "SBLockdownEverRegisteredKey"=>true, 101 | "SIMStatus"=>"kCTSIMSupportSIMStatusReady", 102 | "SerialNumber"=>"889437758M8", 103 | "SoftwareBehavior"=> 104 | "a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 105 | "SoftwareBundleVersion"=>"", 106 | "SupportedDeviceFamilies"=>[1], 107 | "TelephonyCapability"=>true, 108 | "TimeIntervalSince1970"=>1318443449, 109 | "TimeZone"=>"Asia/Shanghai", 110 | "TimeZoneOffsetFromUTC"=>28800.0, 111 | "TrustedHostAttached"=>true, 112 | "UniqueChipID"=>86872710412, 113 | "UniqueDeviceID"=>"39150e1823d53b7c2a8e4ff543881e762392120a", 114 | "UseActivityURL"=>true, 115 | "UseRaptorCerts"=>false, 116 | "Uses24HourClock"=>false, 117 | "WeDelivered"=>true, 118 | "WiFiAddress"=>"ba:ba:ba:ba:ba:ba", 119 | "iTunesHasConnected"=>true} 120 | -------------------------------------------------------------------------------- /win32device.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require "Win32API" 8 | require 'pp' 9 | 10 | #static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; 11 | #static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; 12 | 13 | #TRUE = 1 14 | #FALSE = 0 15 | 16 | #INFINITE = 0xFFFFFFFF 17 | 18 | #GENERIC_READ = 0x80000000 19 | #GENERIC_WRITE = 0x40000000 20 | #GENERIC_EXECUTE = 0x20000000 21 | # 22 | #FILE_SHARE_READ = 0x00000001 23 | #FILE_SHARE_WRITE = 0x00000002 24 | #OPEN_EXISTING = 0x00000003 25 | #FILE_FLAG_OVERLAPPED = 0x40000000 26 | 27 | #DIGCF_PRESENT = 0x00000002 28 | #DIGCF_DEVICEINTERFACE = 0x00000010 29 | 30 | #ERROR_DISK_FULL = 112; 31 | #ERROR_NO_MORE_ITEMS = 259 32 | #ERROR_INVALID_USER_BUFFER = 1784 33 | 34 | class Win32Device 35 | 36 | def initialize 37 | @createFile = Win32API.new('kernel32', 'CreateFile', 'PLLPLLP', 'L') 38 | @deviceIoControl = Win32API.new('kernel32', 'DeviceIoControl', 'LLPLPLPP', 'I') 39 | @closeHandle = Win32API.new("kernel32", "CloseHandle", 'L', 'L') 40 | 41 | @createEvent = Win32API.new('kernel32', 'CreateEvent', 'PLLP', 'L') 42 | @getOverlappedResult = Win32API.new('kernel32', 'GetOverlappedResult', 'LPPL', 'L') 43 | @waitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', 'LL', 'L') 44 | @cancelIo = Win32API.new("kernel32", "CancelIo", 'L', 'L') 45 | 46 | @sleep = Win32API.new('kernel32', 'Sleep', 'L', 'L') 47 | @getLastError = Win32API.new("kernel32", "GetLastError", '', "L") 48 | 49 | @setupDiGetClassDevs = Win32API.new('setupapi', 'SetupDiGetClassDevs', 'PPPL', 'L') 50 | @setupDiEnumDeviceInterfaces = Win32API.new('setupapi', 'SetupDiEnumDeviceInterfaces', 'LPPLP', 'B') 51 | @setupDiGetDeviceInterfaceDetail = Win32API.new('setupapi', 'SetupDiGetDeviceInterfaceDetail', 'LPPLPP', 'B') 52 | @setupDiDestroyDeviceInfoList = Win32API.new('setupapi', 'SetupDiDestroyDeviceInfoList', 'L', 'B') 53 | end 54 | 55 | def sleep(seconds) 56 | @sleep.call(seconds*1000) 57 | end 58 | 59 | def open 60 | 5.times do 61 | sleep(3) 62 | path = get_iboot 63 | puts path 64 | #@h = @createFile.call(path, 0xc0000000, 0x3, nil, 0x3, 0x40000000, 0) 65 | @h = @createFile.call(path, 0xc0000000, 0x3, nil, 0x3, 0, nil) 66 | break if @h > 0 67 | puts "wait for reboot" 68 | end 69 | set_interface(1, 0) 70 | @h 71 | end 72 | 73 | def close 74 | @closeHandle.call(@h) 75 | end 76 | 77 | def get_dfu 78 | uuid = [0xB8085869, 0xFEB9, 0x404B, 0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54].pack('LSSCCCCCCCC') # DFU 79 | get_devpath(uuid) 80 | end 81 | 82 | def get_iboot 83 | uuid = [0xED82A167, 0xD61A, 0x4AF6, 0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76].pack('LSSCCCCCCCC') # iBoot 84 | get_devpath(uuid) 85 | end 86 | 87 | def get_devpath(uuid) 88 | dev_path = "" 89 | 90 | hdi = @setupDiGetClassDevs.call(uuid, nil, nil, 0x12) 91 | idx = 0 92 | loop do 93 | spdid = [0x18+0x4].pack('L')+[0x00].pack('C')*0x18 94 | @setupDiEnumDeviceInterfaces.call(hdi, nil, uuid, idx, spdid) 95 | error = @getLastError.call() 96 | break if error == 259 97 | idx += 1 98 | required_size = [0x00].pack('L') 99 | @setupDiGetDeviceInterfaceDetail.call(hdi, spdid, nil, 0, required_size, nil) 100 | buffer_size = required_size.unpack('L')[0] 101 | spdidd = [0x5].pack('L') + [0x00].pack('C')*(buffer_size-0x4) # here size must be 0x5 102 | @setupDiGetDeviceInterfaceDetail.call(hdi, spdid, spdidd, buffer_size, nil, nil) 103 | #pp spdidd 104 | dev_path = spdidd[4..-1].to_s 105 | end 106 | @setupDiDestroyDeviceInfoList.call(hdi) 107 | dev_path 108 | end 109 | 110 | def send_command(cmd, request=0) 111 | packet = [0x40, request, 0, 0, cmd.size+1].pack('CCSSS') 112 | packet += "#{cmd}\0" 113 | 114 | control_io(cmd.size, packet) 115 | end 116 | 117 | def recv_command(length=0x400) 118 | packet = [0xc0, 0, 0, 0, length].pack('CCSSS') 119 | packet += "\0"*length 120 | 121 | control_io(length, packet) 122 | end 123 | 124 | def send_file(filename) 125 | control_transfer(0x41, 0, 0, 0, 0) 126 | total_size = File.stat(filename).size 127 | packet_size = 0 128 | File.open(filename, 'r') do |fp| 129 | while buffer = fp.read(0x8000) do 130 | bulk_transfer(buffer) 131 | ack 132 | packet_size += buffer.size 133 | puts "#{packet_size}/#{total_size}" 134 | end 135 | end 136 | end 137 | 138 | def reset 139 | bytes_transferred = [0x00].pack('L') 140 | @deviceIoControl.call(@h, 0x22000C, nil, 0, nil, 0, bytes_transferred, nil) 141 | end 142 | 143 | def ack 144 | packet = "\0"*4 145 | bytes_transferred = [0x00].pack('L') 146 | @deviceIoControl.call(@h, 0x2200B8, packet, packet.size, packet, packet.size, bytes_transferred, nil); 147 | packet.unpack('L')[0] 148 | end 149 | 150 | def set_config(config) 151 | #00 09 01 00 00 00 00 00 152 | control_transfer(0x00, 0x09, config, 0, 0) 153 | end 154 | 155 | def get_usb_description(value, index, length) 156 | control_transfer(0x80, 0x06, value, index, length) 157 | end 158 | 159 | def set_interface(interface, alt_setting) 160 | #01 0b 00 00 01 00 00 00 161 | control_transfer(0x01, 0x0b, alt_setting, interface, 0) 162 | end 163 | 164 | def control_transfer(request_type, request, value, index, length) 165 | packet = [request_type, request, value, index, length].pack('CCSSS') 166 | packet += "\x00" * length if length > 0 167 | 168 | control_io(length, packet) 169 | end 170 | 171 | def control_io(length, packet) 172 | bytes_transferred = [0x00].pack('L') 173 | @deviceIoControl.call(@h, 0x2200A0, packet, packet.size, packet, packet.size, bytes_transferred, nil) 174 | #pp "control_io", bytes_transferred 175 | bytes_length = bytes_transferred.unpack('L')[0] 176 | packet[0..bytes_length] 177 | end 178 | 179 | def bulk_transfer(packet) 180 | bytes_transferred = [0x00].pack('L') 181 | @deviceIoControl.call(@h, 0x2201B6, packet, packet.size, packet, packet.size, bytes_transferred, nil) 182 | bytes_transferred.unpack('L')[0] 183 | end 184 | 185 | # not used 186 | #def control_io_async(handle, length, packet, timeout=5000) 187 | # event = @createEvent.Call(nil, 1, 0, nil) 188 | # overlapped = [0, 0, 0, 0, event].pack("L*") 189 | # bytes_transferred = [0x00].pack('L') 190 | # @deviceIoControl.call(handle, 0x2200A0, packet, packet.size, packet, packet.size, bytes_transferred, overlapped) 191 | # @waitForSingleObject.call(event, timeout) 192 | # ret = @getOverlappedResult.Call(handle, overlapped, bytes_transferred, 0) 193 | # @closeHandle.call(event) 194 | # @cancelIo.call(handle) unless ret == 1 195 | # puts "cancelIo" unless ret == 1 196 | # 197 | # bytes_transferred.unpack('L')[0] 198 | #end 199 | 200 | #def bulk_transfer_async(handle, packet, timeout=3000) 201 | # event = @createEvent.Call(nil, 1, 0, nil) 202 | # overlapped = [0, 0, 0, 0, event].pack("L*") 203 | # @deviceIoControl.call(handle, 0x2201B6, packet, packet.size, packet, packet.size, nil, overlapped) 204 | # #@waitForSingleObject.call(event, timeout) 205 | # bytes_transferred = [0x00].pack('L') 206 | # ret = @getOverlappedResult.Call(handle, overlapped, bytes_transferred, 1) 207 | # @closeHandle.call(event) 208 | # bytes_transferred.unpack('L')[0] 209 | #end 210 | 211 | end 212 | -------------------------------------------------------------------------------- /irestore.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'iservice' 8 | require 'openssl' 9 | require 'ipsw_ext' 10 | require 'pp' 11 | 12 | #define WAIT_FOR_STORAGE 11 13 | #define CREATE_PARTITION_MAP 12 14 | #define CREATE_FILESYSTEM 13 15 | #define RESTORE_IMAGE 14 16 | #define VERIFY_RESTORE 15 17 | #define CHECK_FILESYSTEM 16 18 | #define MOUNT_FILESYSTEM 17 19 | #define FLASH_NOR 19 20 | #define UPDATE_BASEBAND 20 21 | #define FINIALIZE_NAND 21 22 | #define MODIFY_BOOTARGS 26 23 | #define LOAD_KERNEL_CACHE 27 24 | #define PARTITION_NAND_DEVICE 28 25 | #define WAIT_FOR_NAND 29 26 | #define UNMOUNT_FILESYSTEM 30 27 | #define WAIT_FOR_DEVICE 33 28 | #define LOAD_NOR 36 29 | 30 | class RestoreService < DeviceService 31 | 32 | def output_progress(operation, progress) 33 | steps = { 34 | 11 => "Waiting for storage device", 35 | 12 => "Creating partition map", 36 | 13 => "Creating filesystem", 37 | 14 => "Restoring image", 38 | 15 => "Verifying restore", 39 | 16 => "Checking filesystems", 40 | 17 => "Mounting filesystems", 41 | 19 => "Flashing NOR", 42 | 20 => "Updating baseband", 43 | 21 => "Finalizing NAND epoch update", 44 | 26 => "Modifying persistent boot-args", 45 | 27 => "Unmounting filesystems", 46 | 28 => "Partition NAND device", 47 | 29 => "Waiting for NAND", 48 | 30 => "Waiting for device", 49 | 33 => "Loading kernelcache", 50 | 36 => "Loading NOR data to flash", 51 | # return "Unknown operation"; 52 | } 53 | puts "#{steps[operation]} (#{operation}) with progress #{progress}" 54 | end 55 | 56 | def query_debug_info 57 | obj = { 58 | "Request" => "QueryValue", 59 | "QueryKey" => "SavedDebugInfo", 60 | } 61 | write_plist(@socket, obj) 62 | end 63 | 64 | def send_start_restore_request 65 | 66 | #RequestStartRestore 67 | #RestoreOptions 68 | # 69 | #AuthInstallRestoreBehaviorErase 70 | #AutoBootDelay0 71 | #BootImageFile018-7919-343.dmg 72 | #CreateFilesystemPartitions 73 | #DFUFileTypeRELEASE 74 | #DataImage 75 | #DeviceTreeFileDeviceTree.n88ap.img3 76 | #FirmwareDirectoryFirmware 77 | #FlashNOR 78 | #KernelCacheFilekernelcache.release.n88 79 | #NORImagePathall_flash.n88ap.production 80 | #PersonalizedRestoreBundlePath 81 | #RestoreBootArgs 82 | #rd=md0 nand-enable-reformat=1 -progress 83 | #RestoreBundlePath 84 | #RootToInstall 85 | #SourceRestoreBundlePath 86 | #SystemImage 87 | #SystemPartitionPadding 88 | # 89 | #16160 90 | #32320 91 | #880 92 | # 93 | #UUIDE6B885AE-227D-4D46-93BF-685F701313C5 94 | #UpdateBaseband 95 | #UserLocalezh_CN.. 96 | #RestoreProtocolVersion12 97 | 98 | obj = { 99 | "Request" => "StartRestore", 100 | "RestoreProtocolVersion" => 12, 101 | } 102 | write_plist(@socket, obj) 103 | end 104 | 105 | def send_nor_data(ipsw_info, upgrade_baseband = true) 106 | puts "Got request for NOR data" 107 | 108 | other_nor_data = File.open(ipsw_info[:file_manifest]).each_line.reject { |x| x =~ /^LLB/ }.map do |line| 109 | fullpath = File.join(ipsw_info[:file_imgdir], line.split("\n")[0]) 110 | nor_data = File.open(fullpath).read 111 | nor_data.blob = true 112 | nor_data 113 | end 114 | 115 | response = {"NorImageData" => other_nor_data} 116 | if upgrade_baseband then 117 | llb_data = File.open(ipsw_info[:file_llb]).read 118 | llb_data.blob = true 119 | response["LlbImageData"] = llb_data 120 | end 121 | write_plist(@socket, response) 122 | end 123 | 124 | def send_kernel_cache(ipsw_info) 125 | puts "Got request for KernelCache data" 126 | 127 | kernel_data = File.open(ipsw_info[:file_kernelcache]).read 128 | kernel_data.blob = true 129 | response = {"KernelCacheFile" => kernel_data} 130 | write_plist(@socket, response) 131 | end 132 | 133 | def send_root_ticket 134 | puts "Got request for RootTicket data" 135 | 136 | root_ticket_data = File.open(FILE_AP_TICKET).read 137 | root_ticket_data.blob = true 138 | response = {"RootTicketData" => root_ticket_data} 139 | write_plist(@socket, response) 140 | end 141 | 142 | def start_asr(ipsw_info) 143 | puts "Got request for system image data" 144 | asr = ASRService.new(PORT_ASR) 145 | asr.start(ipsw_info) 146 | end 147 | 148 | def start_restore(ipsw_info) 149 | #p "query_debug_info" 150 | #query_debug_info() 151 | 152 | p "starting restore" 153 | send_start_restore_request() 154 | 155 | p "loop..." 156 | loop do 157 | plist = read_plist(@socket) 158 | p "got plist", plist 159 | 160 | if plist["MsgType"] =="DataRequestMsg" 161 | puts "DataRequestMsg" 162 | 163 | data_type = plist["DataType"] 164 | if data_type == "SystemImageData" 165 | start_asr(ipsw_info) 166 | elsif data_type == "NORData" 167 | send_nor_data(ipsw_info) 168 | elsif data_type == "KernelCache" 169 | send_kernel_cache(ipsw_info) 170 | elsif data_type == "RootTicket" 171 | send_root_ticket() 172 | end 173 | elsif plist["MsgType"] == "ProgressMsg" 174 | output_progress(plist["Operation"], plist["Progress"]) 175 | elsif plist["MsgType"] == "StatusMsg" 176 | puts "Got status message: #{plist.inspect}" 177 | # break if plist["Status"] == 0 178 | break 179 | end 180 | end 181 | end 182 | 183 | end 184 | 185 | class ASRService < DeviceService 186 | 187 | def send_payload_info(crc_chunk_size, payload_size) 188 | #Checksum Chunk Size131072 189 | #FEC Slice Stride40 190 | #Packet Payload Size1450 191 | #Packets Per FEC25 192 | #Payload 193 | #Port1 194 | #Size677601280 195 | #Stream ID1 196 | #Version1 197 | 198 | obj ={ 199 | "Checksum Chunk Size" => crc_chunk_size, 200 | "FEC Slice Stride" => 40, 201 | "Packet Payload Size" => 1450, 202 | "Packets Per FEC" => 25, 203 | "Payload" => { 204 | "Port" => 1, 205 | "Size" => payload_size, 206 | }, 207 | "Stream ID" => 1, 208 | "Version" => 1 209 | } 210 | 211 | write_plist(obj) 212 | end 213 | 214 | def send_payload(f, crc_chunk_size) 215 | puts "Sending payload" 216 | f.seek(0) 217 | sent_len = 0 218 | total_len = f.stat.size 219 | while data = f.read(crc_chunk_size) do 220 | sha1 = OpenSSL::Digest::Digest.new("SHA1").digest(data) 221 | buffer = data + sha1 222 | @socket.write(buffer) 223 | sent_len += data.size 224 | puts "#{sent_len}/#{total_len}" 225 | end 226 | puts "Sending payload done." 227 | end 228 | 229 | def start(ipsw_info) 230 | 231 | p "start ASR." 232 | f = File.open(ipsw_info[:file_restoredmg]) 233 | payload_size = f.stat.size 234 | crc_chunk_size = 0x20000 235 | 236 | send_payload_info(crc_chunk_size, payload_size) 237 | 238 | while plist = read_plist do 239 | if plist["Command"] == "OOBData" 240 | oob_length = plist["OOB Length"] 241 | oob_offset = plist["OOB Offset"] 242 | puts "Sending #{oob_length} OOB bytes from offset #{oob_offset}" 243 | f.seek(oob_offset) 244 | @socket.write(f.read(oob_length)) 245 | elsif plist["Command"] == "Payload" 246 | send_payload(f, crc_chunk_size) 247 | break 248 | else 249 | puts "Unknown ASR command #{plist.inspect}" 250 | end 251 | end 252 | end 253 | 254 | def read_plist 255 | buffer = "" 256 | 257 | while read_buffer = @socket.gets do 258 | puts "Read: #{read_buffer.inspect}" 259 | buffer << read_buffer 260 | break if read_buffer =~ /<\/plist>/ 261 | end 262 | 263 | PropertyList.load(buffer) 264 | end 265 | 266 | def write_plist(obj) 267 | payload = PropertyList.dump(obj, :xml1) 268 | @socket.write(payload) 269 | end 270 | 271 | end 272 | 273 | def do_restore ipsw_info 274 | restore = RestoreService.new(PORT_RESTORE) 275 | restore.start_restore(ipsw_info) 276 | 277 | p "reboot..." 278 | end 279 | 280 | 281 | if __FILE__ == $0 282 | ipsw_info = get_ipsw_info("n88ap", "ios5_0") 283 | #ipsw_info = get_ipsw_info("m68ap", "ios3_1_3") 284 | do_restore ipsw_info 285 | end 286 | -------------------------------------------------------------------------------- /iservice.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | $: << File.dirname(__FILE__) 5 | 6 | require 'rubygems' 7 | require 'socket' 8 | require 'openssl' 9 | require 'pp' 10 | require 'stringio' 11 | require 'plist_ext' 12 | require 'rexml/document' 13 | 14 | PORT_ASR = 0x3930 # 14540 15 | PORT_RESTORE = 0x7ef2 16 | 17 | # com.apple.mobile.lockdown 18 | # com.apple.mobile.iTunes 19 | # com.apple.mobile.battery 20 | # com.apple.springboard.curvedBatteryCapacity 21 | # com.apple.mobile.internal 22 | # com.apple.mobile.debug 23 | # com.apple.mobile.restriction 24 | # com.apple.mobile.sync_data_class 25 | # com.apple.mobile.data_sync 26 | # com.apple.mobile.nikita 27 | # com.apple.fairplay 28 | # com.apple.international 29 | # com.apple.disk_usage 30 | # and after xcode has done stuff: com.apple.xcode.developerdomain 31 | 32 | AMSVC_AFC = "com.apple.afc" 33 | AMSVC_BACKUP = "com.apple.mobilebackup" 34 | AMSVC_CRASH_REPORT_COPY = "com.apple.crashreportcopy" 35 | AMSVC_DEBUG_IMAGE_MOUNT = "com.apple.mobile.debug_image_mount" 36 | AMSVC_NOTIFICATION_PROXY = "com.apple.mobile.notification_proxy" 37 | AMSVC_INSTALLATION_PROXY = "com.apple.mobile.installation_proxy" 38 | AMSVC_PURPLE_TEST = "com.apple.purpletestr" 39 | AMSVC_SOFTWARE_UPDATE = "com.apple.mobile.software_update" 40 | AMSVC_SYNC = "com.apple.mobilesync" 41 | AMSVC_SCREENSHOT = "com.apple.screenshotr" 42 | AMSVC_SYSLOG_RELAY = "com.apple.syslog_relay" 43 | AMSVC_SYSTEM_PROFILER = "com.apple.mobile.system_profiler" 44 | 45 | #socat -v tcp-l:27015,reuseaddr,fork unix:/var/run/usbmuxd & 46 | module DeviceSocket 47 | 48 | def setup 49 | # @socket = UNIXSocket.new("/var/run/usbmuxd") 50 | socket = TCPSocket.new("127.0.0.1", 27015) 51 | 52 | @tag = 0 53 | obj = {"MessageType" => "Listen"} 54 | data = PropertyList.dump(obj, :xml1) 55 | 56 | send_packet(socket, 8, data) 57 | # send_packet(@socket, 3, "") 58 | recv_packet(socket) 59 | # 60 | # DeviceID1 61 | # 62 | # ProductID4756 63 | # SerialNumber74f5014572b194c356c8157d6221bd9c84da104c 64 | # 65 | # 66 | p "Please unplug your device, then plug it back in" 67 | 68 | data = recv_packet(socket)[2] 69 | result = PropertyList.load(data) 70 | 71 | @device_id = result['DeviceID'] 72 | product_id = result['Properties']['ProductID'] 73 | serial_no = result['Properties']['SerialNumber'] 74 | 75 | p @device_id, product_id, serial_no 76 | 77 | puts "Device ID: 0x#{@device_id.to_s(16)}" 78 | socket 79 | end 80 | 81 | def open_usbmuxd(port) 82 | done = false 83 | @tag = 0 84 | until done do 85 | puts "Retrying connection to port #{port}..." 86 | 87 | # socket = UNIXSocket.new($path) 88 | socket = TCPSocket.new("127.0.0.1", 27015) 89 | 90 | # DeviceID5 91 | # MessageTypeConnect 92 | # PortNumber32498 93 | # obj = {"BundleID"=>PLIST_BUNDLE_ID , "ClientVersionString"=>PLIST_CLIENT_VERSION_STRING, "ProgName"=> PLIST_PROGNAME, 94 | # "MessageType" => "Connect", "DeviceID" => @device_id, "PortNumber" => port } 95 | obj = {"MessageType" => "Connect", "DeviceID" => @device_id, "PortNumber" => port } 96 | data = PropertyList.dump(obj, :xml1) 97 | 98 | send_packet(socket, 8, data) 99 | # send_packet(@socket, 2, [@device_id, port, 0].pack("Vnn")) 100 | data = recv_packet(socket)[2] 101 | # MessageTypeResultNumber0/dict> 102 | result = PropertyList.load(data) 103 | 104 | done = result['Number'] == 0 105 | @tag += 1 106 | end 107 | puts "Connected to port #{port}" 108 | 109 | socket 110 | end 111 | 112 | def write_plist(socket, data, fmt=:xml1) 113 | if data.kind_of?(Hash) || data.kind_of?(Array) 114 | if fmt == :xml1 115 | data = PropertyList.dump(data, fmt) 116 | p "==write_plist==", data.length 117 | else 118 | pp "==write_plist==", data.length 119 | data = PropertyList.dump(data, fmt) 120 | end 121 | end 122 | socket.write([data.length].pack("N") + data) 123 | end 124 | 125 | def read_plist(socket, fmt=:xml1) 126 | buffer = socket.read(4) 127 | if buffer 128 | size = buffer.unpack("N")[0] 129 | buffer = socket.read(size) 130 | 131 | # TODO: a little patch for a plist include '\u0000' 132 | p buffer 133 | buffer = buffer.gsub("\000", "") 134 | buffer = buffer.gsub("\u0000", "") 135 | # xmldoc = REXML::Document.new(buffer) 136 | # p REXML::XPath.first(xmldoc, "//").to_s 137 | 138 | data = PropertyList.load(buffer) 139 | 140 | if fmt == :xml1 141 | p "==read_plist==#{buffer}" 142 | else 143 | pp "==read_plist==", data 144 | end 145 | return data 146 | end 147 | end 148 | 149 | def send_packet(socket, packet_type, data) 150 | packet = [data.length + 16, 1, packet_type, @tag].pack("V4") + data 151 | p "==send_packet==#{packet}" 152 | socket.write(packet) 153 | end 154 | 155 | def recv_packet(socket) 156 | header = socket.read(16) 157 | packet_length, unk, packet_type, tag = header.unpack("V4") 158 | data = socket.read(packet_length - 16) 159 | p "==recv_packet==#{data}" 160 | [packet_type, tag, data] 161 | end 162 | end 163 | 164 | class DeviceRelay 165 | 166 | include DeviceSocket 167 | 168 | def initialize 169 | @root_private_key = OpenSSL::PKey::RSA.new(2048) 170 | @host_private_key = OpenSSL::PKey::RSA.new(2048) 171 | @host_id = gen_hostid 172 | @use_ssl = false 173 | 174 | # @socket = UNIXSocket.new($path) 175 | # @socket = TCPSocket.new("127.0.0.1", 27015) 176 | setup 177 | @socket = open_usbmuxd(PORT_RESTORE) 178 | end 179 | 180 | def ssl_enable(use_ssl) 181 | @use_ssl = use_ssl 182 | end 183 | 184 | def get_value(key="", domain="") 185 | obj = {"Request" => "GetValue"} 186 | obj = obj.merge(key == "" ? {} : {"Key" => key}) 187 | obj = obj.merge(domain == "" ? {} : {"Domain" => domain}) 188 | if @use_ssl 189 | write_plist(@ssl, obj) 190 | plist = read_plist(@ssl) 191 | else 192 | write_plist(@socket, obj) 193 | plist = read_plist(@socket) 194 | end 195 | # KeyDevicePublicKey 196 | # RequestGetValue 197 | # ResultSuccess 198 | # Value... 199 | plist["Value"] 200 | end 201 | 202 | def query_type 203 | obj = {"ProtocolVersion"=>"2", "Request" => "QueryType" } 204 | write_plist(@socket, obj) 205 | read_plist(@socket) 206 | end 207 | 208 | def pair_device(pub_key) 209 | # 210 | @root_ca_cert, @host_cert, @device_cert = gen_cert(pub_key) 211 | 212 | root_pem = @root_ca_cert.to_pem 213 | device_pem = @device_cert.to_pem 214 | host_pem = @host_cert.to_pem 215 | # pub_key.blob = true 216 | # root_pem.blob = true 217 | # device_pem.blob = true 218 | # host_pem.blob = true 219 | p "host_id:", @host_id 220 | 221 | certs = {"DeviceCertificate" => StringIO.new(device_pem), 222 | "DevicePublicKey" => StringIO.new(pub_key), 223 | "HostCertificate" => StringIO.new(host_pem), 224 | "HostID" => @host_id, 225 | "RootCertificate" => StringIO.new(root_pem) 226 | } 227 | # certs = {"DeviceCertificate" => device_pem, 228 | # "DevicePublicKey" => pub_key, 229 | # "HostCertificate" => host_pem, 230 | # "HostID" => @host_id, 231 | # "RootCertificate" => root_pem 232 | # } 233 | obj = {"ProtocolVersion"=> "2", "PairRecord"=>certs, "Request" => "Pair" } 234 | write_plist(@socket, obj) 235 | read_plist(@socket) 236 | # EscrowBag... 237 | # RequestPair 238 | # ResultSuccess 239 | end 240 | 241 | def validate_pair(pub_key) 242 | root_pem = @root_ca_cert.to_pem 243 | device_pem = @device_cert.to_pem 244 | host_pem = @host_cert.to_pem 245 | pub_key.blob = true 246 | root_pem.blob = true 247 | device_pem.blob = true 248 | host_pem.blob = true 249 | p "host_id:", @host_id 250 | 251 | certs = {"DeviceCertificate" => device_pem, 252 | "DevicePublicKey" => pub_key, 253 | "HostCertificate" => host_pem, 254 | "HostID" => @host_id, 255 | "RootCertificate" => root_pem 256 | } 257 | obj = {"ProtocolVersion" => "2", "PairRecord" => certs, "Request" => "ValidatePair" } 258 | write_plist(@socket, obj) 259 | read_plist(@socket) 260 | # RequestValidatePair 261 | # ResultSuccess 262 | end 263 | 264 | def start_session 265 | obj = {"ProtocolVersion" => "2", "HostID"=>@host_id, "Request" => "StartSession"} 266 | write_plist(@socket, obj) 267 | plist = read_plist(@socket) 268 | # EnableSessionSSL 269 | # RequestStartSession 270 | # ResultSuccess 271 | # SessionIDC9223F5C-7FEC-4D52-8BA5-11BEC305C7B1 272 | ctx = OpenSSL::SSL::SSLContext.new(:SSLv3) 273 | ctx.client_cert_cb = Proc.new{|ssl| 274 | p "====ctx.client_cert_cb====#{ssl}" 275 | [@host_cert, @host_private_key] 276 | } 277 | # ctx.cert = @host_cert 278 | # ctx.key = @host_private_key 279 | # ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE 280 | # ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT 281 | @ssl = OpenSSL::SSL::SSLSocket.new(@socket, ctx) 282 | @ssl.sync_close = true 283 | p "connect..." 284 | @ssl.connect 285 | 286 | plist["SessionID"] 287 | end 288 | 289 | def stop_session(session_id) 290 | obj = {"Request" => "StopSession", "SessionID"=>session_id} 291 | write_plist(@ssl, obj) 292 | plist = read_plist(@ssl) 293 | #goodbye 294 | goodbye 295 | @ssl.close 296 | end 297 | 298 | def start_service(serv_name) 299 | # 300 | # RequestStartService 301 | # Service%s 302 | obj = {"Request"=>"StartService", "Service" => serv_name } 303 | write_plist(@ssl, obj) 304 | plist = read_plist(@ssl) 305 | # Port49791 306 | # RequestStartService 307 | # ResultSuccess 308 | # Servicecom.apple.afc 309 | plist["Port"] 310 | end 311 | 312 | def goodbye 313 | # "RequestGoodbye\0" 314 | obj = {"Request" => "Goodbye" } 315 | write_plist(@socket, obj) 316 | # read_plist(@socket) 317 | end 318 | 319 | def gen_cert(pub_key) 320 | p "pub_key:#{pub_key}" 321 | digest = OpenSSL::Digest::Digest.new("SHA1") 322 | 323 | root_ca_cert = OpenSSL::X509::Certificate.new 324 | root_ca_cert.serial = 0 325 | root_ca_cert.not_before = Time.now 326 | root_ca_cert.not_after = Time.now + 60 * 60 * 24 * 365 * 10 327 | root_ca_cert.public_key = @root_private_key.public_key 328 | ef = OpenSSL::X509::ExtensionFactory.new 329 | root_ca_cert.extensions = [ 330 | ef.create_extension("basicConstraints","CA:TRUE", true), 331 | ] 332 | 333 | key = OpenSSL::PKey::RSA.new(pub_key) 334 | p "modulus:#{key.n}, exponent:#{key.e}" 335 | 336 | device_cert = OpenSSL::X509::Certificate.new 337 | device_cert.public_key = key.public_key 338 | device_cert.serial = 0 339 | device_cert.not_before = Time.now 340 | device_cert.not_after = Time.now + 60 * 60 * 24 * 365 * 10 341 | ef = OpenSSL::X509::ExtensionFactory.new 342 | device_cert.extensions = [ 343 | ef.create_extension("basicConstraints","CA:FALSE", true), 344 | ef.create_extension("keyUsage","Digital Signature, Key Encipherment", true), 345 | ] 346 | device_cert.sign(@root_private_key, digest) 347 | 348 | host_cert = OpenSSL::X509::Certificate.new 349 | host_cert.public_key = @host_private_key.public_key 350 | host_cert.serial = 0 351 | host_cert.not_before = Time.now 352 | host_cert.not_after = Time.now + 60 * 60 * 24 * 365 * 10 353 | ef = OpenSSL::X509::ExtensionFactory.new 354 | host_cert.extensions = [ 355 | ef.create_extension("basicConstraints","CA:FALSE", true), 356 | ef.create_extension("keyUsage","Digital Signature, Key Encipherment", true), 357 | ] 358 | 359 | # gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE); 360 | host_cert.sign(@root_private_key, digest) 361 | 362 | [root_ca_cert, host_cert, device_cert] 363 | end 364 | 365 | def rand_digit(l) 366 | "%0#{l}d" % rand(10 ** l) 367 | end 368 | 369 | def gen_hostid 370 | # [8,4,4,4,12].map {|n| rand_hex_3(n)}.join('-') 371 | [8,18].map {|n| rand_digit(n)}.join('-') 372 | end 373 | 374 | end 375 | 376 | class DeviceService 377 | 378 | include DeviceSocket 379 | 380 | def initialize(port) 381 | # @socket = UNIXSocket.new($path) 382 | # @socket = TCPSocket.new("127.0.0.1", 27015) 383 | setup 384 | @socket = open_usbmuxd(port) 385 | end 386 | end 387 | 388 | --------------------------------------------------------------------------------