├── LICENSE ├── Makefile ├── README ├── doc ├── drm.ms ├── drm.ms.pdf ├── sb.1 └── sb.1.pdf ├── h.h ├── md5.c ├── md5.h ├── md_common.c ├── reg.c ├── sb.c ├── snak.c └── snakext.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Amrit Panesar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | OBJS = md5.o md_common.c reg.o sb.o snak.o snakext.o 4 | CFLAGS += -std=c11 -D_POSIX_C_SOURCE=2 5 | 6 | all: sb 7 | 8 | md5.o: md5.c md5.h 9 | md_common.o: md_common.c md5.h 10 | reg.o: h.h md5.h 11 | sb.o: h.h 12 | snak.o: snak.c h.h 13 | snakext.o: snakext.c h.h md5.h 14 | 15 | sb: $(OBJS) 16 | $(CC) -o sb $(OBJS) 17 | 18 | clean: 19 | rm -f sb *.o 20 | 21 | dist: 22 | mkdir sb-1.2 23 | cp *.c *.h README Makefile sb-1.2 24 | cp -r doc/ sb-1.2 25 | groff -Tpdf -mdoc doc/sb.1 > sb-1.2/doc/sb.1.pdf 26 | groff -Tpdf -ms doc/drm.ms > sb-1.2/doc/drm.ms.pdf 27 | tar czf sb-1.2.tar.gz --owner=root --group=root --format=ustar sb-1.2 28 | rm -rf sb-1.2 29 | 30 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Overview 2 | -------- 3 | 4 | sb, the SCO Breaker, generates serial numbers, activation keys and 5 | registration keys for SCO products, such as UnixWare 7.1.4. 6 | 7 | It still works for versions released after SCO was acquired by Xinuos. 8 | Products made by Xinuos themselves (namely OpenServer 10) do not employ any 9 | kind of DRM. 10 | 11 | License 12 | ------- 13 | 14 | MIT 15 | 16 | Installation 17 | ------------ 18 | 19 | $ make 20 | $ install -s sb /usr/local/bin 21 | $ install -m 0644 doc/sb.1 /usr/local/man/man1 22 | 23 | The install step is optional; sb does not depend on any files and can thus be 24 | run from any directory. 25 | 26 | Usage and Documentation 27 | ----------------------- 28 | 29 | See doc/sb.1 for using sb and doc/drm.ms for a description of the DRM 30 | mechanisms employed by SCO. 31 | 32 | For those with no access to troff implementation, a PDF version of the 33 | documents have been included. 34 | 35 | Motivation 36 | ---------- 37 | 38 | UNIX wants to be free. 39 | It's an integral part of the UNIX experience to be able to read and understand 40 | the source code of the system. 41 | If you haven't done so yet, use something from the V6 through 4.4BSD era. 42 | You'll see what I mean. 43 | 44 | So this is a very elaborate "Fuck you" to SCO. 45 | I hope it's to your liking. 46 | 47 | -------------------------------------------------------------------------------- /doc/drm.ms: -------------------------------------------------------------------------------- 1 | .de UX 2 | \s-1UNIX\s0\\$1 3 | .. 4 | .RP 5 | .TL 6 | Digital Rights Management Mechanisms in the SCO Product Line 7 | .AU 8 | Anonymous 9 | .ND 10 | .AB 11 | This document attempts to explain the primary mechanisms with which SCO 12 | has attempted to protect their intellectual property as well as how they fell 13 | short of doing so. 14 | .PP 15 | In particular, the serial number and activation key (SNAK) combination, the 16 | license data and the registration key shall be covered. 17 | .AE 18 | .SH 19 | Overview 20 | .LP 21 | The product line of SCO Group, Inc. (SCO) consists of the OpenServer and 22 | UnixWare operating systems as well as associated add-on software, such as user 23 | licenses or development kits. 24 | After SCO had gone bankrupt, UnXis, Inc. bought their 25 | .UX 26 | business and continued to offer it. 27 | UnXis, Inc. later renamed to Xinuos, Inc. 28 | The new OpenServer 10 operating system offered by Xinuos, Inc. is 29 | .I not 30 | covered by this document; 31 | OpenServer 10 eschews digital rights management entirely. 32 | Because of this, ``SCO products'' is still used, even though the products are 33 | now maintained and sold by a different entity. 34 | .PP 35 | SCO's digital rights management consists of a number of different parts: 36 | .IP 1. 37 | the serial number (henceforth 38 | .B serno ) 39 | and activation key, 40 | .B actkey ), ( 41 | forming the 42 | .B SNAK '', `` 43 | .IP 2. 44 | the license data indicating additional information about a product, and 45 | .IP 3. 46 | the registration procedure. 47 | .LP 48 | The purchase of an SCO product provides the buyer with a SNAK and license data 49 | if required. 50 | For continued use, a product usually also needs to be 51 | .I registered . 52 | When the system prompts for registration, a registration lock 53 | .B reglock ) ( 54 | is displayed. 55 | The reglock is then supposed to be entered together with personally 56 | identifying information on a Xinuos, Inc. web portal. 57 | .SH 58 | Serial Number and Activation Key 59 | .LP 60 | The pair of serial number and activation key, internally also called 61 | ``SNAK'', consists of a nine-character serno and an eight-character actkey. 62 | The actkey encodes the product ID, product version and whether license data is 63 | required as well as a checksum over the serno and the data contained in the 64 | actkey. 65 | The first three characters of the actkey denote the product ID. 66 | The second triplet denotes the product version and whether license data is 67 | required. 68 | The final two characters are the checksum. 69 | While the serno is effectively an arbitrary string, the actkey is an 70 | all-lowercase string. 71 | For example, a valid SNAK would be the serno ``SCO524572'' and the actkey 72 | ``mnridxjo''. 73 | There are two layers of protection: obscurity and a checksum. 74 | .PP 75 | The 76 | .B actkey 77 | is encrypted. 78 | Presumably because of cryptography export restrictions in the United States of 79 | America, the encryption algorithm is weak. 80 | The ciphertext (i.e. the actkey) is processed in reverse, starting with the 81 | NUL. 82 | Each character is 83 | .I rotated 84 | in the alphabet (a-z) by the previous character's offset in the alphabet. 85 | The first character to be decrypted, which is the last character in the 86 | string, will always be unchanged by this algorithm. 87 | See 88 | .I incfrp () 89 | (decryption) and 90 | .I decfrp () 91 | (encryption) 92 | functions for the implementation. 93 | .PP 94 | The second layer of protection is a simple checksum over the serno and 95 | the first six characters of the decrypted actkey. 96 | See the 97 | .I mnsnc () 98 | function for the implementation.\(dg 99 | .FS 100 | \(dg ``incfrp'' and ``mnsnc'' are the names used by SCO. 101 | It is unclear what they stand for. 102 | ``decfrp'' has been named that way because the opposite of increment is 103 | decrement, assuming ``inc'' in ``incfrp'' stands for ``increment''. 104 | .FE 105 | .PP 106 | After decrypting the actkey and validating the checksum, 107 | the decrypted actkey is parsed. 108 | Both of the data triplets in the actkey are encoded in base 26 with alphabet 109 | a-z; the most significant digit is written first. 110 | For the example above, the decrypted actkey is ``ahvneoco''. 111 | ``ahv'' decodes to 0xCB (203), which is the product ID. 112 | ``neo'' decodes to 0x22CA (8906), which contains the flag if license data is 113 | required and the product version. 114 | The version is contained in the lower three nibbles: 115 | 0x22CA & 0xFFF equals 714. 116 | The last decimal digit (4) denotes the minor version, the other ones denote 117 | the major version (71). 118 | ``co'' is the checksum value. 119 | .SH 120 | License Data 121 | .LP 122 | License data is required if bits 11\(en15 of the second triplet in the actkey 123 | equal 3. 124 | In the given example, this wasn't the case; the relevant bits equal 2 instead. 125 | Values other than 2 and 3 are invalid. 126 | License data is used to supply additional information about a license and 127 | enforce restrictions. 128 | If license data is required, it is printed alongside the SNAK on Certificates 129 | of License and Authenticity. 130 | For example, license data could look like this: ``c4;k0;mjqs8r7''. 131 | .PP 132 | The core of the algorithm is an MD5 hash over a secret value, the serno, the 133 | actkey and the rest of the license data. 134 | Then, this hash is translated to a custom base 32 alphabet. 135 | It is important to note that the license data is ordered by the flag 136 | characters, except for the m flag always being at the end. 137 | See the 138 | .Pa snakext.c 139 | file for the implementation. 140 | .PP 141 | This algorithm is actually susceptible to length-extension attacks because the 142 | secret is merely prepended and MD5 has no mitigations for length-extension 143 | attacks. 144 | However, because license data is fairly strict in validating the input, it is 145 | not actually practical. 146 | .PP 147 | The following fields are known: 148 | .IP c 149 | number of CPUs; 150 | .IP d 151 | expiration of license in days; 152 | this is used for evaluation licenses; 153 | .IP k 154 | if no argument or a non-zero argument is given, registration is required, but 155 | more than one user can access the system; 156 | if the argument is zero, no registration is required, but only one user can 157 | access the system and possibly the network stack is gimped; 158 | .IP u 159 | number of users that may access the system at the same time; 160 | .IP m 161 | MD5 hash over the license data secret, the serno, the actkey and a 162 | canonicalized version of the string until this flag. 163 | .LP 164 | More flags exist. 165 | At least b, g, q have been observed but their format and effects are unknown. 166 | .SH 167 | Registration Key 168 | .LP 169 | Some products must be registered, else they expire after a set amount of time. 170 | This involves a per-installation identifier, the host ID (also referred to as 171 | node ID). 172 | The host ID and serial number are then combined to generate the registration 173 | key. 174 | Because the host ID is different for each installation of a product, storing 175 | registration keys is meaningless. 176 | Most likely, SCO realized the deficiencies of the simple SNAK scheme and added 177 | another layer of protection. 178 | Customers are meant to use an online portal to register their products, 179 | which also checks for double registrations of the same product, allowing the 180 | identification of serial numbers that have been shared. 181 | .I "/etc/brand -k serno" 182 | or 183 | .I scoadmin 184 | can be used to generate the registration lock. 185 | Some of the data there is superfluous for registration code generation; 186 | it is surmised that they are collected for statistical purposes. 187 | This also implies that SCO has customers, which may be a stretch in the first 188 | place, especially considering the USD 2,500 price tag. 189 | .PP 190 | The core of the algorithm is an MD5 hash over a secret value, the host ID and 191 | the serno. 192 | The secret value differs from the one used for the license data. 193 | After that, the first four bytes of the hash are swapped and then encoded in 194 | base16 with a custom alphabet and a two-character checksum. 195 | Because this scheme is symmetric, no actual interaction with SCO is required. 196 | Furthermore, no measures were taken to obscure the secret \(en it is stored in 197 | the 198 | .I /etc/brand 199 | binary with no obfuscation whatsoever. 200 | The secret value used to sign a registration key is the same as the one used 201 | to create the m flag for the registration lock. 202 | There is something truly, profoundly wrong with this; I believe I need not 203 | spell it out. 204 | See the 205 | .I reg.c 206 | file for the implementation. 207 | .PP 208 | A registration lock may look like this: 209 | ``d180120;e380106;i203/71.4;oSCO310807;uorxrrwjwxz;mg7fuxu''. 210 | Like the license data, it is a flag string. 211 | The following flags are known: 212 | .IP i 213 | the product ID and version; 214 | the product ID is separated by a slash, the product major and minor versions 215 | are separated by a dot; 216 | .IP o 217 | the serno; 218 | .IP u 219 | the host ID, encoded in some variation of base16 with a custom alphabet 220 | (kbwtacorhzgsejqx) with an appended checksum; 221 | .IP m 222 | MD5 hash over the registration secret, and a canonicalized version of the 223 | string until this flag; 224 | the canonicalization algorithm is the same as for license data. 225 | .LP 226 | Other flags exist, namely d and e, but their purpose is unknown. 227 | .SH 228 | Conclusions 229 | .LP 230 | SCO has done everything wrong that could possibly be done wrong, while also 231 | making matters much more complicated for themselves than necessary. 232 | There are a total of four checksums: 233 | one in the SNAK, a different one in the flag string for the license data, a 234 | different one in the flag string for the registration lock and a different one 235 | for the registration key. 236 | Furthermore, there are two secrets: 237 | the one for the flag string in the license data and the one used for both 238 | sides of the registration process. 239 | And then there are three different encoding schemes: 240 | the encryption of the activation key, the encoding of the activation key (base 241 | 26), the base 32 encoding in the m flag for license data and registration 242 | lock and the base 16 encoding for the registration key. 243 | It would not have been necessary to keep this many separate encodings and 244 | algorithms around. 245 | .PP 246 | Due to poor operational security, a mostly complete code dump of SCO UnixWare 247 | leaked on the Internet. 248 | They realized that keeping the 249 | .I /etc/brand 250 | utility in the main tree would be dangerous, so it was checked in only as a 251 | binary file. 252 | However, the binary was neither optimized nor stripped, making reverse 253 | engineering effectively trivial. 254 | .PP 255 | Because all secrets in this DRM mechanism are known to both SCO and 256 | .I /etc/brand , 257 | reverse engineering is all that is required to break every layer of 258 | protection; 259 | there is no cryptographic layer of protection, such as asymmetric signatures 260 | over the registration key. 261 | Elliptic curve signatures in a base64 encoding would likely have been 262 | tolerable for users. 263 | Alternatively, a truncated RSA signature could have been used \(en a full 264 | signature would be too long for users to type into the terminal. 265 | The short Schnorr signatures would have been another option, used by Microsoft 266 | in the Windows XP era. 267 | .PP 268 | None of this has a real-world impact and the estimated amount of lost sales 269 | tends towards zero. 270 | The only reasons to buy an SCO product are either legacy applications or the 271 | support contract that comes with it. 272 | Legacy applications generally do not generate new sales. 273 | Breaking the DRM mechanisms does not cause a support contract to come into 274 | existence out of thin air. 275 | -------------------------------------------------------------------------------- /doc/drm.ms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo-Desktop/sco-breaker/a2e020660404fa86e5a8d2bd114496fe5da5868d/doc/drm.ms.pdf -------------------------------------------------------------------------------- /doc/sb.1: -------------------------------------------------------------------------------- 1 | .Dd January 27, 2018 2 | .Dt SB 1 3 | .Os 4 | . 5 | .Sh NAME 6 | .Nm sb 7 | .Nd generate serial numbers, activation keys and registration keys 8 | . 9 | .Sh SYNOPSIS 10 | .Nm 11 | .Ar product_id 12 | .Ar major_ver 13 | .Ar minor_ver 14 | .Op Ar license_data 15 | .Nm 16 | .Fl r 17 | .Ar serial_number 18 | .Ar host_id 19 | .Nm 20 | .Fl r 21 | .Ar registration_lock 22 | . 23 | .Sh DESCRIPTION 24 | .Nm 25 | generates serial numbers, activation keys and registration keys for SCO 26 | products. 27 | SCO products from 1992 and earlier may not be compatible with the licensing 28 | information generated by 29 | .Nm . 30 | .Pp 31 | During installation of an SCO product, you will be prompted for at least a 32 | serial number and an activation key. 33 | This information can be generated by calling 34 | .Nm 35 | .Em without 36 | .Fl r . 37 | In order to generate a serial number and activation key 38 | .Dq ( SNAK ) , 39 | you need the correct 40 | product ID, major version and minor version. 41 | The product ID is an SCO-internal integer that denotes the licensed product. 42 | The major and minor versions denote the version of the licensed product. 43 | .Sy This may not be the same as the version the product is marketed as . 44 | For example, UnixWare 7.1.4 has major version 71 and minor version 4. 45 | The distinction between 7.1.4, 7.1.4+ and 7.1.4 Definitive is made through the 46 | product ID, rather than the version fields. 47 | .Pp 48 | On an SCO UNIX installation, you can use 49 | .Cm /etc/brand -d 50 | to list all known product IDs for that installation. 51 | Usually, there is also a pre-installation and post-installation script bundled 52 | with the SCO product that shows the specific expectations for product ID and 53 | version values; 54 | search those scripts for 55 | .Dq Cm brand -Q . 56 | .Pp 57 | Optionally, you can supply 58 | .Em license data . 59 | License data is a semicolon-delimited list of fields that specify various 60 | aspects about the license in question. 61 | Each field consists of a character and then a value, e.g. 62 | .Dq c4;u100 63 | denotes a license for four CPUs and 100 users. 64 | License data may also only consist of a single field. 65 | The following characters are known: 66 | .Bl -tag -width Ds 67 | .It Cm c 68 | Number of CPUs. 69 | .It Cm d 70 | Expiry time of the license in days. 71 | This is used for evaluation licenses. 72 | .It Cm k 73 | If no argument or a non-zero argument is given, registration is required, but 74 | more than one user can access the system. 75 | If the argument is zero, no registration is required, but only one user can 76 | access the system and possibly the network stack is gimped. 77 | .It Cm u 78 | Number of users. 79 | .It Cm m 80 | Checksum over the other fields. 81 | .Sy You must not supply this value . 82 | .El 83 | .Pp 84 | The characters g, q and x have also been observed, but their effect is 85 | unknown. 86 | .Sy This program does not check license data is not checked for validity . 87 | .Sy You must order the license data alphabetically . 88 | .Pp 89 | After installation, you may also be asked for a 90 | .Em registration key 91 | for continued operation. 92 | .Nm 93 | will generate a registration key if the 94 | .Fl r 95 | flag is passed. 96 | You need to supply either the serial number of the product and the host ID, or 97 | a valid registration lock. 98 | The host ID differs for every installation, even for the same product and 99 | serial number. 100 | If you have forgotten the serial number of the product, it can be found with 101 | .Cm /etc/brand -L . 102 | The host ID can be found with 103 | .Pa /etc/brand Fl I . 104 | If you supply a registration lock, please be aware that it contains semicolons 105 | (;). 106 | You will have to quote it to prevent the shell from parsing the semicolons as 107 | command delimiters. 108 | . 109 | .Sh EXIT STATUS 110 | .Ex -std 111 | . 112 | .Sh EXAMPLES 113 | Generate a new serial number and activation key for UnixWare 7.1.4 Definitive 114 | 2018 (product ID 203, version major 71, version minor 4) with no license data: 115 | .Bd -literal -offset indent 116 | $ sb 203 71 4 117 | .Ed 118 | .Pp 119 | Register a product for your host: 120 | .Bd -literal -offset indent 121 | $ /etc/brand -I 122 | orxrrwjwxz 123 | $ sb -r SCO539702 orxrrwjwxz 124 | .Ed 125 | . 126 | .Sh DIAGNOSTICS 127 | .Bl -diag 128 | .It "%s not a number" 129 | The supplied product ID, major version or minor version is not actually a 130 | number. 131 | .It "%s out of range (max %u)" 132 | The supplied product ID, major version or minor version is out of range. 133 | If you are 134 | .Em absolutely certain 135 | that you have the correct parameters, please contact the author to fix the 136 | limit. 137 | The limits are theoretical maximum limits, not ones observed in use by SCO. 138 | .It "malloc" 139 | Memory allocation has failed. 140 | If you had enough memory to display this manual page, you can probably just 141 | try again. 142 | You may need to check 143 | .Pa /etc/malloc.conf 144 | if the issue persists. 145 | .It "internal: limit > UINT16_MAX" 146 | You should never see this. 147 | If you do, your platform is almost certainly clinically insane. 148 | Please stop trying to run 149 | .Nm 150 | on a literal Game Boy. 151 | .El 152 | . 153 | .Sh CAVEATS 154 | While this code appears to work, it may be possible that the product 155 | nonetheless phones home over IP. 156 | No efforts have been made to try and observe such activity. 157 | It may prove advantageous to first strictly firewall an SCO UNIX installation. 158 | If the outgoing connections look okay for approximately 24 hours, it may be 159 | safe to let loose on the Internet. 160 | More cautious users may wish to wait up to 31 days. 161 | -------------------------------------------------------------------------------- /doc/sb.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neo-Desktop/sco-breaker/a2e020660404fa86e5a8d2bd114496fe5da5868d/doc/sb.1.pdf -------------------------------------------------------------------------------- /h.h: -------------------------------------------------------------------------------- 1 | #ifndef SB_H_H 2 | #define SB_H_H 3 | 4 | /* sb.c */ 5 | void usage(bool fail); 6 | _Noreturn void die(const char *msgfmt, ...); 7 | 8 | /* md_common.c */ 9 | char *BSCanon(const char *s); 10 | void extmd(char md[static 7], unsigned int nstr, ...); 11 | 12 | /* reg.c */ 13 | int gen_regcode(int argc, char *argv[]); 14 | 15 | /* snak.c */ 16 | int gen_snak(int argc, char *argv[]); 17 | 18 | /* snakext.c */ 19 | void mdsnakext(const char *serno, const char *actkey, const char *snakext, 20 | char snakextmd[static 7]); 21 | 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /md5.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: md5.c,v 1.11 2015/09/11 09:18:27 guenther Exp $ */ 2 | 3 | /* 4 | * This code implements the MD5 message-digest algorithm. 5 | * The algorithm is due to Ron Rivest. This code was 6 | * written by Colin Plumb in 1993, no copyright is claimed. 7 | * This code is in the public domain; do with it what you wish. 8 | * 9 | * Equivalent code is available from RSA Data Security, Inc. 10 | * This code has been tested against that, and is equivalent, 11 | * except that you don't need to include two pages of legalese 12 | * with every copy. 13 | * 14 | * To compute the message digest of a chunk of bytes, declare an 15 | * MD5Context structure, pass it to MD5Init, call MD5Update as 16 | * needed on buffers full of bytes, and then call MD5Final, which 17 | * will fill a supplied 16-byte array with the digest. 18 | */ 19 | 20 | #include 21 | #include 22 | #include "md5.h" 23 | 24 | #define PUT_64BIT_LE(cp, value) do { \ 25 | (cp)[7] = (value) >> 56; \ 26 | (cp)[6] = (value) >> 48; \ 27 | (cp)[5] = (value) >> 40; \ 28 | (cp)[4] = (value) >> 32; \ 29 | (cp)[3] = (value) >> 24; \ 30 | (cp)[2] = (value) >> 16; \ 31 | (cp)[1] = (value) >> 8; \ 32 | (cp)[0] = (value); } while (0) 33 | 34 | #define PUT_32BIT_LE(cp, value) do { \ 35 | (cp)[3] = (value) >> 24; \ 36 | (cp)[2] = (value) >> 16; \ 37 | (cp)[1] = (value) >> 8; \ 38 | (cp)[0] = (value); } while (0) 39 | 40 | static uint8_t PADDING[MD5_BLOCK_LENGTH] = { 41 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 44 | }; 45 | 46 | /* 47 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 48 | * initialization constants. 49 | */ 50 | void 51 | MD5Init(MD5_CTX *ctx) 52 | { 53 | ctx->count = 0; 54 | ctx->state[0] = 0x67452301; 55 | ctx->state[1] = 0xefcdab89; 56 | ctx->state[2] = 0x98badcfe; 57 | ctx->state[3] = 0x10325476; 58 | } 59 | 60 | /* 61 | * Update context to reflect the concatenation of another buffer full 62 | * of bytes. 63 | */ 64 | void 65 | MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) 66 | { 67 | size_t have, need; 68 | 69 | /* Check how many bytes we already have and how many more we need. */ 70 | have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); 71 | need = MD5_BLOCK_LENGTH - have; 72 | 73 | /* Update bitcount */ 74 | ctx->count += (uint64_t)len << 3; 75 | 76 | if (len >= need) { 77 | if (have != 0) { 78 | memcpy(ctx->buffer + have, input, need); 79 | MD5Transform(ctx->state, ctx->buffer); 80 | input += need; 81 | len -= need; 82 | have = 0; 83 | } 84 | 85 | /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ 86 | while (len >= MD5_BLOCK_LENGTH) { 87 | MD5Transform(ctx->state, input); 88 | input += MD5_BLOCK_LENGTH; 89 | len -= MD5_BLOCK_LENGTH; 90 | } 91 | } 92 | 93 | /* Handle any remaining bytes of data. */ 94 | if (len != 0) 95 | memcpy(ctx->buffer + have, input, len); 96 | } 97 | 98 | /* 99 | * Pad pad to 64-byte boundary with the bit pattern 100 | * 1 0* (64-bit count of bits processed, MSB-first) 101 | */ 102 | void 103 | MD5Pad(MD5_CTX *ctx) 104 | { 105 | uint8_t count[8]; 106 | size_t padlen; 107 | 108 | /* Convert count to 8 bytes in little endian order. */ 109 | PUT_64BIT_LE(count, ctx->count); 110 | 111 | /* Pad out to 56 mod 64. */ 112 | padlen = MD5_BLOCK_LENGTH - 113 | ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); 114 | if (padlen < 1 + 8) 115 | padlen += MD5_BLOCK_LENGTH; 116 | MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ 117 | MD5Update(ctx, count, 8); 118 | } 119 | 120 | /* 121 | * Final wrapup--call MD5Pad, fill in digest and zero out ctx. 122 | */ 123 | void 124 | MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) 125 | { 126 | int i; 127 | 128 | MD5Pad(ctx); 129 | for (i = 0; i < 4; i++) 130 | PUT_32BIT_LE(digest + i * 4, ctx->state[i]); 131 | } 132 | 133 | 134 | /* The four core functions - F1 is optimized somewhat */ 135 | 136 | /* #define F1(x, y, z) (x & y | ~x & z) */ 137 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 138 | #define F2(x, y, z) F1(z, x, y) 139 | #define F3(x, y, z) (x ^ y ^ z) 140 | #define F4(x, y, z) (y ^ (x | ~z)) 141 | 142 | /* This is the central step in the MD5 algorithm. */ 143 | #define MD5STEP(f, w, x, y, z, data, s) \ 144 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 145 | 146 | /* 147 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 148 | * reflect the addition of 16 longwords of new data. MD5Update blocks 149 | * the data and converts bytes into longwords for this routine. 150 | */ 151 | void 152 | MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH]) 153 | { 154 | uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; 155 | 156 | #if BYTE_ORDER == LITTLE_ENDIAN 157 | memcpy(in, block, sizeof(in)); 158 | #else 159 | for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { 160 | in[a] = (uint32_t)( 161 | (uint32_t)(block[a * 4 + 0]) | 162 | (uint32_t)(block[a * 4 + 1]) << 8 | 163 | (uint32_t)(block[a * 4 + 2]) << 16 | 164 | (uint32_t)(block[a * 4 + 3]) << 24); 165 | } 166 | #endif 167 | 168 | a = state[0]; 169 | b = state[1]; 170 | c = state[2]; 171 | d = state[3]; 172 | 173 | MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); 174 | MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); 175 | MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); 176 | MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); 177 | MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); 178 | MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); 179 | MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); 180 | MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); 181 | MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); 182 | MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); 183 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 184 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 185 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 186 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 187 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 188 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 189 | 190 | MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); 191 | MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); 192 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 193 | MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); 194 | MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); 195 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 196 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 197 | MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); 198 | MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); 199 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 200 | MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); 201 | MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); 202 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 203 | MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); 204 | MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); 205 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 206 | 207 | MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); 208 | MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); 209 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 210 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 211 | MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); 212 | MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); 213 | MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); 214 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 215 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 216 | MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); 217 | MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); 218 | MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); 219 | MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); 220 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 221 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 222 | MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); 223 | 224 | MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); 225 | MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); 226 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 227 | MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); 228 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 229 | MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); 230 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 231 | MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); 232 | MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); 233 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 234 | MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); 235 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 236 | MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); 237 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 238 | MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); 239 | MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); 240 | 241 | state[0] += a; 242 | state[1] += b; 243 | state[2] += c; 244 | state[3] += d; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /md5.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* $OpenBSD: md5.h,v 1.17 2012/12/05 23:19:57 deraadt Exp $ */ 4 | 5 | /* 6 | * This code implements the MD5 message-digest algorithm. 7 | * The algorithm is due to Ron Rivest. This code was 8 | * written by Colin Plumb in 1993, no copyright is claimed. 9 | * This code is in the public domain; do with it what you wish. 10 | * 11 | * Equivalent code is available from RSA Data Security, Inc. 12 | * This code has been tested against that, and is equivalent, 13 | * except that you don't need to include two pages of legalese 14 | * with every copy. 15 | */ 16 | 17 | #ifndef _MD5_H_ 18 | #define _MD5_H_ 19 | 20 | #define MD5_BLOCK_LENGTH 64 21 | #define MD5_DIGEST_LENGTH 16 22 | #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) 23 | 24 | typedef struct MD5Context { 25 | uint32_t state[4]; /* state */ 26 | uint64_t count; /* number of bits, mod 2^64 */ 27 | uint8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ 28 | } MD5_CTX; 29 | 30 | void MD5Init(MD5_CTX *); 31 | void MD5Update(MD5_CTX *, const uint8_t *, size_t); 32 | void MD5Pad(MD5_CTX *); 33 | void MD5Final(uint8_t [MD5_DIGEST_LENGTH], MD5_CTX *); 34 | void MD5Transform(uint32_t [4], const uint8_t [MD5_BLOCK_LENGTH]); 35 | 36 | #endif /* _MD5_H_ */ 37 | -------------------------------------------------------------------------------- /md_common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "md5.h" 9 | #include "h.h" 10 | 11 | const char *fromHextet = "0123456789abcdefghjkmnpqrstuwxyz"; 12 | 13 | static int 14 | asciiCode(int c) 15 | { 16 | static const int fromAlpha[] = { 17 | 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 1, 0x12, 0x13, 1, 18 | 0x14, 0x15, 0, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 19 | 0x1c, 0x1d, 0x1e, 0x1f 20 | }; 21 | 22 | if (c >= '0' && c <= '9') 23 | return c - '0'; 24 | if (isupper(c)) 25 | c = tolower(c); 26 | if (islower(c)) 27 | return fromAlpha[c - 'a']; 28 | return -1; 29 | } 30 | 31 | char * 32 | BSCanon(const char *s) 33 | { 34 | char *ret; 35 | int c; 36 | 37 | ret = malloc(strlen(s) + 1); 38 | if (ret == NULL) 39 | die("malloc"); 40 | strcpy(ret, s); 41 | 42 | for (char *p = ret; *p; ++p) { 43 | c = asciiCode(*p); 44 | if (c != -1) 45 | *p = fromHextet[c]; 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | void 52 | extmd(char md[static 7], unsigned int nstr, ...) 53 | { 54 | char *str; 55 | MD5_CTX ctx; 56 | char digest[MD5_DIGEST_LENGTH]; 57 | va_list ap; 58 | 59 | MD5Init(&ctx); 60 | va_start(ap, nstr); 61 | 62 | for (unsigned int i = 0; i < nstr; ++i) { 63 | str = va_arg(ap, char *); 64 | MD5Update(&ctx, (const uint8_t *)str, strlen(str)); 65 | } 66 | 67 | MD5Final(digest, &ctx); 68 | for (size_t i = 0; i < 6; ++i) 69 | md[i] = fromHextet[digest[i] & 0x1F]; 70 | md[6] = '\0'; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /reg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "h.h" 8 | #include "md5.h" 9 | 10 | static const char *regSecret = "\x75\xf8\xe8\x5e\x83\xc4\x5e\x4c\xff\x75\x5e\x48\xe8\x65\x5e\x46\x59\x8b\x45"; 11 | 12 | static char * 13 | toAsciiBase16(uint32_t in, unsigned char ckbase) 14 | { 15 | static const char *charmap = "\005k\001b\002w\003t\004a\005c\001o\002r\003h\004z\005g\001s\002e\003j\004q\005x\001"; 16 | static char buf[11]; 17 | unsigned int a, i; 18 | unsigned char ck; 19 | 20 | ck = ckbase; 21 | for (i = 8; i > 0; --i) { 22 | a = 15 - (in & 0xF); 23 | buf[i - 1] = charmap[2 * a + 1]; 24 | ck += a * (i); 25 | in >>= 4; 26 | } 27 | 28 | /* checksum */ 29 | for (i = 9; i >= 8; --i) { 30 | buf[i] = charmap[2 * (ck & 0xF) + 1]; 31 | ck >>= 4; 32 | } 33 | 34 | buf[10] = '\0'; 35 | return buf; 36 | } 37 | 38 | static uint32_t 39 | l32be(uint8_t i[4]) 40 | { 41 | return (uint32_t)i[3] 42 | | ((uint32_t)i[2] << 8) 43 | | ((uint32_t)i[1] << 16) 44 | | ((uint32_t)i[0] << 24); 45 | } 46 | 47 | static char * 48 | generateRegistrationID(const char *serno, const char *szHostid) 49 | { 50 | static char buf[11]; 51 | uint32_t regkey; 52 | MD5_CTX ctx; 53 | uint8_t digest[MD5_DIGEST_LENGTH]; 54 | 55 | MD5Init(&ctx); 56 | MD5Update(&ctx, (const uint8_t *)regSecret, strlen(regSecret)); 57 | MD5Update(&ctx, (const uint8_t *)szHostid, strlen(szHostid)); 58 | MD5Update(&ctx, (const uint8_t *)serno, strlen(serno)); 59 | MD5Final(digest, &ctx); 60 | 61 | regkey = l32be(digest); 62 | snprintf(buf, sizeof(buf), "%s", toAsciiBase16(regkey, 3)); 63 | return buf; 64 | } 65 | 66 | static bool 67 | valid_reglock(char *reglock) 68 | { 69 | char *canon, *theirs, *p; 70 | char mine[7]; 71 | 72 | if ((p = strstr(reglock, ";m")) == NULL) 73 | return false; 74 | if (*(p + 2) == '\0') 75 | return false; 76 | 77 | *p = '\0'; 78 | theirs = p + 2; 79 | 80 | canon = BSCanon(reglock); 81 | extmd(mine, 2, regSecret, canon); 82 | free(canon); 83 | 84 | return (strcmp(mine, theirs) == 0); 85 | } 86 | 87 | static void 88 | parse_reglock(char *reglock, char **serno, char **hostid) 89 | { 90 | char *last, *p; 91 | 92 | if (!valid_reglock(reglock)) 93 | die("registration lock %s invalid; check for typos", reglock); 94 | 95 | /* Assumption: Nobody generates bogus reglocks, so they're well-behaved 96 | * after the MD5 checks out. 97 | */ 98 | for (p = strtok_r(reglock, ";", &last); 99 | p != NULL; 100 | p = strtok_r(NULL, ";", &last)) { 101 | switch (*p) { 102 | case 'o': 103 | *serno = p + 1; 104 | break; 105 | case 'u': 106 | *hostid = p + 1; 107 | break; 108 | default: 109 | break; 110 | } 111 | } 112 | } 113 | 114 | int 115 | gen_regcode(int argc, char *argv[]) 116 | { 117 | char *serno, *hostid, *regcode; 118 | size_t serno_len, hostid_len; 119 | 120 | if (argc < 1) { 121 | usage(true); 122 | return EXIT_FAILURE; 123 | } 124 | 125 | if (argc == 2) { 126 | serno = argv[0]; 127 | hostid = argv[1]; 128 | serno_len = strlen(serno); 129 | hostid_len = strlen(hostid); 130 | if (serno_len == 10 && hostid_len == 9) { 131 | /* Arguments are probably swapped. */ 132 | serno = argv[1]; 133 | hostid = argv[0]; 134 | } else if (serno_len != 9 || hostid_len != 10) { 135 | die("invalid length for serno or hostid"); 136 | } 137 | } else { 138 | parse_reglock(argv[0], &serno, &hostid); 139 | } 140 | 141 | regcode = generateRegistrationID(serno, hostid); 142 | printf("Registration Key: %s\n", regcode); 143 | 144 | return EXIT_SUCCESS; 145 | } 146 | 147 | 148 | -------------------------------------------------------------------------------- /sb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "h.h" 9 | 10 | static char *progname; 11 | 12 | void 13 | usage(bool fail) 14 | { 15 | fprintf(fail ? stderr : stdout, 16 | "usage: %s product_id major_ver minor_ver " 17 | "[license_data]\n" 18 | " %s -r serial_number host_id\n" 19 | " %s -r registration_lock\n", 20 | progname, progname, progname); 21 | } 22 | 23 | _Noreturn void 24 | die(const char *msgfmt, ...) 25 | { 26 | va_list ap; 27 | 28 | fprintf(stderr, "%s: ", progname); 29 | 30 | va_start(ap, msgfmt); 31 | vfprintf(stderr, msgfmt, ap); 32 | va_end(ap); 33 | 34 | putchar('\n'); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | int 39 | main(int argc, char *argv[]) 40 | { 41 | int c; 42 | int ret = EXIT_SUCCESS; 43 | bool want_regcode = false; 44 | 45 | progname = basename((argv[0] != NULL) ? argv[0] : "sb"); 46 | 47 | while ((c = getopt(argc, argv, "hr")) != -1) { 48 | switch (c) { 49 | case 'r': 50 | want_regcode = true; 51 | break; 52 | 53 | default: 54 | ret = EXIT_FAILURE; 55 | case 'h': 56 | usage(ret == EXIT_FAILURE); 57 | return ret; 58 | } 59 | } 60 | 61 | argc -= optind; 62 | argv += optind; 63 | 64 | if (want_regcode) 65 | ret = gen_regcode(argc, argv); 66 | else 67 | ret = gen_snak(argc, argv); 68 | 69 | return ret; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /snak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "h.h" 11 | 12 | static bool 13 | overflow15(uint32_t a, uint32_t b) 14 | { 15 | return (a + b > 0x7FFF); 16 | } 17 | 18 | static char * 19 | mnsnc(const char *s) 20 | { 21 | static char buf[3]; 22 | uint16_t a; 23 | uint16_t flag; 24 | int c = s[8] % 16; 25 | 26 | for (a = 0; *s != '\0'; ++s) { 27 | if (overflow15(a, *s)) 28 | flag = 1; 29 | else 30 | flag = 0; 31 | a = flag | (2 * (a + *s)); 32 | } 33 | 34 | for (; c > 0; --c) 35 | a = ((a & 0x8000U) >> 15) | (uint16_t)(2 * a); 36 | 37 | buf[2] = 0; 38 | buf[1] = a % 26 + 'a'; 39 | a /= 26; 40 | buf[0] = a % 26 + 'a'; 41 | 42 | return buf; 43 | } 44 | 45 | static void 46 | strbn(char *out, unsigned int in) 47 | { 48 | static const char *alphabet = "abcdefghijklmnopqrstuvwxyz"; 49 | size_t i; 50 | 51 | for (i = 3; i > 0; --i) { 52 | out[i - 1] = alphabet[in % 26]; 53 | in /= 26; 54 | } 55 | } 56 | 57 | static void 58 | decfrp(char *s) 59 | { 60 | char *p; 61 | unsigned char a, b; 62 | 63 | a = b = 0; 64 | 65 | for (p = s + strlen(s) - 1; p >= s; --p) { 66 | a = (*p - 'a' + b) % 26; 67 | a = (a + 'a') & 0xFF; 68 | b += a; 69 | b %= 26; 70 | *p = a; 71 | } 72 | } 73 | 74 | /* Implementation of xorshift* without retaining seed state. */ 75 | static uint64_t 76 | rnd(uint64_t seed) 77 | { 78 | seed ^= seed >> 12; 79 | seed ^= seed << 25; 80 | seed ^= seed >> 27; 81 | return seed * 0x2545F4914F6CDD1D; 82 | } 83 | 84 | static unsigned int 85 | mkver(unsigned int lictype, unsigned int major, unsigned int minor) 86 | { 87 | return ((lictype << 12) | (major * 10 + minor)); 88 | } 89 | 90 | static void 91 | mksnak(bool has_snakext, uint16_t product_id, uint16_t major, uint16_t minor, 92 | char *serno, char *actkey) 93 | { 94 | const char *cksum; 95 | uint64_t serial; 96 | unsigned int version = mkver((has_snakext ? 3 : 2), major, minor); 97 | char merged[18]; 98 | 99 | memset(serno, 0, 10); 100 | memset(actkey, 0, 9); 101 | #ifdef DBG 102 | printf("has_snakext: %d, major: %u, minor: %u, version: %u\n", 103 | !!has_snakext, major, minor, version); 104 | #endif 105 | 106 | /* bitmask to ensure at most six digits */ 107 | serial = rnd((uintptr_t)serno * time(NULL)) & 0xEFFFF; 108 | snprintf(serno, 10, "SCO%06" PRIu64, serial); 109 | 110 | strbn(actkey, product_id); 111 | strbn(actkey + 3, version); 112 | snprintf(merged, sizeof(merged), "%s%s", serno, actkey); 113 | 114 | cksum = mnsnc(merged); 115 | actkey[6] = cksum[0]; 116 | actkey[7] = cksum[1]; 117 | 118 | decfrp(actkey); 119 | } 120 | 121 | static uint16_t 122 | strtou16lim(const char *in, unsigned long limit) 123 | { 124 | char *end; 125 | unsigned long i; 126 | 127 | if (limit > UINT16_MAX) 128 | die("internal: limit > UINT16_MAX"); 129 | 130 | i = strtoul(in, &end, 10); 131 | if (*in == '\0' || *end != '\0') 132 | die("%s not a number", in); 133 | if (i > limit) 134 | die("%s out of range (max %"PRIu16")", in, limit); 135 | 136 | return i; 137 | } 138 | 139 | int 140 | gen_snak(int argc, char *argv[]) 141 | { 142 | char *snakext = NULL; 143 | uint16_t product_id, major, minor; 144 | char serno[10], actkey[9], snakextmd[7]; 145 | 146 | if (argc < 3) { 147 | usage(true); 148 | return EXIT_FAILURE; 149 | } 150 | 151 | /* "zzz" is the maximum possible product ID encoded value. 152 | * This decodes to a value of 17575. 153 | */ 154 | product_id = strtou16lim(argv[0], 17575); 155 | 156 | /* License type (whether snakext is to be read), version major and 157 | * version minor share an integer, max encoded as "zzz". 158 | * The license type is shifted up by 12, leaving 0xFFF (4095) for the 159 | * version major and minor. 160 | * Of that, the version major is all the upper digits, and the minor is 161 | * the bottom digit (i.e. version/10 => major, version%10 => minor). 162 | * 163 | * Without doing too much checking, the maximum major version is 409 and 164 | * the maximum minor version is 9. 165 | */ 166 | major = strtou16lim(argv[1], 409); 167 | minor = strtou16lim(argv[2], 9); 168 | 169 | if (argc >= 4) 170 | snakext = argv[3]; 171 | 172 | mksnak(snakext != NULL, product_id, major, minor, serno, actkey); 173 | printf("Serial number: %s\n" 174 | "Activation key: %s\n", serno, actkey); 175 | 176 | if (snakext != NULL) { 177 | mdsnakext(serno, actkey, snakext, snakextmd); 178 | printf("License data: %s;m%s\n", snakext, snakextmd); 179 | } 180 | 181 | return EXIT_SUCCESS; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /snakext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "md5.h" 9 | #include "h.h" 10 | 11 | static const char *extSecret = "\x5e\x4f\xbe\x45\x5e\x4c\x8d\x40\x9f\xeb\x26\x5e\x4f\xbe\x45\x5e\x4c\x3d\x30\x7c"; 12 | 13 | void 14 | mdsnakext(const char *serno, const char *actkey, const char *snakext, 15 | char snakextmd[static 7]) 16 | { 17 | char *canon; 18 | 19 | canon = BSCanon(snakext); 20 | extmd(snakextmd, 4, extSecret, serno, actkey, canon); 21 | free(canon); 22 | } 23 | 24 | --------------------------------------------------------------------------------