219 lines
4.9 KiB
PHP
Executable File
219 lines
4.9 KiB
PHP
Executable File
<?php
|
|
|
|
function run_query($headerQuery, $httpQuery, &$headers, &$output, $postvars='', $verbose=false) {
|
|
$ch = curl_init();
|
|
|
|
$headers = [];
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $httpQuery);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 99999*60);
|
|
|
|
if (strlen($postvars) > 0) {
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars);
|
|
}
|
|
|
|
if ($verbose) {
|
|
$f = tmpfile();
|
|
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
|
curl_setopt($ch, CURLOPT_STDERR, $f);
|
|
}
|
|
|
|
$tofile = is_resource($output) && (get_resource_type($output) === 'stream' || get_resource_type($output) === 'file');
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
if ($tofile)
|
|
curl_setopt($ch, CURLOPT_FILE, $output);
|
|
|
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$headers) {
|
|
$len = strlen($header);
|
|
$header = explode(':', $header, 2);
|
|
if (count($header) < 2) // ignore invalid headers
|
|
return $len;
|
|
|
|
$name = strtolower(trim($header[0]));
|
|
if (!array_key_exists($name, $headers))
|
|
$headers[$name] = [trim($header[1])];
|
|
else
|
|
$headers[$name][] = trim($header[1]);
|
|
|
|
return $len;
|
|
});
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerQuery);
|
|
|
|
if ($tofile)
|
|
curl_exec($ch);
|
|
else
|
|
$output = curl_exec($ch);
|
|
|
|
curl_close($ch);
|
|
|
|
if ($verbose) {
|
|
fseek($f, 0);
|
|
echo fread($f, 32*1024);
|
|
fclose($f);
|
|
}
|
|
}
|
|
|
|
function dump_query($name, $headers, $output) {
|
|
print 'Performed: ' . $name . PHP_EOL;
|
|
|
|
foreach ($headers as $name => $value) {
|
|
print $name . ': ' . $value[0] . PHP_EOL;
|
|
}
|
|
|
|
print PHP_EOL . 'Output:' . PHP_EOL;
|
|
print $output . PHP_EOL;
|
|
}
|
|
|
|
function setup_session() {
|
|
$headersReq = [
|
|
'Host: osrecovery.apple.com',
|
|
'Connection: close',
|
|
'User-Agent: InternetRecovery/1.0'
|
|
];
|
|
|
|
$output = '';
|
|
$headers = [];
|
|
|
|
run_query($headersReq, 'http://osrecovery.apple.com/', $headers, $output);
|
|
dump_query('setup_session', $headers, $output);
|
|
|
|
$cookie = '';
|
|
|
|
foreach ($headers as $name => $value) {
|
|
if ($name == 'set-cookie') {
|
|
$cookie = explode(';', $value[0])[0];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $cookie;
|
|
}
|
|
|
|
function obtain_images($session, $board, $mlb, $diag = false) {
|
|
$headersReq = [
|
|
'Host: osrecovery.apple.com',
|
|
'Connection: close',
|
|
'User-Agent: InternetRecovery/1.0',
|
|
'Cookie: ' . $session,
|
|
'Content-Type: text/plain',
|
|
'Expect:'
|
|
];
|
|
|
|
$output = '';
|
|
$headers = [];
|
|
|
|
$postvars =
|
|
'cid=F4FDBCCF36190DD4' . PHP_EOL .
|
|
// MLB, board serial number
|
|
'sn=' . $mlb . PHP_EOL .
|
|
// board-id
|
|
'bid=' . $board . PHP_EOL .
|
|
'k=6E7D753C11E1F9652B99D3DB8C80A49E82143EA027CBA516E3E18B3A4FFDCD58' . PHP_EOL .
|
|
'fg=80F6E802A09B8B553202EE0D37AE64662ACFAF30B111E1984C01F64551BB7EFE'
|
|
;
|
|
|
|
$images = [ 'image' => [], 'chunklist' => [] ];
|
|
|
|
if ($diag) {
|
|
run_query($headersReq, 'http://osrecovery.apple.com/InstallationPayload/Diagnostics', $headers, $output, $postvars);
|
|
} else {
|
|
run_query($headersReq, 'http://osrecovery.apple.com/InstallationPayload/RecoveryImage', $headers, $output, $postvars);
|
|
}
|
|
dump_query('obtain_images', $headers, $output);
|
|
|
|
$fields = explode("\n", $output);
|
|
|
|
foreach ($fields as $field) {
|
|
$pair = explode(': ', $field);
|
|
if (count($pair) != 2)
|
|
continue;
|
|
|
|
$name = $pair[0];
|
|
$value = $pair[1];
|
|
|
|
if ($name == 'AU')
|
|
$images['image']['link'] = $value;
|
|
else if ($name == 'AH')
|
|
$images['image']['hash'] = $value;
|
|
else if ($name == 'AT')
|
|
$images['image']['cookie'] = $value;
|
|
else if ($name == 'CU')
|
|
$images['chunklist']['link'] = $value;
|
|
else if ($name == 'CH')
|
|
$images['chunklist']['hash'] = $value;
|
|
else if ($name == 'CT')
|
|
$images['chunklist']['cookie'] = $value;
|
|
}
|
|
|
|
return $images;
|
|
}
|
|
|
|
function download_images($images, $diag = false) {
|
|
foreach ($images as $imagename => $imagefields) {
|
|
$headersReq = [
|
|
'Host: ' . parse_url($imagefields['link'], PHP_URL_HOST),
|
|
'Connection: close',
|
|
'User-Agent: InternetRecovery/1.0',
|
|
'Cookie: AssetToken=' . $imagefields['cookie']
|
|
];
|
|
|
|
if ($diag)
|
|
$type = 'Diagnostics';
|
|
else
|
|
$type = 'Recovery';
|
|
|
|
if ($imagename == 'image')
|
|
$filename = $type . 'Image.dmg';
|
|
else
|
|
$filename = $type . 'Image.chunklist';
|
|
|
|
$headers = [];
|
|
$output = fopen($filename, 'w+');
|
|
|
|
print $imagefields['link'] . ' ' . $filename . PHP_EOL;
|
|
|
|
run_query($headersReq, $imagefields['link'], $headers, $output);
|
|
|
|
fclose($output);
|
|
}
|
|
}
|
|
|
|
if ($argc < 2) {
|
|
print 'Usage: php obtain_recovery.php board-id [MLB] [--diag]' . PHP_EOL;
|
|
exit(1);
|
|
}
|
|
|
|
$board = $argv[1];
|
|
$mlb = '00000000000000000';
|
|
$diag = false;
|
|
|
|
if ($argc > 2) {
|
|
if ($argv[2] == '--diag') {
|
|
$diag = true;
|
|
} else {
|
|
$mlb = $argv[2];
|
|
$diag = $argc > 3 && $argv[3] == '--diag';
|
|
}
|
|
}
|
|
|
|
$sess = setup_session();
|
|
if ($sess == '') {
|
|
print 'Failed to obtain session!' . PHP_EOL;
|
|
exit(1);
|
|
}
|
|
|
|
$images = obtain_images($sess, $board, $mlb, $diag);
|
|
|
|
if (count($images['image']) == 0 || count($images['chunklist']) == 0) {
|
|
print 'Failed to obtain images!' . PHP_EOL;
|
|
exit(1);
|
|
}
|
|
|
|
download_images($images, $diag);
|