FreeCalypso > hg > freecalypso-tools
view rvinterf/lowlevel/tfc139.c @ 597:ca4433b714d2
CHANGES: fc-fsio cleaning commands documented
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Tue, 04 Feb 2020 18:22:22 +0000 |
parents | 6f078c4a5506 |
children |
line wrap: on
line source
/* * This program facilitates the recovery of those Compal/Motorola phones * whose bootloaders have been maliciously locked down. It connects * to a running Mot C1xx firmware through the RVTMUX interface provided * by the latter and uses the Test Mode memory write command (which * these firmwares implement just like TI's reference fw) to inject * some shellcode and to transfer control to it by overwriting a * function return address on the stack. The injected shellcode then * enables the Calypso boot ROM and jumps to it, allowing fc-loadtool * to take over from there. */ #include <sys/types.h> #include <sys/errno.h> #include <stdio.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include "../include/pktmux.h" #include "../include/limits.h" extern int target_fd; extern u_char rxpkt[]; extern size_t rxpkt_len; char *logfname; FILE *logF; time_t logtime; int no_output; /* for output.c */ int wakeup_after_sec = 1; /* see ../../target-utils/tf-breakin/payload.S for the source */ static u_char shellcode[114] = { 0x78, 0x47, 0xC0, 0x46, 0xD3, 0xF0, 0x21, 0xE3, 0x50, 0x10, 0x9F, 0xE5, 0xF5, 0x00, 0xA0, 0xE3, 0xB2, 0x00, 0xC1, 0xE1, 0xA0, 0x00, 0xA0, 0xE3, 0xB2, 0x00, 0xC1, 0xE1, 0x40, 0x60, 0x9F, 0xE5, 0x05, 0x00, 0xD6, 0xE5, 0x20, 0x00, 0x10, 0xE3, 0xFC, 0xFF, 0xFF, 0x0A, 0x38, 0x10, 0x8F, 0xE2, 0x06, 0x20, 0xA0, 0xE3, 0x01, 0x00, 0xD1, 0xE4, 0x00, 0x00, 0xC6, 0xE5, 0x01, 0x20, 0x52, 0xE2, 0xFB, 0xFF, 0xFF, 0x1A, 0x05, 0x00, 0xD6, 0xE5, 0x40, 0x00, 0x10, 0xE3, 0xFC, 0xFF, 0xFF, 0x0A, 0x10, 0x10, 0x9F, 0xE5, 0x01, 0x2C, 0xA0, 0xE3, 0xB0, 0x20, 0xC1, 0xE1, 0x00, 0xF0, 0xA0, 0xE3, 0x02, 0xF8, 0xFF, 0xFF, 0x00, 0x58, 0xFF, 0xFF, 0x10, 0xFB, 0xFF, 0xFF, 0x02, 0x02, 0x02, 0x4F, 0x4B, 0x02 }; static unsigned shellcode_load_addr; static unsigned stack_smash_addr; static int thumb_entry = 1; static u_char stack_smash_payload[4]; static int breakin_in_progress; static char *target_tty_port; static char *baudrate = "57600"; static void send_compal_memwrite(addr, payload, payload_len) unsigned addr; u_char *payload; { u_char pkt[MAX_PKT_TO_TARGET]; int i, csum, csum_offset; pkt[0] = RVT_TM_HEADER; pkt[1] = 0x40; /* old TM3 MEM_WRITE command */ pkt[2] = addr; pkt[3] = addr >> 8; pkt[4] = addr >> 16; pkt[5] = addr >> 24; bcopy(payload, pkt + 6, payload_len); csum_offset = payload_len + 6; csum = 0; for (i = 1; i < csum_offset; i++) csum ^= pkt[i]; pkt[i] = csum; send_pkt_to_target(pkt, i + 1); } static void initiate_breakin() { char msgbuf[80]; unsigned jump_addr; sprintf(msgbuf, "Using shellcode load addr 0x%x, stack smash starting addr 0x%x", shellcode_load_addr, stack_smash_addr); output_line(msgbuf); jump_addr = shellcode_load_addr; if (thumb_entry) jump_addr += 1; else jump_addr += 4; stack_smash_payload[0] = jump_addr; stack_smash_payload[1] = jump_addr >> 8; stack_smash_payload[2] = jump_addr >> 16; stack_smash_payload[3] = jump_addr >> 24; output_line("Sending shellcode RAM write"); send_compal_memwrite(shellcode_load_addr, shellcode, sizeof shellcode); breakin_in_progress = 1; } static void send_memcheck_query() { u_char sendpkt[25]; output_line("Sending GPF MEMCHECK query"); /* fill out the packet */ sendpkt[0] = RVT_L23_HEADER; sendpkt[1] = 0xB7; /* system prim */ sendpkt[2] = 20; sendpkt[3] = 0; /* send zeros for the timestamp */ sendpkt[4] = 0; sendpkt[5] = 0; sendpkt[6] = 0; sendpkt[7] = 0; /* fixed string with all fields */ strcpy(sendpkt + 8, "PCO L1 MEMCHECK"); /* send it! */ send_pkt_to_target(sendpkt, 24); } main(argc, argv) char **argv; { extern char *optarg; extern int optind; int c; fd_set fds; while ((c = getopt(argc, argv, "a:AB:l:ms:w:")) != EOF) switch (c) { case 'a': shellcode_load_addr = strtoul(optarg, 0, 16); continue; case 'B': baudrate = optarg; continue; case 'l': logfname = optarg; continue; case 'm': /* mimic mot931c.exe */ shellcode_load_addr = 0x800000; stack_smash_addr = 0x837C54; /* FALL THRU */ case 'A': thumb_entry = 0; continue; case 's': stack_smash_addr = strtoul(optarg, 0, 16); continue; case 'w': wakeup_after_sec = strtoul(optarg, 0, 0); continue; case '?': default: usage: fprintf(stderr, "usage: %s [options] ttyport\n", argv[0]); exit(1); } if (argc - optind != 1) goto usage; if (stack_smash_addr && !shellcode_load_addr) { fprintf(stderr, "usage error: -a option required with -s\n"); exit(1); } open_serial_port(argv[optind]); target_tty_port = argv[optind]; set_fixed_baudrate(baudrate); set_serial_nonblock(0); setlinebuf(stdout); if (logfname) { logF = fopen(logfname, "w"); if (!logF) { perror(logfname); exit(1); } setlinebuf(logF); fprintf(logF, "*** Log of TFC139 break-in session ***\n"); } time(&logtime); if (stack_smash_addr) initiate_breakin(); else send_memcheck_query(); for (;;) { FD_ZERO(&fds); FD_SET(target_fd, &fds); c = select(target_fd+1, &fds, 0, 0, 0); time(&logtime); if (c < 0) { if (errno == EINTR) continue; perror("select"); exit(1); } if (FD_ISSET(target_fd, &fds)) process_serial_rx(); } } static void handle_tm_response() { char msgbuf[80]; if (!breakin_in_progress) { output_line("TM response unexpected at this time"); return; } if (rxpkt_len != 4 || rxpkt[1] != 0x40 || rxpkt[2] || rxpkt[3] != 0x40){ output_line("TM response differs from expected"); return; } sprintf(msgbuf, "Sending stack smash write at 0x%x", stack_smash_addr); output_line(msgbuf); send_compal_memwrite(stack_smash_addr, stack_smash_payload, 4); stack_smash_addr += 4; } static void analyze_gpf_packet() { unsigned stackbase, untouched; static char format[] = "Name:L1 Stat:%*s Count:%*s Prio:%*s Stack:%x Size:%*s Untouched:%u"; char msgbuf[80]; if (rxpkt_len < 17 || rxpkt_len > 128) return; /* it needs to be a trace packet */ if ((rxpkt[1] & 0xF0) != 0xA0) return; /* check the length */ if (rxpkt[2] + 4 != rxpkt_len) return; if (rxpkt[3]) return; /* skip timestamp, check src and dest */ if (strncmp(rxpkt + 8, "SYSTPCO ", 8)) return; /* terminating NUL for sscanf */ rxpkt[rxpkt_len] = '\0'; if (sscanf(rxpkt + 16, format, &stackbase, &untouched) != 2) return; /* success! */ sprintf(msgbuf, "Parsed L1 stack location: base=0x%x, untouched=%u (0x%x)", stackbase, untouched, untouched); output_line(msgbuf); if (stackbase & 3) { output_line("Error: stack base address is not word-aligned"); exit(1); } untouched &= ~3; if (!shellcode_load_addr) { if (untouched < sizeof shellcode) { output_line("Error: not enough room for shellcode"); exit(1); } shellcode_load_addr = stackbase; } stack_smash_addr = stackbase + untouched; initiate_breakin(); } handle_rx_packet() { if (rxpkt_len == 2 && rxpkt[0] == 'O' && rxpkt[1] == 'K') { output_line( "Success: target should now be in boot ROM download wait"); printf("You can now run fc-loadtool -h compal -c none %s\n", target_tty_port); exit(0); } switch (rxpkt[0]) { case RVT_RV_HEADER: if (rxpkt_len < 6) goto unknown; print_rv_trace(); return; case RVT_L1_HEADER: print_l1_trace(); return; case RVT_L23_HEADER: print_g23_trace(); if (!breakin_in_progress) analyze_gpf_packet(); return; case RVT_TM_HEADER: print_tm_output_raw(); handle_tm_response(); return; default: unknown: print_unknown_packet(); } }