/tools/mr500.c
C | 457 lines | 205 code | 69 blank | 183 comment | 46 complexity | 36c88100701077cf4d29b393e4ab5bbe MD5 | raw file
- /***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (C) 2009 by Karl Kurbjun
- * based on work by Shirour:
- * http://www.mrobe.org/forum/viewtopic.php?f=6&t=2176
- * $Id$
- *
- * All files in this archive are subject to the GNU General Public License.
- * See the file COPYING in the source tree root for full license agreement.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <unistd.h>
- #include <inttypes.h>
- #include <sys/stat.h>
- #include "mr500.h"
- /* Notes about firmware:
- * These notes are based on the work and observations of Shirour on the M:Robe
- * forums.
- *
- * The firmware for the M:Robe has basic encryption on it. The data is XORed
- * and scrambled. The mr500_save_data function provides an implemenation of the
- * encryption/decryption.
- *
- * When a firmware update is done the "{#4F494D4346575550#}" folder is stored
- * in the system folder on the player. The "{#4F494D4346575550#}" should only
- * contain the encrypted N5002-BD.BIN file. At the end of a firmware update
- * the "{#4F494D4346575550#}" folder and it's contents are removed from the
- * player.
- *
- * An interesting note is that the name "{#4F494D4346575550#}" is actually the
- * Hex representation of the magic text found at the beginning of the firmware
- * image "OIMCFWUP".
- */
- /* These two arrays are used for descrambling or scrambling the data */
- int decrypt_array[16]={2, 0, 3, 1, 5, 7, 4, 6, 11, 10, 9, 8, 14, 12, 13, 15};
- int encrypt_array[16];
- /* mr500_patch_file: This function modifies the specified file with the patches
- * struct.
- *
- * Parameters:
- * filename: text filename
- * patches: pointer to structure array of patches
- * num_patches: number of patches to apply (applied in reverse order)
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_patch_file(char *filename, struct patch_single *patches,
- int num_patches) {
- int fdo;
- int ret=0;
- uint32_t endian_int;
-
- /* Open the file write only. */
- fdo = open(filename, O_WRONLY);
-
- if(fdo<0) {
- ret=-1;
- }
-
- while(num_patches--) {
- /* seek to patch offset */
- if(lseek(fdo, patches[num_patches].offset, SEEK_SET)
- != patches[num_patches].offset) {
- ret=-1;
- break;
- }
-
- /* Make sure patch is written in little endian format */
- endian_int = htole32(patches[num_patches].value);
-
- /* Write the patch value to the file */
- if(write(fdo, (void *) &endian_int, sizeof(endian_int)) < 0) {
- ret = -1;
- break;
- }
- }
-
- /* Close the file and check for errors */
- if(close (fdo) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_save_header: This function saves the Olympus header to the firmware
- * image. The values stored in the header are explained above. Note that this
- * will truncate a file. The header is stored in little endian format.
- *
- * Parameters:
- * filename: text filename
- * header: pointer to header structure to be saved
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_save_header(char *filename, struct olympus_header *header) {
- int fdo;
- int ret=0;
-
- /* Temporary header used for storing the header in little endian. */
- struct olympus_header save;
-
- /* Open the file write only and truncate it. If it doesn't exist create. */
- fdo = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
-
- if(fdo<0) {
- ret=-1;
- }
-
- /* Header is stored at offset 0 (Not really needed) */
- if(lseek(fdo, 0, SEEK_SET) != 0) {
- ret=-1;
- }
-
- /* Convert header to Little Endian */
- memcpy(&save.magic_name, &header->magic_name, 8*sizeof(int8_t));
- save.unknown = htole32(header->unknown);
- save.header_length = htole16(header->header_length);
- save.flags = htole16(header->flags);
- save.unknown_zeros = htole32(header->unknown_zeros);
- save.image_length = htole32(header->image_length);
- /* Write the header to the file */
- if(write(fdo, (void *) &save, sizeof(save)) < 0) {
- ret = -1;
- }
-
- /* Close the file and check for errors */
- if(close (fdo) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_read_header: This function reads the Olympus header and converts it to
- * the host endian format. The values stored in the header are explained above.
- * The header is stored in little endian format.
- *
- * Parameters:
- * filename: text filename
- * header: pointer to header structure to store header read from file
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_read_header(char *filename, struct olympus_header *header) {
- int fdi;
- int ret = 0;
-
- /* Open the file read only */
- fdi = open(filename, O_RDONLY);
- if(fdi<0) {
- ret=-1;
- }
-
- /* Header is stored at offset 0 (Not really needed) */
- if(lseek(fdi, 0, SEEK_SET) != 0) {
- ret=-1;
- }
-
- /* Read in the header */
- if(read(fdi, (void *) header, sizeof(*header)) < 0) {
- ret = -1;
- }
-
- /* Convert header to system endian */
- header->unknown = le32toh(header->unknown);
- header->header_length = le16toh(header->header_length);
- header->flags = le16toh(header->flags);
- header->unknown_zeros = le32toh(header->unknown_zeros);
- header->image_length = le32toh(header->image_length);
-
- /* Close the file and check for errors */
- if(close (fdi) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_save_crc: This function saves the 'CRC' of the Olympus firmware image.
- * Note that the 'CRC' must be calculated on the decrytped image. It is stored
- * in little endian.
- *
- * Parameters:
- * filename: text filename
- * offset: Offset to store the 'CRC' (header size + data size)
- * crc_value: pointer to crc value to save
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_save_crc(char *filename, off_t offset, uint32_t *crc_value) {
- int fdo;
- int ret = 0;
- uint32_t save_crc;
-
- /* Open the file write only */
- fdo = open(filename, O_WRONLY);
-
- if(fdo<0) {
- ret=-1;
- }
-
- /* Seek to offset and check for errors */
- if(lseek(fdo, offset, SEEK_SET) != offset) {
- ret=-1;
- }
-
- /* Convert 'CRC' to little endian from system native endian */
- save_crc = htole32(*crc_value);
-
- /* Write the 'CRC' and check for errors */
- if(write(fdo, (void *) &save_crc, sizeof(unsigned int)) < 0) {
- ret = -1;
- }
-
- /* Close the file and check for errors */
- if(close (fdo) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_read_crc: This function reads the 'CRC' of the Olympus firmware image.
- * Note that the 'CRC' is calculated on the decrytped values. It is stored
- * in little endian.
- *
- * Parameters:
- * filename: text filename
- * offset: Offset to read the 'CRC' (header size + data size)
- * crc_value: pointer to crc value to save
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_read_crc(char *filename, off_t offset, uint32_t *crc_value) {
- int fdi;
- int ret = 0;
-
- /* Open the file read only */
- fdi = open(filename, O_RDONLY);
-
- if(fdi<0) {
- ret = -1;
- }
-
- /* Seek to offset and check for errors */
- if(lseek(fdi, offset, SEEK_SET) != offset) {
- ret=-1;
- }
-
- /* Read in the 'CRC' */
- if(read(fdi, (void *) crc_value, sizeof(uint32_t)) < 0) {
- ret = -1;
- }
-
- /* Convert the 'CRC' from little endian to system native format */
- *crc_value = le32toh(*crc_value);
-
- /* Close the file and check for errors */
- if(close (fdi) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_calculate_crc: This function calculates the 'CRC' of the Olympus
- * firmware image. Note that the 'CRC' must be calculated on decrytped values.
- * It is stored in little endian.
- *
- * Parameters:
- * filename: text filename
- * offset: Offset to the start of the data (header size)
- * length: Length of data to calculate
- * crc_value: pointer to crc value to save
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_calculate_crc( char *filename, off_t offset, unsigned int length,
- uint32_t *crc_value){
- uint32_t temp;
- int fdi;
- int ret = 0;
-
- /* Open the file read only */
- fdi = open(filename, O_RDONLY);
-
- if(fdi<0) {
- ret = -1;
- }
-
- /* Seek to offset and check for errors */
- if(lseek(fdi, offset, SEEK_SET) != offset) {
- ret=-1;
- }
-
- /* Initialize the crc_value to make sure this starts at 0 */
- *crc_value = 0;
- /* Run this loop till the entire sum is created */
- do {
- /* Read an integer at a time */
- if(read(fdi, &temp, sizeof(uint32_t)) < 0) {
- ret = -1;
- break;
- }
-
- /* Keep summing the values */
- *crc_value+=temp;
- } while (length-=4);
-
- /* Close the file and check for errors */
- if(close (fdi) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_save_data: This function encypts or decrypts the Olympus firmware
- * image based on the dictionary passed to it.
- *
- * Parameters:
- * source_filename: text filename where data is read from
- * dest_filename: text filename where data is written to
- * offset: Offset to the start of the data (header size)
- * length: Length of data to modify
- * dictionary: pointer to dictionary used for scrambling
- *
- * Returns:
- * Returns 0 if there was no error, -1 if there was an error
- */
- int mr500_save_data(
- char *source_filename, char *dest_filename, off_t offset,
- unsigned int length, int* dictionary) {
- int fdi, fdo;
- int ret = 0;
- int i;
-
- /* read_count stores the number of bytes actually read */
- int read_count;
-
- /* read_request stores the number of bytes to be requested */
- int read_request;
-
- /* These two buffers are used for reading data and scrambling or
- * descrambling
- */
- int8_t buffer_original[16];
- int8_t buffer_modified[16];
-
- /* Open input read only, output write only */
- fdi = open(source_filename, O_RDONLY);
- fdo = open(dest_filename, O_WRONLY);
-
- /* If there was an error loading the files set ret appropriately */
- if(fdi<0 || fdo < 0) {
- ret = -1;
- }
-
- /* Input file: Seek to offset and check for errors */
- if(lseek(fdi, offset, SEEK_SET) != offset) {
- ret=-1;
- }
-
- /* Output file: Seek to offset and check for errors */
- if(lseek(fdo, offset, SEEK_SET) != offset) {
- ret=-1;
- }
- /* Run this loop till size is 0 */
- do {
- /* Choose the amount of data to read - normally in 16 byte chunks, but
- * when the end of the file is near may be less.
- */
- if( length > sizeof(buffer_original)){
- read_request = sizeof(buffer_original);
- } else {
- read_request = length;
- }
-
- /* Read in the data */
- read_count = read(fdi, (void *) &buffer_original, read_request);
-
- /* If there was an error set the flag and break */
- if(read_count < 0) {
- ret = -1;
- break;
- }
- for(i=0; i<read_count; i++) {
- /* XOR all of the bits to de/encrypt them */
- buffer_original[i] ^= 0xFF;
- /* Handle byte scrambling */
- buffer_modified[dictionary[i]] = buffer_original[i];
- }
-
- /* write the data: If there was an error set the flag and break */
- if(write(fdo, (void *) &buffer_modified, read_count) < 0) {
- ret = -1;
- break;
- }
- } while (length -= read_count);
-
- /* Close the files and check for errors */
- if(close (fdi) < 0) {
- ret = -1;
- }
- if(close (fdo) < 0) {
- ret = -1;
- }
-
- return ret;
- }
- /* mr500_init: This function initializes the encryption array
- *
- * Parameters:
- * None
- *
- * Returns:
- * Returns 0
- */
- int mr500_init(void) {
- int i;
- /* Initialize the encryption array */
- for(i=0; i<16; i++) {
- encrypt_array[decrypt_array[i]]=i;
- }
- return 0;
- }