about summary refs log tree commit diff stats
path: root/source/multiboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/multiboot.c')
-rw-r--r--source/multiboot.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/source/multiboot.c b/source/multiboot.c new file mode 100644 index 0000000..06cbd6f --- /dev/null +++ b/source/multiboot.c
@@ -0,0 +1,246 @@
1#include "multiboot.h"
2#include <gccore.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include "link.h"
7
8extern u8 gba_mb_gba[];
9extern u32 gba_mb_gba_size;
10
11unsigned int docrc(u32 crc,u32 val)
12{
13 u32 result;
14
15 result = val ^ crc;
16 for (int i = 0; i < 0x20; i++)
17 {
18 if (result & 1)
19 {
20 result >>= 1;
21 result ^= 0xA1C1;
22 } else {
23 result >>= 1;
24 }
25 }
26
27 return result;
28}
29
30u32 genKeyA()
31{
32 u32 retries = 0;
33
34 for (;;)
35 {
36 u32 key = 0;
37
38 if (retries > 32)
39 {
40 key = 0xDD654321;
41 } else {
42 key = (rand() & 0x00ffffff) | 0xDD000000;
43 }
44
45 u32 unk = (key % 2 != 0);
46 u32 v12 = key;
47 for (u32 v13 = 1; v13 < 32; v13++)
48 {
49 v12 >>= 1;
50 unk += (v12 % 2 != 0);
51 }
52
53 if ((unk >= 10 && unk <= 24))
54 {
55 if (retries > 4)
56 {
57 printf("KeyA retries = %ld", retries);
58 }
59
60 printf("KeyA = 0x%08lx\n", key);
61
62 return key;
63 }
64
65 retries++;
66 }
67}
68
69u32 checkKeyB(u32 KeyBRaw)
70{
71 if ((KeyBRaw & 0xFF) != 0xEE)
72 {
73 printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",
74 ((u8)(KeyBRaw)));
75
76 return 0;
77 }
78
79 u32 KeyB = KeyBRaw & 0xffffff00;
80 u32 val = KeyB;
81 u32 unk = (val < 0);
82 for (u32 i = 1; i < 24; i++)
83 {
84 val <<= 1;
85 unk += (val < 0);
86 }
87
88 if (unk > 14)
89 {
90 printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n", KeyB);
91
92 return 0;
93 }
94
95 printf("Valid KeyB: 0x%08lx\n", KeyB);
96
97 return KeyB;
98}
99
100u32 deriveKeyC(u32 keyCderive, u32 kcrc)
101{
102 u32 keyc = 0;
103 u32 keyCi = 0;
104
105 do
106 {
107 u32 v5 = 0x1000000 * keyCi - 1;
108 u32 keyCattempt = docrc(kcrc,v5);
109
110 if (keyCderive == keyCattempt)
111 {
112 keyc = v5;
113
114 printf("Found keyC: %08lx\n",keyc);
115
116 return keyc;
117 }
118
119 keyCi++;
120 } while (keyCi < 256);
121
122 return keyc;
123}
124
125bool sendMultibootImage()
126{
127 printf("Waiting on BIOS\n");
128 waitForBIOS();
129
130 printf("BIOS handed over to game, waiting on game\n");
131 waitForGame();
132
133 // receive the game-code from GBA side.
134 u32 gamecode = recv();
135
136 printf("Ready, sending multiboot ROM\n");
137
138 unsigned int sendsize = ((gba_mb_gba_size+7)&~7);
139
140 // generate KeyA
141 unsigned int ourkey = genKeyA();
142
143 //printf("Our Key: %08x\n", ourkey);
144 printf("Sending game code that we got: 0x%08lx\n",
145 __builtin_bswap32(gamecode));
146
147 // send the game code back, then KeyA.
148 send(__builtin_bswap32(gamecode));
149 send(ourkey);
150
151 // get KeyB from GBA, check it to make sure its valid, then xor with KeyA
152 // to derive the initial CRC value and the sessionkey.
153 u32 sessionkeyraw = 0;
154 do
155 {
156 sessionkeyraw = recv();
157 } while (sessionkeyraw == gamecode);
158
159 sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw));
160 if (sessionkeyraw == 0)
161 {
162 return false;
163 }
164
165 u32 sessionkey = sessionkeyraw ^ ourkey;
166 u32 kcrc = sessionkey;
167 printf("start kCRC=%08lx\n",kcrc);
168
169 sessionkey = (sessionkey*0x6177614b)+1;
170
171 // send hacked up send-size in uint32s
172 u32 hackedupsize = (sendsize >> 3) - 1;
173
174 printf("Sending hacked up size 0x%08lx\n",hackedupsize);
175 send(hackedupsize);
176
177 //unsigned int fcrc = 0x00bb;
178 // send over multiboot binary header, in the clear until the end of the
179 // nintendo logo. GBA checks this, if nintendo logo does not match the
180 // one in currently inserted cart's ROM, it will not accept any more data.
181 for (int i = 0; i < 0xA0; i+=4)
182 {
183 vu32 rom_dword = *(vu32*)(gba_mb_gba+i);
184 send(__builtin_bswap32(rom_dword));
185 }
186
187 printf("\n");
188 printf("Header done! Sending ROM...\n");
189
190 // Add each uint32 of the multiboot image to the checksum, encrypt the
191 // uint32 with the session key, increment the session key, send the
192 // encrypted uint32.
193 for (int i = 0xA0; i < sendsize; i+=4)
194 {
195 u32 dec = (
196 (((gba_mb_gba[i+3]) << 24) & 0xff000000) |
197 (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) |
198 (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) |
199 (((gba_mb_gba[i]) << 0) & 0x000000ff)
200 );
201
202 u32 enc = (dec - kcrc) ^ sessionkey;
203 kcrc = docrc(kcrc,dec);
204 sessionkey = (sessionkey * 0x6177614B) + 1;
205 //enc^=((~(i+(0x20<<20)))+1);
206 //enc^=0x6f646573;//0x20796220;
207
208 send(enc);
209 }
210
211 //fcrc |= (sendsize<<16);
212 printf("ROM done! CRC: %08lx\n", kcrc);
213 //get crc back (unused)
214
215 // Get KeyC derivation material from GBA (eventually)
216 u32 keyCderive = 0;
217 do
218 {
219 keyCderive = recv();
220 } while (keyCderive <= 0xfeffffff);
221
222 keyCderive = __builtin_bswap32(keyCderive);
223 keyCderive >>= 8;
224
225 printf("KeyC derivation material: %08lx\n",keyCderive);
226
227 // (try to) find the KeyC, using the checksum of the multiboot image, and
228 // the derivation material that GBA sent to us
229 u32 keyc = deriveKeyC(keyCderive,kcrc);
230 if (keyc == 0)
231 {
232 printf("Could not find keyC - kcrc=0x%08lx\n",kcrc);
233
234 return false;
235 }
236
237 // derive the boot key from the found KeyC, and send to GBA. if this is
238 // not correct, GBA will not jump to the multiboot image it was sent.
239 u32 bootkey = docrc(0xBB,keyc) | 0xbb000000;
240 printf("BootKey = 0x%08lx\n",bootkey);
241
242 send(bootkey);
243 sleep(2);
244
245 return true;
246}