changeset 984:8c83777f856c

tfc139 reworked for the new "universal" break-in method
author Mychaela Falconia <falcon@ivan.Harhan.ORG>
date Sat, 12 Dec 2015 03:17:12 +0000
parents 7166c8311b0d
children 8109185528c1
files rvinterf/lowlevel/tfc139.c
diffstat 1 files changed, 108 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/rvinterf/lowlevel/tfc139.c	Thu Dec 10 08:07:47 2015 +0000
+++ b/rvinterf/lowlevel/tfc139.c	Sat Dec 12 03:17:12 2015 +0000
@@ -1,8 +1,13 @@
 /*
- * This program is a contender for the title of the ugliest hack
- * in the FreeCalypso project.  It will attempt to break into a
- * locked-down TracFone C139 by mimicking the actions of the
- * mot931c.exe TF "unlocker".
+ * 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>
@@ -48,11 +53,12 @@
 	0x4B, 0x02
 };
 
-static unsigned shellcode_load_addr = 0x800000;
-static unsigned stack_smash_addr = 0x837C54;
-static int thumb_entry = 0;
+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;
 
@@ -80,10 +86,15 @@
 }
 
 static void
-build_stack_smash_payload()
+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;
@@ -93,6 +104,31 @@
 	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)
@@ -104,11 +140,14 @@
 	fd_set fds;
 
 	baudrate_name = "57600";	/* what C139 firmware uses */
-	while ((c = getopt(argc, argv, "a:B:l:s:tw:")) != EOF)
+	while ((c = getopt(argc, argv, "a:AB:l:s:w:")) != EOF)
 		switch (c) {
 		case 'a':
 			shellcode_load_addr = strtoul(optarg, 0, 16);
 			continue;
+		case 'A':
+			thumb_entry = 0;
+			continue;
 		case 'B':
 			baudrate_name = optarg;
 			continue;
@@ -118,9 +157,6 @@
 		case 's':
 			stack_smash_addr = strtoul(optarg, 0, 16);
 			continue;
-		case 't':
-			thumb_entry = 1;
-			continue;
 		case 'w':
 			wakeup_after_sec = strtoul(optarg, 0, 0);
 			continue;
@@ -132,6 +168,10 @@
 		}
 	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];
 
@@ -147,9 +187,10 @@
 		fprintf(logF, "*** Log of TFC139 break-in session ***\n");
 	}
 	time(&logtime);
-	output_line("Sending shellcode RAM write");
-	send_compal_memwrite(shellcode_load_addr, shellcode, sizeof shellcode);
-	build_stack_smash_payload();
+	if (stack_smash_addr)
+		initiate_breakin();
+	else
+		send_memcheck_query();
 	for (;;) {
 		FD_ZERO(&fds);
 		FD_SET(target_fd, &fds);
@@ -171,6 +212,10 @@
 {
 	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;
@@ -181,6 +226,52 @@
 	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, 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') {
@@ -201,6 +292,8 @@
 		return;
 	case RVT_L23_HEADER:
 		print_g23_trace();
+		if (!breakin_in_progress)
+			analyze_gpf_packet();
 		return;
 	case RVT_TM_HEADER:
 		print_tm_output_raw();