macserialrb 0.2.0

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