447 lines
9.8 KiB
C
447 lines
9.8 KiB
C
#include "gm6.h"
|
|
|
|
static char key_name[6];
|
|
|
|
// The Mark Overmas Sequence.
|
|
const uint8_t mark_overmars_seq[0x29] = { 0x01, 0x07, 0x11, 0x17, 0x19, 0x1b, 0x24, 0x27, 0x2b, 0x31, 0x33, 0x3d, 0x3f, 0x4d, 0x53, 0x59, 0x5e, 0x63, 0x68, 0x69, 0x76, 0x7f, 0x89, 0x8b, 0x97, 0x9b, 0x9e, 0xA6, 0xB9, 0xBD, 0xBF, 0xC6, 0xC9, 0xD3, 0xD6, 0xDE, 0xE5, 0xEA, 0xEB, 0xF5, 0xF9 };
|
|
|
|
|
|
|
|
int add_multiply(uint8_t* key_arr) {
|
|
int sum = 0;
|
|
for (int i = 0; i < KEY_LEN - 1; i++) {
|
|
int c = key_arr[i];
|
|
sum += (i + 1) * c;
|
|
}
|
|
return sum % 0x100;
|
|
}
|
|
|
|
int check_checksum(uint8_t* key_arr) {
|
|
int c = key_arr[KEY_LEN - 1];
|
|
int chksum = add_multiply(key_arr);
|
|
if (c == chksum) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
int trunc_c(int c) {
|
|
while (c > 0xFF)
|
|
c -= 0x100;
|
|
|
|
while (c < 0x100)
|
|
c += 0x100;
|
|
return c;
|
|
}
|
|
|
|
uint8_t CheckThing(int inp)
|
|
{
|
|
int ret = 0;
|
|
if (inp - 1 < 2)
|
|
return 1;
|
|
int max = inp - 3 + 1;
|
|
for (int i = 2; inp % i; ++i)
|
|
{
|
|
if (!--max)
|
|
return 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int IterateCheckThing(int start)
|
|
{
|
|
int i = 0;
|
|
int ret = 2;
|
|
do
|
|
{
|
|
if (CheckThing(++ret))
|
|
++i;
|
|
} while (start > i);
|
|
return ret;
|
|
}
|
|
|
|
void gen_checksum(uint8_t* key_arr) {
|
|
int curchk = key_arr[KEY_LEN - 1];
|
|
int chksum = add_multiply(key_arr);
|
|
if (curchk != chksum) {
|
|
//printf("Updating checksum, %x -> %x\n", curchk, chksum);
|
|
key_arr[KEY_LEN - 1] = (uint8_t)chksum;
|
|
|
|
}
|
|
}
|
|
|
|
void deobfuscate_key_pt1(uint8_t* key_arr, uint8_t* out_key, int chk2) {
|
|
int start = 0x11;
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int32_t c = (key_arr[i] + 0x100);
|
|
c -= (start * chk2);
|
|
c = trunc_c(c);
|
|
out_key[i] = (uint8_t)c;
|
|
start += 0x1F;
|
|
}
|
|
}
|
|
|
|
void obfuscate_key_pt1(uint8_t* key_arr, uint8_t* out_key, int chk2) {
|
|
int start = 0x11;
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int32_t c = (key_arr[i] + 0x100);
|
|
c += (start * chk2);
|
|
c = trunc_c(c);
|
|
out_key[i] = (uint8_t)c;
|
|
start += 0x1F;
|
|
}
|
|
}
|
|
|
|
void obfuscate_key_pt2(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int res = IterateCheckThing(chk2 + ((i + 1) * (i + 1)));
|
|
int c = (key_arr[i] + res);
|
|
c = trunc_c(c);
|
|
out_key[i] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void deobfuscate_key_pt2(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int res = IterateCheckThing(chk2 + ((i + 1) * (i + 1)));
|
|
int c = (key_arr[i] - res);
|
|
c = trunc_c(c);
|
|
out_key[i] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void obfuscate_key_pt3(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
|
|
for (int i = 0; i < KEY_LEN - 1; i++) {
|
|
int c = key_arr[i + 1] - (chk2 * key_arr[i]);
|
|
c = trunc_c(c);
|
|
out_key[i + 1] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void deobfuscate_key_pt3(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
|
|
for (int i = 0; i < KEY_LEN - 1; i++) {
|
|
int c = (chk2 * out_key[i]) + out_key[i + 1];
|
|
c = trunc_c(c);
|
|
out_key[i + 1] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void deobfuscate_key_pt4(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
|
|
int seed = 1;
|
|
for (int multi = 0x64; seed % 9 == 1; ++multi)
|
|
seed = IterateCheckThing(multi + chk1);
|
|
|
|
|
|
for (int i = 0x0; i < 0x9; i++) {
|
|
int c = key_arr[seed * (i + 1) % 9];
|
|
out_key[i] = (uint8_t)c;
|
|
}
|
|
|
|
}
|
|
|
|
void deobfuscate_key_pt5(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
int multi = 0xD;
|
|
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int c = (key_arr[i] + 0x100) - (chk1 * multi);
|
|
multi += 0x35;
|
|
c = trunc_c(c);
|
|
|
|
out_key[i] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void obfuscate_key_pt5(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {
|
|
|
|
memcpy(out_key, key_arr, KEY_LEN);
|
|
int multi = 0xD;
|
|
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
int c = (key_arr[i] + 0x100) + (chk1 * multi);
|
|
multi += 0x35;
|
|
|
|
c = trunc_c(c);
|
|
|
|
out_key[i] = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void obfuscate_key_pt4(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {
|
|
deobfuscate_key_pt4(key_arr, out_key, chk1);
|
|
}
|
|
|
|
|
|
char* decode_name(int encoded_name)
|
|
{
|
|
memset(key_name, 0x00, 6);
|
|
for (int i = 4; i >= 0; i--) {
|
|
int c = (encoded_name % 0x1A) + 0x41;
|
|
key_name[i] = c;
|
|
encoded_name /= 0x1A;
|
|
}
|
|
return key_name;
|
|
}
|
|
|
|
void sanitize_name(char* namein, char* nameout) {
|
|
memset(nameout, 0, 6);
|
|
|
|
if (strlen(namein) != 5) {
|
|
int sz = strlen(namein);
|
|
|
|
strncpy(nameout, "XXXXX", 5);
|
|
|
|
if (sz < 5) {
|
|
memcpy(&nameout[5 - sz], namein, sz);
|
|
}
|
|
else if (sz > 5) {
|
|
memcpy(nameout, &namein[sz - 5], 5);
|
|
}
|
|
|
|
}
|
|
else {
|
|
strncpy(nameout, namein, 5);
|
|
}
|
|
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
if (!(namein[i] >= 'A' && namein[i] <= 'Z')) {
|
|
nameout[i] = 'X';
|
|
}
|
|
else {
|
|
nameout[i] = namein[i];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int encode_name(char* name) {
|
|
int total = 0;
|
|
int multi = 1;
|
|
|
|
for (int i = strlen(name) - 1; i >= 0; i--) {
|
|
total += (name[i] - 0x41) * multi;
|
|
multi *= 0x1A;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
|
|
int final_key_check(uint8_t* key_arr, char* name, uint8_t chk2, uint8_t chk1, uint32_t exp_date, uint32_t timed) {
|
|
|
|
int encoded_name = 0;
|
|
((uint8_t*)(&encoded_name))[0] = key_arr[0x5];
|
|
((uint8_t*)(&encoded_name))[1] = key_arr[0x4];
|
|
((uint8_t*)(&encoded_name))[2] = key_arr[0x3];
|
|
|
|
|
|
int encoded_time = 0;
|
|
((uint8_t*)(&encoded_time))[0] = key_arr[0x1];
|
|
((uint8_t*)(&encoded_time))[1] = key_arr[0x0];
|
|
encoded_time += 0x8EAE;
|
|
|
|
if (timed) {
|
|
if (encoded_time != (exp_date + 0x8EAE)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int is_timed = key_arr[0x2];
|
|
|
|
if (is_timed >= 0xA && is_timed <= 0xF) {
|
|
if (!timed) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (strcmp(decode_name(encoded_name), name) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint8_t chk1chk = key_arr[0x7];
|
|
uint8_t chk2chk = key_arr[0x8];
|
|
|
|
if (chk1 == chk1chk && chk2 == chk2chk) {
|
|
return 1;
|
|
}
|
|
else {
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void deobfuscate_key(uint8_t* key_arr, uint8_t* out_key, uint8_t* out_chk2, uint8_t* out_chk1) {
|
|
char pt1_key[KEY_LEN];
|
|
deobfuscate_key_pt1(key_arr, pt1_key, 0xD);
|
|
uint8_t chk2 = pt1_key[0xA];
|
|
|
|
char pt2_key[KEY_LEN];
|
|
deobfuscate_key_pt2(pt1_key, pt2_key, chk2);
|
|
|
|
|
|
char pt3_key[KEY_LEN];
|
|
deobfuscate_key_pt3(pt2_key, pt3_key, chk2);
|
|
|
|
|
|
uint8_t chk1 = pt3_key[0x9];
|
|
char pt4_key[KEY_LEN];
|
|
deobfuscate_key_pt4(pt3_key, pt4_key, chk1);
|
|
|
|
|
|
char pt5_key[KEY_LEN];
|
|
deobfuscate_key_pt5(pt4_key, pt5_key, chk1);
|
|
|
|
|
|
memcpy(out_key, pt5_key, KEY_LEN);
|
|
|
|
*out_chk2 = chk2;
|
|
*out_chk1 = chk1;
|
|
|
|
|
|
}
|
|
|
|
void obfuscate_key(uint8_t* key_arr, uint8_t* out_key, int chk2, int chk1) {
|
|
|
|
char pt5_key[KEY_LEN];
|
|
obfuscate_key_pt5(key_arr, pt5_key, chk1);
|
|
|
|
char pt4_key[KEY_LEN];
|
|
obfuscate_key_pt4(pt5_key, pt4_key, chk1);
|
|
|
|
pt4_key[0x9] = chk1;
|
|
char pt3_key[KEY_LEN];
|
|
obfuscate_key_pt3(pt4_key, pt3_key, chk2);
|
|
|
|
char pt2_key[KEY_LEN];
|
|
obfuscate_key_pt2(pt3_key, pt2_key, chk2);
|
|
pt2_key[0xA] = chk2;
|
|
|
|
char pt1_key[KEY_LEN];
|
|
obfuscate_key_pt1(pt2_key, pt1_key, 0xD);
|
|
|
|
memcpy(out_key, pt1_key, KEY_LEN);
|
|
|
|
}
|
|
|
|
int check_key_6(uint8_t* key_arr, char* name, uint32_t exp_date, uint32_t timed) {
|
|
if (check_checksum(key_arr)) {
|
|
|
|
uint8_t chk1;
|
|
uint8_t chk2;
|
|
uint8_t out_key[KEY_LEN];
|
|
|
|
deobfuscate_key(key_arr, out_key, &chk2, &chk1);
|
|
|
|
return final_key_check(out_key, name, chk2, chk1, exp_date, timed);
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void gen_key_6(char* name, uint8_t chk1, uint8_t chk2, uint32_t exp_date, uint32_t timed, uint8_t* out_key) {
|
|
|
|
uint8_t keydata[KEY_LEN];
|
|
|
|
for (int i = 0; i < KEY_LEN; i++) {
|
|
keydata[i] = rand() % 0x100;
|
|
}
|
|
|
|
keydata[0x7] = chk1;
|
|
keydata[0x8] = chk2;
|
|
|
|
int encoded_name = encode_name(name);
|
|
|
|
keydata[0x5] = ((uint8_t*)(&encoded_name))[0];
|
|
keydata[0x4] = ((uint8_t*)(&encoded_name))[1];
|
|
keydata[0x3] = ((uint8_t*)(&encoded_name))[2];
|
|
|
|
|
|
|
|
int encoded_time = exp_date - 0x8EAE;
|
|
keydata[0x1] = ((uint8_t*)(&encoded_time))[0];
|
|
keydata[0x0] = ((uint8_t*)(&encoded_time))[1];
|
|
|
|
int is_timed = rand();
|
|
if (timed) {
|
|
is_timed = (rand() % 0x6) + 0xA;
|
|
}
|
|
else {
|
|
is_timed = rand() % 0x100;
|
|
if (is_timed >= 0xA && is_timed <= 0xF)
|
|
is_timed += 0x10;
|
|
}
|
|
|
|
keydata[0x2] = is_timed;
|
|
|
|
uint8_t obfus_key[KEY_LEN];
|
|
obfuscate_key(keydata, obfus_key, chk2, chk1);
|
|
gen_checksum(obfus_key);
|
|
|
|
memcpy(out_key, obfus_key, KEY_LEN);
|
|
}
|
|
|
|
void key_to_string(uint8_t* key, char* out_str, int buffer_sz) {
|
|
snprintf(out_str, buffer_sz, "%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X", key[0x0], key[0x1], key[0x2], key[0x3], key[0x4], key[0x5], key[0x6], key[0x7], key[0x8], key[0x9], key[0xA], key[0xB]);
|
|
}
|
|
|
|
|
|
void keygen_gm6(char* name, uint8_t* out_key) {
|
|
char checked_name[6];
|
|
sanitize_name(name, checked_name);
|
|
uint8_t keybuf[KEY_LEN];
|
|
|
|
// the key data must survive the obfuscation step, while placing the chk1 and chk2 into the correct places
|
|
// in the obfuscation steps, and the final checksum byte as well,
|
|
// for this reason, key generation does not always work
|
|
// so i simply retry forever until we find a key that does work.
|
|
|
|
while (1) {
|
|
int chk1 = mark_overmars_seq[rand() % sizeof(mark_overmars_seq)];
|
|
int chk2 = rand() % 0x100;
|
|
int exp_date = rand() % 0xFFFF;
|
|
|
|
gen_key_6(checked_name, chk1, chk2, exp_date, 0, keybuf);
|
|
if (check_key_6(keybuf, checked_name, exp_date, 0)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy(out_key, keybuf, KEY_LEN);
|
|
}
|
|
#ifdef STANDALONE
|
|
int main(int argc, char* argv[]) {
|
|
srand(time(0));
|
|
char keystr[31];
|
|
uint8_t out_key[KEY_LEN];
|
|
keygen_gm6(argv[1], out_key);
|
|
key_to_string(out_key, keystr, sizeof(keystr));
|
|
printf("Key found: %s", keystr);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
// GM6 Keys
|
|
// Freeware
|
|
// 6f58-0ac7-90f2-533f-22f9-343a
|
|
// Pyrofol
|
|
// 6f58-0a44-6806-223f-22f9-3487
|
|
// Silica
|
|
// d655-6db3-4f53-43d4-a75f-6571
|