diff rvinterf/lowlevel/tfc139.c @ 0:e7502631a0f9

initial import from freecalypso-sw rev 1033:5ab737ac3ad7
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 11 Jun 2016 00:13:35 +0000
parents
children 6f078c4a5506
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/tfc139.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,311 @@
+/*
+ * 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 char *baudrate_name;
+
+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 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;
+
+	baudrate_name = "57600";	/* what C139 firmware uses */
+	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_name = 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_target_serial(argv[optind]);
+	target_tty_port = argv[optind];
+
+	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();
+	}
+}