mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-22 03:45:51 +08:00
first commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
// Detect AFC2 (Apple File Conduit 2) support
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct JailbreakDetectionResult {
|
||||
bool is_jailbroken;
|
||||
std::vector<std::string> found_folders;
|
||||
};
|
||||
|
||||
// This is because afc_read_directory accepts "/var/mobile/Media" as "/"
|
||||
std::string possible_root = "../../../../";
|
||||
|
||||
JailbreakDetectionResult detect_has_jailbroken_before(afc_client_t afc)
|
||||
{
|
||||
std::vector<std::string> jailbreak_folders = {".installed_palera1n",
|
||||
".procursus_strapped"};
|
||||
|
||||
JailbreakDetectionResult result = {false, {}};
|
||||
|
||||
char **dirs = NULL;
|
||||
if (afc_read_directory(afc, possible_root.c_str(), &dirs) ==
|
||||
AFC_E_SUCCESS) {
|
||||
for (char **dir = dirs; *dir != nullptr; ++dir) {
|
||||
std::string dirname = *dir;
|
||||
for (const auto &jb_folder : jailbreak_folders) {
|
||||
if (dirname == jb_folder) {
|
||||
result.found_folders.push_back(jb_folder);
|
||||
result.is_jailbroken = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
afc_dictionary_free(dirs);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// parse boot args from idevice info
|
||||
// NonVolatileRAM: auto-boot: dHJ1ZQ==
|
||||
// backlight-level: MTUyNg==
|
||||
// boot-args:
|
||||
// com.apple.System.tz0-size: MHg2MDAwMDA=
|
||||
// oblit-begins:
|
||||
// T2JsaXRUeXBlOiBPYmxpdGVyYXRlRGF0YVBhcnRpdGlvbi4gUmVhc29uOiB1bmtub3du
|
||||
// obliteration: aGFuZGxlX21lc3NhZ2U6IE9ibGl0ZXJhdGlvbiBDb21wbGV0ZQo=
|
||||
// PartitionType: GUID_partition_scheme
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <libimobiledevice/afc.h>
|
||||
// char *possible_jailbreak_paths[] = {
|
||||
// "/Applications/Cydia.app",
|
||||
// "/Library/MobileSubstrate/MobileSubstrate.dylib",
|
||||
// "/bin/bash",
|
||||
// "/usr/sbin/sshd",
|
||||
// "/etc/apt",
|
||||
// NULL
|
||||
// };
|
||||
#include <string>
|
||||
// This is because afc_read_directory accepts "/var/mobile/Media" as "/"
|
||||
std::string possible_root = "../../../../";
|
||||
bool detect_jailbroken(afc_client_t afc)
|
||||
{
|
||||
char **dirs = NULL;
|
||||
if (afc_read_directory(afc, (possible_root + "bin").c_str(), &dirs) ==
|
||||
AFC_E_SUCCESS) {
|
||||
// if we can loop through the directory, it means we have access to the
|
||||
// file system
|
||||
for (char **dir = dirs; *dir != nullptr; ++dir) {
|
||||
afc_dictionary_free(dirs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
afc_dictionary_free(dirs);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// https://github.com/libimobiledevice/libimobiledevice/blob/master/tools/ideviceinfo.c
|
||||
/*
|
||||
* ideviceinfo.c
|
||||
* Simple utility to show information about an attached device
|
||||
*
|
||||
* Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
|
||||
* Copyright (c) 2009 Martin Szulecki All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <plist/plist.h>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#define FORMAT_KEY_VALUE 1
|
||||
#define FORMAT_XML 2
|
||||
|
||||
static const char *domains[] = {
|
||||
"com.apple.disk_usage", "com.apple.disk_usage.factory",
|
||||
"com.apple.mobile.battery",
|
||||
/* FIXME: For some reason lockdownd segfaults on this, works sometimes
|
||||
though "com.apple.mobile.debug",. */
|
||||
"com.apple.iqagent", "com.apple.purplebuddy", "com.apple.PurpleBuddy",
|
||||
"com.apple.mobile.chaperone", "com.apple.mobile.third_party_termination",
|
||||
"com.apple.mobile.lockdownd", "com.apple.mobile.lockdown_cache",
|
||||
"com.apple.xcode.developerdomain", "com.apple.international",
|
||||
"com.apple.mobile.data_sync", "com.apple.mobile.tethered_sync",
|
||||
"com.apple.mobile.mobile_application_usage", "com.apple.mobile.backup",
|
||||
"com.apple.mobile.nikita", "com.apple.mobile.restriction",
|
||||
"com.apple.mobile.user_preferences", "com.apple.mobile.sync_data_class",
|
||||
"com.apple.mobile.software_behavior",
|
||||
"com.apple.mobile.iTunes.SQLMusicLibraryPostProcessCommands",
|
||||
"com.apple.mobile.iTunes.accessories",
|
||||
"com.apple.mobile.internal", /**< iOS 4.0+ */
|
||||
"com.apple.mobile.wireless_lockdown", /**< iOS 4.0+ */
|
||||
"com.apple.fairplay", "com.apple.iTunes", "com.apple.mobile.iTunes.store",
|
||||
"com.apple.mobile.iTunes", "com.apple.fmip", "com.apple.Accessibility",
|
||||
NULL};
|
||||
|
||||
plist_t get_device_info(const char *udid, int use_network, int simple,
|
||||
lockdownd_client_t client, idevice_t device)
|
||||
{
|
||||
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
|
||||
plist_t node = NULL;
|
||||
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
// FIXME: network support does not properly work yet
|
||||
// ret = idevice_new_with_options(&device, udid, (use_network) ?
|
||||
// IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
|
||||
ret = idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX);
|
||||
if (ret != IDEVICE_E_SUCCESS) {
|
||||
if (udid) {
|
||||
fprintf(stderr, "ERROR: Device %s not found!\n", udid);
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: No device found!\n");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (LOCKDOWN_E_SUCCESS !=
|
||||
(ldret = simple ? lockdownd_client_new(device, &client, "iDescriptor")
|
||||
: lockdownd_client_new_with_handshake(device, &client,
|
||||
"iDescriptor"))) {
|
||||
fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n",
|
||||
lockdownd_strerror(ldret), ldret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* run query and output information */
|
||||
if (lockdownd_get_value(client, NULL, NULL, &node) != LOCKDOWN_E_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: Could not get value\n");
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Change return type to void
|
||||
void get_device_info_xml(const char *udid, int use_network, int simple,
|
||||
pugi::xml_document &infoXml, lockdownd_client_t client,
|
||||
idevice_t device)
|
||||
{
|
||||
plist_t node = get_device_info(udid, use_network, simple, client, device);
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
char *xml_string = nullptr;
|
||||
uint32_t xml_length = 0;
|
||||
plist_to_xml(node, &xml_string, &xml_length);
|
||||
plist_free(node);
|
||||
|
||||
if (xml_string) {
|
||||
infoXml.load_string(xml_string);
|
||||
free(xml_string);
|
||||
}
|
||||
// No return statement needed
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "./get-media.h"
|
||||
#include "../../iDescriptor.h"
|
||||
#include <iostream>
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <string.h>
|
||||
|
||||
MediaFileTree getMediaFileTree(afc_client_t afcClient,
|
||||
lockdownd_service_descriptor_t lockdownService,
|
||||
|
||||
const std::string &path = "/")
|
||||
{
|
||||
|
||||
MediaFileTree result;
|
||||
result.currentPath = path;
|
||||
|
||||
if (afcClient == nullptr) {
|
||||
qDebug() << "AFC client is not initialized in getMediaFileTree";
|
||||
}
|
||||
|
||||
if (lockdownService == nullptr) {
|
||||
qDebug() << "Lockdown service is not initialized in getMediaFileTree";
|
||||
}
|
||||
|
||||
char **dirs = NULL;
|
||||
if (afc_read_directory(afcClient, path.c_str(), &dirs) != AFC_E_SUCCESS) {
|
||||
// afc_client_free(afcClient);
|
||||
// lockdownd_service_descriptor_free(lockdownService);
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
for (int i = 0; dirs[i]; i++) {
|
||||
std::string entryName = dirs[i];
|
||||
if (entryName == "." || entryName == "..")
|
||||
continue;
|
||||
|
||||
// Determine if entry is a directory
|
||||
char **info = NULL;
|
||||
std::string fullPath = path;
|
||||
if (fullPath.back() != '/')
|
||||
fullPath += "/";
|
||||
fullPath += entryName;
|
||||
|
||||
bool isDir = false;
|
||||
if (afc_get_file_info(afcClient, fullPath.c_str(), &info) ==
|
||||
AFC_E_SUCCESS &&
|
||||
info) {
|
||||
for (int j = 0; info[j]; j += 2) {
|
||||
if (strcmp(info[j], "st_ifmt") == 0) {
|
||||
if (strcmp(info[j + 1], "S_IFDIR") == 0)
|
||||
isDir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
afc_dictionary_free(info);
|
||||
}
|
||||
result.entries.push_back({entryName, isDir});
|
||||
}
|
||||
// TODO : Freed when device is disconnected
|
||||
// afc_client_free(afc);
|
||||
// lockdownd_service_descriptor_free(service);
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct MediaEntry {
|
||||
std::string name;
|
||||
bool isDir;
|
||||
};
|
||||
|
||||
struct MediaFileTree {
|
||||
std::vector<MediaEntry> entries;
|
||||
bool success;
|
||||
std::string currentPath;
|
||||
};
|
||||
@@ -0,0 +1,171 @@
|
||||
#include "../../iDescriptor.h"
|
||||
#include "../helpers/parse_product_type.cpp"
|
||||
#include "./detect_jailbroken.cpp"
|
||||
#include "./get-device-info.cpp"
|
||||
#include "libirecovery.h"
|
||||
#include <QDebug>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
DeviceInfo xmlToDeviceInfo(const pugi::xml_document &doc,
|
||||
afc_client_t afcClient)
|
||||
{
|
||||
DeviceInfo d;
|
||||
pugi::xml_node dict = doc.child("plist").child("dict");
|
||||
|
||||
auto safeGet = [&](const char *key) -> std::string {
|
||||
for (pugi::xml_node child = dict.first_child(); child;
|
||||
child = child.next_sibling()) {
|
||||
if (strcmp(child.name(), "key") == 0 &&
|
||||
strcmp(child.text().as_string(), key) == 0) {
|
||||
pugi::xml_node value = child.next_sibling();
|
||||
if (value)
|
||||
return value.text().as_string();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
};
|
||||
d.deviceName = safeGet("DeviceName");
|
||||
d.deviceClass = safeGet("DeviceClass");
|
||||
d.deviceColor = safeGet("DeviceColor");
|
||||
d.modelNumber = safeGet("ModelNumber");
|
||||
d.cpuArchitecture = safeGet("CPUArchitecture");
|
||||
d.buildVersion = safeGet("BuildVersion");
|
||||
d.hardwareModel = safeGet("HardwareModel");
|
||||
d.hardwarePlatform = safeGet("HardwarePlatform");
|
||||
d.ethernetAddress = safeGet("EthernetAddress");
|
||||
d.bluetoothAddress = safeGet("BluetoothAddress");
|
||||
d.firmwareVersion = safeGet("FirmwareVersion");
|
||||
d.productVersion = safeGet("ProductVersion");
|
||||
std::string _activationState = safeGet("ActivationState");
|
||||
if (_activationState == "Activated") {
|
||||
d.activationState = DeviceInfo::ActivationState::Activated;
|
||||
} else if (_activationState == "FactoryActivated") {
|
||||
d.activationState = DeviceInfo::ActivationState::FactoryActivated;
|
||||
} else if (_activationState == "Unactivated") {
|
||||
d.activationState = DeviceInfo::ActivationState::Unactivated;
|
||||
} else {
|
||||
d.activationState =
|
||||
DeviceInfo::ActivationState::Unactivated; // Default value
|
||||
}
|
||||
d.productType = parse_product_type(safeGet("ProductType"));
|
||||
d.jailbroken = detect_jailbroken(
|
||||
afcClient); // Default value, can be set later if needed
|
||||
// d.udid = safeGet("UniqueDeviceID");
|
||||
return d;
|
||||
}
|
||||
|
||||
IDescriptorInitDeviceResult init_idescriptor_device(const char *udid)
|
||||
{
|
||||
qDebug() << "Initializing iDescriptor device with UDID: "
|
||||
<< QString::fromUtf8(udid);
|
||||
IDescriptorInitDeviceResult result = {};
|
||||
|
||||
lockdownd_client_t client;
|
||||
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
lockdownd_service_descriptor_t lockdownService = nullptr;
|
||||
afc_client_t afcClient = nullptr;
|
||||
try {
|
||||
idevice_error_t ret = idevice_new_with_options(&result.device, udid,
|
||||
IDEVICE_LOOKUP_USBMUX);
|
||||
|
||||
if (ret != IDEVICE_E_SUCCESS) {
|
||||
qDebug() << "Failed to connect to device: " << ret;
|
||||
return result;
|
||||
}
|
||||
if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(
|
||||
result.device, &client, APP_LABEL))) {
|
||||
result.error = ldret;
|
||||
qDebug() << "Failed to create lockdown client: " << ldret;
|
||||
idevice_free(result.device);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (LOCKDOWN_E_SUCCESS !=
|
||||
(ldret = lockdownd_start_service(client, "com.apple.afc",
|
||||
&lockdownService))) {
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(result.device);
|
||||
qDebug() << "Failed to start AFC service: " << ldret;
|
||||
return result;
|
||||
}
|
||||
if (lockdownService) {
|
||||
qDebug() << "AFC service started successfully.";
|
||||
} else {
|
||||
qDebug() << "AFC service descriptor is null.";
|
||||
// lockdownd_client_free(result.client);
|
||||
// idevice_free(result.device);
|
||||
// return result;
|
||||
}
|
||||
|
||||
if (afc_client_new(result.device, lockdownService, &afcClient) !=
|
||||
AFC_E_SUCCESS) {
|
||||
lockdownd_service_descriptor_free(lockdownService);
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(result.device);
|
||||
qDebug() << "Failed to create AFC client: " << ldret;
|
||||
return result;
|
||||
}
|
||||
|
||||
pugi::xml_document infoXml;
|
||||
get_device_info_xml(udid, 0, 0, infoXml, client, result.device);
|
||||
|
||||
if (infoXml.empty()) {
|
||||
qDebug() << "Failed to retrieve device info XML for UDID: "
|
||||
<< QString::fromUtf8(udid);
|
||||
// Clean up resources before returning
|
||||
// afc_client_free(result.afcClient);
|
||||
// lockdownd_service_descriptor_free(result.lockdownService);
|
||||
// lockdownd_client_free(result.client);
|
||||
idevice_free(result.device);
|
||||
return result;
|
||||
}
|
||||
|
||||
// if (result.device) idevice_free(result.device);
|
||||
|
||||
result.deviceInfo = xmlToDeviceInfo(infoXml, afcClient);
|
||||
result.success = true;
|
||||
|
||||
if (afcClient)
|
||||
afc_client_free(afcClient);
|
||||
if (lockdownService)
|
||||
lockdownd_service_descriptor_free(lockdownService);
|
||||
if (client)
|
||||
lockdownd_client_free(client);
|
||||
return result;
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
qDebug() << "Exception in init_idescriptor_device: " << e.what();
|
||||
// Clean up any allocated resources
|
||||
// if (result.afcClient) afc_client_free(result.afcClient);
|
||||
// if (result.lockdownService)
|
||||
// lockdownd_service_descriptor_free(result.lockdownService);
|
||||
if (client)
|
||||
lockdownd_client_free(client);
|
||||
if (result.device)
|
||||
idevice_free(result.device);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
IDescriptorInitDeviceResultRecovery
|
||||
init_idescriptor_recovery_device(irecv_device_info *info)
|
||||
{
|
||||
IDescriptorInitDeviceResultRecovery result;
|
||||
result.deviceInfo = *info;
|
||||
uint64_t ecid = info->ecid;
|
||||
// irecv_client_t client = nullptr;
|
||||
// Docs say that clients are not long-lived, so instead of storing, we
|
||||
// create a new one each time we need it. irecv_error_t ret =
|
||||
// irecv_open_with_ecid_and_attempts(&client, ecid,
|
||||
// RECOVERY_CLIENT_CONNECTION_TRIES);
|
||||
|
||||
// if (ret != IRECV_E_SUCCESS)
|
||||
// {
|
||||
// return result;
|
||||
// }
|
||||
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* ideviceimagemounter.c
|
||||
* Mount developer/debug disk images on the device
|
||||
*
|
||||
* Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// we need to dynamically fetch dev imgs because every iOS version has a
|
||||
// different one
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define TOOL_NAME "ideviceimagemounter"
|
||||
|
||||
#include <stdlib.h>
|
||||
#define _GNU_SOURCE 1
|
||||
#define __USE_GNU 1
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <libimobiledevice-glue/sha.h>
|
||||
#include <libimobiledevice-glue/utils.h>
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libimobiledevice/mobile_image_mounter.h>
|
||||
#include <libimobiledevice/notification_proxy.h>
|
||||
#include <libtatsu/tss.h>
|
||||
#include <plist/plist.h>
|
||||
#include <printf.h>
|
||||
|
||||
static int list_mode = 0;
|
||||
static int use_network = 0;
|
||||
static int xml_mode = 0;
|
||||
static const char *udid = NULL;
|
||||
static const char *imagetype = NULL;
|
||||
|
||||
static const char PKG_PATH[] = "PublicStaging";
|
||||
static const char PATH_PREFIX[] = "/private/var/mobile/Media";
|
||||
|
||||
typedef enum {
|
||||
DISK_IMAGE_UPLOAD_TYPE_AFC,
|
||||
DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
|
||||
} disk_image_upload_type_t;
|
||||
|
||||
enum cmd_mode {
|
||||
CMD_NONE = 0,
|
||||
CMD_MOUNT,
|
||||
CMD_UNMOUNT,
|
||||
CMD_LIST,
|
||||
CMD_DEVMODESTATUS
|
||||
};
|
||||
|
||||
// int cmd = CMD_NONE;
|
||||
|
||||
#ifndef SOURCE_DIR
|
||||
#define SOURCE_DIR "."
|
||||
#endif
|
||||
static ssize_t mim_upload_cb(void *buf, size_t size, void *userdata)
|
||||
{
|
||||
return fread(buf, 1, size, (FILE *)userdata);
|
||||
}
|
||||
// TODO: cleanup
|
||||
bool mount_dev_image(char *udid)
|
||||
{
|
||||
mobile_image_mounter_client_t mim = NULL;
|
||||
int res = -1;
|
||||
size_t image_size = 0;
|
||||
lockdownd_client_t lckd = NULL;
|
||||
lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
afc_client_t afc = NULL;
|
||||
lockdownd_service_descriptor_t service = NULL;
|
||||
idevice_t device = NULL;
|
||||
|
||||
if (IDEVICE_E_SUCCESS !=
|
||||
idevice_new_with_options(&device, udid,
|
||||
(use_network) ? IDEVICE_LOOKUP_NETWORK
|
||||
: IDEVICE_LOOKUP_USBMUX)) {
|
||||
qDebug() << "ERROR: Could not create idevice!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(
|
||||
device, &lckd, TOOL_NAME))) {
|
||||
qDebug() << "ERROR: Could not connect to lockdownd service!";
|
||||
return false;
|
||||
// goto leave;
|
||||
}
|
||||
|
||||
lockdownd_error_t lerr =
|
||||
lockdownd_start_service(lckd, "com.apple.afc", &service);
|
||||
if (lerr != LOCKDOWN_E_SUCCESS) {
|
||||
qDebug() << "ERROR: Could not start AFC service!"
|
||||
<< lockdownd_strerror(lerr) << "(" << lerr << ")";
|
||||
lockdownd_client_free(lckd);
|
||||
lckd = NULL;
|
||||
idevice_free(device);
|
||||
device = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
afc_error_t rafc = afc_client_new(device, service, &afc);
|
||||
if (rafc != AFC_E_SUCCESS) {
|
||||
qDebug() << "ERROR: Could not connect to AFC!" << afc_strerror(rafc)
|
||||
<< "(" << rafc << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
char *image_path = nullptr;
|
||||
char *image_sig_path = nullptr;
|
||||
|
||||
if (asprintf(&image_path,
|
||||
"%s/resources/dev-images/15/DeveloperDiskImage.dmg",
|
||||
SOURCE_DIR) < 0) {
|
||||
qDebug() << "Out of memory constructing image path!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (asprintf(&image_sig_path,
|
||||
"%s/resources/dev-images/15/DeveloperDiskImage.dmg.signature",
|
||||
SOURCE_DIR) < 0) {
|
||||
qDebug() << "Out of memory constructing signature path!";
|
||||
free(image_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// #ifndef _WIN32
|
||||
// signal(SIGPIPE, SIG_IGN);
|
||||
// #endif
|
||||
|
||||
unsigned int device_version = idevice_get_device_version(device);
|
||||
|
||||
disk_image_upload_type_t disk_image_upload_type =
|
||||
DISK_IMAGE_UPLOAD_TYPE_AFC;
|
||||
if (device_version >= IDEVICE_DEVICE_VERSION(7, 0, 0)) {
|
||||
disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
|
||||
}
|
||||
|
||||
if (device_version >= IDEVICE_DEVICE_VERSION(16, 0, 0)) {
|
||||
uint8_t dev_mode_status = 0;
|
||||
plist_t val = NULL;
|
||||
ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi",
|
||||
"DeveloperModeStatus", &val);
|
||||
if (ldret == LOCKDOWN_E_SUCCESS) {
|
||||
plist_get_bool_val(val, &dev_mode_status);
|
||||
plist_free(val);
|
||||
}
|
||||
if (!dev_mode_status) {
|
||||
qDebug() << "ERROR: You have to enable Developer Mode on the given "
|
||||
"device in order to allowing mounting a developer disk "
|
||||
"image.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter",
|
||||
&service);
|
||||
|
||||
if (!service || service->port == 0) {
|
||||
qDebug() << "ERROR: Could not start mobile_image_mounter service!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mobile_image_mounter_new(device, service, &mim) !=
|
||||
MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
qDebug() << "ERROR: Could not connect to mobile_image_mounter!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (service)
|
||||
// {
|
||||
// lockdownd_service_descriptor_free(service);
|
||||
// service = NULL;
|
||||
// }
|
||||
|
||||
struct stat fst;
|
||||
if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
|
||||
if (!service || !service->port) {
|
||||
qDebug() << "Could not start com.apple.afc!";
|
||||
return false;
|
||||
}
|
||||
if (!afc) {
|
||||
qDebug() << "Could not connect to AFC!";
|
||||
return false;
|
||||
}
|
||||
if (service) {
|
||||
// lockdownd_service_descriptor_free(service);
|
||||
service = NULL;
|
||||
}
|
||||
}
|
||||
if (stat(image_path, &fst) != 0) {
|
||||
qDebug() << "ERROR: stat:" << image_path << ":" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
image_size = fst.st_size;
|
||||
if (device_version < IDEVICE_DEVICE_VERSION(17, 0, 0) &&
|
||||
stat(image_sig_path, &fst) != 0) {
|
||||
qDebug() << "ERROR: stat:" << image_sig_path << ":" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// lockdownd_client_free(lckd);
|
||||
// lckd = NULL;
|
||||
|
||||
mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
|
||||
plist_t result = NULL;
|
||||
|
||||
// if (cmd == CMD_LIST)
|
||||
// {
|
||||
// /* list mounts mode */
|
||||
// if (!imagetype)
|
||||
// {
|
||||
// if (device_version < IDEVICE_DEVICE_VERSION(17, 0, 0))
|
||||
// {
|
||||
// imagetype = "Developer";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// imagetype = "Personalized";
|
||||
// }
|
||||
// }
|
||||
// err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
|
||||
// if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS)
|
||||
// {
|
||||
// res = 0;
|
||||
// plist_write_to_stream(result, stdout, (xml_mode) ?
|
||||
// PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// printf("Error: lookup_image returned %d\n", err);
|
||||
// }
|
||||
// }
|
||||
|
||||
unsigned char *sig = NULL;
|
||||
size_t sig_length = 0;
|
||||
FILE *f;
|
||||
// struct stat fst;
|
||||
plist_t mount_options = NULL;
|
||||
|
||||
if (device_version < IDEVICE_DEVICE_VERSION(17, 0, 0)) {
|
||||
f = fopen(image_sig_path, "rb");
|
||||
if (!f) {
|
||||
qDebug() << "Error opening signature file" << image_sig_path << ":"
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
}
|
||||
if (fstat(fileno(f), &fst) != 0) {
|
||||
qDebug() << "Error: fstat:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
sig = (unsigned char *)malloc(fst.st_size);
|
||||
sig_length = fread(sig, 1, fst.st_size, f);
|
||||
fclose(f);
|
||||
if (sig_length == 0) {
|
||||
qDebug() << "Could not read signature from file" << image_sig_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
f = fopen(image_path, "rb");
|
||||
if (!f) {
|
||||
qDebug() << "Error opening image file" << image_path << ":"
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (stat(image_path, &fst) != 0) {
|
||||
qDebug() << "Error: stat:" << image_path << ":" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
if (!S_ISDIR(fst.st_mode)) {
|
||||
qDebug() << "Error: Personalized Disk Image mount expects a "
|
||||
"directory as image path.";
|
||||
return false;
|
||||
}
|
||||
char *build_manifest_path =
|
||||
string_build_path(image_path, "BuildManifest.plist", NULL);
|
||||
plist_t build_manifest = NULL;
|
||||
if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) !=
|
||||
0) {
|
||||
free(build_manifest_path);
|
||||
build_manifest_path = string_build_path(
|
||||
image_path, "Restore", "BuildManifest.plist", NULL);
|
||||
if (plist_read_from_file(build_manifest_path, &build_manifest,
|
||||
NULL) == 0) {
|
||||
char *image_path_new =
|
||||
string_build_path(image_path, "Restore", NULL);
|
||||
free(image_path);
|
||||
image_path = image_path_new;
|
||||
}
|
||||
}
|
||||
if (!build_manifest) {
|
||||
qDebug() << "Error: Could not locate BuildManifest.plist inside "
|
||||
"given disk image path!";
|
||||
return false;
|
||||
}
|
||||
|
||||
plist_t identifiers = NULL;
|
||||
mobile_image_mounter_error_t merr =
|
||||
mobile_image_mounter_query_personalization_identifiers(
|
||||
mim, NULL, &identifiers);
|
||||
if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
qDebug() << "Failed to query personalization identifiers:" << merr;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
|
||||
unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
|
||||
|
||||
plist_t build_identities =
|
||||
plist_dict_get_item(build_manifest, "BuildIdentities");
|
||||
plist_array_iter iter;
|
||||
plist_array_new_iter(build_identities, &iter);
|
||||
plist_t item = NULL;
|
||||
plist_t build_identity = NULL;
|
||||
do {
|
||||
plist_array_next_item(build_identities, iter, &item);
|
||||
if (!item) {
|
||||
break;
|
||||
}
|
||||
unsigned int bi_board_id =
|
||||
(unsigned int)plist_dict_get_uint(item, "ApBoardID");
|
||||
unsigned int bi_chip_id =
|
||||
(unsigned int)plist_dict_get_uint(item, "ApChipID");
|
||||
if (bi_chip_id == chip_id && bi_board_id == board_id) {
|
||||
build_identity = item;
|
||||
break;
|
||||
}
|
||||
} while (item);
|
||||
plist_mem_free(iter);
|
||||
if (!build_identity) {
|
||||
qDebug() << "Error: The given disk image is not compatible with "
|
||||
"the current device.";
|
||||
return false;
|
||||
}
|
||||
plist_t p_tc_path =
|
||||
plist_access_path(build_identity, 4, "Manifest",
|
||||
"LoadableTrustCache", "Info", "Path");
|
||||
if (!p_tc_path) {
|
||||
qDebug() << "Error: Could not determine path for trust cache!";
|
||||
return false;
|
||||
}
|
||||
plist_t p_dmg_path = plist_access_path(
|
||||
build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
|
||||
if (!p_dmg_path) {
|
||||
qDebug() << "Error: Could not determine path for disk image!";
|
||||
return false;
|
||||
}
|
||||
char *tc_path = string_build_path(
|
||||
image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
|
||||
unsigned char *trust_cache = NULL;
|
||||
uint64_t trust_cache_size = 0;
|
||||
if (!buffer_read_from_filename(tc_path, (char **)&trust_cache,
|
||||
&trust_cache_size)) {
|
||||
qDebug() << "Error: Trust cache does not exist at" << tc_path
|
||||
<< "!";
|
||||
return false;
|
||||
}
|
||||
mount_options = plist_new_dict();
|
||||
plist_dict_set_item(
|
||||
mount_options, "ImageTrustCache",
|
||||
plist_new_data((char *)trust_cache, trust_cache_size));
|
||||
free(trust_cache);
|
||||
char *dmg_path = string_build_path(
|
||||
image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
|
||||
free(image_path);
|
||||
image_path = dmg_path;
|
||||
f = fopen(image_path, "rb");
|
||||
if (!f) {
|
||||
qDebug() << "Error opening image file" << image_path << ":"
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buf[8192];
|
||||
unsigned char sha384_digest[48];
|
||||
sha384_context ctx;
|
||||
sha384_init(&ctx);
|
||||
fstat(fileno(f), &fst);
|
||||
image_size = fst.st_size;
|
||||
while (!feof(f)) {
|
||||
ssize_t fr = fread(buf, 1, sizeof(buf), f);
|
||||
if (fr <= 0) {
|
||||
break;
|
||||
}
|
||||
sha384_update(&ctx, buf, fr);
|
||||
}
|
||||
rewind(f);
|
||||
sha384_final(&ctx, sha384_digest);
|
||||
unsigned char *manifest = NULL;
|
||||
unsigned int manifest_size = 0;
|
||||
/* check if the device already has a personalization manifest for this
|
||||
* image */
|
||||
if (mobile_image_mounter_query_personalization_manifest(
|
||||
mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest),
|
||||
&manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
qDebug() << "Using existing personalization manifest from device.";
|
||||
} else {
|
||||
/* we need to re-connect in this case */
|
||||
mobile_image_mounter_free(mim);
|
||||
mim = NULL;
|
||||
if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) !=
|
||||
MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
qDebug() << "No personalization manifest, requesting from TSS...";
|
||||
unsigned char *nonce = NULL;
|
||||
unsigned int nonce_size = 0;
|
||||
|
||||
/* create new TSS request and fill parameters */
|
||||
plist_t request = tss_request_new(NULL);
|
||||
plist_t params = plist_new_dict();
|
||||
tss_parameters_add_from_manifest(params, build_identity, 1);
|
||||
|
||||
/* copy all `Ap,*` items from identifiers */
|
||||
plist_dict_iter di = NULL;
|
||||
plist_dict_new_iter(identifiers, &di);
|
||||
plist_t node = NULL;
|
||||
do {
|
||||
char *key = NULL;
|
||||
plist_dict_next_item(identifiers, di, &key, &node);
|
||||
if (node) {
|
||||
if (!strncmp(key, "Ap,", 3)) {
|
||||
plist_dict_set_item(request, key, plist_copy(node));
|
||||
}
|
||||
}
|
||||
free(key);
|
||||
} while (node);
|
||||
plist_mem_free(di);
|
||||
|
||||
plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
|
||||
plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
|
||||
plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
|
||||
plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
|
||||
|
||||
/* query nonce from image mounter service */
|
||||
merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage",
|
||||
&nonce, &nonce_size);
|
||||
if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
plist_dict_set_item(params, "ApNonce",
|
||||
plist_new_data((char *)nonce, nonce_size));
|
||||
} else {
|
||||
qDebug()
|
||||
<< "ERROR: Failed to query nonce for developer disk image:"
|
||||
<< merr;
|
||||
return false;
|
||||
}
|
||||
mobile_image_mounter_free(mim);
|
||||
mim = NULL;
|
||||
|
||||
plist_dict_set_item(
|
||||
params, "ApSepNonce",
|
||||
plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
20));
|
||||
plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
|
||||
tss_request_add_ap_tags(request, params, NULL);
|
||||
tss_request_add_common_tags(request, params, NULL);
|
||||
tss_request_add_ap_img4_tags(request, params);
|
||||
plist_free(params);
|
||||
|
||||
/* request IM4M from TSS */
|
||||
plist_t response = tss_request_send(request, NULL);
|
||||
plist_free(request);
|
||||
|
||||
plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
|
||||
if (!PLIST_IS_DATA(p_manifest)) {
|
||||
qDebug() << "Failed to get Img4Ticket";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t m4m_len = 0;
|
||||
plist_get_data_val(p_manifest, (char **)&manifest, &m4m_len);
|
||||
manifest_size = m4m_len;
|
||||
plist_free(response);
|
||||
qDebug() << "Done.";
|
||||
}
|
||||
sig = manifest;
|
||||
sig_length = manifest_size;
|
||||
|
||||
imagetype = "Personalized";
|
||||
}
|
||||
|
||||
char *targetname = NULL;
|
||||
if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
|
||||
qDebug() << "Out of memory!?";
|
||||
return false;
|
||||
}
|
||||
char *mountname = NULL;
|
||||
if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
|
||||
qDebug() << "Out of memory!?";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!imagetype) {
|
||||
imagetype = "Developer";
|
||||
}
|
||||
|
||||
if (!mim) {
|
||||
if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) !=
|
||||
MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (disk_image_upload_type) {
|
||||
case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
|
||||
qDebug() << "Uploading" << image_path;
|
||||
err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig,
|
||||
sig_length, mim_upload_cb, f);
|
||||
break;
|
||||
case DISK_IMAGE_UPLOAD_TYPE_AFC:
|
||||
default:
|
||||
qDebug() << "Uploading" << image_path << "--> afc:///" << targetname;
|
||||
plist_t fileinfo = NULL;
|
||||
if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) !=
|
||||
AFC_E_SUCCESS) {
|
||||
if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
|
||||
qDebug() << "WARNING: Could not create directory" << PKG_PATH
|
||||
<< "on device!";
|
||||
}
|
||||
}
|
||||
plist_free(fileinfo);
|
||||
|
||||
uint64_t af = 0;
|
||||
if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
|
||||
AFC_E_SUCCESS) ||
|
||||
!af) {
|
||||
fclose(f);
|
||||
qDebug() << "afc_file_open on" << targetname << "failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[8192];
|
||||
size_t amount = 0;
|
||||
do {
|
||||
amount = fread(buf, 1, sizeof(buf), f);
|
||||
if (amount > 0) {
|
||||
uint32_t written, total = 0;
|
||||
while (total < amount) {
|
||||
written = 0;
|
||||
if (afc_file_write(afc, af, buf + total, amount - total,
|
||||
&written) != AFC_E_SUCCESS) {
|
||||
qDebug() << "AFC Write error!";
|
||||
break;
|
||||
}
|
||||
total += written;
|
||||
}
|
||||
if (total != amount) {
|
||||
qDebug() << "Error: wrote only" << total << "of"
|
||||
<< (unsigned int)amount;
|
||||
afc_file_close(afc, af);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (amount > 0);
|
||||
|
||||
afc_file_close(afc, af);
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) {
|
||||
qDebug() << "ERROR: Device is locked, can't mount. Unlock device "
|
||||
"and try again.";
|
||||
} else {
|
||||
qDebug() << "ERROR: Unknown error occurred, can't mount.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
qDebug() << "done.";
|
||||
|
||||
qDebug() << "Mounting...";
|
||||
err = mobile_image_mounter_mount_image_with_options(
|
||||
mim, mountname, sig, sig_length, imagetype, mount_options, &result);
|
||||
if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
|
||||
if (result) {
|
||||
plist_t node = plist_dict_get_item(result, "Status");
|
||||
if (node) {
|
||||
char *status = NULL;
|
||||
plist_get_string_val(node, &status);
|
||||
if (status) {
|
||||
if (!strcmp(status, "Complete")) {
|
||||
qDebug() << "Done.";
|
||||
res = 0;
|
||||
} else {
|
||||
qDebug() << "unexpected status value:";
|
||||
plist_write_to_stream(result, stdout,
|
||||
(xml_mode) ? PLIST_FORMAT_XML
|
||||
: PLIST_FORMAT_LIMD,
|
||||
(plist_write_options_t)0);
|
||||
}
|
||||
free(status);
|
||||
} else {
|
||||
qDebug() << "unexpected result:";
|
||||
plist_write_to_stream(result, stdout,
|
||||
(xml_mode) ? PLIST_FORMAT_XML
|
||||
: PLIST_FORMAT_LIMD,
|
||||
(plist_write_options_t)0);
|
||||
}
|
||||
}
|
||||
node = plist_dict_get_item(result, "Error");
|
||||
if (node) {
|
||||
char *error = NULL;
|
||||
plist_get_string_val(node, &error);
|
||||
if (error) {
|
||||
qDebug() << "Error:" << error;
|
||||
free(error);
|
||||
} else {
|
||||
qDebug() << "unexpected result:";
|
||||
plist_write_to_stream(result, stdout,
|
||||
(xml_mode) ? PLIST_FORMAT_XML
|
||||
: PLIST_FORMAT_LIMD,
|
||||
(plist_write_options_t)0);
|
||||
}
|
||||
node = plist_dict_get_item(result, "DetailedError");
|
||||
if (node) {
|
||||
qDebug()
|
||||
<< "DetailedError:" << plist_get_string_ptr(node, NULL);
|
||||
}
|
||||
} else {
|
||||
plist_write_to_stream(result, stdout,
|
||||
(xml_mode) ? PLIST_FORMAT_XML
|
||||
: PLIST_FORMAT_LIMD,
|
||||
(plist_write_options_t)0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Error: mount_image returned" << err;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
plist_free(result);
|
||||
}
|
||||
// error_out:
|
||||
// /* perform hangup command */
|
||||
// mobile_image_mounter_hangup(mim);
|
||||
// /* free client */
|
||||
// mobile_image_mounter_free(mim);
|
||||
|
||||
// leave:
|
||||
// if (afc)
|
||||
// {
|
||||
// afc_client_free(afc);
|
||||
// }
|
||||
// if (lckd)
|
||||
// {
|
||||
// lockdownd_client_free(lckd);
|
||||
// }
|
||||
// idevice_free(device);
|
||||
|
||||
// if (image_path)
|
||||
// free(image_path);
|
||||
// if (image_sig_path)
|
||||
// free(image_sig_path);
|
||||
|
||||
// return res;
|
||||
return true;
|
||||
}
|
||||
|
||||
// int main(){return 0;}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* idevicediagnostics.c
|
||||
* Retrieves diagnostics information from device
|
||||
*
|
||||
* Copyright (c) 2012 Martin Szulecki All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define TOOL_NAME "idevicediagnostics"
|
||||
|
||||
#include <libimobiledevice/diagnostics_relay.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
// TODO:break all the client because device wont restart if any client is still
|
||||
// connected we need to change the main device init function to not connect to
|
||||
// any client
|
||||
bool restart(idevice_t device)
|
||||
{
|
||||
lockdownd_client_t lockdown_client = NULL;
|
||||
diagnostics_relay_client_t diagnostics_client = NULL;
|
||||
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
lockdownd_service_descriptor_t service = NULL;
|
||||
const char *udid = NULL;
|
||||
int use_network = 0;
|
||||
|
||||
if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(
|
||||
device, &lockdown_client, TOOL_NAME))) {
|
||||
idevice_free(device);
|
||||
printf("ERROR: Could not connect to lockdownd, error code %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* attempt to use newer diagnostics service available on iOS 5 and later */
|
||||
ret = lockdownd_start_service(
|
||||
lockdown_client, "com.apple.mobile.diagnostics_relay", &service);
|
||||
if (ret == LOCKDOWN_E_INVALID_SERVICE) {
|
||||
/* attempt to use older diagnostics service */
|
||||
ret = lockdownd_start_service(
|
||||
lockdown_client, "com.apple.iosdiagnostics.relay", &service);
|
||||
}
|
||||
lockdownd_client_free(lockdown_client);
|
||||
|
||||
if (ret != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
printf("ERROR: Could not start diagnostics relay service: %s\n",
|
||||
lockdownd_strerror(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) {
|
||||
if (diagnostics_relay_client_new(device, service,
|
||||
&diagnostics_client) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
printf("ERROR: Could not connect to diagnostics_relay!\n");
|
||||
} else {
|
||||
|
||||
if (diagnostics_relay_restart(
|
||||
diagnostics_client,
|
||||
DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) ==
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
printf("Restarting device.\n");
|
||||
return true;
|
||||
} else {
|
||||
printf("ERROR: Failed to restart device.\n");
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics_relay_goodbye(diagnostics_client);
|
||||
diagnostics_relay_client_free(diagnostics_client);
|
||||
}
|
||||
|
||||
if (service) {
|
||||
lockdownd_service_descriptor_free(service);
|
||||
service = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* idevicesetlocation.c
|
||||
* Simulate location on iOS device with mounted developer disk image
|
||||
*
|
||||
* Copyright (c) 2016-2020 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define DT_SIMULATELOCATION_SERVICE "com.apple.dt.simulatelocation"
|
||||
#define TOOL_NAME "idevicesetlocation"
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libimobiledevice/service.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#include <machine/endian.h>
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#else
|
||||
#include <endian.h>
|
||||
#endif
|
||||
#include <QDebug>
|
||||
|
||||
// TODO: check for these
|
||||
// if (device_version >= IDEVICE_DEVICE_VERSION(17,0,0)) {
|
||||
// printf("Note: This tool is currently not supported on iOS 17+\n");
|
||||
// } else {
|
||||
// printf("Make sure a developer disk image is mounted!\n");
|
||||
// }
|
||||
|
||||
enum { SET_LOCATION = 0, RESET_LOCATION = 1 };
|
||||
bool set_location(idevice_t device, char *lat, char *lon)
|
||||
{
|
||||
uint32_t mode = 0;
|
||||
lockdownd_client_t lockdown = NULL;
|
||||
lockdownd_error_t lerr =
|
||||
lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
|
||||
try {
|
||||
/* code */
|
||||
|
||||
if (lerr != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
printf("ERROR: Could not connect to lockdownd: %s (%d)\n",
|
||||
lockdownd_strerror(lerr), lerr);
|
||||
return false;
|
||||
}
|
||||
|
||||
lockdownd_service_descriptor_t svc = NULL;
|
||||
lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE,
|
||||
&svc);
|
||||
if (lerr != LOCKDOWN_E_SUCCESS) {
|
||||
unsigned int device_version = idevice_get_device_version(device);
|
||||
lockdownd_client_free(lockdown);
|
||||
idevice_free(device);
|
||||
|
||||
return false;
|
||||
}
|
||||
lockdownd_client_free(lockdown);
|
||||
|
||||
service_client_t service = NULL;
|
||||
|
||||
service_error_t serr = service_client_new(device, svc, &service);
|
||||
|
||||
lockdownd_service_descriptor_free(svc);
|
||||
|
||||
if (serr != SERVICE_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t l;
|
||||
uint32_t s = 0;
|
||||
|
||||
l = htobe32(mode);
|
||||
service_send(service, (const char *)&l, 4, &s);
|
||||
if (mode == SET_LOCATION) {
|
||||
int len = 4 + strlen(lat) + 4 + strlen(lon);
|
||||
char *buf = (char *)malloc(len);
|
||||
uint32_t latlen;
|
||||
latlen = strlen(lat);
|
||||
l = htobe32(latlen);
|
||||
memcpy(buf, &l, 4);
|
||||
memcpy(buf + 4, lat, latlen);
|
||||
uint32_t longlen = strlen(lon);
|
||||
l = htobe32(longlen);
|
||||
memcpy(buf + 4 + latlen, &l, 4);
|
||||
memcpy(buf + 4 + latlen + 4, lon, longlen);
|
||||
|
||||
s = 0;
|
||||
service_send(service, buf, len, &s);
|
||||
free(buf); // <-- free the buffer after use
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
if (lockdown) {
|
||||
lockdownd_client_free(lockdown);
|
||||
}
|
||||
if (device) {
|
||||
idevice_free(device);
|
||||
}
|
||||
qDebug() << "Exception occurred while setting location.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* idevicediagnostics.c
|
||||
* Retrieves diagnostics information from device
|
||||
*
|
||||
* Copyright (c) 2012 Martin Szulecki All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define TOOL_NAME "idevicediagnostics"
|
||||
|
||||
#include <libimobiledevice/diagnostics_relay.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
// TODO:break all the client because device wont restart if any client is still
|
||||
// connected we need to change the main device init function to not connect to
|
||||
// any client
|
||||
bool shutdown(idevice_t device)
|
||||
{
|
||||
lockdownd_client_t lockdown_client = NULL;
|
||||
diagnostics_relay_client_t diagnostics_client = NULL;
|
||||
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
|
||||
lockdownd_service_descriptor_t service = NULL;
|
||||
const char *udid = NULL;
|
||||
int use_network = 0;
|
||||
|
||||
if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(
|
||||
device, &lockdown_client, TOOL_NAME))) {
|
||||
idevice_free(device);
|
||||
printf("ERROR: Could not connect to lockdownd, error code %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* attempt to use newer diagnostics service available on iOS 5 and later */
|
||||
ret = lockdownd_start_service(
|
||||
lockdown_client, "com.apple.mobile.diagnostics_relay", &service);
|
||||
if (ret == LOCKDOWN_E_INVALID_SERVICE) {
|
||||
/* attempt to use older diagnostics service */
|
||||
ret = lockdownd_start_service(
|
||||
lockdown_client, "com.apple.iosdiagnostics.relay", &service);
|
||||
}
|
||||
lockdownd_client_free(lockdown_client);
|
||||
|
||||
if (ret != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
printf("ERROR: Could not start diagnostics relay service: %s\n",
|
||||
lockdownd_strerror(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) {
|
||||
if (diagnostics_relay_client_new(device, service,
|
||||
&diagnostics_client) !=
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
printf("ERROR: Could not connect to diagnostics_relay!\n");
|
||||
} else {
|
||||
|
||||
if (diagnostics_relay_shutdown(
|
||||
diagnostics_client,
|
||||
DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) ==
|
||||
DIAGNOSTICS_RELAY_E_SUCCESS) {
|
||||
printf("Shutting down device.\n");
|
||||
return true;
|
||||
} else {
|
||||
printf("ERROR: Failed to shut down device.\n");
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics_relay_goodbye(diagnostics_client);
|
||||
diagnostics_relay_client_free(diagnostics_client);
|
||||
}
|
||||
|
||||
if (service) {
|
||||
lockdownd_service_descriptor_free(service);
|
||||
service = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* idevicescreenshot.c
|
||||
* Gets a screenshot from a device
|
||||
*
|
||||
* Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define TOOL_NAME "idevicescreenshot"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/screenshotr.h>
|
||||
|
||||
|
||||
|
||||
static void get_image_filename(char *imgdata, char **filename)
|
||||
{
|
||||
// If the provided filename already has an extension, use it as is.
|
||||
if (*filename) {
|
||||
char *last_dot = strrchr(*filename, '.');
|
||||
if (last_dot && !strchr(last_dot, '/')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the appropriate file extension for the filename.
|
||||
const char *fileext = NULL;
|
||||
if (memcmp(imgdata, "\x89PNG", 4) == 0) {
|
||||
fileext = ".png";
|
||||
} else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
|
||||
fileext = ".tiff";
|
||||
} else {
|
||||
printf("WARNING: screenshot data has unexpected image format.\n");
|
||||
fileext = ".dat";
|
||||
}
|
||||
|
||||
// If a filename without an extension is provided, append the extension.
|
||||
// Otherwise, generate a filename based on the current time.
|
||||
char *basename = NULL;
|
||||
if (*filename) {
|
||||
basename = (char*)malloc(strlen(*filename) + 1);
|
||||
strcpy(basename, *filename);
|
||||
free(*filename);
|
||||
*filename = NULL;
|
||||
} else {
|
||||
time_t now = time(NULL);
|
||||
basename = (char*)malloc(32);
|
||||
strftime(basename, 31, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
|
||||
}
|
||||
|
||||
// Ensure the filename is unique on disk.
|
||||
char *unique_filename = (char*)malloc(strlen(basename) + strlen(fileext) + 7);
|
||||
sprintf(unique_filename, "%s%s", basename, fileext);
|
||||
int i;
|
||||
for (i = 2; i < (1 << 16); i++) {
|
||||
if (access(unique_filename, F_OK) == -1) {
|
||||
*filename = unique_filename;
|
||||
break;
|
||||
}
|
||||
sprintf(unique_filename, "%s-%d%s", basename, i, fileext);
|
||||
}
|
||||
if (!*filename) {
|
||||
free(unique_filename);
|
||||
}
|
||||
free(basename);
|
||||
}
|
||||
|
||||
int take_screenshot(idevice_t device, lockdownd_service_descriptor_t service) {
|
||||
screenshotr_client_t *shotr = nullptr;
|
||||
char *filename = NULL;
|
||||
int result=1;
|
||||
try
|
||||
{
|
||||
/* code */
|
||||
|
||||
if (screenshotr_client_new(device, service, &shotr) != SCREENSHOTR_E_SUCCESS) {
|
||||
printf("Could not connect to screenshotr!\n");
|
||||
} else {
|
||||
char *imgdata = NULL;
|
||||
uint64_t imgsize = 0;
|
||||
if (screenshotr_take_screenshot(*shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
|
||||
get_image_filename(imgdata, &filename);
|
||||
if (!filename) {
|
||||
printf("FATAL: Could not find a unique filename!\n");
|
||||
} else {
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if (f) {
|
||||
if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
|
||||
printf("Screenshot saved to %s\n", filename);
|
||||
result = 0;
|
||||
} else {
|
||||
printf("Could not save screenshot to file %s!\n", filename);
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
printf("Could not open %s for writing: %s\n", filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("Could not get screenshot!\n");
|
||||
}
|
||||
screenshotr_client_free(shotr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
return result;
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* idevicescreenshot.c
|
||||
* Gets a screenshot from a device
|
||||
*
|
||||
* Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "../../iDescriptor.h"
|
||||
#include <QByteArray>
|
||||
#include <QDate>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/screenshotr.h>
|
||||
|
||||
QImage get_image(const char *imgdata, uint64_t imgsize)
|
||||
{
|
||||
QByteArray byteArray(imgdata, static_cast<int>(imgsize));
|
||||
QImage image;
|
||||
image.loadFromData(byteArray);
|
||||
return image;
|
||||
}
|
||||
|
||||
TakeScreenshotResult take_screenshot(screenshotr_client_t shotr)
|
||||
{
|
||||
TakeScreenshotResult result;
|
||||
try {
|
||||
char *imgdata = NULL;
|
||||
uint64_t imgsize = 0;
|
||||
if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) ==
|
||||
SCREENSHOTR_E_SUCCESS) {
|
||||
QImage image = get_image(imgdata, imgsize);
|
||||
if (image.isNull()) {
|
||||
printf("Could not decode screenshot image!\n");
|
||||
} else {
|
||||
result.img = image;
|
||||
result.success = true;
|
||||
}
|
||||
// Always free imgdata after use!
|
||||
free(imgdata);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
qDebug() << "Exception occurred while taking screenshot:" << e.what();
|
||||
}
|
||||
// Always return result!
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user