rkwtool: The tool to inspect and extract update RKW files

Change-Id: Ie32d0a597b93d23a7d5946a3d9409572b41b45bc
This commit is contained in:
Marcin Bukat 2014-08-07 19:09:35 +02:00
parent 81ffd9bfee
commit 53d9f2e6a7
5 changed files with 812 additions and 0 deletions

View file

@ -36,6 +36,21 @@ rockchip player.
You need libusb-1.0 + header files in order to compile this utility.
rkwtool
This directory contains tool to inspect and extract content of
'full' update RKW file.
The RKW file contains specific section which instruct update
routine of the DAP what to do, custom archive with firmware files,
images of stage1 and stage2 nand bootloader and Rock27Boot.bin file
image. Nand bootloader images are stored in scrambled form but
the tool deciphers it to plain binary during extraction.
More complete descritpion of file format can be found on wiki:
http://www.rockbox.org/wiki/RKWFileFormat
nandextract
This directory contains quick and dirty tool which allows to extract
nand bootloader from raw dump of the first nand block. The main reason

View file

@ -0,0 +1,7 @@
all: rkwtool
rkwtool: rkw.c main.c
gcc -g -std=c99 -o $@ -W -Wall $^
clean:
rm -fr rkwtool

View file

@ -0,0 +1,129 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2014 by Marcin Bukat
*
* 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 <getopt.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "rkw.h"
#define VERSION "v0.1"
static void banner(void)
{
printf("RKWtool " VERSION " (C) Marcin Bukat 2014\n");
printf("This is free software; see the source for copying conditions. There is NO\n");
printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
}
static void usage(char *name)
{
banner();
printf("Usage: %s [-i] [-b] [-e] [-a] [-o prefix] file.rkw\n", name);
printf("-i\t\tprint info about RKW file\n");
printf("-b\t\textract nand bootloader images (s1.bin and s2.bin)\n");
printf("-e\t\textract firmware files stored in RKST section\n");
printf("-o prefix\twhen extracting firmware files put it there\n");
printf("-a\t\textract additional file(s) (usually Rock27Boot.bin)\n");
printf("-A\t\textract all data\n");
printf("file.rkw\tRKW file to be processed\n");
}
int main(int argc, char **argv)
{
int opt;
struct rkw_info_t *rkw_info = NULL;
char *prefix = NULL;
bool info = false;
bool extract = false;
bool bootloader = false;
bool addfile = false;
while ((opt = getopt(argc, argv, "iebo:aA")) != -1)
{
switch (opt)
{
case 'i':
info = true;
break;
case 'e':
extract = true;
break;
case 'b':
bootloader = true;
break;
case 'o':
prefix = optarg;
break;
case 'a':
addfile = true;
break;
case 'A':
extract = true;
bootloader = true;
addfile = true;
break;
default:
usage(argv[0]);
break;
}
}
if ((argc - optind) != 1 ||
(!info && !extract && ! bootloader && !addfile))
{
usage(argv[0]);
return -1;
}
banner();
rkw_info = rkw_slurp(argv[optind]);
if (rkw_info)
{
if (info)
{
rkrs_list_named_items(rkw_info);
rkst_list_named_items(rkw_info);
}
if (extract)
unpack_rkst(rkw_info, prefix);
if (bootloader)
unpack_bootloader(rkw_info, prefix);
if (addfile)
unpack_addfile(rkw_info, prefix);
rkw_free(rkw_info);
return 0;
}
return -1;
}

View file

@ -0,0 +1,538 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2014 by Marcin Bukat
*
* 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 <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "rkw.h"
const char *section_name[] = {
"RKLD",
"RKRS",
"RKST"
};
const uint32_t section_magic[] = {
RKLD_MAGIC,
RKRS_MAGIC,
RKST_MAGIC
};
const char *rkrs_action_name[] = {
[act_null] = "null",
[act_mkdir] = "mkdir",
[act_fcopy] = "fcopy",
[act_fsoper] = "fsoper",
[act_format] = "format",
[act_loader] = "loader",
[act_dispbmp] = "dispbmp",
[act_dispstr] = "dispstr",
[act_setfont] = "setfont",
[act_delay] = "delay",
[act_system] = "system",
[act_readme] = "readme",
[act_copyright] = "copyright",
[act_select] = "select",
[act_restart] = "restart",
[act_regkey] = "regkey",
[act_version] = "version",
[act_freplace] = "freplace",
[act_fpreplace] = "fpreplace",
[act_fsdel] = "fsdel",
[act_space] = "space",
[act_addfile] = "addfile",
[act_setmem] = "setmem",
[act_getmem] = "getmem"
};
/* scrambling/descrambling reverse engineered by AleMaxx */
static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
{
uint8_t key[] = {
0x7C, 0x4E, 0x03, 0x04,
0x55, 0x05, 0x09, 0x07,
0x2D, 0x2C, 0x7B, 0x38,
0x17, 0x0D, 0x17, 0x11
};
int i, i3, x, val, idx;
uint8_t key1[0x100];
uint8_t key2[0x100];
for (i=0; i<0x100; i++) {
key1[i] = i;
key2[i] = key[i&0xf];
}
i3 = 0;
for (i=0; i<0x100; i++) {
x = key1[i];
i3 = key1[i] + i3;
i3 += key2[i];
i3 &= 0xff;
key1[i] = key1[i3];
key1[i3] = x;
}
idx = 0;
for (i=0; i<size; i++) {
x = key1[(i+1) & 0xff];
val = x;
idx = (x + idx) & 0xff;
key1[(i+1) & 0xff] = key1[idx];
key1[idx] = (x & 0xff);
val = (key1[(i+1)&0xff] + x) & 0xff;
val = key1[val];
outpg[i] = val ^ inpg[i];
}
}
/* take path as stored in RKST and convert it to unix path
* with optional prefix
*/
static char *unixpath(char *path, char *prefix)
{
char *parsed, *ptr;
size_t size = 0;
if (NULL == path)
return NULL;
size = strlen(path) + 1;
/* full windows path i.e C:\something */
if (strlen(path) > 2 && ':' == path[1])
path += 3;
if (prefix)
{
/* account for '/' after prefix */
size += strlen(prefix) + 1;
}
/* allocate buffer */
parsed = malloc(size);
ptr = parsed;
/* malloc failed */
if (NULL == ptr)
return NULL;
/* copy prefix */
if (prefix)
{
strcpy(ptr, prefix);
ptr += strlen(prefix);
*ptr++ = '/';
}
do
{
if (*path == '\\')
*ptr = '/';
else
*ptr = *path;
ptr++;
} while ('\0' != *(path++));
return parsed;
}
/* returns pointer to the rkrs header in rkw file */
static char *find_section(struct rkw_info_t *rkw_info, enum section_type_t type)
{
char *ptr;
struct section_header_t *h;
switch(type)
{
case ST_RKRS:
case ST_RKST:
for (ptr=(char *)rkw_info->rkw; ptr<(char *)rkw_info->rkw+rkw_info->size; ptr++)
{
h = (struct section_header_t *)ptr;
if (h->magic == section_magic[type] &&
h->size == sizeof(struct section_header_t) &&
h->number_of_named_entries != 0)
{
fprintf(stderr, "[info]: %s found at 0x%0x\n",
section_name[type], (int)(ptr - rkw_info->rkw));
return ptr;
}
}
break;
default:
fprintf(stderr, "[error]: Not supported section type %d\n", type);
return NULL;
}
return NULL;
}
/* load rkw file into memory and setup pointers to various sections */
struct rkw_info_t *rkw_slurp(char *filename)
{
FILE *fp;
struct rkw_info_t *rkw_info;
rkw_info = (struct rkw_info_t *)malloc(sizeof(struct rkw_info_t));
if (NULL == rkw_info)
{
fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
sizeof(struct rkw_info_t));
return NULL;
}
fp = fopen(filename, "rb");
if (NULL == fp)
{
fprintf(stderr, "[error]: Can't open %s\n", filename);
free(rkw_info);
return NULL;
}
fseek(fp, 0, SEEK_END);
rkw_info->size = ftell(fp);
fseek(fp, 0, SEEK_SET);
rkw_info->rkw = (char *)malloc(rkw_info->size);
if (NULL == rkw_info->rkw)
{
fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
rkw_info->size);
free(rkw_info);
fclose(fp);
return NULL;
}
if (fread(rkw_info->rkw, rkw_info->size, 1, fp) != 1)
{
fprintf(stderr, "[error]: Can't read %s\n", filename);
free(rkw_info);
fclose(fp);
return NULL;
}
rkw_info->rkrs_info.header = find_section(rkw_info, ST_RKRS);
rkw_info->rkrs_info.items = rkw_info->rkrs_info.header +
((struct section_header_t *)
(rkw_info->rkrs_info.header))->offset_of_named_entries;
rkw_info->rkst_info.header = find_section(rkw_info, ST_RKST);
rkw_info->rkst_info.items = rkw_info->rkst_info.header +
((struct section_header_t *)
(rkw_info->rkst_info.header))->offset_of_named_entries;
fclose(fp);
return rkw_info;
}
void rkw_free(struct rkw_info_t *rkw_info)
{
free(rkw_info->rkw);
free(rkw_info);
}
static void rkrs_named_item_info(struct rkrs_named_t *item)
{
fprintf(stderr, "[info]: size=0x%0x (%d)\n", item->size, item->size);
fprintf(stderr, "[info]: type=0x%0x (%d) %s\n", item->type, item->type, rkrs_action_name[item->type]);
fprintf(stderr, "[info]: data_offset=0x%0x (%d)\n", item->data_offset, item->data_offset);
fprintf(stderr, "[info]: data_size=0x%0x (%d)\n", item->data_size, item->data_size);
fprintf(stderr, "[info]: param[0]=0x%0x (%d)\n", item->param[0], item->param[0]);
fprintf(stderr, "[info]: param[1]=0x%0x (%d)\n", item->param[1], item->param[1]);
fprintf(stderr, "[info]: param[2]=0x%0x (%d)\n", item->param[2], item->param[2]);
fprintf(stderr, "[info]: param[3]=0x%0x (%d)\n", item->param[3], item->param[3]);
}
static void rkst_named_item_info(struct rkst_named_t *item)
{
fprintf(stderr, "[info]: size=0x%0x (%d)\n", item->size, item->size);
fprintf(stderr, "[info]: action=0x%0x (%d) %s\n", item->action, item->action, rkrs_action_name[item->action]);
fprintf(stderr, "[info]: data_offset=0x%0x (%d)\n", item->data_offset, item->data_offset);
fprintf(stderr, "[info]: data_size=0x%0x (%d)\n", item->data_size, item->data_size);
fprintf(stderr, "[info]: name=\"%s\"\n", &item->name);
}
static struct rkrs_named_t *find_item(struct rkw_info_t *rkw_info, enum rkst_action_t type, bool search_start)
{
static struct rkrs_named_t *item;
if (search_start)
{
item = (struct rkrs_named_t *)rkw_info->rkrs_info.items;
}
else
{
if (item)
item++;
else
return NULL;
}
while (item->size > 0)
{
if (item->type == type)
{
fprintf(stderr, "[info]: Item type=%d found at 0x%x\n", type,
(int)((char *)item - rkw_info->rkw));
return item;
}
item++;
}
return NULL;
}
void rkrs_list_named_items(struct rkw_info_t *rkw_info)
{
struct rkrs_named_t *item = (struct rkrs_named_t *)rkw_info->rkrs_info.items;
struct section_header_t *rkrs_header = (struct section_header_t *)(rkw_info->rkrs_info.header);
int i;
for (i=0; i<rkrs_header->number_of_named_entries; i++)
{
fprintf(stderr, "[info]: rkrs named entry %d\n", i);
rkrs_named_item_info(item++);
fprintf(stderr, "\n");
}
}
void rkst_list_named_items(struct rkw_info_t *rkw_info)
{
struct rkst_named_t *item = (struct rkst_named_t *)rkw_info->rkst_info.items;
struct section_header_t *rkst_header = (struct section_header_t *)(rkw_info->rkst_info.header);
int i;
for (i=0; i<rkst_header->number_of_named_entries; i++)
{
fprintf(stderr, "[info]: rkst named entry %d\n", i);
rkst_named_item_info(item);
item = (struct rkst_named_t *)((char *)item + item->size);
fprintf(stderr, "\n");
}
}
void unpack_bootloader(struct rkw_info_t *rkw_info, char *prefix)
{
FILE *fp;
char *ptr;
size_t size;
int len;
char *buf;
struct rkrs_named_t *item = find_item(rkw_info, act_loader, true);
if (NULL == item)
{
fprintf(stderr, "[error]: Can't find nand bootloader\n");
return;
}
ptr = (char *)(rkw_info->rkrs_info.header) + item->data_offset;
size = item->param[0];
buf = malloc(size);
if (NULL == buf)
{
fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
size);
return;
}
/* make a copy for decryption */
memcpy(buf, ptr, size);
encode_page((uint8_t *)buf, (uint8_t *)buf, size);
fp = fopen(unixpath("s1.bin", prefix), "w");
if (NULL == fp)
{
fprintf(stderr, "[error]: Can't open s1.bin for writing\n");
free(buf);
return;
}
if (fwrite(buf, size, 1, fp) != 1)
{
fprintf(stderr, "[error]: Can't write s1.bin file\n");
free(buf);
fclose(fp);
return;
}
fclose(fp);
ptr = (char *)(rkw_info->rkrs_info.header) + item->param[1];
size = item->param[2];
len = size;
buf = realloc(buf, size);
if (NULL == buf)
{
fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
size);
free(buf);
return;
}
memcpy(buf, ptr, size);
ptr = buf;
while (len >= 0x200)
{
encode_page((uint8_t *)ptr, (uint8_t *)ptr, 0x200);
ptr += 0x200;
len -= 0x200;
}
encode_page((uint8_t *)ptr, (uint8_t *)ptr, len);
fp = fopen(unixpath("s2.bin", prefix), "w");
if (NULL == fp)
{
fprintf(stderr, "[error]: Can't open s2.bin for writing\n");
free(buf);
return;
}
if (fwrite(buf, size, 1, fp) != 1)
{
fprintf(stderr, "[error]: Can't write s2.bin file\n");
free(buf);
fclose(fp);
return;
}
fclose(fp);
free(buf);
fprintf(stderr, "[info]: Extracted bootloader version: %x.%x\n",
(item->param[3] >> 8) & 0xff, item->param[3] & 0xff);
}
void unpack_addfile(struct rkw_info_t *rkw_info, char *prefix)
{
FILE *fp;
char *name;
int name_len;
struct rkrs_named_t *item = find_item(rkw_info, act_addfile, true);
do
{
name = unixpath(rkw_info->rkrs_info.header + item->data_offset, prefix);
name_len = item->param[0];
fprintf(stderr, "[info]: unpacking addfile %s\n", name);
fp = fopen(name, "w");
if (NULL == fp)
{
fprintf(stderr, "[error]: Can't open %s for writing\n", name);
return;
}
if (fwrite(rkw_info->rkrs_info.header + item->data_offset + name_len,
item->data_size - name_len, 1, fp) != 1)
{
fprintf(stderr, "[error]: Can't write %s file\n", name);
fclose(fp);
return;
}
fclose(fp);
} while (NULL != (item = find_item(rkw_info, act_addfile, false)));
}
/* unpack content of RKST section
* this mimics what is done when processing 'fsoper' field of RKRS
*/
void unpack_rkst(struct rkw_info_t *rkw_info, char *prefix)
{
FILE *fp;
struct rkst_named_t *item = (struct rkst_named_t *)rkw_info->rkst_info.items;
struct section_header_t *rkst_header = (struct section_header_t *)(rkw_info->rkst_info.header);
char *name;
int i;
if (prefix)
{
if (0 != mkdir(prefix, 0755))
{
fprintf(stderr, "[error]: Can't create %s directory (%s)\n",
prefix, strerror(errno));
return;
}
}
fprintf(stderr, "[info]: Unpacking content of RKST section\n");
for (i=0; i<rkst_header->number_of_named_entries; i++)
{
name = unixpath((char *)&(item->name), prefix);
switch (item->action)
{
case act_mkdir:
if (0 != mkdir(name, 0755))
{
fprintf(stderr, "[error]: Can't create %s directory (%s)\n",
name, strerror(errno));
return;
}
fprintf(stderr, "[info]: mkdir %s\n", name);
break;
case act_fcopy:
fp = fopen(name, "w");
if (NULL == fp)
{
fprintf(stderr, "[error]: Can't open %s for writing (%s)\n",
name, strerror(errno));
return;
}
fwrite((char *)rkst_header + item->data_offset, item->data_size, 1, fp);
fprintf(stderr, "[info]: unpack %s\n", name);
fclose(fp);
break;
default:
break;
}
if (name) free(name);
item = (struct rkst_named_t *)((char *)item + item->size);
}
}

View file

@ -0,0 +1,123 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2014 by Marcin Bukat
*
* 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 <stdint.h>
#define RKLD_MAGIC 0x4C44524B
#define RKRS_MAGIC 0x53524B52
#define RKST_MAGIC 0X53544B52
enum section_type_t {
ST_RKLD,
ST_RKRS,
ST_RKST
};
enum rkst_action_t {
act_null = 0,
act_mkdir = 1,
act_fcopy = 2,
act_fsoper = 3,
act_format = 4,
act_loader = 5,
act_dispbmp = 10,
act_dispstr = 11,
act_setfont = 12,
act_delay = 20,
act_system = 100,
act_uilogo = 100,
act_readme = 101,
act_copyright = 102,
act_select = 103,
act_restart = 104,
act_regkey = 120,
act_version = 121,
act_freplace = 130,
act_fpreplace = 131,
act_fsdel = 132,
act_space = 200,
act_addfile = 300,
act_setmem = 1000,
act_getmem = 1001,
};
struct section_header_t {
uint32_t size;
uint32_t magic;
uint32_t property;
uint32_t timestamp;
uint32_t allign;
uint32_t file_size;
uint16_t size_of_name_dir;
uint16_t size_of_id_dir;
uint16_t number_of_named_entries;
uint16_t number_of_id_entries;
uint32_t offset_of_named_entries;
uint32_t offset_of_id_entries;
} __attribute__((__packed__));
struct rkrs_named_t {
uint32_t size;
uint32_t type;
uint32_t data_offset;
uint32_t data_size;
uint32_t param[4];
} __attribute__((__packed__));
struct rkst_named_t {
uint32_t size;
uint32_t action;
uint32_t data_offset;
uint32_t data_size;
uint8_t name;
};
struct section_info_t {
char *header;
char *items;
};
struct rkw_info_t {
char *rkw;
long size;
struct section_info_t rkrs_info;
struct section_info_t rkst_info;
};
/* general functions */
struct rkw_info_t *rkw_slurp(char *filename);
void rkw_free(struct rkw_info_t *rkw_info);
/* info functions */
void rkrs_list_named_items(struct rkw_info_t *rkw_info);
void rkst_list_named_items(struct rkw_info_t *rkw_info);
/* extract functions */
void unpack_bootloader(struct rkw_info_t *rkw_info, char *prefix);
void unpack_rkst(struct rkw_info_t *rkw_info, char *prefix);
void unpack_addfile(struct rkw_info_t *rkw_info, char *prefix);