| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define VERSION "v0.3" |
| |
| /* time field stucture */ |
| struct rktime_t |
| { |
| uint16_t year; |
| uint16_t month; |
| uint16_t day; |
| uint16_t hour; |
| uint16_t minute; |
| uint16_t second; |
| }; |
| |
| /* Rock27Boot.bin header structure */ |
| struct rkboot_info_t |
| { |
| char sign[32]; |
| uint8_t check_values[16]; |
| struct rktime_t time; |
| uint32_t ui_master_version; |
| uint32_t ui_slave_version; |
| uint32_t s1_offset; |
| int32_t s1_len; |
| uint32_t s2_offset; |
| int32_t s2_len; |
| uint32_t s3_offset; |
| int32_t s3_len; |
| uint32_t s4_offset; |
| int32_t s4_len; |
| uint32_t version_flag; |
| }; |
| |
| /* actions */ |
| enum { |
| NONE = 0, |
| INFO = 1, |
| EXTRACT = 2, |
| SCRAMBLE = 4 |
| }; |
| |
| /* scramble mode */ |
| enum { |
| CONTINOUS_ENC, /* scramble whole block at once */ |
| PAGE_ENC /* nand bootloader is scrambled in 0x200 chunks */ |
| }; |
| |
| /* 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]; |
| } |
| } |
| |
| static void *binary_extract(FILE *fp, uint32_t offset, uint32_t len, int descramble, int encode_mode) |
| { |
| void *buff, *buff_ptr; |
| uint32_t ret; |
| |
| if ((fp == NULL) || len == 0) |
| return NULL; |
| |
| /* allocate buff */ |
| if ((buff = malloc(len)) == NULL) |
| return NULL; |
| |
| /* seek to the begining of the data */ |
| fseek(fp, offset, SEEK_SET); |
| |
| /* read into the buffer */ |
| ret = fread(buff, 1, len, fp); |
| |
| if (ret != len) |
| { |
| free(buff); |
| return NULL; |
| } |
| |
| /* descramble */ |
| if ( descramble ) |
| { |
| buff_ptr = buff; |
| if (encode_mode == PAGE_ENC) |
| { |
| while (len >= 0x200) |
| { |
| encode_page((uint8_t *)buff_ptr, |
| (uint8_t *)buff_ptr, |
| 0x200); |
| |
| buff_ptr += 0x200; |
| len -= 0x200; |
| } |
| } |
| encode_page((uint8_t *)buff_ptr, (uint8_t *)buff_ptr, len); |
| } |
| |
| return buff; |
| } |
| |
| static void usage(void) |
| { |
| printf("Usage: rkboottool [options] Rock27Boot.bin\n"); |
| printf("-h|--help This help message\n"); |
| printf("-e|--extract Extract binary images from Rock27Boot.bin file\n"); |
| printf("-d|--descramble Descramble extracted binary images\n"); |
| printf("-i|--info Print info about Rock27Boot.bin file\n"); |
| printf("\n"); |
| printf("Usually you would like to use -d -e together to obtain raw binary\n"); |
| printf("(out files rkboot_s1.bin, rkboot_s2.bin, rkboot_s3.bin, rkboot_s4.bin)\n"); |
| } |
| |
| int main (int argc, char **argv) |
| { |
| struct rkboot_info_t rkboot_info; |
| FILE *fp_in, *fp_out; |
| int32_t i = 0, action = NONE; |
| int32_t ret; |
| void *buff; |
| char *in_filename = NULL; |
| |
| if ( argc < 2 ) |
| { |
| usage(); |
| return -1; |
| } |
| |
| /* print banner */ |
| fprintf(stderr,"rkboottool " VERSION "\n"); |
| fprintf(stderr,"(C) Marcin Bukat 2011\n"); |
| fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); |
| fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); |
| |
| /* arguments handling */ |
| while (i < argc) |
| { |
| if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0)) |
| { |
| action |= INFO; |
| } |
| else if ((strcmp(argv[i],"-e")==0) || (strcmp(argv[i],"--extract")==0)) |
| { |
| action |= EXTRACT; |
| } |
| else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--descramble")==0)) |
| { |
| action |= SCRAMBLE; |
| } |
| else if ((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0)) |
| { |
| usage(); |
| return 0; |
| } |
| else if ( argv[i][0] != '-' ) |
| { |
| /* file argument */ |
| in_filename = argv[i]; |
| } |
| i++; |
| } |
| |
| if ( (fp_in = fopen(in_filename, "rb")) == NULL ) |
| { |
| fprintf(stderr, "error: can't open %s file for reading\n", in_filename); |
| return -1; |
| } |
| |
| ret = fread(&rkboot_info, 1, sizeof(rkboot_info), fp_in); |
| |
| if (ret != sizeof(rkboot_info)) |
| { |
| fclose(fp_in); |
| fprintf(stderr, "error: can't read %s file header\n", in_filename); |
| fprintf(stderr, "read %d, expected %d\n", ret, sizeof(rkboot_info)); |
| return -2; |
| } |
| |
| if (action & INFO) |
| { |
| printf("file: %s\n", in_filename); |
| printf("signature: %s\n", rkboot_info.sign); |
| printf("check bytes: "); |
| for (i = 0; i < 16; i++) |
| printf("0x%0x ", rkboot_info.check_values[i]); |
| |
| printf("\n"); |
| printf("timestamp %d.%d.%d %d:%d:%d\n", rkboot_info.time.day, |
| rkboot_info.time.month, |
| rkboot_info.time.year, |
| rkboot_info.time.hour, |
| rkboot_info.time.minute, |
| rkboot_info.time.second); |
| printf("UI master version: 0x%0x\n", rkboot_info.ui_master_version); |
| printf("UI slave version: 0x%0x\n", rkboot_info.ui_slave_version); |
| printf("s1 data offset: 0x%0x\n", rkboot_info.s1_offset); |
| printf("s1 data len: 0x%0x\n", rkboot_info.s1_len); |
| printf("s2 offset: 0x%0x\n", rkboot_info.s2_offset); |
| printf("s2 len: 0x%0x\n", rkboot_info.s2_len); |
| printf("s3 offset: 0x%0x\n", rkboot_info.s3_offset); |
| printf("s3 len: 0x%0x\n", rkboot_info.s3_len); |
| printf("s4 offset: 0x%0x\n", rkboot_info.s4_offset); |
| printf("s4 len: 0x%0x\n", rkboot_info.s4_len); |
| printf("UI version flag: 0x%0x\n", rkboot_info.version_flag); |
| } |
| |
| if (action & EXTRACT) |
| { |
| /* first stage */ |
| buff = binary_extract(fp_in, rkboot_info.s1_offset, |
| rkboot_info.s1_len, |
| action & SCRAMBLE, |
| CONTINOUS_ENC); |
| |
| if ( buff == NULL ) |
| { |
| fclose(fp_in); |
| fprintf(stderr, "error: can't extract image\n"); |
| return -2; |
| } |
| |
| /* output */ |
| if ((fp_out = fopen("rkboot_s1.bin", "wb")) == NULL) |
| { |
| free(buff); |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't open rkboot_s1.bin for writing\n"); |
| return -3; |
| } |
| |
| fwrite(buff, 1, rkboot_info.s1_len, fp_out); |
| |
| fprintf(stderr, "[info]: extracted rkboot_s1.bin file\n"); |
| free(buff); |
| fclose(fp_out); |
| |
| /* second stage */ |
| buff = binary_extract(fp_in, rkboot_info.s2_offset, |
| rkboot_info.s2_len, |
| action & SCRAMBLE, |
| CONTINOUS_ENC); |
| |
| if ( buff == NULL ) |
| { |
| fclose(fp_in); |
| fprintf(stderr, "error: can't extract image\n"); |
| return -2; |
| } |
| |
| if ((fp_out = fopen("rkboot_s2.bin", "wb")) == NULL) |
| { |
| free(buff); |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't open rkboot_s2.bin for writing\n"); |
| return -4; |
| } |
| |
| fwrite(buff, 1, rkboot_info.s2_len, fp_out); |
| |
| fprintf(stderr, "[info]: extracted rkboot_s2.bin file\n"); |
| free(buff); |
| fclose(fp_out); |
| |
| /* third stage */ |
| buff = binary_extract(fp_in, rkboot_info.s3_offset, |
| rkboot_info.s3_len, |
| action & SCRAMBLE, |
| PAGE_ENC); |
| if ( buff == NULL ) |
| { |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't extract image.\n"); |
| return -2; |
| } |
| |
| if ((fp_out = fopen("rkboot_s3.bin", "wb")) == NULL) |
| { |
| free(buff); |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't open rkboot_s3.bin for writing\n"); |
| return -4; |
| } |
| |
| fwrite(buff, 1, rkboot_info.s3_len, fp_out); |
| |
| fprintf(stderr, "[info]: extracted rkboot_s3.bin file\n"); |
| free(buff); |
| fclose(fp_out); |
| |
| /* forth stage */ |
| buff = binary_extract(fp_in, rkboot_info.s4_offset, |
| rkboot_info.s4_len, |
| action & SCRAMBLE, |
| CONTINOUS_ENC); |
| if ( buff == NULL ) |
| { |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't extract image\n"); |
| return -2; |
| } |
| |
| if ((fp_out = fopen("rkboot_s4.bin", "wb")) == NULL) |
| { |
| free(buff); |
| fclose(fp_in); |
| fprintf(stderr, "[error]: can't open rkboot_s4.bin for writing\n"); |
| return -4; |
| } |
| |
| fwrite(buff, 1, rkboot_info.s4_len, fp_out); |
| |
| fprintf(stderr, "[info]: extracted rkboot_s4.bin file\n"); |
| free(buff); |
| fclose(fp_out); |
| } |
| |
| fclose(fp_in); |
| return 0; |
| } |
| |