OpenWrt Forum Archive

Topic: Poray and MPR-L8 Packtool

The content of this topic has been archived on 27 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

I'm continuing this discussion from https://forum.openwrt.org/viewtopic.php?id=37002 because it is no longer HAME specific.    Go read my posts on pages 15-16 to see the discussion leading up to this.  The short story is that the Poray (and MPR) devices use an encrypted/obfuscated firmware for factory upgrades.  This is a pain because it means we can't flash OpenWRT from the factory web app.  Until now.

This code is a packtool intended to take an OpenWRT (or other) kernel uImage (e.g. openwrt-xxx-sysupgrade.bin) and pack it into the format expected by the web admin tool in those devices. 

This is presently USE AT YOUR OWN RISK!  Meaning that unless you have a serial connection, it's probably not worth trying just yet.  This is because you could potentially write a bad kernel and "soft" brick your router.  UBoot will still be alive, so it's only considered a soft brick - it can be recovered by using serial connection to reflash via UBoot.

That being said..  I just successfully packed a customized Openwrt build into a "factory" image for MPR-L8 (HAME A1 clone) and used it to update a virgin MPR-L8 from the factory software.  Worked first try!.  The tool has been verified to generate binary identical images (from an unpack/pack round trip) on MPR-L8, Poray: M3, Q3, X5/X6, R50x, and X8. 

One important note: If you're watching in the serial console, you may see some scary messages after flash:

SQUASHFS error: sb_bread failed reading block 0x697
SQUASHFS error: Unable to read page, block 1a1f26, size 3f50
SQUASHFS error: sb_bread failed reading block 0x697
SQUASHFS error: Unable to read page, block 1a1f26, size 3f50

and so on and so on.  It looks like the factory software attempts some sort of verify after write that just fails miserably due to the new filesystem not matching the old one.  But although the device was blinking it's solid red + slow blue blink of death, upon power cycling it booted the newly-flashed OpenWRT perfectly.


I was also successful in using UBoot to flash a decrypted factory software from the previously released unpack tool in order to restore an OpenWRT device back to factory software.  So if you later decide you want your old software back, you can do it with a serial console at least.  (Or maybe LuCi, but that fat bitch doesn't yet fit in 4MB so it might be a moot point).


At any rate, here's the tool.  I'd like to enlist some users of various Poray devices (with a serial connection, of course) to act as beta testers:
1) Start with factory firmware, then pack a known good OpenWRT build for your device and attempt upgrading via the original factory web admin interface.   If possible, also have the serial port connected and capturing, to give us some postmortem if it bombs.
2) Report back success or failure.  No need for long serial dumps if it works, but please include them if it bricks.
3) Include any general comments about the tool or your experience.  What do we need to do to make it noob-friendly?

Good luck, and I wish you easy success in liberating your devices from factory tyranny!

Usage: pack <device> <infile> <outfile>
Example: pack PM3 OpenWRT.bin Poray_M3_OpenWRT.bin

/************************************************************************************
 * Router firmware packing tool
 * Copyright (c) 2013 openschemes.com and Michel Stempin <michel.stempin@wanadoo.fr>
 ***********************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

typedef struct 
{
   uint32_t magic;
   uint32_t len;
   uint32_t imgflags;
   uint32_t junk[4];
} fheader;

int main(int argc, char *argv[])
{
    char *infname;
    char *outfname;
    char *device;
    FILE* infile;
    FILE* outfile;
    
    uint32_t checksum, mychecksum;
    uint32_t i;
    
    fheader f;
    
    uint8_t MPRkey[] = {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08};
    uint8_t PORkey[] = {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO2key[] = {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t mykey[15];
    uint8_t *data;  //Pointer to our big data array

    printf("===============================================================\n");    
    printf(" PACK - Encrypt and prepare Linux image for router firmware\n");
    printf(" (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>\n");
    printf(" (C) 2013 www.openschemes.com\n");
    printf("===============================================================\n\n");
    //Check arguments
    if (argc > 3) 
    {  
             device = argv[1];
             printf("Device: %s\n", device);
             infname = argv[2];
             printf("Input File: %s\n", infname);
             outfname = argv[3];
             printf("Output File: %s\n", outfname);

             
    }
    else
    {
             printf("Wrong number of arguments.\n");
             printf("Usage: %s device inputfile outputfile\n", argv[0]); 
             printf("Choices for <device> Case sensitive!\n"); 
             printf("PM3 - Poray M3\n");
             printf("PQ3 - Poray Q3\n");
             printf("PX5/6 - Poray X5/X6\n");
             printf("PX8 - Poray X8\n");
             printf("PR50 - Poray R50\n");
             printf("MPR - MPR-L8 (HAME clone)");
             exit(0);
    }
   
   checksum=0;
   
   //Check device and choose encryption key
   //M4 encrypt/decrypt not verified 0x32353335, so not presently included

          if (!strcmp(device, "PM3")){
               printf("Packing Poray M3 Image: ");
               f.magic=0x31353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
               checksum=-1; //HACK, I don't know why it needs this.
          } else if (!strcmp(device, "PQ3")){
               printf("Packing Poray Q3 Image: ");
               f.magic= 0x33353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PX5/6")){
               printf("Packing Poray X5/X6 Image: ");
               f.magic=0x35353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PX8")){
               printf("Packing Poray X8 Image: ");
               f.magic=0x36353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PR50")){
               f.magic=0x34353033;
               memcpy(mykey, PO2key, sizeof(PO2key));             
          } else if (!strcmp(device, "MPR")){
               printf("Packing MPR-L8 Image: ");
               f.magic=0x32473352;
               memcpy(mykey, MPRkey, sizeof(MPRkey));
               checksum=-1; //HACK, I don't know why it needs this.
          } else {
               printf("I don't recognize the %s device, bailing out\n", device);
               exit(1);
          }                  

    
    //Fill rest of header
    f.imgflags = 0x020e0000;
    f.junk[0]=f.junk[1]=f.junk[2]=f.junk[3]=0;
    
    //Get files
    
    //Open input and output files
    infile = fopen(infname, "rb");
    if (infile==NULL)
    {
        printf("Couldn't open input file\n");
        exit(1);
    }
   
    outfile = fopen(outfname, "r");

    if (outfile == NULL)
        outfile = fopen(outfname, "wb");
    else
    {
        printf("Output file already exists!  \n-Quitting to avoid accidentally overwriting \n-your only copy of a firmware file.. :) \n");
        fclose(outfile);
        fclose(infile);
        return(1);
    }

    if (outfile==NULL)
    {
        printf("Couldn't open output file\n");
        fclose(infile);
        exit(1);    
    }
    
    //Read file to get data len
    fseek(infile, 0, SEEK_END);
    f.len=ftell(infile);
    fseek(infile, 0, SEEK_SET);
    
    data = (uint8_t*)malloc(f.len+2);
    if(data==0)
    {
      printf("Couldn't allocate memory, bailing out\n");
      exit(1);
     }
    
    printf("Reading %X bytes from input file\n", f.len);
    
    //Get the data
    fread(data, f.len, 1, infile);
    
    //Look for right data
    if (data[0]!=0x27 || data[1]!=0x05 || data[2]!=0x19 || data[3]!=0x56)
    {
         printf("Error - Input data doesn't look like a uImage (bad header).  I can't continue for risk of brick..\n");
         exit(1);
    }
    
    //DO CHECKSUM
    printf("Checksum Calculation...\n");
     
    for (i=0; i<f.len-1; i+=2)
    {
           //if ((i<8)||(i>f.len-8)) printf("---i=%x, tempH=%X, tempL=%X\n", i, data[i+1], data[i]);  //debug see some data
           checksum += (data[i+1] << 8 ) | data[i];
    }

    printf("i=%X, f.len=%X\n",i,f.len);
    
    if (i < f.len)
    {
          checksum += data[i];
          printf("Got odd byte: %X.\n", data[i]);
          i+=1;
    }

    //printf("Generating checksum...\n");
       
    //printf("Checksum1 = %X\n", checksum);
    checksum = checksum + (checksum >> 16);
    //printf("Checksum2 = %X\n", checksum);
    checksum = 0xFFFF + checksum;
    //printf("Checksum3 = %X\n", checksum);
    checksum = ~(checksum + (checksum >> 16));
    //printf("Checksum4 = %X\n", checksum);
    checksum = checksum & 0xFFFF;
    printf("Checksum = %X\n", checksum);
    
    data[i] = checksum & 0xFF;
    data[i+1] = (checksum >> 8) & 0xFF;
    printf("File Checksum: %X, %X\n", data[i+1], data[i]);
    
    printf("Encrypting %X bytes...\n", f.len+2);
    
    //Encrypt
    for (i=0; i<=f.len+2; i++)
    {
        data[i] = data[i] ^ mykey[i % 15];
    }
        
    //Write to the new file
    printf("Writing header\n");
    fwrite(&f, sizeof(f), 1, outfile);
    
    printf("Writing %X bytes...\n", f.len);
    fwrite(data, f.len+2, 1, outfile);
    
    fclose(infile);
    fclose(outfile);
    
    free(data);
    
    return 0;
}

(Last edited by oschemes on 10 Apr 2013, 04:05)

I'll test it on the Poray M3 and X5 later today.

X5 works.

M3 fails. Like you mentioned, checksum seems to be off by one:

Packing:

./pack PM3 openwrt-ramips-rt305x-m3-squashfs-sysupgrade.bin openwrt-ramips-rt305x-m3-squashfs-factory.bin
===============================================================
 PACK - Encrypt and prepare Linux image for router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Device: PM3
Input File: openwrt-ramips-rt305x-m3-squashfs-sysupgrade.bin
Output File: openwrt-ramips-rt305x-m3-squashfs-factory.bin
Packing Poray M3 Image: Reading 300004 bytes from input file
Checksum Calculation...
i=300004, f.len=300004
Checksum = 3067
File Checksum: 30, 67
Encrypting 300006 bytes...
Writing header
Writing 300004 bytes...

serial output:

Upgrading
total = 3145808
................................
............................................................................................................................................................................................................................................................................................................
................................................................................................................................................................................................................................................
......................................................................................................................................................................................................done
imaglength = 3145732
imagflag = 20e0000
codepattern = 5351 5351

integrity_check : type = 20e0000
length = 3145732
iii = 1c
MAX: 67 30
p1 = 67 p2 = 30 i = 300021
Chksum error!  check=0x3066 orig=0x3067

Oh, wow - actually it looks like the PM3 does not need the checksum hack.  Hmm, maybe my notes were wrong, or this firmware you've packed happens to show the unique case.

Anyway, to fix the checksum just comment out the line

checksum=-1; //HACK, I don't know why it needs this

in the "PM3" device selection area at the top.

Also - THANKS!  For testing and posting your results

(Last edited by oschemes on 10 Apr 2013, 22:52)

Obviously it depends on the image size (guessing it has to do with odd/even number of bytes):

Built a new image and tried with the hack removed. This yielded:

Upgrading
total = 3145808
.....................................................................................................................................................e
imaglength = 3145732
imagflag = 20e0000
codepattern = 5351 5351
integrity_check : type = 20e0000
length = 3145732
iii = 1c
MAX: ffffff82 2e
p1 = 82 p2 = 2e i = 300021
Chksum error!  check=0x2e83 orig=0x2e82

With the hack added back it upgrades flawlessly.

Thanks, Heffer.  I see that the image length reported is identical in both of your console outputs.  Did you really have two different builds with identical image size? 

The tool does have to deal with even/ odd lengths but it should report that in the pack output, if it finds an extra byte.

I've tried with some more images. But I think I'll need to test some more. Looks like we're missing some check somewhere.

This upgrade here is a bit weird:

[felix@alpine ramips]$ ~/pack PM3 openwrt-ramips-rt305x-m3-squashfs-sysupgrade.bin openwrt-ramips-rt305x-m3-squashfs-factory-4.bin 
===============================================================
 PACK - Encrypt and prepare Linux image for router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Device: PM3
Input File: openwrt-ramips-rt305x-m3-squashfs-sysupgrade.bin
Output File: openwrt-ramips-rt305x-m3-squashfs-factory-4.bin
Packing Poray M3 Image: Reading 300004 bytes from input file
Checksum Calculation...
i=300004, f.len=300004
Checksum = BA00
File Checksum: BA, 0
Encrypting 300006 bytes...
Writing header
Writing 300004 bytes...
Upgrading
total = 3145808
.....................................................................................................................................................e
imaglength = 3145732
imagflag = 20e0000
codepattern = 5351 5351
integrity_check : type = 20e0000
length = 3145732
iii = 1c
MAX: 0 ffffffba
p1 = 0 p2 = ba i = 300021
Chksum error!  check=0xb9ff orig=0xba00
-rw-rw-r--. 1 felix felix 3145762 11. Apr 16:55 openwrt-ramips-rt305x-m3-squashfs-factory-4.bin

Note how the number of bytes differ here? Is that normal?

Do the number of bytes differ?  Input file seems to be 3,145,732 (0x300004).  A two byte checksum and a 28 byte header are added, giving a total of 3145762.

The weird thing is that the second console line always says 3145808.  I don't think either of your images are that size.  But, are all of your images really 3145732?  That would be surprising, but would give us a clue that it's content based and not solely length based.

That they are the same size is not surprising I would say. In the cases on hand I only altered the DTS file without adding or removing things (i.e. just moving stuff around) This file is injected into the kernel file after everything has been compiled. It is then not compressed but written directly into the image. So the file size doesn't change.

I've uploaded the two files here: http://heffer.fedorapeople.org/openwrt/packtest/
On a binary level they differ substantially, also some bytes after the header are a bit different.

(Last edited by Heffer on 12 Apr 2013, 17:54)

Ok, I tried with all the official upgrade files from Poray:
http://www.poray.com.cn/en/download.asp

All X files checksums are correct (X1, X5/X6 and X8).

Both M files checksums are offset by 1 (M3 and M4).

R50B+ and R50D/R50D+ seems to use yet another key, I haven't bothered to find them, though, I am lazy roll

We may have to look into the httpd of these ones to check that it is not different from the one on the X5 we already looked at... I am hoping for a +1 somewhere in the code smile

(Last edited by Squonk on 12 Apr 2013, 23:24)

Here are the keys:

R50B+:

0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7

R50D(+):

0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7

R50E:

0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7

Look familiar? wink
Seems there's some rule we don't know yet tongue

I've made this little python script to get the key from those Bococom-type (Hame, Poray, et al.) XOR'd images:

#!/usr/bin/env python
# Get key from Bococom type XOR'd image files
# (c) 2013 Felix Kaechele <felix@fetzig.org>
# Licenced under the terms of the GNU GPLv2+
# Usage:
#    getkey.py -i foo.bin

from optparse import OptionParser

# String: "Linux Kernel Image"
knownstring = ['\x4c', '\x69', '\x6e', '\x75', '\x78', '\x20', '\x4b', '\x65', '\x72', '\x6e', '\x65', '\x6c', '\x20', '\x49', '\x6d', '\x61', '\x67', '\x65']

parser = OptionParser()
parser.add_option("-i", "--input", action="store", dest="inputfile", help="path to input file", metavar="FILE")
(options, args) = parser.parse_args()

if options.inputfile == None:
    parser.error("Path to input file needed")

f = open(options.inputfile, 'r')
# The string "Linux Kernel Image" is usually found at offset 60 (0x3C)
pos = f.seek(60, 0)
fstr = f.read(18)
ret = list(knownstring)
for i, v in enumerate(knownstring):
    ret[i] = hex(ord(chr(ord(fstr[i]) ^ ord(knownstring[i]))))
output = ret[13:15] + ret[0:13]
csyntax = "{"
for byte in output:
    csyntax += byte[0:2] + byte[2:4].upper() + ", "
csyntax = csyntax[:-2] + "}"
print "Python list:\t" + str(output)
print "C list:\t\t" + csyntax
Heffer wrote:

I've made this little python script to get the key from those Bococom-type (Hame, Poray, et al.) XOR'd images:

I am not at all a Python programmer, but I modified your script to add magic display

#!/usr/bin/env python
# Get key from Bococom type XOR'd image files
# (c) 2013 Felix Kaechele <felix@fetzig.org>
# (c) 2013 Michel Stempin <michel.Stempin@wanadoo.fr>
# Licenced under the terms of the GNU GPLv2+
# Usage:
#    getkey.py -i foo.bin

from optparse import OptionParser

# String: "Linux Kernel Image"
knownstring = ['\x4c', '\x69', '\x6e', '\x75', '\x78', '\x20', '\x4b', '\x65', '\x72', '\x6e', '\x65', '\x6c', '\x20', '\x49', '\x6d', '\x61', '\x67', '\x65']

parser = OptionParser()
parser.add_option("-i", "--input", action="store", dest="inputfile", help="path to input file", metavar="FILE")
(options, args) = parser.parse_args()

if options.inputfile == None:
    parser.error("Path to input file needed")

f = open(options.inputfile, 'r')
pos = f.seek(0, 0)
fstr = f.read(4)
magic_ascii = fstr[3] + fstr[2] + fstr[1] + fstr[0]
magic = ""
for i, v in enumerate(magic_ascii):
    magic += hex(ord(chr(ord(magic_ascii[i]))))
# The string "Linux Kernel Image" is usually found at offset 60 (0x3C)
pos = f.seek(60, 0)
fstr = f.read(18)
ret = list(knownstring)
for i, v in enumerate(knownstring):
    ret[i] = hex(ord(chr(ord(fstr[i]) ^ ord(knownstring[i]))))
pkey = ret[13:15] + ret[0:13]
ckey = "{"
for byte in pkey:
    ckey += byte[0:2] + byte[2:4].upper() + ", "
ckey = ckey[:-2] + "}"
print "Magic:\t\t0x" + str(magic).replace("0x", "") + " (\"" + magic_ascii + "\")"
print "Python key:\t" + str(pkey)
print "C key:\t\t" + ckey

Here is the output for all Poray stock firmware files:

for i in OEM*.bin; do echo; echo $i |sed 's/OEMTest_\(.*\)_English.*\.bin$/\1/g'; ./getkey.py -i $i; done

M3
Magic:        0x31353335 ("1535")
Python key:    ['0x89', '0x6b', '0x5a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

M4
Magic:        0x32353335 ("2535")
Python key:    ['0x89', '0x6b', '0x5a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

R50B
Magic:        0x31353033 ("1503")
Python key:    ['0xc9', '0x1c', '0x3a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

R50D
Magic:        0x33353033 ("3503")
Python key:    ['0x19', '0x1b', '0x3a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

R50E
Magic:        0x34353033 ("4503")
Python key:    ['0x79', '0x7b', '0x7a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

X1
Magic:        0x38353335 ("8535")
Python key:    ['0x89', '0x6b', '0x5a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

X5
Magic:        0x35353335 ("5535")
Python key:    ['0x89', '0x6b', '0x5a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

X8
Magic:        0x36353335 ("6535")
Python key:    ['0x89', '0x6b', '0x5a', '0x93', '0x92', '0x95', '0xc3', '0x63', '0xd0', '0xa3', '0x9c', '0x92', '0x2e', '0xe6', '0xc7']
C key:        {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}

I also added the unknown R50B/R50D to the list of know models and adjusted the checksum in the unpack tool:

/************************************************************************************
 * Router firmware upgrade file de-obfuscation and check
 * Copyright (c) 2013 openschemes.com and Michel Stempin <michel.stempin@wanadoo.fr>
 ***********************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

typedef struct 
{
   uint32_t magic;
   uint32_t len;
   uint32_t imgflags;
   uint32_t junk[4];
} fheader;

int main(int argc, char *argv[])
{
    char *infname;
    char *outfname;
    FILE* infile;
    FILE* outfile;
    
    uint32_t checksum, mychecksum;
    uint32_t i;
    int adjust = 0;
    fheader f;
    
    uint8_t MPRkey[] = {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08};
    uint8_t PORkey[] = {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO2key[] = {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO3key[] = {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO4key[] = {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t mykey[15];
    uint8_t *data;  //Pointer to our big data array

    printf("===============================================================\n");    
    printf(" UNPACK - Extract and decrypt Linux image from router firmware\n");
    printf(" (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>\n");
    printf(" (C) 2013 www.openschemes.com\n");
    printf("===============================================================\n\n");
    //Check arguments
    if (argc > 2) 
    {  
             infname = argv[1];
             printf("Input File: %s\n", infname);
             outfname = argv[2];
             printf("Output File: %s\n", outfname);
    }
    else
    {
             printf("Wrong number of arguments.\n");
             printf("Usage: %s inputfile outputfile\n", argv[0]); 
             exit(0);
    }
    

    //Get files
    
    //Open input and output files
    infile = fopen(infname, "rb");
    if (infile==NULL)
    {
        printf("Couldn't open input file\n");
        exit(1);
    }
   
    outfile = fopen(outfname, "r");

    if (outfile == NULL)
        outfile = fopen(outfname, "wb");
    else
    {
        printf("Output file already exists!  \n-Quitting to avoid accidentally overwriting \n-your only copy of a firmware file.. :) \n");
        fclose(outfile);
        fclose(infile);
        return(1);
    }

    if (outfile==NULL)
    {
        printf("Couldn't open output file\n");
        fclose(infile);
        exit(1);    
    }
    
    //Read and check header
    fread(&f, sizeof(f),1,infile);
    printf("Found Magic: %X..",f.magic);
    
    //Magic looks like PRODID, MFGID
    //TODO: Strip PRODID and just use MFGID
    switch(f.magic)
    {
          case 0x31353335: // Poray M3
          case 0x32353335: // Poray M4
           adjust = 1;
          case 0x33353335:
          case 0x35353335: // Poray X5
          case 0x36353335: // Poray X8
          case 0x38353335: // Poray X1
               printf(" Poray Magic, using Poray key\n");
               memcpy(mykey, PORkey, sizeof(PORkey));
               break;
          case 0x31353033: // Poray R50B
                printf(" Poray Magic2, using Poray key 2\n");
               memcpy(mykey, PO2key, sizeof(PO2key));
           adjust = 1;
               break;              
          case 0x33353033: // Poray R50D
                printf(" Poray Magic3, using Poray key 3\n");
               memcpy(mykey, PO3key, sizeof(PO3key));
           adjust = 1;
               break;              
          case 0x34353033: // Poray R50E
                printf(" Poray Magic4, using Poray key 4\n");
               memcpy(mykey, PO4key, sizeof(PO4key));
               break;              
          case 0x32473352: // Hame MPR-A1
               printf(" MPR Magic, using MPR key\n");
               memcpy(mykey, MPRkey, sizeof(MPRkey));
               break;
          default:
               printf(" Unrecognized magic, bailing out\n");
               exit(1);
                            
    }
    
    printf("Header reports %X encrypted bytes\n", f.len);
    
    //Allocate memory for holding the data
    data = (uint8_t*)malloc(f.len+2);
    if(data==0)
    {
      printf("Couldn't allocate memory, bailing out\n");
      exit(1);
     }
    
    //printf("Allocated %X bytes for decryption\n",sizeof(data));
    
    printf("Reading %X bytes...\n", f.len+2);
    
    //Get the data
    fread(data, f.len+2, 1, infile);
    
    printf("Decrypting %X bytes...\n", f.len+2);
    
    //Decrypt
    for (i=0; i<=f.len+2; i++)
    {
        data[i] = data[i] ^ mykey[i % 15];
    }
    
    if (data[0]==0x27 && data[1]==0x05 && data[2]==0x19 && data[3]==0x56)
      printf("Seems Legit!  Found start of uImage header.\n");
    else
      printf("Error - Decrypted data doesn't start with uImage header.  Output file is probably bad..\n");

    //Write to the new file
    
    printf("Writing %X bytes...\n", f.len);
    
    fwrite(data, f.len, 1, outfile);
    
    fclose(infile);
    fclose(outfile);
    
    //Do Checksum

    printf("Checksum Calculation...\n");
     
    mychecksum = data[i-2] << 8 | data[i-3];
    printf("File Checksum: %X\n",mychecksum);
    
    checksum=0;
    
    for (i=0; i<f.len-1; i+=2)
    {
           //if ((i<8)||(i>f.len-8)) printf("---i=%x, tempH=%X, tempL=%X\n", i, data[i+1], data[i]);  //debug see some data
           checksum += (data[i+1] << 8 ) | data[i];
    }

    printf("i=%X, f.len=%X\n",i,f.len);
    
    if (i < f.len)
    {
          checksum += data[i];
          printf("Got odd byte: %X.\n", data[i+1]);
   }

    //printf("Generating checksum...\n");
       
    //printf("Checksum1 = %X\n", checksum);
    checksum = checksum + (checksum >> 16);
    //printf("Checksum2 = %X\n", checksum);
    checksum = 0xFFFF + checksum;
    //printf("Checksum3 = %X\n", checksum);
    checksum = ~(checksum + (checksum >> 16));
    //printf("Checksum4 = %X\n", checksum);
    checksum = (checksum + adjust) & 0xFFFF;
    printf("Checksum = %X\n", checksum);

    if (checksum == mychecksum) 
       printf("Checksum MATCH!\n");
    else
        printf("Checksum FAIL!\n");

    free(data);
    
    return 0;
}

Here is the result for all Poray stock firmware files:

for i in OEM*.bin; do n=$(echo $i |sed 's/OEMTest_\(.*\)_English.*$/\1/g'); ./poray $i $n.bin; done
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_M3_English_V5.13_20120723151141_upgrade.bin
Output File: M3.bin
Found Magic: 31353335.. Poray Magic, using Poray key
Header reports 323DE8 encrypted bytes
Reading 323DEA bytes...
Decrypting 323DEA bytes...
Seems Legit!  Found start of uImage header.
Writing 323DE8 bytes...
Checksum Calculation...
File Checksum: 4525
i=323DE8, f.len=323DE8
Checksum = 4525
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_M4_English_V6.05_20130106173631_upgrade.bin
Output File: M4.bin
Found Magic: 32353335.. Poray Magic, using Poray key
Header reports 3215BC encrypted bytes
Reading 3215BE bytes...
Decrypting 3215BE bytes...
Seems Legit!  Found start of uImage header.
Writing 3215BC bytes...
Checksum Calculation...
File Checksum: 6F77
i=3215BC, f.len=3215BC
Checksum = 6F77
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50B_English_V2.19_20101226034656_upgrade.bin
Output File: R50B.bin
Found Magic: 31353033.. Poray Magic2, using Poray key 2
Header reports 33127C encrypted bytes
Reading 33127E bytes...
Decrypting 33127E bytes...
Seems Legit!  Found start of uImage header.
Writing 33127C bytes...
Checksum Calculation...
File Checksum: E6C2
i=33127C, f.len=33127C
Checksum = E6C2
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50D_English_V1.25_20101223002147_upgrade.bin
Output File: R50D.bin
Found Magic: 33353033.. Poray Magic3, using Poray key 3
Header reports 33175E encrypted bytes
Reading 331760 bytes...
Decrypting 331760 bytes...
Seems Legit!  Found start of uImage header.
Writing 33175E bytes...
Checksum Calculation...
File Checksum: 6BF9
i=33175E, f.len=33175E
Checksum = 6BF9
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50E_English_V3.08_20110530154828_upgrade.bin
Output File: R50E.bin
Found Magic: 34353033.. Poray Magic4, using Poray key 4
Header reports 30F5EC encrypted bytes
Reading 30F5EE bytes...
Decrypting 30F5EE bytes...
Seems Legit!  Found start of uImage header.
Writing 30F5EC bytes...
Checksum Calculation...
File Checksum: B02
i=30F5EC, f.len=30F5EC
Checksum = B02
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X1_English_V12.04_20130223135300_upgrade.bin
Output File: X1.bin
Found Magic: 38353335.. Poray Magic, using Poray key
Header reports 41A2A0 encrypted bytes
Reading 41A2A2 bytes...
Decrypting 41A2A2 bytes...
Seems Legit!  Found start of uImage header.
Writing 41A2A0 bytes...
Checksum Calculation...
File Checksum: AECE
i=41A2A0, f.len=41A2A0
Checksum = AECE
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X5_English_V9.10_20130106194401_upgrade.bin
Output File: X5.bin
Found Magic: 35353335.. Poray Magic, using Poray key
Header reports 41A6CC encrypted bytes
Reading 41A6CE bytes...
Decrypting 41A6CE bytes...
Seems Legit!  Found start of uImage header.
Writing 41A6CC bytes...
Checksum Calculation...
File Checksum: 57FC
i=41A6CC, f.len=41A6CC
Checksum = 57FC
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X8_English_V10.07_20121123192920_upgrade.bin
Output File: X8.bin
Found Magic: 36353335.. Poray Magic, using Poray key
Header reports 419715 encrypted bytes
Reading 419717 bytes...
Decrypting 419717 bytes...
Seems Legit!  Found start of uImage header.
Writing 419715 bytes...
Checksum Calculation...
File Checksum: F732
i=419714, f.len=419715
Got odd byte: 32.
Checksum = F732
Checksum MATCH!

(Last edited by Squonk on 13 Apr 2013, 09:48)

Heffer wrote:

I've optimized the tool further. Put it on my GitHub here: https://github.com/kaechele/firmware-tools
Thanks for your feedback, Squonk.

Much nicer than my mod wink

I definitely need to learn Python when I have time!

Got it!

I found the bug which caused the checksum to be off by one on some firmware file: basically, the checksum values need to be stored into a signed 32-bit integer, not into an unsigned 32-bit integer wink

Here is the code of the unpack tool with the fix and a more compact checksum computation and a few other cosmetic changes:

/************************************************************************************
 * Router firmware upgrade file de-obfuscation and check
 * Copyright (c) 2013 openschemes.com and Michel Stempin <michel.stempin@wanadoo.fr>
 ***********************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

typedef struct 
{
   uint32_t magic;
   uint32_t len;
   uint32_t imgflags;
   uint32_t junk[4];
} fheader;

int main(int argc, char *argv[])
{
    char *infname;
    char *outfname;
    FILE* infile;
    FILE* outfile;
    
    int32_t checksum, mychecksum;
    uint32_t i;
    fheader f;
    
    uint8_t MPRkey[] = {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08};
    uint8_t PORkey[] = {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO2key[] = {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO3key[] = {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PO4key[] = {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t mykey[15];
    uint8_t *data;  //Pointer to our big data array

    printf("===============================================================\n");    
    printf(" UNPACK - Extract and decrypt Linux image from router firmware\n");
    printf(" (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>\n");
    printf(" (C) 2013 www.openschemes.com\n");
    printf("===============================================================\n\n");
    //Check arguments
    if (argc > 2) 
    {  
             infname = argv[1];
             printf("Input File: %s\n", infname);
             outfname = argv[2];
             printf("Output File: %s\n", outfname);
    }
    else
    {
             printf("Wrong number of arguments.\n");
             printf("Usage: %s inputfile outputfile\n", argv[0]); 
             exit(0);
    }
    

    //Get files
    
    //Open input and output files
    infile = fopen(infname, "rb");
    if (infile==NULL)
    {
        printf("Couldn't open input file\n");
        exit(1);
    }
   
    outfile = fopen(outfname, "r");

    if (outfile == NULL)
        outfile = fopen(outfname, "wb");
    else
    {
        printf("Output file already exists!  \n-Quitting to avoid accidentally overwriting \n-your only copy of a firmware file.. :) \n");
        fclose(outfile);
        fclose(infile);
        return(1);
    }

    if (outfile==NULL)
    {
        printf("Couldn't open output file\n");
        fclose(infile);
        exit(1);    
    }
    
    //Read and check header
    fread(&f, sizeof(f),1,infile);
    printf("Found Magic: 0x%08X (\"%4.4s\")...",f.magic,(char *) &f.magic);
    
    //Magic looks like PRODID, MFGID
    //TODO: Strip PRODID and just use MFGID
    switch(f.magic)
    {
          case 0x31353335: // Poray M3
          case 0x32353335: // Poray M4
          case 0x33353335:
          case 0x35353335: // Poray X5
          case 0x36353335: // Poray X8
          case 0x38353335: // Poray X1
               printf(" Poray Magic, using Poray key\n");
               memcpy(mykey, PORkey, sizeof(PORkey));
               break;
          case 0x31353033: // Poray R50B
                printf(" Poray Magic2, using Poray key 2\n");
               memcpy(mykey, PO2key, sizeof(PO2key));
               break;              
          case 0x33353033: // Poray R50D
                printf(" Poray Magic3, using Poray key 3\n");
               memcpy(mykey, PO3key, sizeof(PO3key));
               break;              
          case 0x34353033: // Poray R50E
                printf(" Poray Magic4, using Poray key 4\n");
               memcpy(mykey, PO4key, sizeof(PO4key));
               break;              
          case 0x32473352: // Hame MPR-A1
               printf(" MPR Magic, using MPR key\n");
               memcpy(mykey, MPRkey, sizeof(MPRkey));
               break;
          default:
               printf(" Unrecognized magic, bailing out\n");
               exit(1);
                            
    }
    
    printf("Header reports 0x%08X encrypted bytes\n", f.len);
    
    //Allocate memory for holding the data
    data = (uint8_t*)malloc(f.len+2);
    if(data==0)
    {
      printf("Couldn't allocate memory, bailing out\n");
      exit(1);
     }
    
    //printf("Allocated %X bytes for decryption\n",sizeof(data));
    
    printf("Reading 0x%08X bytes...\n", f.len+2);
    
    //Get the data
    fread(data, f.len+2, 1, infile);
    
    printf("Decrypting 0x%08X bytes...\n", f.len+2);
    
    //Decrypt
    for (i=0; i<=f.len+2; i++)
    {
        data[i] = data[i] ^ mykey[i % 15];
    }
    
    if (data[0]==0x27 && data[1]==0x05 && data[2]==0x19 && data[3]==0x56)
      printf("Seems Legit!  Found start of uImage header.\n");
    else
      printf("Error - Decrypted data doesn't start with uImage header.  Output file is probably bad..\n");

    //Write to the new file
    
    printf("Writing 0x%08X bytes...\n", f.len);
    
    fwrite(data, f.len, 1, outfile);
    
    fclose(infile);
    fclose(outfile);
    
    //Do Checksum

    printf("Checksum Calculation...\n");
     
    mychecksum = data[i-2] << 8 | data[i-3];
    printf("File Checksum: 0x%04X\n",mychecksum);
    
    checksum=0;
    
    for (i=0; i<f.len-1; i+=2)
    {
           //if ((i<8)||(i>f.len-8)) printf("---i=%x, tempH=%X, tempL=%X\n", i, data[i+1], data[i]);  //debug see some data
           checksum += (data[i+1] << 8 ) | data[i];
    }

    printf("i=0x%08X, f.len=0x%08X\n",i,f.len);
    
    if (i < f.len)
    {
          checksum += data[i];
          printf("Got odd byte: 0x%02X\n", data[i+1]);
   }

    checksum = checksum + (checksum >> 16) + 0xffff;
    checksum = ~(checksum + (checksum >> 16)) & 0xffff;
    printf("Checksum = 0x%04X\n", checksum);

    if (checksum == mychecksum) 
       printf("Checksum MATCH!\n");
    else
        printf("Checksum FAIL!\n");

    free(data);
    
    return 0;
}

Here is the result for all Poray stock firmware files:

for i in OEM*.bin; do n=$(echo $i |sed 's/OEMTest_\(.*\)_English.*$/\1/g'); ./poray $i $n.bin; done
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_M3_English_V5.13_20120723151141_upgrade.bin
Output File: M3.bin
Found Magic: 0x31353335 ("5351")... Poray Magic, using Poray key
Header reports 0x00323DE8 encrypted bytes
Reading 0x00323DEA bytes...
Decrypting 0x00323DEA bytes...
Seems Legit!  Found start of uImage header.
Writing 0x00323DE8 bytes...
Checksum Calculation...
File Checksum: 0x4525
i=0x00323DE8, f.len=0x00323DE8
Checksum = 0x4525
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_M4_English_V6.05_20130106173631_upgrade.bin
Output File: M4.bin
Found Magic: 0x32353335 ("5352")... Poray Magic, using Poray key
Header reports 0x003215BC encrypted bytes
Reading 0x003215BE bytes...
Decrypting 0x003215BE bytes...
Seems Legit!  Found start of uImage header.
Writing 0x003215BC bytes...
Checksum Calculation...
File Checksum: 0x6F77
i=0x003215BC, f.len=0x003215BC
Checksum = 0x6F77
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50B_English_V2.19_20101226034656_upgrade.bin
Output File: R50B.bin
Found Magic: 0x31353033 ("3051")... Poray Magic2, using Poray key 2
Header reports 0x0033127C encrypted bytes
Reading 0x0033127E bytes...
Decrypting 0x0033127E bytes...
Seems Legit!  Found start of uImage header.
Writing 0x0033127C bytes...
Checksum Calculation...
File Checksum: 0xE6C2
i=0x0033127C, f.len=0x0033127C
Checksum = 0xE6C2
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50D_English_V1.25_20101223002147_upgrade.bin
Output File: R50D.bin
Found Magic: 0x33353033 ("3053")... Poray Magic3, using Poray key 3
Header reports 0x0033175E encrypted bytes
Reading 0x00331760 bytes...
Decrypting 0x00331760 bytes...
Seems Legit!  Found start of uImage header.
Writing 0x0033175E bytes...
Checksum Calculation...
File Checksum: 0x6BF9
i=0x0033175E, f.len=0x0033175E
Checksum = 0x6BF9
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_R50E_English_V3.08_20110530154828_upgrade.bin
Output File: R50E.bin
Found Magic: 0x34353033 ("3054")... Poray Magic4, using Poray key 4
Header reports 0x0030F5EC encrypted bytes
Reading 0x0030F5EE bytes...
Decrypting 0x0030F5EE bytes...
Seems Legit!  Found start of uImage header.
Writing 0x0030F5EC bytes...
Checksum Calculation...
File Checksum: 0x0B02
i=0x0030F5EC, f.len=0x0030F5EC
Checksum = 0x0B02
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X1_English_V12.04_20130223135300_upgrade.bin
Output File: X1.bin
Found Magic: 0x38353335 ("5358")... Poray Magic, using Poray key
Header reports 0x0041A2A0 encrypted bytes
Reading 0x0041A2A2 bytes...
Decrypting 0x0041A2A2 bytes...
Seems Legit!  Found start of uImage header.
Writing 0x0041A2A0 bytes...
Checksum Calculation...
File Checksum: 0xAECE
i=0x0041A2A0, f.len=0x0041A2A0
Checksum = 0xAECE
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X5_English_V9.10_20130106194401_upgrade.bin
Output File: X5.bin
Found Magic: 0x35353335 ("5355")... Poray Magic, using Poray key
Header reports 0x0041A6CC encrypted bytes
Reading 0x0041A6CE bytes...
Decrypting 0x0041A6CE bytes...
Seems Legit!  Found start of uImage header.
Writing 0x0041A6CC bytes...
Checksum Calculation...
File Checksum: 0x57FC
i=0x0041A6CC, f.len=0x0041A6CC
Checksum = 0x57FC
Checksum MATCH!
===============================================================
 UNPACK - Extract and decrypt Linux image from router firmware
 (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>
 (C) 2013 www.openschemes.com
===============================================================

Input File: OEMTest_X8_English_V10.07_20121123192920_upgrade.bin
Output File: X8.bin
Found Magic: 0x36353335 ("5356")... Poray Magic, using Poray key
Header reports 0x00419715 encrypted bytes
Reading 0x00419717 bytes...
Decrypting 0x00419717 bytes...
Seems Legit!  Found start of uImage header.
Writing 0x00419715 bytes...
Checksum Calculation...
File Checksum: 0xF732
i=0x00419714, f.len=0x00419715
Got odd byte: 0x32.
Checksum = 0xF732
Checksum MATCH!

I have not applied the fix to the packing tool, should be easy, though.

Great news!   I just tried your fix on the packtool, you are absolutely right.  Congratulations and thank you for knocking out this persistent bug.  How did you find it?

oschemes wrote:

Great news!   I just tried your fix on the packtool, you are absolutely right.  Congratulations and thank you for knocking out this persistent bug.  How did you find it?

I verified that the original decoding routine was the same for the models with an offset checksum, then tried to reproduce the same code in assembly from C, comparing the assembly outputs.

I found then that the original code was using arithmetic shifts vs. logical ones: this is a good indication that the values are signed, tried it, bingo!

Bingo, indeed!  Great stuff, it looks like we've got ourselves a set of firmware tools.  Very nice work!

A big thanks to @Heffer as well for the very slick key extraction tool!  I think it will be a big help to the Bococom-variety users to have the factory bins available, as well as the option to splice in a bit more functionality on the vendor tools.

Here is the new version of the packtool, updated with Heffer's keys and Squonk's enhancements.  I have another key to add but I must obtain it from the disassembly of extracted firmware.  That may take a couple days with my limited time, so best to just post the main update now.

I have verified system upgrade of the HAME clone, and binary round trip for most of the others but typos may exist.  Please note any errors you may find, or successes you have.  Thanks!

/************************************************************************************
 * PACK.c
 * Router firmware packing tool
 * Copyright (c) 2013 openschemes.com, Michel Stempin <michel.stempin@wanadoo.fr>,
 * and Felix Kaechele <felix@fetzig.org>
 ***********************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

typedef struct 
{
   uint32_t magic;
   uint32_t len;
   uint32_t imgflags;
   uint32_t junk[4];
} fheader;

int main(int argc, char *argv[])
{
    char *infname;
    char *outfname;
    char *device;
    FILE* infile;
    FILE* outfile;
    
    int32_t checksum, mychecksum;
    uint32_t i;
    fheader f;
    
    uint8_t MPRkey[] = {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08};
    uint8_t PORkey[] = {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PR50Bkey[] = {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PR50Dkey[] = {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t PR50Ekey[] = {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7};
    uint8_t mykey[15];
    uint8_t *data;  //Pointer to our big data array

    printf("===============================================================\n");    
    printf(" PACK - Encrypt and prepare Linux image for router firmware\n");
    printf(" (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr>\n");
    printf(" (C) 2013 www.openschemes.com\n");
    printf(" (C) 2013 Felix Kaechele <felix@fetzig.org> \n");
    printf("===============================================================\n\n");
    //Check arguments
    if (argc > 3) 
    {  
             device = argv[1];
             printf("Device: %s\n", device);
             infname = argv[2];
             printf("Input File: %s\n", infname);
             outfname = argv[3];
             printf("Output File: %s\n", outfname);
    }
    else
    {
             printf("Wrong number of arguments.\n");
             printf("Usage: %s device inputfile outputfile\n\n", argv[0]); 
             printf("Choices for <device> Case sensitive!\n"); 
             printf("PM3 - Poray M3\n");
             printf("PM4 - Poray M4\n");
             printf("PQ3 - Poray Q3\n");
             printf("PR50B - Poray R50B\n");
             printf("PR50D - Poray R50D\n");
             printf("PR50E - Poray R50\n");
             printf("PX1 - Poray X1\n");
             printf("PX6 - Poray X5-X6\n");
             printf("PX8 - Poray X8\n");
             printf("MPR - MPR-L8 (HAME clone)");
             exit(0);
    }
   
   checksum=0;
   
   //Check device and choose encryption key
   //M4 encrypt/decrypt not verified 0x32353335, so not presently included

          if (!strcmp(device, "PM3")){
               printf("Packing Poray M3 Image: ");
               f.magic=0x31353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PM4")){
               printf("Packing Poray M4 Image: ");
               f.magic= 0x32353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PQ3")){
               printf("Packing Poray Q3 Image: ");
               f.magic= 0x33353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PR50B")){
               f.magic=0x31353033;
               memcpy(mykey, PR50Bkey, sizeof(PR50Bkey));             
          } else if (!strcmp(device, "PR50D")){
               f.magic=0x33353033;
               memcpy(mykey, PR50Dkey, sizeof(PR50Dkey));             
          } else if (!strcmp(device, "PR50E")){
               f.magic=0x34353033;
               memcpy(mykey, PR50Ekey, sizeof(PR50Ekey));             
          } else if (!strcmp(device, "PX1")){
               printf("Packing Poray X1 Image: ");
               f.magic=0x38353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PX6")){
               printf("Packing Poray X5/X6 Image: ");
               f.magic=0x35353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "PX8")){
               printf("Packing Poray X8 Image: ");
               f.magic=0x36353335;
               memcpy(mykey, PORkey, sizeof(PORkey));
          } else if (!strcmp(device, "MPR")){
               printf("Packing MPR-L8 Image: ");
               f.magic=0x32473352;
               memcpy(mykey, MPRkey, sizeof(MPRkey));
          } else {
               printf("I don't recognize the %s device, bailing out\n", device);
               exit(1);
          }                  

    
    //Fill rest of header
    f.imgflags = 0x020e0000;
    f.junk[0]=f.junk[1]=f.junk[2]=f.junk[3]=0;
    
    //Get files
    
    //Open input and output files
    infile = fopen(infname, "rb");
    if (infile==NULL)
    {
        printf("Couldn't open input file\n");
        exit(1);
    }
   
    outfile = fopen(outfname, "r");

    if (outfile == NULL)
        outfile = fopen(outfname, "wb");
    else
    {
        printf("Output file already exists!  \n-Quitting to avoid accidentally overwriting \n-your only copy of a firmware file.. :) \n");
        fclose(outfile);
        fclose(infile);
        return(1);
    }

    if (outfile==NULL)
    {
        printf("Couldn't open output file\n");
        fclose(infile);
        exit(1);    
    }
    
    //Read file to get data len
    fseek(infile, 0, SEEK_END);
    f.len=ftell(infile);
    fseek(infile, 0, SEEK_SET);
    
    data = (uint8_t*)malloc(f.len+2);
    if(data==0)
    {
      printf("Couldn't allocate memory, bailing out\n");
      exit(1);
     }
    
    printf("Reading 0x%08X bytes from input file\n", f.len);
    
    //Get the data
    fread(data, f.len, 1, infile);
    
    //Look for right data
    if (data[0]!=0x27 || data[1]!=0x05 || data[2]!=0x19 || data[3]!=0x56)
    {
         printf("Error - Input data doesn't look like a uImage (bad header).  I can't continue for risk of brick..\n");
         exit(1);
    }
    
    //DO CHECKSUM
    printf("Checksum Calculation...\n");
     
    for (i=0; i<f.len-1; i+=2)
    {
           //if ((i<8)||(i>f.len-8)) printf("---i=%x, tempH=%X, tempL=%X\n", i, data[i+1], data[i]);  //debug see some data
           checksum += (data[i+1] << 8 ) | data[i];
    }

    printf("i=0x%08X, f.len=0x%08X\n",i,f.len);
    
    if (i < f.len)
    {
          checksum += data[i];
          printf("Got odd byte: 0x%02X\n", data[i]);
          i+=1;
    }


    checksum = checksum + (checksum >> 16) + 0xffff;
    checksum = ~(checksum + (checksum >> 16)) & 0xffff;
    printf("Checksum = 0x%04X\n", checksum);
    
    data[i] = checksum & 0xFF;
    data[i+1] = (checksum >> 8) & 0xFF;
    printf("File Checksum: 0x %02X, 0x %02X\n", data[i+1], data[i]);
    
    printf("Encrypting 0x%08X bytes...\n", f.len+2);
    
    //Encrypt
    for (i=0; i<=f.len+2; i++)
    {
        data[i] = data[i] ^ mykey[i % 15];
    }
        
    //Write to the new file
    printf("Writing header\n");
    fwrite(&f, sizeof(f), 1, outfile);
    
    printf("Writing 0x%08X bytes...\n", f.len);
    fwrite(data, f.len+2, 1, outfile);
    
    fclose(infile);
    fclose(outfile);
    
    free(data);
    
    return 0;
}

Next task is to convert the packtool into something that can be called by the build script.  Are there any examples of this that I could work from to generate factory.bin?  I really haven't even looked yet, so please pardon my ignorance if it's blatantly obvious.

Not blatantly but fairly obvious wink

See tools/firmware-utils/src

I guess @Squonk is working on this already.

smile Yes I suppose so. 
I also heard from Squonk, it looks like the tool will be committed soon.  Factory bins for all! 

Ok, what's next?

i compliced a openWRT firmware for Poray X6,encrypted.use factory update , it tell me ,update failed.

I use firmware for hame mpr ,encrypted.use factory update , it tell me ,update sucessed. but WIFI , USB do not work.

I do not let my device this.

then use openWRT to update the firmware which from factory and decrypted .

now the device didn't work.just with LED ligth.

but i insert netwire to Ethernet , LED flashing.

I think  it's waiting TFTP ......

someone know how fix it ?

I cann't find my USB to RS232TTL cable,maybe must buy one?

The discussion might have continued from here.