OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
macserial.c
Go to the documentation of this file.
1//
2// Decode mac serial number
3//
4// Copyright (c) 2018-2020 vit9696
5// Copyright (c) 2020 Matis Schotte
6//
7
8#include <limits.h>
9#include <stdbool.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <stdint.h>
13#include <string.h>
14#include <time.h>
15
16#ifdef __APPLE__
17#include <CoreFoundation/CoreFoundation.h>
18#include <IOKit/IOKitLib.h>
19#endif
20
21#include <UserPseudoRandom.h>
22
23#include "macserial.h"
24#include "modelinfo.h"
25
26#ifdef __APPLE__
27static CFTypeRef get_ioreg_entry(const char *path, CFStringRef name, CFTypeID type) {
28 CFTypeRef value = NULL;
29 io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
30 if (entry) {
31 value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0);
32 if (value) {
33 if (CFGetTypeID(value) != type) {
34 CFRelease(value);
35 value = NULL;
36 printf("%s in %s has wrong type!\n", CFStringGetCStringPtr(name, kCFStringEncodingMacRoman), path);
37 }
38 } else {
39 printf("Failed to find to %s in %s!\n", CFStringGetCStringPtr(name, kCFStringEncodingMacRoman), path);
40 }
41 IOObjectRelease(entry);
42 } else {
43 printf("Failed to connect to %s!\n", path);
44 }
45 return value;
46}
47#endif
48
49// Apple uses various conversion tables (e.g. AppleBase34) for value encoding.
50static int32_t alpha_to_value(char c, int32_t *conv, const char *blacklist) {
51 if (c < 'A' || c > 'Z')
52 return -1;
53
54 while (blacklist && *blacklist != '\0')
55 if (*blacklist++ == c)
56 return -1;
57
58 return conv[c - 'A'];
59}
60
61// This is modified base34 used by Apple with I and O excluded.
62static int32_t base34_to_value(char c, int32_t mul) {
63 if (c >= '0' && c <= '9')
64 return (c - '0') * mul;
65 if (c >= 'A' && c <= 'Z') {
66 int32_t tmp = alpha_to_value(c, AppleTblBase34, AppleBase34Blacklist);
67 if (tmp >= 0)
68 return tmp * mul;
69 }
70 return -1;
71}
72
73static int32_t line_to_rmin(int32_t line) {
74 // info->line[0] is raw decoded copy, but it is not the real first produced unit.
75 // To get the real copy we need to find the minimal allowed raw decoded copy,
76 // which allows to obtain info->decodedLine.
77 int rmin = 0;
78 if (line > SERIAL_LINE_REPR_MAX)
79 rmin = (line - SERIAL_LINE_REPR_MAX + 67) / 68;
80 return rmin;
81}
82
83// This one is modded to implement CCC algo for better generation.
84// Changed base36 to base34, since that's what Apple uses.
85// The algo is trash but is left for historical reasons.
86static bool get_ascii7(uint32_t value, char *dst, size_t sz) {
87 // This is CCC conversion.
88 if (value < 1000000)
89 return false;
90
91 while (value > 10000000)
92 value /= 10;
93
94 // log(2**64) / log(34) = 12.57 => max 13 char + '\0'
95 char buffer[14];
96 size_t offset = sizeof(buffer);
97
98 buffer[--offset] = '\0';
99 do {
100 buffer[--offset] = AppleBase34Reverse[value % 34];
101 } while (value /= 34);
102
103 strncpy(dst, &buffer[offset], sz-1);
104 dst[sz-1] = 0;
105
106 return true;
107}
108
109static bool verify_mlb_checksum(const char *mlb, size_t len) {
110 const char alphabet[] = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
111 size_t checksum = 0;
112 for (size_t i = 0; i < len; ++i) {
113 for (size_t j = 0; j <= sizeof (alphabet); ++j) {
114 if (j == sizeof (alphabet))
115 return false;
116 if (mlb[i] == alphabet[j]) {
117 checksum += (((i & 1) == (len & 1)) * 2 + 1) * j;
118 break;
119 }
120 }
121 }
122 return checksum % (sizeof(alphabet) - 1) == 0;
123}
124
125static int32_t get_current_model(void) {
126#ifdef __APPLE__
127 CFDataRef model = get_ioreg_entry("IODeviceTree:/", CFSTR("model"), CFDataGetTypeID());
128 if (model) {
129 const char *cptr = (const char *)CFDataGetBytePtr(model);
130 size_t len = (size_t)CFDataGetLength(model);
131 int32_t i;
132 for (i = 0; i < APPLE_MODEL_MAX; i++) {
133 if (!strncmp(ApplePlatformData[i].productName, cptr, len))
134 break;
135 }
136 CFRelease(model);
137 if (i < APPLE_MODEL_MAX)
138 return i;
139 }
140#endif
141 return -1;
142}
143
144static uint32_t get_production_year(AppleModel model, bool print) {
145 uint32_t *years = &AppleModelYear[model][0];
146 uint32_t num = 0;
147
148 for (num = 0; num < APPLE_MODEL_YEAR_MAX && years[num]; num++) {
149 if (print) {
150 if (num+1 != APPLE_MODEL_YEAR_MAX && years[num+1])
151 printf("%d, ", years[num]);
152 else
153 printf("%d\n", years[num]);
154 }
155 }
156
157 if (ApplePreferredModelYear[model] > 0)
158 return ApplePreferredModelYear[model];
159
160 return years[pseudo_random() % num];
161}
162
163static const char *get_model_code(AppleModel model, bool print) {
164 const char **codes = &AppleModelCode[model][0];
165
166 if (print) {
167 for (uint32_t i = 0; i < APPLE_MODEL_CODE_MAX && codes[i]; i++)
168 if (i+1 != APPLE_MODEL_CODE_MAX && codes[i+1])
169 printf("%s, ", codes[i]);
170 else
171 printf("%s\n", codes[i]);
172 }
173
174 // Always choose the first model for stability by default.
175 return codes[0];
176}
177
178static const char *get_board_code(AppleModel model, bool print) {
179 const char **codes = &AppleBoardCode[model][0];
180
181 if (print) {
182 for (uint32_t i = 0; i < APPLE_BOARD_CODE_MAX && codes[i]; i++)
183 if (i+1 != APPLE_BOARD_CODE_MAX && codes[i+1])
184 printf("%s, ", codes[i]);
185 else
186 printf("%s\n", codes[i]);
187 }
188
189 // Always choose the first model for stability by default.
190 return codes[0];
191}
192
193static bool get_serial_info(const char *serial, SERIALINFO *info, bool print) {
194 if (!info)
195 return false;
196
197 memset(info, 0, sizeof(SERIALINFO));
198
199 // Verify length.
200 size_t serial_len = strlen(serial);
201 if (serial_len != SERIAL_OLD_LEN && serial_len != SERIAL_NEW_LEN) {
202 printf("ERROR: Invalid serial length, must be %d or %d\n", SERIAL_NEW_LEN, SERIAL_OLD_LEN);
203 return false;
204 }
205
206 // Assume every serial valid by default.
207 info->valid = true;
208
209 // Verify alphabet (base34 with I and O exclued).
210 for (size_t i = 0; i < serial_len; i++) {
211 if (!((serial[i] >= 'A' && serial[i] <= 'Z' && serial[i] != 'O' && serial[i] != 'I') ||
212 (serial[i] >= '0' && serial[i] <= '9'))) {
213 printf("WARN: Invalid symbol '%c' in serial!\n", serial[i]);
214 info->valid = false;
215 }
216 }
217
218 size_t model_len = 0;
219
220 // Start with looking up the model.
221 info->modelIndex = -1;
222 for (uint32_t i = 0; i < ARRAY_SIZE(AppleModelCode); i++) {
223 for (uint32_t j = 0; j < APPLE_MODEL_CODE_MAX; j++) {
224 const char *code = AppleModelCode[i][j];
225 if (!code)
226 break;
227 model_len = strlen(code);
228 if (model_len == 0)
229 break;
230 assert(model_len == MODEL_CODE_OLD_LEN || model_len == MODEL_CODE_NEW_LEN);
231 if (((serial_len == SERIAL_OLD_LEN && model_len == MODEL_CODE_OLD_LEN)
232 || (serial_len == SERIAL_NEW_LEN && model_len == MODEL_CODE_NEW_LEN))
233 && !strncmp(serial + serial_len - model_len, code, model_len)) {
234 strncpy(info->model, code, sizeof(info->model));
235 info->model[sizeof(info->model)-1] = '\0';
236 info->modelIndex = (int32_t)i;
237 break;
238 }
239 }
240 }
241
242 // Also lookup apple model.
243 for (uint32_t i = 0; i < ARRAY_SIZE(AppleModelDesc); i++) {
244 const char *code = AppleModelDesc[i].code;
245 model_len = strlen(code);
246 assert(model_len == MODEL_CODE_OLD_LEN || model_len == MODEL_CODE_NEW_LEN);
247 if (((serial_len == SERIAL_OLD_LEN && model_len == MODEL_CODE_OLD_LEN)
248 || (serial_len == SERIAL_NEW_LEN && model_len == MODEL_CODE_NEW_LEN))
249 && !strncmp(serial + serial_len - model_len, code, model_len)) {
250 info->appleModel = AppleModelDesc[i].name;
251 break;
252 }
253 }
254
255 // Fallback to possibly valid values if model is unknown.
256 if (info->modelIndex == -1) {
257 if (serial_len == SERIAL_NEW_LEN)
258 model_len = MODEL_CODE_NEW_LEN;
259 else
260 model_len = MODEL_CODE_OLD_LEN;
261 strncpy(info->model, serial + serial_len - model_len, model_len);
262 info->model[model_len] = '\0';
263 }
264
265 // Lookup production location
266 info->legacyCountryIdx = -1;
267 info->modernCountryIdx = -1;
268
269 if (serial_len == SERIAL_NEW_LEN) {
270 strncpy(info->country, serial, COUNTRY_NEW_LEN);
271 info->country[COUNTRY_NEW_LEN] = '\0';
272 serial += COUNTRY_NEW_LEN;
273 for (size_t i = 0; i < ARRAY_SIZE(AppleLocations); i++) {
274 if (!strcmp(info->country, AppleLocations[i])) {
275 info->modernCountryIdx = (int32_t)i;
276 break;
277 }
278 }
279 } else {
280 strncpy(info->country, serial, COUNTRY_OLD_LEN);
281 info->country[COUNTRY_OLD_LEN] = '\0';
282 serial += COUNTRY_OLD_LEN;
283 for (size_t i = 0; i < ARRAY_SIZE(AppleLegacyLocations); i++) {
284 if (!strcmp(info->country, AppleLegacyLocations[i])) {
285 info->legacyCountryIdx = (int32_t)i;
286 break;
287 }
288 }
289 }
290
291 // Decode production year and week
292 if (serial_len == SERIAL_NEW_LEN) {
293 // These are not exactly year and week, lower year bit is used for week encoding.
294 info->year[0] = *serial++;
295 info->week[0] = *serial++;
296
297 // New encoding started in 2010.
298 info->decodedYear = alpha_to_value(info->year[0], AppleTblYear, AppleYearBlacklist);
299 // Since year can be encoded ambiguously, check the model code for 2010/2020 difference.
300 // Old check relies on first letter of model to be greater than or equal to H, which breaks compatibility with iMac20,2 (=0).
301 // Added logic checks provided model years `AppleModelYear` first year greater than or equal to 2020.
302 if ((info->modelIndex >= 0 && AppleModelYear[info->modelIndex][0] >= 2017 && info->decodedYear < 7)
303 || (info->decodedYear == 0 && info->model[0] >= 'H')) {
304 info->decodedYear += 2020;
305 } else if (info->decodedYear >= 0) {
306 info->decodedYear += 2010;
307 } else {
308 printf("WARN: Invalid year symbol '%c'!\n", info->year[0]);
309 info->valid = false;
310 }
311
312 if (info->week[0] > '0' && info->week[0] <= '9')
313 info->decodedWeek = info->week[0] - '0';
314 else
315 info->decodedWeek = alpha_to_value(info->week[0], AppleTblWeek, AppleWeekBlacklist);
316 if (info->decodedWeek > 0) {
317 if (info->decodedYear > 0)
318 info->decodedWeek += alpha_to_value(info->year[0], AppleTblWeekAdd, NULL);
319 } else {
320 printf("WARN: Invalid week symbol '%c'!\n", info->week[0]);
321 info->valid = false;
322 }
323 } else {
324 info->year[0] = *serial++;
325 info->week[0] = *serial++;
326 info->week[1] = *serial++;
327
328 // This is proven by MacPro5,1 valid serials from 2011 and 2012.
329 if (info->year[0] >= '0' && info->year[0] <= '2') {
330 info->decodedYear = 2010 + info->year[0] - '0';
331 } else if (info->year[0] >= '3' && info->year[0] <= '9') {
332 info->decodedYear = 2000 + info->year[0] - '0';
333 } else {
334 info->decodedYear = -1;
335 printf("WARN: Invalid year symbol '%c'!\n", info->year[0]);
336 info->valid = false;
337 }
338
339 for (int32_t i = 0; i < 2; i++) {
340 if (info->week[i] >= '0' && info->week[i] <= '9') {
341 info->decodedWeek += (i == 0 ? 10 : 1) * (info->week[i] - '0');
342 } else {
343 info->decodedWeek = -1;
344 printf("WARN: Invalid week symbol '%c'!\n", info->week[i]);
345 info->valid = false;
346 break;
347 }
348 }
349 }
350
352 printf("WARN: Decoded week %d is out of valid range [%d, %d]!\n", info->decodedWeek, SERIAL_WEEK_MIN, SERIAL_WEEK_MAX);
353 info->decodedWeek = -1;
354 }
355
356 if (info->decodedYear > 0 && info->modelIndex >= 0) {
357 bool found = false;
358 for (size_t i = 0; !found && i < APPLE_MODEL_YEAR_MAX && AppleModelYear[info->modelIndex][i]; i++)
359 if ((int32_t)AppleModelYear[info->modelIndex][i] == info->decodedYear)
360 found = true;
361 if (!found) {
362 printf("WARN: Invalid year %d for model %s\n", info->decodedYear, ApplePlatformData[info->modelIndex].productName);
363 info->valid = false;
364 }
365 }
366
367 // Decode production line and copy
368 int32_t mul[] = {68, 34, 1};
369 for (uint32_t i = 0; i < ARRAY_SIZE(mul); i++) {
370 info->line[i] = *serial++;
371 int32_t tmp = base34_to_value(info->line[i], mul[i]);
372 if (tmp >= 0) {
373 info->decodedLine += tmp;
374 } else {
375 printf("WARN: Invalid line symbol '%c'!\n", info->line[i]);
376 info->valid = false;
377 break;
378 }
379 }
380
381 if (info->decodedLine >= 0)
382 info->decodedCopy = base34_to_value(info->line[0], 1) - line_to_rmin(info->decodedLine);
383
384 if (print) {
385 printf("%14s: %4s - ", "Country", info->country);
386 if (info->legacyCountryIdx >= 0)
387 printf("%s\n", AppleLegacyLocationNames[info->legacyCountryIdx]);
388 else if (info->modernCountryIdx >= 0)
389 printf("%s\n", AppleLocationNames[info->modernCountryIdx]);
390 else
391 puts("Unknown, please report!");
392 printf("%14s: %4s - %d\n", "Year", info->year, info->decodedYear);
393 printf("%14s: %4s - %d", "Week", info->week, info->decodedWeek);
394 if (info->decodedYear > 0 && info->decodedWeek > 0) {
395 struct tm startd = {
396 .tm_isdst = -1,
397 .tm_year = info->decodedYear - 1900,
398 .tm_mday = 1 + 7 * (info->decodedWeek-1),
399 .tm_mon = 0
400 };
401 if (mktime(&startd) >= 0) {
402 printf(" (%02d.%02d.%04d", startd.tm_mday, startd.tm_mon+1, startd.tm_year+1900);
403 if (info->decodedWeek == 53 && startd.tm_mday != 31) {
404 printf("-31.12.%04d", startd.tm_year+1900);
405 } else if (info->decodedWeek < 53) {
406 startd.tm_mday += 6;
407 if (mktime(&startd))
408 printf("-%02d.%02d.%04d", startd.tm_mday, startd.tm_mon+1, startd.tm_year+1900);
409 }
410 puts(")");
411 }
412 } else {
413 puts("");
414 }
415 printf("%14s: %4s - %d (copy %d)\n", "Line", info->line, info->decodedLine,
416 info->decodedCopy >= 0 ? info->decodedCopy + 1 : -1);
417 printf("%14s: %4s - %s\n", "Model", info->model, info->modelIndex >= 0 ?
418 ApplePlatformData[info->modelIndex].productName : "Unknown");
419 printf("%14s: %s\n", "SystemModel", info->appleModel != NULL ? info->appleModel : "Unknown, please report!");
420 printf("%14s: %s\n", "Valid", info->valid ? "Possibly" : "Unlikely");
421 }
422
423 return true;
424}
425
426static bool get_serial(SERIALINFO *info) {
427 if (info->modelIndex < 0 && info->model[0] == '\0') {
428 printf("ERROR: Unable to determine model!\n");
429 return false;
430 }
431
432 if (info->model[0] == '\0') {
433 strncpy(info->model, get_model_code((AppleModel)info->modelIndex, false), MODEL_CODE_NEW_LEN);
434 info->model[MODEL_CODE_NEW_LEN] = '\0';
435 }
436
437 size_t country_len = strlen(info->country);
438 if (country_len == 0) {
439 // Random country choice strongly decreases key verification probability.
440 country_len = strlen(info->model) == MODEL_CODE_NEW_LEN ? COUNTRY_NEW_LEN : COUNTRY_OLD_LEN;
441 if (info->modelIndex < 0) {
442 strncpy(info->country, country_len == COUNTRY_OLD_LEN ? AppleLegacyLocations[0] : AppleLocations[0], COUNTRY_NEW_LEN+1);
443 } else {
444 strncpy(info->country, &ApplePlatformData[info->modelIndex].serialNumber[0], country_len);
445 info->country[country_len] = '\0';
446 }
447 }
448
449 if (info->decodedYear < 0) {
450 if (info->modelIndex < 0)
452 else
453 info->decodedYear = (int32_t)get_production_year((AppleModel)info->modelIndex, false);
454 }
455
456 // Last week is too rare to care
457 if (info->decodedWeek < 0)
459
460 if (country_len == COUNTRY_OLD_LEN) {
462 printf("ERROR: Year %d is out of valid legacy range [%d, %d]!\n", info->decodedYear, SERIAL_YEAR_OLD_MIN, SERIAL_YEAR_OLD_MAX);
463 return false;
464 }
465
466 info->year[0] = '0' + (char)((info->decodedYear - 2000) % 10);
467 info->week[0] = '0' + (char)((info->decodedWeek) / 10);
468 info->week[1] = '0' + (info->decodedWeek) % 10;
469 } else {
471 printf("ERROR: Year %d is out of valid modern range [%d, %d]!\n", info->decodedYear, SERIAL_YEAR_NEW_MIN, SERIAL_YEAR_NEW_MAX);
472 return false;
473 }
474
475 size_t base_new_year = 2010;
476 if (info->decodedYear >= SERIAL_YEAR_NEW_MID) {
477 base_new_year = 2020;
478 }
479
480 info->year[0] = AppleYearReverse[(info->decodedYear - base_new_year) * 2 + (info->decodedWeek >= 27)];
481 info->week[0] = AppleWeekReverse[info->decodedWeek];
482 }
483
484 if (info->decodedLine < 0)
486
487 int32_t rmin = line_to_rmin(info->decodedLine);
488
489 // Verify and apply user supplied copy if any
490 if (info->decodedCopy >= 0) {
491 rmin += info->decodedCopy - 1;
492 if (rmin * 68 > info->decodedLine) {
493 printf("ERROR: Copy %d cannot represent line %d!\n", info->decodedCopy, info->decodedLine);
494 return false;
495 }
496 }
497
498 info->line[0] = AppleBase34Reverse[rmin];
499 info->line[1] = AppleBase34Reverse[(info->decodedLine - rmin * 68) / 34];
500 info->line[2] = AppleBase34Reverse[(info->decodedLine - rmin * 68) % 34];
501
502 return true;
503}
504
505static void get_mlb(SERIALINFO *info, char *dst, size_t sz) {
506 // This is a direct reverse from CCC, rework it later...
507 if (info->modelIndex < 0) {
508 printf("WARN: Unknown model, assuming default!\n");
509 info->modelIndex = APPLE_MODEL_MAX - 1;
510 }
511 do {
512 uint32_t year = 0, week = 0;
513
514 bool legacy = strlen(info->country) == COUNTRY_OLD_LEN;
515
516 if (legacy) {
517 year = (uint32_t)(info->year[0] - '0');
518 week = (uint32_t)(info->week[0] - '0') * 10 + (uint32_t)(info->week[1] - '0');
519 } else {
520 char syear = info->year[0];
521 char sweek = info->week[0];
522
523 const char srcyear[] = "CDFGHJKLMNPQRSTVWXYZ";
524 const char dstyear[] = "00112233445566778899";
525 for (size_t i = 0; i < ARRAY_SIZE(srcyear)-1; i++) {
526 if (syear == srcyear[i]) {
527 year = (uint32_t)(dstyear[i] - '0');
528 break;
529 }
530 }
531
532 const char overrides[] = "DGJLNQSVXZ";
533 for (size_t i = 0; i < ARRAY_SIZE(overrides)-1; i++) {
534 if (syear == overrides[i]) {
535 week = 27;
536 break;
537 }
538 }
539
540 const char srcweek[] = "123456789CDFGHJKLMNPQRSTVWXYZ";
541 for (size_t i = 0; i < ARRAY_SIZE(srcweek)-1; i++) {
542 if (sweek == srcweek[i]) {
543 week += i + 1;
544 break;
545 }
546 }
547
548 // This is silently not handled, and it should not be needed for normal serials.
549 // Bugged MacBookPro6,2 and MacBookPro7,1 will gladly hit it.
550 if (week < SERIAL_WEEK_MIN) {
551 snprintf(dst, sz, "FAIL-ZERO-%c", sweek);
552 return;
553 }
554 }
555
556 week--;
557
558 if (week <= 9) {
559 if (week == 0) {
560 week = SERIAL_WEEK_MAX;
561 if (year == 0)
562 year = 9;
563 else
564 year--;
565 }
566 }
567
568 if (legacy) {
569 char code[4] = {0};
570 // The loop is not present in CCC, but it throws an exception here,
571 // and effectively generates nothing. The logic is crazy :/.
572 // Also, it was likely meant to be written as pseudo_random() % 0x8000.
573 while (!get_ascii7(pseudo_random_between(0, 0x7FFE) * 0x73BA1C, code, sizeof(code)));
574 const char *board = get_board_code(info->modelIndex, false);
575 char suffix = AppleBase34Reverse[pseudo_random() % 34];
576 snprintf(dst, sz, "%s%d%02d0%s%s%c", info->country, year, week, code, board, suffix);
577 } else {
578 const char *part1 = MLBBlock1[pseudo_random() % ARRAY_SIZE(MLBBlock1)];
579 const char *part2 = MLBBlock2[pseudo_random() % ARRAY_SIZE(MLBBlock2)];
580 const char *board = get_board_code(info->modelIndex, false);
581 const char *part3 = MLBBlock3[pseudo_random() % ARRAY_SIZE(MLBBlock3)];
582
583 snprintf(dst, sz, "%s%d%02d%s%s%s%s", info->country, year, week, part1, part2, board, part3);
584 }
585 } while (!verify_mlb_checksum(dst, strlen(dst)));
586}
587
588static void get_system_info(void) {
589#ifdef __APPLE__
590 CFDataRef model = get_ioreg_entry("IODeviceTree:/", CFSTR("model"), CFDataGetTypeID());
591 CFDataRef board = get_ioreg_entry("IODeviceTree:/", CFSTR("board-id"), CFDataGetTypeID());
592 CFDataRef efiver = get_ioreg_entry("IODeviceTree:/rom", CFSTR("version"), CFDataGetTypeID());
593 CFStringRef serial = get_ioreg_entry("IODeviceTree:/", CFSTR("IOPlatformSerialNumber"), CFStringGetTypeID());
594 CFStringRef hwuuid = get_ioreg_entry("IODeviceTree:/", CFSTR("IOPlatformUUID"), CFStringGetTypeID());
595 CFDataRef smuuid = get_ioreg_entry("IODeviceTree:/efi/platform", CFSTR("system-id"), CFDataGetTypeID());
596 CFDataRef rom = get_ioreg_entry("IODeviceTree:/options", CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:ROM"), CFDataGetTypeID());
597 CFDataRef mlb = get_ioreg_entry("IODeviceTree:/options", CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:MLB"), CFDataGetTypeID());
598
599 CFDataRef pwr[5] = {0};
600 CFStringRef pwrname[5] = {
601 CFSTR("Gq3489ugfi"),
602 CFSTR("Fyp98tpgj"),
603 CFSTR("kbjfrfpoJU"),
604 CFSTR("oycqAZloTNDm"),
605 CFSTR("abKPld1EcMni"),
606 };
607
608 for (size_t i = 0; i < ARRAY_SIZE(pwr); i++)
609 pwr[i] = get_ioreg_entry("IOPower:/", pwrname[i], CFDataGetTypeID());
610
611 if (model) {
612 printf("%14s: %.*s\n", "Model", (int)CFDataGetLength(model), CFDataGetBytePtr(model));
613 CFRelease(model);
614 }
615
616 if (board) {
617 printf("%14s: %.*s\n", "Board ID", (int)CFDataGetLength(board), CFDataGetBytePtr(board));
618 CFRelease(board);
619 }
620
621 if (efiver) {
622 printf("%14s: %.*s\n", "FW Version", (int)CFDataGetLength(efiver), CFDataGetBytePtr(efiver));
623 CFRelease(efiver);
624 }
625
626 if (hwuuid) {
627 printf("%14s: %s\n", "Hardware UUID", CFStringGetCStringPtr(hwuuid, kCFStringEncodingMacRoman));
628 CFRelease(hwuuid);
629 }
630
631 puts("");
632
633 if (serial) {
634 const char *cstr = CFStringGetCStringPtr(serial, kCFStringEncodingMacRoman);
635 printf("%14s: %s\n", "Serial Number", cstr);
636 SERIALINFO info;
637 get_serial_info(cstr, &info, true);
638 CFRelease(serial);
639 puts("");
640 }
641
642 if (smuuid) {
643 if (CFDataGetLength(smuuid) == SZUUID) {
644 const uint8_t *p = CFDataGetBytePtr(smuuid);
645 printf("%14s: " PRIUUID "\n", "System ID", CASTUUID(p));
646 }
647 CFRelease(smuuid);
648 }
649
650 if (rom) {
651 if (CFDataGetLength(rom) == 6) {
652 const uint8_t *p = CFDataGetBytePtr(rom);
653 printf("%14s: %02X%02X%02X%02X%02X%02X\n", "ROM", p[0], p[1], p[2], p[3], p[4], p[5]);
654 }
655 CFRelease(rom);
656 }
657
658 if (mlb) {
659 printf("%14s: %.*s\n", "MLB", (int)CFDataGetLength(mlb), CFDataGetBytePtr(mlb));
660 if (!verify_mlb_checksum((const char *)CFDataGetBytePtr(mlb), CFDataGetLength(mlb)))
661 printf("WARN: Invalid MLB checksum!\n");
662 CFRelease(mlb);
663 }
664
665 puts("");
666
667 for (size_t i = 0; i < ARRAY_SIZE(pwr); i++) {
668 if (pwr[i]) {
669 printf("%14s: ", CFStringGetCStringPtr(pwrname[i], kCFStringEncodingMacRoman));
670 const uint8_t *p = CFDataGetBytePtr(pwr[i]);
671 CFIndex sz = CFDataGetLength(pwr[i]);
672 for (CFIndex j = 0; j < sz; j++)
673 printf("%02X", p[j]);
674 puts("");
675 CFRelease(pwr[i]);
676 }
677 }
678
679 puts("");
680#endif
681
682 printf("Version %s. Use -h argument to see usage options.\n", PROGRAM_VERSION);
683}
684
685static int usage(const char *app) {
686 printf(
687 "%s arguments:\n"
688 " --help (-h) show this help\n"
689 " --version (-v) show program version\n"
690 " --deriv <serial> (-d) generate all derivative serials\n"
691 " --generate (-g) generate serial for current model\n"
692 " --generate-all (-a) generate serial for all models\n"
693 " --info <serial> (-i) decode serial information\n"
694 " --verify <mlb> verify MLB checksum\n"
695 " --list (-l) list known mac models\n"
696 " --list-products (-lp) list known product codes\n"
697 " --mlb <serial> generate MLB based on serial\n"
698 " --sys (-s) get system info\n\n"
699 "Tuning options:\n"
700 " --model <model> (-m) mac model used for generation\n"
701 " --num <num> (-n) number of generated pairs\n"
702 " --year <year> (-y) year used for generation\n"
703 " --week <week> (-w) week used for generation\n"
704 " --country <loc> (-c) country location used for generation\n"
705 " --copy <copy> (-o) production copy index\n"
706 " --line <line> (-e) production line\n"
707 " --platform <ppp> (-p) platform code used for generation\n\n", app);
708
709 return EXIT_FAILURE;
710}
711
712int main(int argc, char *argv[]) {
714 const char *passed_serial = NULL;
715 SERIALINFO info = {
716 .modelIndex = -1,
717 .decodedYear = -1,
718 .decodedWeek = -1,
719 .decodedCopy = -1,
720 .decodedLine = -1
721 };
722 int32_t limit = 10;
723
724 for (int i = 1; i < argc; i++) {
725 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
726 usage(argv[0]);
727 return EXIT_SUCCESS;
728 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
729 printf("ugrobator %s\n", PROGRAM_VERSION);
730 return EXIT_SUCCESS;
731 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--generate")) {
733 } else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--generate-all")) {
734 mode = MODE_GENERATE_ALL;
735 } else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--info")) {
736 if (++i == argc) return usage(argv[0]);
737 mode = MODE_SERIAL_INFO;
738 passed_serial = argv[i];
739 } else if (!strcmp(argv[i], "--verify")) {
740 if (++i == argc) return usage(argv[0]);
741 mode = MODE_MLB_INFO;
742 passed_serial = argv[i];
743 } else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) {
744 mode = MODE_LIST_MODELS;
745 } else if (!strcmp(argv[i], "-lp") || !strcmp(argv[i], "--list-products")) {
746 mode = MODE_LIST_PRODUCTS;
747 } else if (!strcmp(argv[i], "-mlb") || !strcmp(argv[i], "--mlb")) {
748 // -mlb is supported due to legacy versions.
749 if (++i == argc) return usage(argv[0]);
750 mode = MODE_GENERATE_MLB;
751 passed_serial = argv[i];
752 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--deriv")) {
753 if (++i == argc) return usage(argv[0]);
755 passed_serial = argv[i];
756 } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--sys")) {
757 mode = MODE_SYSTEM_INFO;
758 } else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--model")) {
759 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
760 if (++i == argc) return usage(argv[0]);
761 if (argv[i][0] >= '0' && argv[i][0] <= '9') {
762 info.modelIndex = atoi(argv[i]);
763 } else {
764 for (int32_t j = 0; j < APPLE_MODEL_MAX; j++) {
765 if (!strcmp(argv[i], ApplePlatformData[j].productName)) {
766 info.modelIndex = j;
767 break;
768 }
769 }
770 }
771 if (info.modelIndex < 0 || info.modelIndex > APPLE_MODEL_MAX) {
772 printf("Model id (%d) or name (%s) is out of valid range [0, %d]!\n", info.modelIndex, argv[i], APPLE_MODEL_MAX-1);
773 return EXIT_FAILURE;
774 }
775 } else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--num")) {
776 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
777 if (++i == argc) return usage(argv[0]);
778 limit = atoi(argv[i]);
779 if (limit <= 0) {
780 printf("Cannot generate %d pairs!\n", limit);
781 return EXIT_FAILURE;
782 }
783 } else if (!strcmp(argv[i], "-y") || !strcmp(argv[i], "--year")) {
784 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
785 if (++i == argc) return usage(argv[0]);
786 info.decodedYear = atoi(argv[i]);
788 printf("Year %d is out of valid range [%d, %d]!\n", info.decodedYear, SERIAL_YEAR_MIN, SERIAL_YEAR_MAX);
789 return EXIT_FAILURE;
790 }
791 } else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--week")) {
792 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
793 if (++i == argc) return usage(argv[0]);
794 info.decodedWeek = atoi(argv[i]);
796 printf("Week %d is out of valid range [%d, %d]!\n", info.decodedWeek, SERIAL_WEEK_MIN, SERIAL_WEEK_MAX);
797 return EXIT_FAILURE;
798 }
799 } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--country")) {
800 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
801 if (++i == argc) return usage(argv[0]);
802 size_t len = strlen(argv[i]);
803 if (len != COUNTRY_OLD_LEN && len != COUNTRY_NEW_LEN) {
804 printf("Country location %s is neither %d nor %d symbols long!\n", argv[i], COUNTRY_OLD_LEN, COUNTRY_NEW_LEN);
805 return EXIT_FAILURE;
806 }
807 strncpy(info.country, argv[i], COUNTRY_NEW_LEN+1);
808 } else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--platform")) {
809 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
810 if (++i == argc) return usage(argv[0]);
811 size_t len = strlen(argv[i]);
812 if (len != MODEL_CODE_OLD_LEN && len != MODEL_CODE_NEW_LEN) {
813 printf("Platform code %s is neither %d nor %d symbols long!\n", argv[i], MODEL_CODE_OLD_LEN, MODEL_CODE_NEW_LEN);
814 return EXIT_FAILURE;
815 }
816 strncpy(info.model, argv[i], MODEL_CODE_NEW_LEN+1);
817 } else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--copy")) {
818 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
819 if (++i == argc) return usage(argv[0]);
820 info.decodedCopy = atoi(argv[i]);
822 printf("Copy %d is out of valid range [%d, %d]!\n", info.decodedCopy, SERIAL_COPY_MIN, SERIAL_COPY_MAX);
823 return EXIT_FAILURE;
824 }
825 } else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--line")) {
826 if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
827 if (++i == argc) return usage(argv[0]);
828 info.decodedLine = atoi(argv[i]);
830 printf("Line %d is out of valid range [%d, %d]!\n", info.decodedLine, SERIAL_LINE_MIN, SERIAL_LINE_MAX);
831 return EXIT_FAILURE;
832 }
833 }
834 }
835
836 if (mode == MODE_SYSTEM_INFO) {
837 get_system_info();
838 } else if (mode == MODE_SERIAL_INFO) {
839 get_serial_info(passed_serial, &info, true);
840 } else if (mode == MODE_MLB_INFO) {
841 size_t len = strlen(passed_serial);
842 if (len == 13 || len == 17) {
843 printf("Valid MLB length: %s\n", len == 13 ? "legacy" : "modern");
844 } else {
845 printf("WARN: Invalid MLB length: %u\n", (unsigned) len);
846 }
847 if (verify_mlb_checksum(passed_serial, strlen(passed_serial))) {
848 printf("Valid MLB checksum.\n");
849 } else {
850 printf("WARN: Invalid MLB checksum!\n");
851 }
852 } else if (mode == MODE_LIST_MODELS) {
853 printf("Available models:\n");
854 for (int32_t j = 0; j < APPLE_MODEL_MAX; j++) {
855 printf("%14s: %s\n", "Model", ApplePlatformData[j].productName);
856 printf("%14s: ", "Prod years");
857 get_production_year((AppleModel)j, true);
858 printf("%14s: %s\n", "Base Serial", ApplePlatformData[j].serialNumber);
859 printf("%14s: ", "Model codes");
860 get_model_code((AppleModel)j, true);
861 printf("%14s: ", "Board codes");
862 get_board_code((AppleModel)j, true);
863 puts("");
864 }
865 printf("Available legacy location codes:\n");
866 for (size_t j = 0; j < ARRAY_SIZE(AppleLegacyLocations); j++)
867 printf(" - %s, %s\n", AppleLegacyLocations[j], AppleLegacyLocationNames[j]);
868 printf("\nAvailable new location codes:\n");
869 for (size_t j = 0; j < ARRAY_SIZE(AppleLocations); j++)
870 printf(" - %s, %s\n", AppleLocations[j], AppleLocationNames[j]);
871 puts("");
872 } else if (mode == MODE_LIST_PRODUCTS) {
873 for (size_t j = 0; j < ARRAY_SIZE(AppleModelDesc); j++)
874 printf("%4s - %s\n", AppleModelDesc[j].code, AppleModelDesc[j].name);
875 } else if (mode == MODE_GENERATE_MLB) {
876 if (get_serial_info(passed_serial, &info, false)) {
877 char mlb[MLB_MAX_SIZE];
878 get_mlb(&info, mlb, MLB_MAX_SIZE);
879 printf("%s\n", mlb);
880 }
881 } else if (mode == MODE_GENERATE_CURRENT) {
882 if (info.modelIndex < 0)
883 info.modelIndex = get_current_model();
884 for (int32_t i = 0; i < limit; i++) {
885 SERIALINFO tmp = info;
886 if (get_serial(&tmp)) {
887 char mlb[MLB_MAX_SIZE];
888 get_mlb(&tmp, mlb, MLB_MAX_SIZE);
889 printf("%s%s%s%s%s | %s\n", tmp.country, tmp.year, tmp.week, tmp.line, tmp.model, mlb);
890 }
891 }
892 } else if (mode == MODE_GENERATE_ALL) {
893 for (int32_t i = 0; i < APPLE_MODEL_MAX; i++) {
894 info.modelIndex = i;
895 for (int32_t j = 0; j < limit; j++) {
896 SERIALINFO tmp = info;
897 if (get_serial(&tmp)) {
898 char mlb[MLB_MAX_SIZE];
899 get_mlb(&tmp, mlb, MLB_MAX_SIZE);
900 printf("%14s | %s%s%s%s%s | %s\n", ApplePlatformData[info.modelIndex].productName,
901 tmp.country, tmp.year, tmp.week, tmp.line, tmp.model, mlb);
902 }
903 }
904 }
905 } else if (mode == MODE_GENERATE_DERIVATIVES) {
906 if (get_serial_info(passed_serial, &info, false)) {
907 int rmin = line_to_rmin(info.decodedLine);
908 for (int32_t k = 0; k < 34; k++) {
909 int32_t start = k * 68;
910 if (info.decodedLine > start && info.decodedLine - start <= SERIAL_LINE_REPR_MAX) {
911 int32_t rem = info.decodedLine - start;
912 printf("%s%s%s%c%c%c%s - copy %d\n", info.country, info.year, info.week, AppleBase34Reverse[k],
913 AppleBase34Reverse[rem / 34], AppleBase34Reverse[rem % 34], info.model, k - rmin + 1);
914 }
915 }
916 } else {
917 return EXIT_FAILURE;
918 }
919 }
920}
#define ARRAY_SIZE(Array)
Definition AppleMacEfi.h:34
cache_type_t type
UINT8 value
#define size_t
Definition Ubsan.h:87
#define snprintf
Definition Ubsan.h:205
#define int32_t
Definition Ubsan.h:55
#define uint32_t
Definition Ubsan.h:59
uint32_t pseudo_random(void)
uint32_t pseudo_random_between(uint32_t from, uint32_t to)
int main()
Definition acdtinfo.c:181
#define memset(ptr, c, len)
UINT8 uint8_t
UINT32 uint32_t
#define assert
INT32 int32_t
Definition lzss.h:34
PROGRAMMODE
Definition macserial.h:80
@ MODE_LIST_MODELS
Definition macserial.h:84
@ MODE_SYSTEM_INFO
Definition macserial.h:81
@ MODE_LIST_PRODUCTS
Definition macserial.h:85
@ MODE_GENERATE_ALL
Definition macserial.h:88
@ MODE_MLB_INFO
Definition macserial.h:83
@ MODE_GENERATE_CURRENT
Definition macserial.h:87
@ MODE_SERIAL_INFO
Definition macserial.h:82
@ MODE_GENERATE_MLB
Definition macserial.h:86
@ MODE_GENERATE_DERIVATIVES
Definition macserial.h:89
#define SERIAL_LINE_REPR_MAX
Definition macserial.h:39
#define SERIAL_LINE_MIN
Definition macserial.h:38
#define SERIAL_YEAR_OLD_MAX
Definition macserial.h:29
#define SERIAL_OLD_LEN
Definition macserial.h:42
#define PROGRAM_VERSION
Definition macserial.h:14
#define MODEL_CODE_OLD_LEN
Definition macserial.h:45
#define SERIAL_NEW_LEN
Definition macserial.h:43
#define CASTUUID(uuid)
Definition macserial.h:20
#define MLB_MAX_SIZE
Definition macserial.h:51
#define SERIAL_YEAR_MIN
Definition macserial.h:25
#define SZUUID
Definition macserial.h:18
#define SERIAL_YEAR_NEW_MIN
Definition macserial.h:31
#define SERIAL_YEAR_OLD_MIN
Definition macserial.h:28
#define SERIAL_COPY_MAX
Definition macserial.h:36
#define MODEL_CODE_NEW_LEN
Definition macserial.h:46
#define SERIAL_YEAR_NEW_MAX
Definition macserial.h:33
#define SERIAL_WEEK_MAX
Definition macserial.h:24
#define SERIAL_LINE_MAX
Definition macserial.h:40
#define COUNTRY_OLD_LEN
Definition macserial.h:48
#define SERIAL_WEEK_MIN
Definition macserial.h:23
#define PRIUUID
Definition macserial.h:19
#define SERIAL_YEAR_MAX
Definition macserial.h:26
#define COUNTRY_NEW_LEN
Definition macserial.h:49
#define SERIAL_YEAR_NEW_MID
Definition macserial.h:32
#define SERIAL_COPY_MIN
Definition macserial.h:35
#define APPLE_BOARD_CODE_MAX
#define APPLE_MODEL_MAX
#define APPLE_MODEL_YEAR_MAX
AppleModel
#define APPLE_MODEL_CODE_MAX
char country[4]
Definition macserial.h:65
int32_t modernCountryIdx
Definition macserial.h:71
int32_t decodedWeek
Definition macserial.h:74
int32_t modelIndex
Definition macserial.h:72
bool valid
Definition macserial.h:77
char model[5]
Definition macserial.h:69
char line[4]
Definition macserial.h:68
char year[3]
Definition macserial.h:66
char week[3]
Definition macserial.h:67
const char * appleModel
Definition macserial.h:64
int32_t decodedLine
Definition macserial.h:76
int32_t decodedYear
Definition macserial.h:73
int32_t legacyCountryIdx
Definition macserial.h:70
int32_t decodedCopy
Definition macserial.h:75