Sun, 25 Sep 2011

Uncompressing the kernel

According to various sites such as asbokid's, it should be possible to uncompress the kernel available in a Vodafone Huawei image by stripping the 12 byte header as so:

$ ./bfw.pl -x b66fw.bin
Image: 1
tag: 8
sig1: Broadcom Corporatio
sig2: ver. 2.0
chipid: 6358
boardid: HW553
bigendian: 1
image size: 7072560
cfe addr: 3217031168 bfc00000
cfe size: 66716 next: bfc1049c
root addr: 3217162496 bfc20100
root size: 6078464 next: c01ec100
kernel addr: 3223240960 c01ec100
kernel size: 927380 next: c02ce794
dual 1 inactive 0 info1 EchoLife_HG553 info2 V100R001C06B066 u1 no u2 3
total crc: 17775b21
root crc: ae8898ad
kernel crc: 7bcaa0f
header crc: d43ab3b
calculated header crc: d43ab3b
calculated cfe crc: 7235fcf2
calculated rootfs crc: ae8898ad
calculated kernel crc: 7bcaa0f
calculated total crc: 17775b21

Image: 2
tag: 8
sig1: Broadcom Corporatio
sig2: ver. 2.0
chipid: 6358
boardid: HW553
bigendian: 1
image size: 3961699
cfe addr: 0 0
cfe size: 0 next: 0
root addr: 3227648256 c0620100
root size: 3035136 next: c0905100
kernel addr: 3230683392 c0905100
kernel size: 926563 next: c09e7463
dual 0 inactive 0 info1 EchoLife_HG553 info2 V100R001C06B066SM u1 no u2 2
total crc: 19ace33d
root crc: 61a426d3
kernel crc: ebc4b126
header crc: 4462783f
calculated header crc: 4462783f
calculated rootfs crc: 61a426d3
calculated kernel crc: ebc4b126
calculated total crc: 19ace33d
$ ls
b66fw.bin  bfw.pl  cfe1  header1  header2  kernel1  kernel2  rootfs1  rootfs2
$ dd if=kernel1 bs=12 skip=1 of=linux.lz 2>/dev/null
$ lzcat linux.lz
lzcat: linux.lz: File format not recognized

Evidently this doesn't work on the Vodafone firmware. However, Wilmer Gaast's site gives a shell script alternative to the binary cmplzma program available in various Broadcom toolchains. By following this script, it appears to be necessary to insert 8 unknown (but typically 0xff) bytes after the 5th byte in the converted image:

$ (dd if=kernel1 bs=12 skip=1|dd bs=5 count=1;perl -e 'print "\xff"x8';dd if=kernel1 bs=17 skip=1) > kernel.lz 2>/dev/null
$ lzcat kernel.lz > kernel.bin
lzcat: kernel.lz: Unexpected end of input
$ ls -l kernel.bin
-rw-rw-r-- 1 paul paul 2752096 Sep 25 15:48 kernel.bin
$ strings kernel.bin|grep gcc
Linux version 2.6.21.5 (root@IBM_x3500) (gcc version 4.2.3) #2 Wed May 25 19:46:40 CST 2011
%s version %s (root@IBM_x3500) (gcc version 4.2.3) %s

Although the output from lzcat indicated that the input may be corrupt, tests with cmplzma to compress a kernel and the above procedure produced the original compressed file.

Expanding the firmware image is done using bfw.pl which I wrote as all the firmware extraction tools I could find, couldn't handle dual images in a single firmware file.

permalink | | 2011.09.25

GPL part 2

Although I intended to produce patch files from the original sources available on the net, and the sources that Huawei provided for the 2.6.21.5 kernel, this is turning out to be a time consuming task. It appears that Huawei have incorporated sources from multiple versions of programs in a way that makes it difficult to immediately determine which sources are original and which are Huawei modified.

What I had hoped for is something like this, which is the result of diffing dnsmasq-2.45 with the sources Huawei provided. This is one of the few cases that was acually straightforward:

$ cat dnsmasq-2.45-b066.patch
diff -urN dnsmasq-2.45/Makefile dnsmasq/Makefile
--- dnsmasq-2.45/Makefile	2008-07-20 20:26:07.000000000 +0200
+++ dnsmasq/Makefile	2011-09-07 18:49:15.657998053 +0200
@@ -49,10 +49,19 @@
 
 install : all install-common
 
+#huawei begin
+dynamic : install
+#huawei end
+
+#huawei begin
 install-common :
-	$(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
-	$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 
-	$(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
+	install -m 755 $(SRC)/dnsmasq $(INSTALL_DIR)/bin
+#huawei end
+
+#install-common :
+#	install -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
+#	install -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 
+#	install -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
 
 all-i18n :
 	cd $(SRC) && $(MAKE) \
diff -urN dnsmasq-2.45/src/dnsmasq.c dnsmasq/src/dnsmasq.c
--- dnsmasq-2.45/src/dnsmasq.c	2008-07-20 20:26:07.000000000 +0200
+++ dnsmasq/src/dnsmasq.c	2011-09-07 18:49:15.655998052 +0200
@@ -56,6 +56,11 @@
 static volatile pid_t pid = 0;
 static volatile int pipewrite;
 
+//==============================================
+int g_need_dns_response_flag = 0;
+int g_get_response_timeout_count = 0;
+//==============================================
+
 static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
 static void check_dns_listeners(fd_set *set, time_t now);
 static void sig_handler(int sig);
@@ -578,13 +583,33 @@
 	}
 
       /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
-      if (daemon->tftp_trans ||
-	  ((daemon->options & OPT_DBUS) && !daemon->dbus))
+    //if (daemon->tftp_trans ||
+	//  ((daemon->options & OPT_DBUS) && !daemon->dbus))
 	{
-	  t.tv_sec = 0;
-	  t.tv_usec = 250000;
+	  //½«³¬Ê±Ôö´óµ½1s
+	  t.tv_sec = 1;
+	  t.tv_usec = 0;
 	  tp = &t;
 	}
+	
+	//=======================================================
+	if (g_need_dns_response_flag) 
+	{
+		/* If the time waiting for DNS response over 500ms, 
+		   then change the query port to DNS 
+	     */
+		if (g_get_response_timeout_count++ > 5)
+		{
+			change_dns_query_ports(daemon);
+			g_get_response_timeout_count = 0;
+			g_need_dns_response_flag = 0;
+		}
+	}
+	else
+	{
+		g_get_response_timeout_count = 0;
+	}
+	//=======================================================
 
 #ifdef HAVE_DBUS
       set_dbus_listeners(&maxfd, &rset, &wset, &eset);
diff -urN dnsmasq-2.45/src/filter.c dnsmasq/src/filter.c
--- dnsmasq-2.45/src/filter.c	1970-01-01 01:00:00.000000000 +0100
+++ dnsmasq/src/filter.c	2011-09-07 18:49:15.655998052 +0200
@@ -0,0 +1,109 @@
+
+#include 
+#include 
+#include 
+#include 
+
+#define NAME_SIZE  64
+#define LIST_STEP  4
+
+typedef struct {
+	char szName[NAME_SIZE];
+} NAME_S;
+
+/* 1 -white, 0 -black. */
+static unsigned int s_uMode = 0;
+static NAME_S* s_pstNameList = 0;
+static unsigned int s_uNameSize = 0;
+
+
+unsigned int filterInitialize(const char* lpszFile)
+{
+	FILE* pFile = 0;
+	char* pszTemp = 0;
+	NAME_S* pstName = 0;
+	unsigned int uInd = 0;
+	char szName[NAME_SIZE] = {0};
+
+	if (s_pstNameList != 0) {
+		printf("URL Filer: Has been initialized!\n");
+		return 0;
+	}
+	
+	pFile = fopen(lpszFile, "r");
+	if (pFile == 0) {
+		printf("URL Filer: open rule file failed!\n");
+		return 1;
+	}
+
+    /* get white/black mode */
+	if (!fgets(szName, sizeof(szName), pFile)) {
+		printf("URL Filer: Not sepcial white/black mode in file!\n");
+		fclose(pFile);
+		return 1;
+	}
+	s_uMode = (unsigned int)atoi(szName);
+
+	/* get name list */
+	while (fgets(szName, sizeof(szName), pFile)) {
+		if (s_uNameSize <= uInd) {
+			pstName = realloc(s_pstNameList, 
+				(sizeof(NAME_S) * (s_uNameSize + LIST_STEP)));
+			if (pstName == 0) {
+				if (s_pstNameList != 0) {
+					free(s_pstNameList);
+					s_pstNameList = 0;
+				}
+				uInd = 0;
+				break;
+			}
+			memset(&(pstName[uInd]), 0, (sizeof(NAME_S) * LIST_STEP));
+			s_pstNameList = pstName;
+			s_uNameSize += LIST_STEP;
+		}
+		if (szName[0] == '\n'
+			|| szName[0] == '\r'
+			|| szName[0] == ' '
+			|| szName[0] == '\t')
+		{
+			continue;
+		}
+		
+		pszTemp = strpbrk(szName, " \r\n");
+		if (pszTemp != 0) {
+			*pszTemp = '\0';
+		}
+		strncpy(s_pstNameList[uInd].szName, szName, NAME_SIZE);
+		s_pstNameList[uInd].szName[NAME_SIZE - sizeof(char)] = '\0';
+		
+		uInd++;
+	}
+
+	fclose(pFile);
+	s_uNameSize = uInd;
+	return 1;
+}
+
+void filterClean()
+{
+	s_uMode = 0;
+	if (s_pstNameList != 0) {
+		free(s_pstNameList);
+		s_pstNameList = 0;
+		s_uNameSize = 0;
+	}
+}
+
+unsigned int filterCheck(const char* lpszName)
+{
+	
+	if (s_pstNameList != 0) {
+		unsigned int uInd = 0;
+		for (uInd = 0; uInd < s_uNameSize; uInd++) {
+			if (strcasestr(lpszName, s_pstNameList[uInd].szName) != 0) {
+				return s_uMode;
+			}
+		}
+	}
+	return (!s_uMode);
+}
diff -urN dnsmasq-2.45/src/filter.h dnsmasq/src/filter.h
--- dnsmasq-2.45/src/filter.h	1970-01-01 01:00:00.000000000 +0100
+++ dnsmasq/src/filter.h	2011-09-07 18:49:15.656998052 +0200
@@ -0,0 +1,4 @@
+
+extern unsigned int filterInitialize(const char* lpszFile);
+extern void filterClean();
+extern unsigned int filterCheck(const char* lpszName);
diff -urN dnsmasq-2.45/src/forward.c dnsmasq/src/forward.c
--- dnsmasq-2.45/src/forward.c	2008-07-20 20:26:07.000000000 +0200
+++ dnsmasq/src/forward.c	2011-09-07 18:49:15.656998052 +0200
@@ -325,6 +325,10 @@
 		}
 	      else
 		{
+   		  extern int g_need_dns_response_flag;
+   		  extern int g_get_response_timeout_count;
+		
+		  g_need_dns_response_flag = 1;
 		  /* Keep info in case we want to re-send this packet */
 		  daemon->srv_save = start;
 		  daemon->packet_len = plen;
diff -urN dnsmasq-2.45/src/network.c dnsmasq/src/network.c
--- dnsmasq-2.45/src/network.c	2008-07-20 21:00:24.000000000 +0200
+++ dnsmasq/src/network.c	2011-09-07 18:49:15.656998052 +0200
@@ -705,6 +705,10 @@
     }
   
   daemon->servers = ret;
+
+  //==========================================
+  change_dns_query_ports(daemon);
+  //==========================================  
 }
 
 /* Return zero if no servers found, in that case we keep polling.
@@ -842,3 +846,125 @@
 
 
 
+#define DNSMASQ_QUERY_PORT_MIN 1050
+#define DNSMASQ_QUERY_PORT_MAX 4095
+
+int change_dns_query_ports(struct daemon *daemon)
+{
+	int port = 0;
+	static int changed_times = 0;
+	struct in_addr addr;
+	struct serverfd *sfd, *tmp_sfd;
+	char buff[128];
+	
+	for (sfd = daemon->sfds; sfd; sfd = tmp_sfd)
+	{
+		int fd = -1;
+		tmp_sfd = sfd->next;
+		short src_port = 0;
+
+		syslog(LOG_WARNING, _("\nchange_dns_query_ports:fd[%d][%08x]."), sfd->fd, sfd);
+		
+		// close socket first
+		if (sfd->fd != -1)
+		{
+			struct sockaddr_in localaddr;
+			int len = sizeof(struct sockaddr);				
+
+			memset(&localaddr, 0, sizeof(localaddr));
+			getsockname(sfd->fd, (struct sockaddr *)&localaddr, (socklen_t *)&len);		
+			
+			sfd->source_addr.in.sin_port = localaddr.sin_port;
+			close(sfd->fd);
+			sfd->fd = -1;
+		}
+		else
+		{
+			syslog(LOG_INFO, _("\ncontinue change_dns_query_ports."));
+			continue;
+		}
+
+		addr = sfd->source_addr.in.sin_addr;
+		syslog(LOG_WARNING, _("\nwarning: change_dns_query_ports[%s][%d][%d] pre."), 
+					inet_ntoa(addr),
+					ntohs(sfd->source_addr.in.sin_port),
+					changed_times
+					);
+
+		// check port range
+		port = ntohs(sfd->source_addr.in.sin_port);
+		port += 3;
+		if (port > DNSMASQ_QUERY_PORT_MAX || port < DNSMASQ_QUERY_PORT_MIN)
+		{
+			port = DNSMASQ_QUERY_PORT_MIN + rand16()%3000;
+		}
+		sfd->source_addr.in.sin_port = htons(port);
+
+		addr = sfd->source_addr.in.sin_addr;
+		syslog(LOG_WARNING, _("\nwarning: change_dns_query_ports[%s][%d][%d] aft."),
+					inet_ntoa(addr),
+					ntohs(sfd->source_addr.in.sin_port),
+					changed_times++
+					);
+		
+		// close socket first
+		fd = socket(sfd->source_addr.sa.sa_family, SOCK_DGRAM, 0);
+		while(fd != (-1))
+		{		
+			if (bind(fd, (struct sockaddr *)&sfd->source_addr, sa_len(&sfd->source_addr)) == -1 ||
+			  !fix_fd(fd))
+			{			
+				addr = sfd->source_addr.in.sin_addr;
+				syslog(LOG_WARNING, _("\nwarning: change_dns_query_ports[%s][%d][%d] pre."),
+							inet_ntoa(addr),
+							ntohs(sfd->source_addr.in.sin_port),
+							changed_times
+							);		
+				
+				// check port range
+				port = ntohs(sfd->source_addr.in.sin_port);
+				port +=3;
+				if (port >DNSMASQ_QUERY_PORT_MAX || port < DNSMASQ_QUERY_PORT_MIN)
+				{
+					port = DNSMASQ_QUERY_PORT_MIN + rand16()%3000;
+				}
+				sfd->source_addr.in.sin_port = htons(port);
+				usleep(100000);
+
+				addr = sfd->source_addr.in.sin_addr;				
+				syslog(LOG_WARNING, _("\nwarning: change_dns_query_ports[%s][%d][%d] aft."), 
+							inet_ntoa(addr),
+							ntohs(sfd->source_addr.in.sin_port),
+							changed_times++
+							);
+			}
+			else
+			{
+				break;
+			}
+		}
+
+		if (fd != (-1))
+		{
+			addr = sfd->source_addr.in.sin_addr;
+			syslog(LOG_WARNING, _("\nwarning:change_dns_query_ports[%s][%d][%d] successfully."), 
+						inet_ntoa(addr),
+						htons(sfd->source_addr.in.sin_port),
+						changed_times
+						);
+		}	
+		else
+		{
+			addr = sfd->source_addr.in.sin_addr;
+			syslog(LOG_WARNING, _("\nwarning:change_dns_query_ports[%s][%d][%d] failed."), 
+						inet_ntoa(addr),
+						htons(sfd->source_addr.in.sin_port),
+						changed_times
+						);
+		}
+		sfd->fd = fd;
+	}
+
+	return (0);
+}
+
diff -urN dnsmasq-2.45/src/option.c dnsmasq/src/option.c
--- dnsmasq-2.45/src/option.c	2008-07-20 20:42:53.000000000 +0200
+++ dnsmasq/src/option.c	2011-09-07 18:49:15.655998052 +0200
@@ -2457,7 +2457,7 @@
   daemon->default_resolv.is_default = 1;
   daemon->default_resolv.name = RESOLVFILE;
   daemon->resolv_files = &daemon->default_resolv;
-  daemon->username = CHUSER;
+  //daemon->username = CHUSER; /* for HG553 VF */
   daemon->runfile =  RUNFILE;
   daemon->dhcp_max = MAXLEASES;
   daemon->tftp_max = TFTP_MAX_CONNECTIONS;

Here we see a patch that mainly appears to resolve an issue where, perhaps, the Vodafone firewall is interfering with the port selected at random by dnsmasq.

However, as this is taking too long, I've put up the GPL sources that Huawei provided here: www.sbrk.co.uk/d/hg553.tar.bz2 (46MB).

Sorry it's not a link, you'll have to copy and paste the location (saves bandwidth from spiders). Note that these aren't the sources available on Huawei's web site here. Those are for a considerably early firmware revision using kernel 2.6.8.

Also note that I only put sources up in that archive. If you need the miscellaneous binaries for some reason, let me know.

permalink | | 2011.09.25

Fri, 23 Sep 2011

Cloning a device

In order to operate with Vodafone as the ISP, a modem must support the MER protocol and it then uses the modem's MAC address of the appropriate interface to acquire an address over DHCP. As part of this request, the modem also sends the device serial number, manufacture OUI and product class. These are sent in order to acquire the appropriate ACS configuration to perform TR-069 remote management.

The DHCP request looks like this:

Bootstrap Protocol
    Message type: Boot Request (1)
    Hardware type: Ethernet
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0xb1202113
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
        0... .... .... .... = Broadcast flag: Unicast
        .000 0000 0000 0000 = Reserved flags: 0x0000
    Client IP address: 0.0.0.0 (0.0.0.0)
    Your (client) IP address: 0.0.0.0 (0.0.0.0)
    Next server IP address: 0.0.0.0 (0.0.0.0)
    Relay agent IP address: 0.0.0.0 (0.0.0.0)
    Client MAC address: Vodafone_10:11:12 (00:24:89:10:11:12)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (t=53,l=1) DHCP Message Type = DHCP Discover
        Option: (53) DHCP Message Type
        Length: 1
        Value: 01
    Option: (t=61,l=16) Client identifier
        Option: (61) Client identifier
        Length: 16
        Value: 0033...
    Option: (t=12,l=15) Host Name = "391223K123321"
        Option: (12) Host Name
        Length: 15
        Value: 33...
    Option: (t=60,l=12) Vendor class identifier = "dslforum.org"
        Option: (60) Vendor class identifier
        Length: 12
        Value: 64736c666f72756d2e6f7267
    Option: (t=55,l=14) Parameter Request List
        Option: (55) Parameter Request List
        Length: 14
        Value: 790103060f111c0204072a42432b
        121 = Classless Static Route
        1 = Subnet Mask
        3 = Router
        6 = Domain Name Server
        15 = Domain Name
        17 = Root Path
        28 = Broadcast Address
        2 = Time Offset
        4 = Time Server
        7 = Log Server
        42 = Network Time Protocol Servers
        66 = TFTP Server Name
        67 = Bootfile name
        43 = Vendor-Specific Information
    Option: (t=125,l=40) V-I Vendor-specific Information
        Option: (125) V-I Vendor-specific Information
        Length: 40
        Value: 000...
        Enterprise-number: ADSL Forum (3561)
            Suboption 4: GatewayManufacturerOUI = "002489"
            Suboption 5: GatewaySerialNumber = "391223K123321"
            Suboption 6: GatewayProductClass = "EchoLife"
        no room left in option for enterprise 3561 data
    Option: (t=77,l=4) User Class Information
        Option: (77) User Class Information
        Length: 4
        Value: 03401232
    End Option
    Padding

and a response will be received like so:

Bootstrap Protocol
    Message type: Boot Reply (2)
    Hardware type: Ethernet
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0xb1202113
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
        0... .... .... .... = Broadcast flag: Unicast
        .000 0000 0000 0000 = Reserved flags: 0x0000
    Client IP address: 0.0.0.0 (0.0.0.0)
    Your (client) IP address: 188.217.90.39 (188.217.90.39)
    Next server IP address: 0.0.0.0 (0.0.0.0)
    Relay agent IP address: 0.0.0.0 (0.0.0.0)
    Client MAC address: Vodafone_10:11:12 (00:24:89:10:11:12)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (t=53,l=1) DHCP Message Type = DHCP Offer
        Option: (53) DHCP Message Type
        Length: 1
        Value: 02
    Option: (t=54,l=4) DHCP Server Identifier = 93.64.160.1
        Option: (54) DHCP Server Identifier
        Length: 4
        Value: 5d40a001
    Option: (t=51,l=4) IP Address Lease Time = 15 minutes
        Option: (51) IP Address Lease Time
        Length: 4
        Value: 00000384
    Option: (t=1,l=4) Subnet Mask = 255.255.224.0 
        Option: (1) Subnet Mask
        Length: 4
        Value: ffffe000
    Option: (t=3,l=4) Router = 188.217.32.1
        Option: (3) Router
        Length: 4
        Value: bcd92001
    Option: (t=6,l=8) Domain Name Server
        Option: (6) Domain Name Server
        Length: 8
        Value: 5b5023a65b502385
        IP Address: 91.80.35.166
        IP Address: 91.80.35.133
    Option: (t=42,l=8) Network Time Protocol Servers
        Option: (42) Network Time Protocol Servers
        Length: 8
        Value: 5b5023ab5b50238b
        IP Address: 91.80.35.171
        IP Address: 91.80.35.139
    Option: (t=43,l=47) Vendor-Specific Information
        Option: (43) Vendor-Specific Information
        Length: 47
        Value: "https://acs.dsl.vodafone.it:7006/cwmpWeb/CPEMgt"
    Option: (t=58,l=4) Renewal Time Value = 7 minutes, 30 seconds
        Option: (58) Renewal Time Value
        Length: 4
        Value: 000001c2
    Option: (t=59,l=4) Rebinding Time Value = 13 minutes, 7 seconds
        Option: (59) Rebinding Time Value
        Length: 4
        Value: 00000313
    Option: (t=60,l=12) Vendor class identifier = "dslforum.org"
        Option: (60) Vendor class identifier
        Length: 12
        Value: 64736c666f72756d2e6f7267
    End Option
    Padding

The serial number may not be an essential part of the DHCP IP address acquisition, but is likely to cause alarms if it doesn't match the device MAC address.

Modifying the MAC address and serial number can be done in several ways:

  • Install a CFE with the MAC address and serial number preconfigured
  • Issue the CFM serserialnum and macaddr command but these are hidden in some way and I'm not sure how to enable them
  • Program them with TR-069
  • write to flash directly
  • call sysSetBoardSerialNumber() and sysSetBaseMacAddress() functions in libpsi.so

I chose to do the latter using this info program.

# ./info
sysGetBaseMacAddress: 00:24:89:11:12:13
sysGetBoardId: HW553
sysGetBoardIdName: HW553
sysGetBoardManufacturer: N/A
sysGetBoardSerialNumber: 391223K123321
sysGetBoardVersion: HG55MAGV
sysGetBootline: 
sysGetCFEVersion: d080.5003
sysGetChipId: 0x6358
sysGetChipRevisionID: 0xa1
sysGetEasyResetFlag: 0
sysGetNumEnet: 1
sysGetMacAddress(0): 00:24:89:11:12:13
sysGetMacNumbers: 6
sysGetPsiActualSizeFromFlash: 0x0
sysGetPsiSize: 0x10000
sysGetSdramSize: 0x4000000
sysGetVoipServiceStatus: 
sysGetVoipServiceStatus: 0

The rstinfo program will modify the MAC address and Serial Number labelled by sysGetBaseMacAddress: and sysGetBoardSerialNumber:

# ./info > /var/mnt/saved.info
[edit as required, or just copy from one device to another]
# ./rstinfo /var/mnt/saved.info
macbase: 00:24:89:11:12:13
base: 00:24:89:11:12:13
serial: 391223K123321
setting serno: 391223K123321
rc 0
setting mac base: 00:24:89:11:12:13
rc 0

Note: For Linux 2.4 kernel based firmware images (practically anything other than B066), you will need info3 and rstinfo3 compiled using an earlier toolchain.

permalink | | 2011.09.23

Thu, 08 Sep 2011

GPL Sources

The router is delivered with a written offer to receive the source code of the GPL software delivered with the device (and updated via TR-069), which can be received by sending a request to terminal@huawei.com. After several attempts, due, I think, to the broken Huawei mail servers (4 MXs, three don't work), I received a reply saying that they would prepare the source code and deliver it to me in a week or so. This duly arrived (thank you Huawei), and I ended up with the following (edited):

Some open source Broadcom driver code:

bcmdrivers/opensource/char/serial/impl1/bcm63xx_cons.c
bcmdrivers/opensource/char/board/bcm963xx/impl1/bcm63xx_flash.c
bcmdrivers/opensource/char/board/bcm963xx/impl1/board.c
bcmdrivers/opensource/char/board/bcm963xx/impl1/bcm63xx_led.c
bcmdrivers/opensource/include/bcm963xx/*h

shared/opensource/boardparms/bcm963xx/boardparms.c
shared/opensource/flash/cfiflash.c
shared/opensource/flash/spiflash.c
shared/opensource/flash/Makefile
shared/opensource/flash/flash_api.c
shared/opensource/include/bcm963xx/*h

ELF 32-bit executables to generate firmware images:

hostTools/mksquashfs
hostTools/Makefile
hostTools/createimg
hostTools/cmplzma
hostTools/addvtoken
hostTools/bcmImageBuilder

Compiled kernel and rootfs

images/bcm96358GWV_fs_kernel_Echolife_HG553V100R001C06B066.-110906_1053
$ bcminfo images/bcm96358GWV_fs_kernel_Echolife_HG553V100R001C06B066.-110906_1053 
--- Firmware information ---
Magic: 0x38, 0x00, 0x00, 0x00
VendorName: Broadcom Corporatio
Version: ver. 2.0
Board ID: 6358
Model: HW553
Image size: 2687126 (2687382)
Loader address: 0x00000000
Loader size: 0
Root filesystem address: 0xbfc20100
Root filesystem size: 1761280
Kernel address: 0xbfdce100
Kernel size: 925846
Data checksum: 0xb9b553dc
Header checksum: 0x578c297

The binaries in the rootfs appear to be recent but don't correspond either to the sources or the binaries on my router:

$ strings hg553/images/squashfs-root/bin/busybox |grep nat_red
$ strings b66orig/squashfs-root/bin/busybox |grep nat_red
iptables -t nat -L PREROUTING_UTILITY > /var/nat_redirect
$ grep nat_redirect hg553/userapps/opensource/busybox/*
hg553/userapps/opensource/busybox/syscall.c:   bcmSystem("iptables -t nat -L PREROUTING_UTILITY > /var/nat_redirect");

2.6.21.5 kernel plus a few thousand lines of changes/additions

kernel/linux/

Compilation "script"

readme
$ cat readme 
compile command:

fakeroot make PROFILE=96358GWV clean && fakeroot make PROFILE=96358GWV

Open source drivers

shared/opensource/gspcav/ - "gspca" video for linux (v4l1) driver
shared/opensource/spca5xx/ - spca5xx video for linux (v4l) driver

Config files and a prebuilt CFE

targets/defaultcfg/default.cfg
targets/96358GWV/96358GWV.config
targets/buildFS
targets/fs.bin
targets/cfe/cfe6358.bin

Leftover binaries that don't correspond with the sources:

targets/96358GWV/sstrip/bin/{udhcpd,busybox,scout,etc} 

The non-proprietary binaries ended up in the rootfs above

Various default files many of which don't exist on the router image:

targets/fs.src/etc/{res/ToneRes.wav,wlan/bcm4306_map.bin,etc/rsa_host_key,etc}
targets/fs.src/lib/*.so
targets/fs.src/bin/* - symlinks and other versions of files with unknown heritage

In the end I can see three copies of busybox:

207240 Sep  6 04:53 ./images/squashfs-root/bin/busybox
207240 Sep  3 12:58 ./targets/96358GWV/sstrip/bin/busybox
221664 Sep  6 04:52 ./targets/fs.src/bin/busybox

The first two have identical sizes but different build dates:

< BusyBox v1.00 (2011.09.06-02:52+0000) Built-in shell (msh)
---
> BusyBox v1.00 (2011.09.03-07:45+0000) Built-in shell (msh)

Various open source components, usually several years old, patched:

userapps/opensource/busybox/
userapps/opensource/libcreduction/
userapps/opensource/ebtables/
userapps/opensource/samba/
userapps/opensource/libosip2/
userapps/opensource/ntfs-3g/
userapps/opensource/siproxd/
userapps/opensource/iptables/
userapps/opensource/motion-3.2.9/
userapps/opensource/zebra/
userapps/opensource/dnsmasq/
userapps/opensource/atm2684/
userapps/opensource/iproute2/
userapps/opensource/ppp/
userapps/opensource/udhcp/
userapps/opensource/bridge-utils/
userapps/opensource/jpeg-6b/
userapps/opensource/msmtp1.4.14/
userapps/opensource/openssl/
userapps/opensource/ftpd/

Some precompiled tools

userapps/opensource/tools/strace - compiled for MIPS
userapps/opensource/tools/iostat - compiled for MIPS

I did try building the sources and got most compiled after applying various patches, but in the end noted that it would be impossible to make arbitrary modifications to the router as the CFM binary contains an embedded copy of, at least, the GPL v2 bftp program:

diff -urN bftpd/main.c ftpd/main.c
--- bftpd/main.c  2008-09-17 01:36:33.000000000 +0200
+++ ftpd/main.c 2011-09-08 01:11:37.389681706 +0200
...
-int main(int argc, char **argv)
+int bftp_main(int lPort)

$ strings b66orig/squashfs-root/bin/cfm|grep ftp_main
bftp_main

Since this has been done, then CFM should be released according the obligations of the GPL. Will request this from Huawei and see what they say.

When I've cleaned up the sources I'll make them available. If anyone really wants to see them earlier, send me a mail:paul@something.sbrk.co.uk where "something" equals "hg553a" (email address will change as soon as it receives spam).

permalink | | 2011.09.08

Thu, 01 Sep 2011

Modifying the read-only filesystem

Although it is quite straightforward to create a new firmware image and change the filesystem, it is possible to perform just about any modification to an out-of-the-box router without sacrificing your warranty. Two useful activities are to add or modify web pages, and add or modify binaries:

To modify web pages, we first make a copy of the /webs directory and then mount that copy on top of the original.

# cd /
# cp -r webs /var/mnt
# mount -o bind /var/mnt/webs /webs
# cd /webs
# wget -g -r /d/ej.html -l ej.html www.sbrk.co.uk
# wget -g -r /d/busybox -l /var/mnt/busybox www.sbrk.co.uk
# /var/mnt/busybox sed 3q ej.html
<html><body><pre>
ejFncCmd(stsadslupdate) - <%ejFncCmd(stsadslupdate)%>
ejGetCookie() - <%ejGetCookie()%>

Then if we browse to this web page we see all the "ej" dynamic variables available to the web server:

ejFncCmd(stsadslupdate) - 
ejGetCookie() - pd3c56Y8eH00G7T68K935EpT8v00Fubr
...
ejGet(HardwareVersion) - HG55MAGV  VER.A
...
ejGetOther(diagnostic, 10) - connessione presente
ejGetOther(diagnostic, 17) - connessione presente
...
ejGetOther(lineRate, 0, 0) - 640
ejGetOther(lineRate, 1, 0) - 8128
...

Similarly with binaries, we make a copy of /bin and then mount the copy on top.

# cd /
# cp -r bin /var/mnt
# mount -o bind /var/mnt/bin /bin
# wget -g -r /d/strace -l /bin/strace www.sbrk.co.uk
# chmod 755 /bin/strace
# strace -fe trace=execve -p [pid of cfm]
...
execve("/bin/iptables", ["iptables", "-t", "filter", "-A", "INPUT", "-j", "INPUT_SECDOS"], [/* 7 vars */]) = 0

Thus we see cfm executing /bin/iptables and decide we wish to disable that or intercept it by replacing /bin/iptables with another script or program that modifies its arguments and executings the original iptables program.

Replacement script left as an exercise to the reader :)

permalink | | 2011.09.01