samsungtool: allow firmware creation

The new tool fwcrypt can create a firmware image with a specified
model, version, region and so on.

Change-Id: I0e90e9ab905398a3e7ae3f4fb8b8bbfb2d12d703
This commit is contained in:
Amaury Pouly 2012-12-06 12:16:57 +01:00
parent 8666e83aaa
commit fb43a137e7
4 changed files with 229 additions and 1 deletions

View file

@ -3,7 +3,7 @@ CC=gcc
LD=gcc LD=gcc
CFLAGS=-g -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` CFLAGS=-g -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl`
LDFLAGS=`pkg-config --libs openssl` LDFLAGS=`pkg-config --libs openssl`
BINS=fwdecrypt BINS=fwdecrypt fwcrypt
all: $(BINS) all: $(BINS)
@ -13,5 +13,8 @@ all: $(BINS)
fwdecrypt: fwdecrypt.o samsung.o fwdecrypt: fwdecrypt.o samsung.o
$(LD) -o $@ $^ $(LDFLAGS) $(LD) -o $@ $^ $(LDFLAGS)
fwcrypt: fwcrypt.o samsung.o
$(LD) -o $@ $^ $(LDFLAGS)
clean: clean:
rm -fr *.o $(BINS) rm -fr *.o $(BINS)

View file

@ -0,0 +1,170 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2012 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "samsung.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <getopt.h>
#include <string.h>
static bool g_debug = false;
static char *g_out_prefix = NULL;
static void usage(void)
{
printf("Usage: fwcrypt [options] content\n");
printf("Options:\n");
printf(" -o <prefix>\tSet output file\n");
printf(" -m/--model <model>\tSet model\n");
printf(" -v/--version <ver>\tSet version\n");
printf(" -r/--region <region>\tSet region\n");
printf(" -e/--extra <extra>\tSet extra\n");
printf(" -?/--help\tDisplay this message\n");
printf(" -d/--debug\tDisplay debug messages\n");
exit(1);
}
static int s_write(void *user, int offset, void *buf, int size)
{
FILE *f = user;
if(fseek(f, offset, SEEK_SET) != 0)
return 0;
return fwrite(buf, 1, size, f);
}
static void s_printf(void *user, bool error, const char *fmt, ...)
{
if(!g_debug && !error)
return;
(void) user;
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
int main(int argc, char **argv)
{
struct samsung_firmware_t *fw = malloc(sizeof(struct samsung_firmware_t));
memset(fw, 0, sizeof(struct samsung_firmware_t));
if(argc <= 1)
usage();
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, '?'},
{"debug", no_argument, 0, 'd'},
{"model", required_argument, 0, 'm'},
{"version", required_argument, 0, 'v'},
{"region", required_argument, 0, 'r'},
{"extra", required_argument, 0, 'e'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "?do:m:v:r:e:", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case 'd':
g_debug = true;
break;
case '?':
usage();
break;
case 'o':
g_out_prefix = optarg;
break;
case 'm':
strncpy(fw->model, optarg, sizeof(fw->model));
if(strlen(optarg) > sizeof(fw->model))
printf("Warning: truncate model string\n");
break;
case 'r':
strncpy(fw->region, optarg, sizeof(fw->region));
if(strlen(optarg) > sizeof(fw->region))
printf("Warning: truncate region string\n");
break;
case 'v':
strncpy(fw->version, optarg, sizeof(fw->version));
if(strlen(optarg) > sizeof(fw->version))
printf("Warning: truncate vesion string\n");
break;
case 'e':
strncpy(fw->extra, optarg, sizeof(fw->extra));
if(strlen(optarg) > sizeof(fw->extra))
printf("Warning: truncate extra string\n");
break;
default:
abort();
}
}
if(optind != argc - 1)
usage();
FILE *fin = fopen(argv[optind], "rb");
if(fin == NULL)
{
printf("Cannot open file for reading: %m\n");
samsung_free(fw);
return 1;
}
fseek(fin, 0, SEEK_END);
fw->data_size = ftell(fin);
fseek(fin, 0, SEEK_SET);
fw->data = malloc(fw->data_size);
if((int)fread(fw->data, 1, fw->data_size, fin) != fw->data_size)
{
printf("Cannot read input file: %m\n");
samsung_free(fw);
return 2;
}
fclose(fin);
if(g_out_prefix)
{
FILE *f = fopen(g_out_prefix, "wb");
if(f == NULL)
{
printf("Cannot open file for writing: %m\n");
samsung_free(fw);
return 1;
}
enum samsung_error_t err = samsung_write(s_write, s_printf, f, fw);
if(err != SAMSUNG_SUCCESS)
{
printf("Error writing firmware: %d\n", err);
samsung_free(fw);
return 3;
}
fclose(f);
}
samsung_free(fw);
return 0;
}

View file

@ -117,6 +117,57 @@ struct samsung_firmware_t *samsung_read(samsung_read_t read,
return fw; return fw;
} }
enum samsung_error_t samsung_write(samsung_write_t write, samsung_printf_t printf,
void *user, struct samsung_firmware_t *fw)
{
struct yp_header_t yp_hdr;
struct yp_md5_t yp_md5;
// write header
strncpy(yp_hdr.signature, YP_SIGNATURE, sizeof(yp_hdr.signature));
strncpy(yp_hdr.version, fw->version, sizeof(yp_hdr.version));
strncpy(yp_hdr.region, fw->region, sizeof(yp_hdr.region));
strncpy(yp_hdr.extra, fw->extra, sizeof(yp_hdr.extra));
strncpy(yp_hdr.model, fw->model, sizeof(yp_hdr.model));
yp_hdr.datasize = fw->data_size;
printf(user, false, "Model: %s\n", yp_hdr.model);
printf(user, false, "Version: %s %s %s\n", yp_hdr.version, yp_hdr.region, yp_hdr.extra);
if(write(user, 0, &yp_hdr, sizeof(yp_hdr)) != sizeof(yp_hdr))
{
printf(user, true, "Cannot write header\n");
return SAMSUNG_WRITE_ERROR;
}
// encrypt data
cyclic_xor(fw->data, fw->data_size, g_yp_key, sizeof(g_yp_key));
// compute MD5
MD5_CTX c;
MD5_Init(&c);
MD5_Update(&c, fw->data, fw->data_size);
MD5_Final(yp_md5.md5, &c);
// write data
if(write(user, sizeof(yp_hdr), fw->data, fw->data_size) != fw->data_size)
{
// decrypt data so that the firmware data is the same after the call
cyclic_xor(fw->data, fw->data_size, g_yp_key, sizeof(g_yp_key));
printf(user, true, "Cannot write data\n");
return SAMSUNG_WRITE_ERROR;
}
// decrypt data so that the firmware data is the same after the call
cyclic_xor(fw->data, fw->data_size, g_yp_key, sizeof(g_yp_key));
// write md5
if(write(user, sizeof(yp_hdr) + fw->data_size, &yp_md5, sizeof(yp_md5)) != sizeof(yp_md5))
{
printf(user, true, "Cannot write md5\n");
return SAMSUNG_WRITE_ERROR;
}
return SAMSUNG_SUCCESS;
}
void samsung_free(struct samsung_firmware_t *fw) void samsung_free(struct samsung_firmware_t *fw)
{ {
if(fw) if(fw)

View file

@ -66,13 +66,17 @@ enum samsung_error_t
SAMSUNG_READ_ERROR = -1, SAMSUNG_READ_ERROR = -1,
SAMSUNG_FORMAT_ERROR = -2, SAMSUNG_FORMAT_ERROR = -2,
SAMSUNG_MD5_ERROR = -3, SAMSUNG_MD5_ERROR = -3,
SAMSUNG_WRITE_ERROR = -4,
}; };
typedef int (*samsung_read_t)(void *user, int offset, void *buffer, int size); typedef int (*samsung_read_t)(void *user, int offset, void *buffer, int size);
typedef int (*samsung_write_t)(void *user, int offset, void *buffer, int size);
typedef void (*samsung_printf_t)(void *user, bool error, const char *fmt, ...); typedef void (*samsung_printf_t)(void *user, bool error, const char *fmt, ...);
struct samsung_firmware_t *samsung_read(samsung_read_t read, struct samsung_firmware_t *samsung_read(samsung_read_t read,
samsung_printf_t printf, void *user, enum samsung_error_t *err); samsung_printf_t printf, void *user, enum samsung_error_t *err);
enum samsung_error_t samsung_write(samsung_write_t write, samsung_printf_t printf,
void *user, struct samsung_firmware_t *fw);
void samsung_free(struct samsung_firmware_t *fw); void samsung_free(struct samsung_firmware_t *fw);
#endif /* __SAMSUNG_H__ */ #endif /* __SAMSUNG_H__ */