Scrambling

From dreamcast.wiki
Jump to navigation Jump to search

As a form of piracy protection, when booting from MIL-CD formatted discs, the Dreamcast loads the main program binary specified in the IP.BIN using a unique "de-scramble" decryption algorithm. Therefore, in order to load a program into memory from disc correctly, the binary must be encrypted using the correct "scramble" algorithm. This process does not apply to programs stored on GD-ROM media.

When pirate groups in the warez scene started releasing "self-boot" versions of Dreamcast games, they chose the alternative route of implementing the algorithm inside the IP.BIN, so that the program is stored normally on disc, becomes "de-scrambled" into memory by the system at load time, and then is "re-scrambled" by the injected IP.BIN code just before execution.

A scramble/descramble algorithm was published by Dreamcast hobbyist pioneer Marcus Comstedt into the public domain. His algorithm is published below.

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define MAXCHUNK (2048*1024)
  5 
  6 static unsigned int seed;
  7 
  8 void my_srand(unsigned int n)
  9 {
 10   seed = n & 0xffff;
 11 }
 12 
 13 unsigned int my_rand()
 14 {
 15   seed = (seed * 2109 + 9273) & 0x7fff;
 16   return (seed + 0xc000) & 0xffff;
 17 }
 18 
 19 void load(FILE *fh, unsigned char *ptr, unsigned long sz)
 20 {
 21   if(fread(ptr, 1, sz, fh) != sz)
 22     {
 23       fprintf(stderr, "Read error!\n");
 24       exit(1);
 25     }
 26 }
 27 
 28 void load_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
 29 {
 30   static int idx[MAXCHUNK/32];
 31   int i;
 32 
 33   /* Convert chunk size to number of slices */
 34   sz /= 32;
 35 
 36   /* Initialize index table with unity,
 37      so that each slice gets loaded exactly once */
 38   for(i = 0; i < sz; i++)
 39     idx[i] = i;
 40 
 41   for(i = sz-1; i >= 0; --i)
 42     {
 43       /* Select a replacement index */
 44       int x = (my_rand() * i) >> 16;
 45 
 46       /* Swap */
 47       int tmp = idx[i];
 48       idx[i] = idx[x];
 49       idx[x] = tmp;
 50 
 51       /* Load resulting slice */
 52       load(fh, ptr+32*idx[i], 32);
 53     }
 54 }
 55 
 56 void load_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
 57 {
 58   unsigned long chunksz;
 59 
 60   my_srand(filesz);
 61 
 62   /* Descramble 2 meg blocks for as long as possible, then
 63      gradually reduce the window down to 32 bytes (1 slice) */
 64   for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
 65     while(filesz >= chunksz)
 66       {
 67 	load_chunk(fh, ptr, chunksz);
 68 	filesz -= chunksz;
 69 	ptr += chunksz;
 70       }
 71 
 72   /* Load final incomplete slice */
 73   if(filesz)
 74     load(fh, ptr, filesz);
 75 }
 76 
 77 void read_file(char *filename, unsigned char **ptr, unsigned long *sz)
 78 {
 79   FILE *fh = fopen(filename, "rb");
 80   if(fh == NULL)
 81     {
 82       fprintf(stderr, "Can't open \"%s\".\n", filename);
 83       exit(1);
 84     }
 85   if(fseek(fh, 0, SEEK_END)<0)
 86     {
 87       fprintf(stderr, "Seek error.\n");
 88       exit(1);
 89     }
 90   *sz = ftell(fh);
 91   *ptr = malloc(*sz);
 92   if( *ptr == NULL )
 93     {
 94       fprintf(stderr, "Out of memory.\n");
 95       exit(1);
 96     }
 97   if(fseek(fh, 0, SEEK_SET)<0)
 98     {
 99       fprintf(stderr, "Seek error.\n");
100       exit(1);
101     }
102   load_file(fh, *ptr, *sz);
103   fclose(fh);
104 }
105 
106 void save(FILE *fh, unsigned char *ptr, unsigned long sz)
107 {
108   if(fwrite(ptr, 1, sz, fh) != sz)
109     {
110       fprintf(stderr, "Write error!\n");
111       exit(1);
112     }
113 }
114 
115 void save_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
116 {
117   static int idx[MAXCHUNK/32];
118   int i;
119 
120   /* Convert chunk size to number of slices */
121   sz /= 32;
122 
123   /* Initialize index table with unity,
124      so that each slice gets saved exactly once */
125   for(i = 0; i < sz; i++)
126     idx[i] = i;
127 
128   for(i = sz-1; i >= 0; --i)
129     {
130       /* Select a replacement index */
131       int x = (my_rand() * i) >> 16;
132 
133       /* Swap */
134       int tmp = idx[i];
135       idx[i] = idx[x];
136       idx[x] = tmp;
137 
138       /* Save resulting slice */
139       save(fh, ptr+32*idx[i], 32);
140     }
141 }
142 
143 void save_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
144 {
145   unsigned long chunksz;
146 
147   my_srand(filesz);
148 
149   /* Descramble 2 meg blocks for as long as possible, then
150      gradually reduce the window down to 32 bytes (1 slice) */
151   for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
152     while(filesz >= chunksz)
153       {
154 	save_chunk(fh, ptr, chunksz);
155 	filesz -= chunksz;
156 	ptr += chunksz;
157       }
158 
159   /* Save final incomplete slice */
160   if(filesz)
161     save(fh, ptr, filesz);
162 }
163 
164 void write_file(char *filename, unsigned char *ptr, unsigned long sz)
165 {
166   FILE *fh = fopen(filename, "wb");
167   if(fh == NULL)
168     {
169       fprintf(stderr, "Can't open \"%s\".\n", filename);
170       exit(1);
171     }
172   save_file(fh, ptr, sz);
173   fclose(fh);
174 }
175 
176 void descramble(char *src, char *dst)
177 {
178   unsigned char *ptr = NULL;
179   unsigned long sz = 0;
180   FILE *fh;
181 
182   read_file(src, &ptr, &sz);
183 
184   fh = fopen(dst, "wb");
185   if(fh == NULL)
186     {
187       fprintf(stderr, "Can't open \"%s\".\n", dst);
188       exit(1);
189     }
190   if( fwrite(ptr, 1, sz, fh) != sz )
191     {
192       fprintf(stderr, "Write error.\n");
193       exit(1);
194     }
195   fclose(fh);
196   free(ptr);
197 }
198 
199 void scramble(char *src, char *dst)
200 {
201   unsigned char *ptr = NULL;
202   unsigned long sz = 0;
203   FILE *fh;
204 
205   fh = fopen(src, "rb");
206   if(fh == NULL)
207     {
208       fprintf(stderr, "Can't open \"%s\".\n", src);
209       exit(1);
210     }
211   if(fseek(fh, 0, SEEK_END)<0)
212     {
213       fprintf(stderr, "Seek error.\n");
214       exit(1);
215     }
216   sz = ftell(fh);
217   ptr = malloc(sz);
218   if( ptr == NULL )
219     {
220       fprintf(stderr, "Out of memory.\n");
221       exit(1);
222     }
223   if(fseek(fh, 0, SEEK_SET)<0)
224     {
225       fprintf(stderr, "Seek error.\n");
226       exit(1);
227     }
228   if( fread(ptr, 1, sz, fh) != sz )
229     {
230       fprintf(stderr, "Read error.\n");
231       exit(1);
232     }
233   fclose(fh);
234 
235   write_file(dst, ptr, sz);
236 
237   free(ptr);
238 }
239 
240 int main(int argc, char *argv[])
241 {
242   int opt = 0;
243 
244   if(argc > 1 && !strcmp(argv[1], "-d"))
245     opt ++;
246 
247   if(argc != 3+opt)
248     {
249       fprintf(stderr, "Usage: %s [-d] from to\n", argv[0]);
250       exit(1);
251     }
252   
253   if(opt)
254     descramble(argv[2], argv[3]);
255   else
256     scramble(argv[1], argv[2]);
257 
258   return 0;
259 }