diff --git a/src/event.cpp b/src/event.cpp index d57211e..85a5f41 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -157,14 +157,11 @@ void recovery_device_event_cb( if (client) { - LOG("Opened client after %d attempts", attempts); + VERBOSE("Opened client after %d attempts", attempts); irecv_device_t device = nullptr; if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device) { - LOG("device product type: %s", device->product_type); - LOG("device display name: %s", device->display_name); - state.productType = device->product_type ? device->product_type : ""; state.displayName = device->display_name ? device->display_name : ""; } @@ -173,7 +170,7 @@ void recovery_device_event_cb( } else { - LOG("Failed to open irecv client after retries"); + ERROR("Failed to open irecv client after retries"); } state.isSupported = SequenceIsSupported(state.productType); diff --git a/src/m8/checkm8.c b/src/m8/checkm8.c index 9344a61..0e27b99 100644 --- a/src/m8/checkm8.c +++ b/src/m8/checkm8.c @@ -165,28 +165,28 @@ bool checkm8_stage_patch(const usb_handle_t *handle, struct DeviceConfiguration create_pongo_payload_for_device(deviceConfig->cpid, &payload, &data_sz); if (payload == NULL || data_sz == 0) { - LOG("unsupported cpid 0x%X", (unsigned)deviceConfig->cpid); + ERROR("unsupported cpid 0x%X", (unsigned)deviceConfig->cpid); return false; } - LOG("setting up stage 2 for CPID 0x%X", (unsigned)deviceConfig->cpid); + VERBOSE("setting up stage 2 for CPID 0x%X", (unsigned)deviceConfig->cpid); data = calloc(1, data_sz); if (!data) { - LOG("failed to allocate payload buffer"); + ERROR("failed to allocate payload buffer"); return false; } memcpy(data, payload, data_sz); if(checkm8_usb_request_stall(handle) && checkm8_usb_request_leak(handle)) { - LOG("successfully leaked data"); + VERBOSE("successfully leaked data"); } else { - LOG("failed to leak data"); + ERROR("failed to leak data"); return false; } for(i = 0; i < 2; i++) { - LOG("i = %zu", i); + VERBOSE("i = %zu", i); send_usb_control_request_no_data(handle, 2, 3, 0, 0x80, 0, NULL); } if(p != NULL && send_usb_control_request(handle, 0x00, 0, 0, 0x00, p, 0x30, &transfer_ret) && transfer_ret.ret == USB_TRANSFER_STALL) { diff --git a/src/m8/payload.c b/src/m8/payload.c index 0af8a09..9a38b81 100644 --- a/src/m8/payload.c +++ b/src/m8/payload.c @@ -56,11 +56,7 @@ bool checkm8_find_device_configuration_for_cpid( config->config_hole = 0; break; default: - LOG("CPID 0x%x is not supported!", cpid); - if (cpid > 0x8015) { - LOG("arm64e devices (A12 and newer) will never be supported."); - } - + ERROR("CPID 0x%x is not supported!", cpid); foundMatchingConfiguration = false; break; } @@ -230,7 +226,7 @@ bool checkm8_find_payload_configuration_for_cpid( config->usb_serial_number_string_descriptor = 0x18008062A; break; default: - LOG("CPID 0x%x is not supported!", cpid); + ERROR("CPID 0x%x is not supported!", cpid); foundMatchingConfiguration = false; } return foundMatchingConfiguration; @@ -241,7 +237,7 @@ void create_pongo_payload_for_device( uint8_t **payload, size_t *payloadSize) { - LOG("Preparing YoloDFU payload for CPID 0x%x.", cpid); + VERBOSE("Preparing YoloDFU payload for CPID 0x%x.", cpid); *payload = NULL; *payloadSize = 0; diff --git a/src/m8/run.c b/src/m8/run.c index b8a4d09..0c13bcc 100644 --- a/src/m8/run.c +++ b/src/m8/run.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "../utils.h" #include "../usb/shim.h" @@ -19,14 +20,6 @@ #include "../gen/payloads/ramdisk.h" #include "../gen/payloads/binpack.h" -typedef struct { - atomic_int result; - atomic_bool stop; - - atomic_bool exploit_done; - atomic_bool pongo_done; -} shared_t; - static void *exploit_thread(void *arg) { LOG("Waiting for DFU devices..."); @@ -41,13 +34,15 @@ static void *exploit_thread(void *arg) init_usb_handle(&handle, 0x5AC, 0x1227); - while (stage != STAGE_DONE && !atomic_load(&s->stop)) { + while (stage != STAGE_JAILBREAK && !atomic_load(&s->stop)) { if (!wait_usb_handle(&handle)) { atomic_store(&s->result, -1); atomic_store(&s->stop, true); break; } + atomic_store(&s->stage, stage); + switch (stage) { case STAGE_PREPARE: { char *sn = get_usb_serial_number(&handle); @@ -74,21 +69,19 @@ static void *exploit_thread(void *arg) stage = STAGE_RESET; break; } - case STAGE_RESET: ret = checkm8_stage_reset(&handle); - stage = ret ? STAGE_SETUP : STAGE_RESET; + stage = ret ? STAGE_SETUP : STAGE_PREPARE; break; - case STAGE_SETUP: LOG("Setting up the device for exploitation"); ret = checkm8_stage_setup(&handle, &deviceConfig); - stage = ret ? STAGE_SPRAY : STAGE_RESET; + stage = ret ? STAGE_SPRAY : STAGE_PREPARE; break; case STAGE_SPRAY: LOG("Spraying the device with USB requests"); ret = checkm8_stage_spray(&handle, &deviceConfig); - stage = ret ? STAGE_PATCH : STAGE_RESET; + stage = ret ? STAGE_PATCH : STAGE_PREPARE; break; case STAGE_PATCH: LOG("Right before trigger (bug setup)"); @@ -104,19 +97,20 @@ static void *exploit_thread(void *arg) checkm8_boot_pongo(&handle); - atomic_store(&s->result, 1); atomic_store(&s->exploit_done, true); + atomic_store(&s->pongo_done, true); + atomic_store(&s->stage, STAGE_JAILBREAK); - stage = STAGE_DONE; + stage = STAGE_JAILBREAK; break; default: goto fail; } reset_usb_handle(&handle); - continue; } + atomic_store(&s->result, 1); return NULL; fail: @@ -136,6 +130,7 @@ static void *pongo_thread(void *arg) init_usb_handle(&handle, 0x5AC, 0x4141); while (!atomic_load(&s->stop)) { + if (!wait_usb_handle(&handle)) { atomic_store(&s->result, -1); atomic_store(&s->stop, true); @@ -143,7 +138,9 @@ static void *pongo_thread(void *arg) } if (!seen) { - LOG("Pongo device detected!"); + seen = true; + + SUCCESS("Pongo device detected!"); char paleinfo[64]; snprintf(paleinfo, sizeof(paleinfo), "palera1n_flags 0x%" PRIx64, palerain_flags); @@ -159,7 +156,9 @@ static void *pongo_thread(void *arg) issue_pongo_command(&handle, "overlay"); issue_pongo_command(&handle, "bootx"); - atomic_store(&s->result, 1); + atomic_store(&s->pongo_done, true); + atomic_store(&s->stage, STAGE_DONE); + break; } } @@ -168,23 +167,17 @@ static void *pongo_thread(void *arg) return NULL; } -bool exploit(void) +bool exploit(shared_t *state) { pthread_t t1, t2; - shared_t state = {0}; - atomic_init(&state.result, 0); - atomic_init(&state.stop, false); - atomic_init(&state.exploit_done, false); - atomic_init(&state.pongo_done, false); - - pthread_create(&t1, NULL, exploit_thread, &state); - pthread_create(&t2, NULL, pongo_thread, &state); + pthread_create(&t1, NULL, exploit_thread, state); + pthread_create(&t2, NULL, pongo_thread, state); pthread_join(t1, NULL); pthread_join(t2, NULL); - return atomic_load(&state.result) == 1 && - atomic_load(&state.exploit_done) && - atomic_load(&state.pongo_done); + return atomic_load(&state->result) == 1 && + atomic_load(&state->exploit_done) && + atomic_load(&state->pongo_done); } diff --git a/src/m8/run.h b/src/m8/run.h index 633b53c..7ecd979 100644 --- a/src/m8/run.h +++ b/src/m8/run.h @@ -2,6 +2,7 @@ #define RUN_H #include +#include #ifdef __cplusplus extern "C" { @@ -14,10 +15,21 @@ enum exploit_stage { STAGE_SPRAY = 3, STAGE_PATCH = 4, STAGE_PONGO = 5, - STAGE_DONE = 6, + STAGE_JAILBREAK = 6, + STAGE_DONE = 7, }; -bool exploit(void); +typedef struct { + atomic_int result; + atomic_bool stop; + + atomic_bool exploit_done; + atomic_bool pongo_done; + + atomic_int stage; +} shared_t; + +bool exploit(shared_t *state); #ifdef __cplusplus } diff --git a/src/main.cpp b/src/main.cpp index 1eb7e0c..9b8ecf0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,7 +139,7 @@ void parse_arguments(int argc, char* argv[]) { palerain_flags |= palerain_option_verbose_boot; break; case '?': - LOG("Unknown option"); + ERROR("Unknown option"); print_usage(argv[0]); exit(1); default: break; @@ -148,7 +148,7 @@ void parse_arguments(int argc, char* argv[]) { if (!(palerain_flags & palerain_option_gui)) { if (!(palerain_flags & palerain_option_rootful) && !(palerain_flags & palerain_option_rootless)) { - LOG("You must specify either -l, --rootless or -f, --rootful.\n"); + ERROR("You must specify either -l, --rootless or -f, --rootful.\n"); print_usage(argv[0]); exit(1); } @@ -159,6 +159,7 @@ int main(int argc, char* argv[], char* envp[]) { print_credits(); parse_arguments(argc, argv); LOG("palera1n_flags: %llu", palerain_flags); + shared_t state{}; #ifdef WITH_GUI if (palerain_flags & palerain_option_gui) { @@ -169,10 +170,10 @@ int main(int argc, char* argv[], char* envp[]) { wxEntryCleanup(); return 0; } else { - return exploit(); + return exploit(&state); } #else - return exploit(); + return exploit(&state); #endif return 0; diff --git a/src/usb/pongo_helper.c b/src/usb/pongo_helper.c index dbbb27a..93479ef 100644 --- a/src/usb/pongo_helper.c +++ b/src/usb/pongo_helper.c @@ -21,15 +21,15 @@ void compress_pongo(void *out, size_t *out_len) { void checkm8_boot_pongo(usb_handle_t *handle) { transfer_ret_t transfer_ret; - LOG("Booting pongoOS"); - LOG("Compressing pongoOS"); - LOG("Appending shellcode to the top of pongoOS (512 bytes)"); + VERBOSE("Booting pongoOS"); + VERBOSE("Compressing pongoOS"); + VERBOSE("Appending shellcode to the top of pongoOS (512 bytes)"); void *shellcode = malloc(512); memcpy(shellcode, payloads_lz4dec_bin, payloads_lz4dec_bin_len); size_t out_len = payloads_Pongo_bin_len; void *out = malloc(out_len); compress_pongo(out, &out_len); - LOG("Compressed pongoOS from %u to %zu bytes", payloads_Pongo_bin_len, out_len); + VERBOSE("Compressed pongoOS from %u to %zu bytes", payloads_Pongo_bin_len, out_len); void *tmp = malloc(out_len + 512); memcpy(tmp, shellcode, 512); memcpy(tmp + 512, out, out_len); @@ -37,12 +37,12 @@ void checkm8_boot_pongo(usb_handle_t *handle) { out = tmp; out_len += 512; free(shellcode); - LOG("Setting the compressed size into the shellcode"); + VERBOSE("Setting the compressed size into the shellcode"); uint32_t* size = (uint32_t*)(out + 0x1fc); *size = out_len - 512; - LOG("Reconnecting to device"); + VERBOSE("Reconnecting to device"); init_usb_handle(handle, 0x5AC, 0x1227); - LOG("Waiting for device to be ready"); + VERBOSE("Waiting for device to be ready"); wait_usb_handle(handle); { size_t len = 0; @@ -54,16 +54,16 @@ void checkm8_boot_pongo(usb_handle_t *handle) { send_usb_control_request(handle, 0x21, DFU_DNLOAD, 0, 0, (unsigned char*)&out[len], size, &transfer_ret); if(transfer_ret.sz != size || transfer_ret.ret != USB_TRANSFER_OK) { - LOG("retrying at len = %zu", len); + VERBOSE("retrying at len = %zu", len); sleep_ms(100); goto retry; } len += size; - LOG("len = %zu", len); + VERBOSE("len = %zu", len); } } send_usb_control_request_no_data(handle, 0x21, 4, 0, 0, 0, NULL); - LOG("pongoOS sent, should be booting"); + VERBOSE("pongoOS sent, should be booting"); } int issue_pongo_command(const usb_handle_t *handle, const char *command) { @@ -81,11 +81,11 @@ int issue_pongo_command(const usb_handle_t *handle, const char *command) { size_t len = strlen(command); if (len > 510) { - LOG("Pongo command too long: %s", command); + ERROR("Pongo command too long: %s", command); return -1; } - LOG("Executing PongoOS command: '%s'", command); + VERBOSE("Executing PongoOS command: '%s'", command); snprintf(command_buf, sizeof(command_buf), "%s\n", command); len = strlen(command_buf); @@ -115,7 +115,7 @@ int issue_pongo_command(const usb_handle_t *handle, const char *command) { return 0; } - LOG("Pongo USB error: %d", tx_status.ret); + ERROR("Pongo USB error: %d", tx_status.ret); return -1; } @@ -124,11 +124,11 @@ 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) { if (data == NULL || length == 0) { - LOG("Invalid data buffer or length.\n"); + ERROR("Invalid data buffer or length."); return false; } - LOG("Uploading %zu bytes to PongoOS...\n", length); + VERBOSE("Uploading %zu bytes to PongoOS...", length); bool ret = false; transfer_ret_t tx_status = {0}; diff --git a/src/utils.c b/src/utils.c index c9efab0..2d25287 100644 --- a/src/utils.c +++ b/src/utils.c @@ -6,7 +6,44 @@ # include #endif -uint64_t palerain_flags = 0; +#include +#include +#include +#include + +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 GRAY_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 "WARN"; + case LOG_ERROR: return "ERROR"; + case LOG_VERBOSE: return "VERBOSE"; + default: return "LOG"; + } +} + +void log_write(log_level_t level, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + printf("%s[%s]%s [%s] => ", level_color(level), level_name(level), RESET_COLOR, time_now()); + + vprintf(fmt, args); + printf("\n"); + + va_end(args); +} void sleep_ms(unsigned ms) { #ifdef _WIN32 @@ -19,3 +56,5 @@ void sleep_ms(unsigned ms) { nanosleep(&ts, NULL); #endif } + +uint64_t palerain_flags = 0; diff --git a/src/utils.h b/src/utils.h index 84f8494..5d9ab0f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,12 +5,51 @@ #include #include +#include -#define LOG(fmt, ...) \ -printf("(%s:%d) => " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#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 SUCCESS(fmt, ...) log_write(LOG_SUCCESS, fmt, ##__VA_ARGS__) +#define WARN(fmt, ...) log_write(LOG_WARN, fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) log_write(LOG_ERROR, fmt, ##__VA_ARGS__) +#define 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; +} + extern uint64_t palerain_flags; #endif // CONSTANTS_H diff --git a/src/wx/DfuPanel.cpp b/src/wx/DfuPanel.cpp index e6cb1ae..45fd353 100644 --- a/src/wx/DfuPanel.cpp +++ b/src/wx/DfuPanel.cpp @@ -77,6 +77,7 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent) m_stagnentTimer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) { + m_headerText->SetLabel("Time to put the device into DFU mode. Locate the buttons as marked below\non your device and check the instructions on the right."); m_backButton->Enable(); m_startButton->Enable(); GetMainFrame()->ShowExploit(); diff --git a/src/wx/ExploitPanel.cpp b/src/wx/ExploitPanel.cpp index 9f207b0..aef64d4 100644 --- a/src/wx/ExploitPanel.cpp +++ b/src/wx/ExploitPanel.cpp @@ -2,41 +2,72 @@ #include "ExploitPanel.hpp" -#include - #include +#include +#include + #include "AppFrame.hpp" -#include "DevicePanel.hpp" -#include "../state.hpp" -#include "../utils.h" #include "../m8/run.h" +static const char* stage_to_string(int stage) +{ + switch (stage) { + case STAGE_PREPARE: return "Checking if device is ready"; + case STAGE_RESET: return "Checking if device is ready"; + case STAGE_SETUP: return "Setting up the exploit (this is the heap spray)"; + 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_PONGO: return "Booting..."; + case STAGE_JAILBREAK: return "Booting..."; + case STAGE_DONE: return "Done."; + default: return "Working..."; + } +} + +static int stage_to_progress(int stage) +{ + switch (stage) { + case STAGE_PREPARE: return 5; + case STAGE_RESET: return 15; + case STAGE_SETUP: return 30; + case STAGE_SPRAY: return 50; + case STAGE_PATCH: return 70; + case STAGE_PONGO: return 90; + case STAGE_JAILBREAK: return 95; + case STAGE_DONE: return 100; + default: return 0; + } +} + ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent) : DevicePanel(frame, parent) { auto* root = new wxBoxSizer(wxVERTICAL); + auto* text = new wxStaticText(this, wxID_ANY, "Installing jailbreak, this will take a moment. If the device asks for a passcode\nplease enter it. Do not disconnect the device until finished."); - m_statusText = new wxStaticText(this, wxID_ANY, ""); - - root->Add(text, 0, wxALL, 10); - root->Add(m_statusText, 0, wxALL, 10); + m_statusText = new wxStaticText(this, wxID_ANY, "Idle."); + m_progressBar = new wxGauge(this, wxID_ANY, 100); + m_progressBar->SetValue(0); m_backButton = new wxButton(this, wxID_ANY, "Back"); m_backButton->Disable(); - m_backButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&) - { + + m_backButton->Bind(wxEVT_BUTTON, [this, frame](wxCommandEvent&) { + m_statusText->SetLabel("Idle."); + m_progressBar->SetValue(0); + m_backButton->Disable(); frame->ShowMain(); }); - auto* bottomRow = new wxBoxSizer(wxHORIZONTAL); - bottomRow->AddStretchSpacer(); - bottomRow->Add(m_backButton, 0, wxALL, 10); + root->Add(text, 0, wxALL, 10); + root->Add(m_statusText, 0, wxALL, 10); + root->Add(m_progressBar, 0, wxALL | wxEXPAND, 20); root->AddStretchSpacer(); - root->Add(bottomRow, 0, wxEXPAND); + root->Add(m_backButton, 0, wxALL | wxALIGN_RIGHT, 10); SetSizer(root); } @@ -45,27 +76,53 @@ void ExploitPanel::SetDeviceState(const DeviceState& state) { DevicePanel::SetDeviceState(state); - if (!(this->IsShown())) + if (!IsShown()) return; - - if (state.mode == DeviceMode::Normal) - { - m_statusText->SetLabel("Done."); - m_backButton->Enable(); - } } void ExploitPanel::StartExploit() { - m_statusText->SetLabel("Exploiting device..."); + m_statusText->SetLabel("Starting..."); m_backButton->Disable(); std::thread([this]() { - const auto state = GetDeviceState(); - if (!state.connected || state.mode != DeviceMode::DFU) + const auto deviceState = GetDeviceState(); + if (!deviceState.connected || deviceState.mode != DeviceMode::DFU) return; - exploit(); + + shared_t state{}; + atomic_init(&state.result, 0); + atomic_init(&state.stop, false); + atomic_init(&state.exploit_done, false); + atomic_init(&state.pongo_done, false); + atomic_init(&state.stage, STAGE_PREPARE); + + std::thread worker([&]() { + exploit(&state); + }); + + worker.detach(); + + while (true) + { + int stage = atomic_load(&state.stage); + int progress = stage_to_progress(stage); + + wxTheApp->CallAfter([this, stage, progress]() { + m_statusText->SetLabel(stage_to_string(stage)); + m_progressBar->SetValue(progress); + }); + + if (stage == STAGE_DONE) { + wxTheApp->CallAfter([this]() { + m_backButton->Enable(); + }); + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } }).detach(); } diff --git a/src/wx/ExploitPanel.hpp b/src/wx/ExploitPanel.hpp index 0a9921f..461a10a 100644 --- a/src/wx/ExploitPanel.hpp +++ b/src/wx/ExploitPanel.hpp @@ -17,6 +17,7 @@ public: private: wxButton* m_backButton = nullptr; wxStaticText* m_statusText = nullptr; + wxGauge* m_progressBar = nullptr; }; #endif // WITH_GUI