blob: 2d60fdd94c64643aa6107da65348cbdeedf093cf [file] [log] [blame]
Maurus Cuelenaeree8107212008-04-09 11:53:28 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 by Maurus Cuelenaere
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000022#include <stdio.h>
23#include <stdbool.h>
Maurus Cuelenaere0c23c662008-04-09 12:08:24 +000024#include <stdlib.h>
Daniel Stenberg3e628002008-04-09 12:32:25 +000025#include <string.h>
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000026#include <elf.h>
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000027
Daniel Stenberg49e6a292008-04-09 13:07:50 +000028#include "creative.h"
29#include "hmac-sha1.h"
30
31static const char null_key_v1[] = "CTL:N0MAD|PDE0.SIGN.";
32static const char null_key_v2[] = "CTL:N0MAD|PDE0.DPMP.";
33static const char null_key_v3[] = "CTL:Z3N07|PDE0.DPMP.";
34static const char null_key_v4[] = "CTL:N0MAD|PDE0.DPFP.";
35
Maurus Cuelenaeread9d45a2008-04-24 15:54:49 +000036static const struct device_info devices[] =
37{
Maurus Cuelenaere6d121092008-05-05 15:22:43 +000038 /* Creative Zen Vision:M */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000039 {"C\0r\0e\0a\0t\0i\0v\0e\0 \0Z\0e\0n\0 \0V\0i\0s\0i\0o\0n\0:\0M", 42, null_key_v2},
Maurus Cuelenaere6d121092008-05-05 15:22:43 +000040 /* Creative Zen Vision:M Go! */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000041 {"C\0r\0e\0a\0t\0i\0v\0e\0 \0Z\0e\0n\0 \0V\0i\0s\0i\0o\0n\0:\0M\0 \0G\0o\0!", 50, null_key_v2},
Maurus Cuelenaere6d121092008-05-05 15:22:43 +000042 /* Creative Zen Vision © TL */
43 /* The "©" should be ANSI encoded or the device won't accept the firmware package. */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000044 {"C\0r\0e\0a\0t\0i\0v\0e\0 \0Z\0e\0n\0 \0V\0i\0s\0i\0o\0n\0 \0©\0T\0L", 46, null_key_v2},
Maurus Cuelenaere6d121092008-05-05 15:22:43 +000045 /* Creative ZEN V */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000046 {"C\0r\0e\0a\0t\0i\0v\0e\0 \0Z\0E\0N\0 \0V", 42, null_key_v4},
Maurus Cuelenaere6d121092008-05-05 15:22:43 +000047 /* Creative ZEN */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000048 {"C\0r\0e\0a\0t\0i\0v\0e\0 \0Z\0E\0N", 48, null_key_v3}
Maurus Cuelenaeread9d45a2008-04-24 15:54:49 +000049};
Daniel Stenberg49e6a292008-04-09 13:07:50 +000050
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000051/*
52Create a Zen Vision:M FRESCUE structure file
53*/
Daniel Stenberg49e6a292008-04-09 13:07:50 +000054extern void int2le(unsigned int val, unsigned char* addr);
55extern unsigned int le2int(unsigned char* buf);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000056
57
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000058static int make_ciff_file(const unsigned char *inbuf, unsigned int length,
Daniel Stenberg49e6a292008-04-09 13:07:50 +000059 unsigned char *outbuf, int device)
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000060{
Daniel Stenbergae64d262008-04-09 14:04:39 +000061 unsigned char key[20];
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000062 memcpy(outbuf, "FFIC", 4);
63 int2le(length+90, &outbuf[4]);
64 memcpy(&outbuf[8], "FNIC", 4);
65 int2le(96, &outbuf[0xC]);
66 memcpy(&outbuf[0x10], devices[device].cinf, devices[device].cinf_size);
Daniel Stenberg49e6a292008-04-09 13:07:50 +000067 memset(&outbuf[0x10+devices[device].cinf_size], 0,
68 96 - devices[device].cinf_size);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000069 memcpy(&outbuf[0x70], "ATAD", 4);
70 int2le(length+32, &outbuf[0x74]);
Daniel Stenberg49e6a292008-04-09 13:07:50 +000071 memcpy(&outbuf[0x78], "H\0j\0u\0k\0e\0b\0o\0x\0\x32\0.\0j\0r\0m",
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000072 25); /*Unicode encoded*/
73 memset(&outbuf[0x78+25], 0, 32);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000074 memcpy(&outbuf[0x98], inbuf, length);
75 memcpy(&outbuf[0x98+length], "LLUN", 4);
76 int2le(20, &outbuf[0x98+length+4]);
77 /* Do checksum */
Maurus Cuelenaere49236bb2008-04-09 16:29:05 +000078 hmac_sha1((unsigned char *)devices[device].null, strlen(devices[device].null),
79 outbuf, 0x98+length, key);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000080 memcpy(&outbuf[0x98+length+8], key, 20);
81 return length+0x90+0x1C+8;
82}
83
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000084static int elf_convert(const unsigned char *inbuf, unsigned char *outbuf)
Maurus Cuelenaeree8107212008-04-09 11:53:28 +000085{
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000086 Elf32_Ehdr *main_header;
87 Elf32_Shdr *section_header;
Maurus Cuelenaere589bb1c2008-05-17 14:44:26 +000088 unsigned int i, j, sum;
Maurus Cuelenaere7f67b0a2008-05-17 15:51:49 +000089 intptr_t startaddr;
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +000090
91 main_header = (Elf32_Ehdr*)inbuf;
92 if( !( main_header->e_ident[0] == ELFMAG0 && main_header->e_ident[1] == ELFMAG1
93 && main_header->e_ident[2] == ELFMAG2 && main_header->e_ident[3] == ELFMAG3 ) )
94 {
95 printf("Invalid ELF header!\n");
96 return -1;
97 }
98
Maurus Cuelenaere589bb1c2008-05-17 14:44:26 +000099 startaddr = (intptr_t)outbuf;
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000100
101 for(i = 0; i < main_header->e_shnum; i++)
102 {
103 section_header = (Elf32_Shdr*)(inbuf+main_header->e_shoff+i*sizeof(Elf32_Shdr));
104
105 if( (section_header->sh_flags & SHF_WRITE || section_header->sh_flags & SHF_ALLOC
106 || section_header->sh_flags & SHF_EXECINSTR) && section_header->sh_size > 0
107 && section_header->sh_type != SHT_NOBITS )
Maurus Cuelenaere589bb1c2008-05-17 14:44:26 +0000108 {
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000109 /* Address */
110 int2le(section_header->sh_addr, outbuf);
111 outbuf += 4;
112 /* Size */
113 int2le(section_header->sh_size, outbuf);
114 outbuf += 4;
115 /* Checksum */
116 sum = 0;
117 for(j=0; j<section_header->sh_size; j+= 4)
118 sum += le2int((unsigned char*)(inbuf+section_header->sh_offset+j)) + (le2int((unsigned char*)(inbuf+section_header->sh_offset+j))>>16);
119 int2le(sum, outbuf);
120 outbuf += 2;
121 memset(outbuf, 0, 2);
122 outbuf += 2;
123 /* Data */
124 memcpy(outbuf, inbuf+section_header->sh_offset, section_header->sh_size);
125 outbuf += section_header->sh_size;
126 }
127 }
Maurus Cuelenaere7f67b0a2008-05-17 15:51:49 +0000128 return (int)((intptr_t)outbuf - startaddr);
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000129}
130
131static int make_jrm_file(const unsigned char *inbuf, unsigned char *outbuf)
132{
133 int length;
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000134
135 /* Clear the header area to zero */
136 memset(outbuf, 0, 0x18);
137
138 /* Header (EDOC) */
139 memcpy(outbuf, "EDOC", 4);
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000140 /* Total Size: temporarily set to 0 */
141 memset(&outbuf[0x4], 0, 4);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000142 /* 4 bytes of zero */
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000143 memset(&outbuf[0x8], 0, 4);
Maurus Cuelenaeread9d45a2008-04-24 15:54:49 +0000144
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000145 length = elf_convert(inbuf, &outbuf[0xC]);
146 if(length < 0)
147 return -1;
148 /* Now set the actual Total Size */
149 int2le(4+length, &outbuf[0x4]);
150
151 return 0xC+length;
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000152}
153
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000154int zvm_encode(const char *iname, const char *oname, int device)
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000155{
156 size_t len;
157 int length;
158 FILE *file;
159 unsigned char *outbuf;
160 unsigned char *buf;
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000161
162 file = fopen(iname, "rb");
163 if (!file) {
164 perror(iname);
165 return -1;
166 }
167 fseek(file, 0, SEEK_END);
168 length = ftell(file);
169
170 fseek(file, 0, SEEK_SET);
171
172 buf = (unsigned char*)malloc(length);
173 if ( !buf ) {
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000174 printf("Out of memory!\n");
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000175 return -1;
176 }
177
178 len = fread(buf, 1, length, file);
Daniel Stenberg49e6a292008-04-09 13:07:50 +0000179 if(len < (size_t)length) {
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000180 perror(iname);
181 return -2;
182 }
183 fclose(file);
184
185 outbuf = (unsigned char*)malloc(length+0x300);
186 if ( !outbuf ) {
187 free(buf);
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000188 printf("Out of memory!\n");
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000189 return -1;
190 }
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000191 length = make_jrm_file(buf, outbuf);
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000192 free(buf);
Maurus Cuelenaereaffa4f62008-05-17 14:20:09 +0000193 if(length < 0)
194 {
195 free(outbuf);
196 printf("Error in making JRM file!\n");
197 return -1;
198 }
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000199 buf = (unsigned char*)malloc(length+0x200);
200 memset(buf, 0, length+0x200);
201 length = make_ciff_file(outbuf, length, buf, device);
202 free(outbuf);
203
204 file = fopen(oname, "wb");
205 if (!file) {
206 free(buf);
207 perror(oname);
208 return -3;
209 }
210
211 len = fwrite(buf, 1, length, file);
Daniel Stenberg49e6a292008-04-09 13:07:50 +0000212 if(len < (size_t)length) {
Maurus Cuelenaeree8107212008-04-09 11:53:28 +0000213 free(buf);
214 perror(oname);
215 return -4;
216 }
217
218 free(buf);
219
220 fclose(file);
221
222 return 0;
223}