navigaid
3/17/2019 - 9:14 AM

Mathematica Keygen Algorithm Study

Mathematica Keygen Algorithm Study

package main

import "fmt"
import "math"
import "strconv"
import "os"

func init() {
	if len(os.Args) != 3 {
		fmt.Fprintln(os.Stderr, "wrong number of args")
		os.Exit(1)
	}
	mathId = os.Args[1]
	activationKey = os.Args[2]
}

// var mathId = document.getElementById("str").value;
var mathId string

// var activationKey = document.getElementById("act").value;
var activationKey string

func f1(n int, b byte, c int) int {
	for bitIndex := 0; bitIndex <= 7; bitIndex++ {
		bit := (b >> uint8(bitIndex)) & 1
		if int(bit)+((n-int(bit)) & ^1) == n {
			n = (n - int(bit)) >> 1
		} else {
			n = ((c - int(bit)) ^ n) >> 1
		}
	}
	return n
}

// str is fmt.Sprintf("%s$1&%s", mathId, activationKey)
func genPassword(mathId, activationKey string) string {
	str := fmt.Sprintf("%s$1&%s", mathId, activationKey)
	hash := 0xA439
	for byteIndex := len(str) - 1; byteIndex >= 0; byteIndex -= 1 {
		hash = f1(hash, byte(str[byteIndex]), 0x105C3)
	}
	n1 := 0
	for f1(f1(hash, byte(n1&0xFF), 0x105C3), byte(n1>>8), 0x105C3) != 0xA5B6 {
		n1 += 1
		if n1 >= 0xFFFF {
			panic(fmt.Errorf("Error"))
		}
	}
	n1 = int(math.Floor(float64((n1+0x72FA)&0xFFFF) * 99999.0 / 0xFFFF))
	n1str := fmt.Sprintf("0000%d", n1)
	n1str = n1str[len(n1str)-5:]
	temp, _ := strconv.Atoi(n1str[0:2] + n1str[3:] + n1str[2:3]) // 12 + 45 + 3
	temp = int(math.Ceil((float64(temp) / 99999.0) * 0xFFFF))
	temp = f1(f1(0, byte(temp&0xFF), 0x1064B), byte(temp>>8), 0x1064B)
	for byteIndex := len(str) - 1; byteIndex >= 0; byteIndex-- {
		temp = f1(temp, byte(str[byteIndex]), 0x1064B)
	}

	n2 := 0
	for f1(f1(temp, byte(n2&0xFF), 0x1064B), byte(n2>>8), 0x1064B) != 0xA5B6 {
		n2 += 1
		if n2 >= 0xFFFF {
			panic(fmt.Errorf("Error"))
		}
	}
	n2 = int(math.Floor(float64(n2&0xFFFF) * 99999.0 / 0xFFFF))
	n2str := fmt.Sprintf("0000%d", n2)
	n2str = n2str[len(n2str)-5:]

	return n2str[3:4] +
		n1str[3:4] +
		n1str[1:2] +
		n1str[0:1] +
		"-" +
		n2str[4:5] +
		n1str[2:3] +
		n2str[0:1] +
		"-" +
		n2str[2:3] +
		n1str[4:5] +
		n2str[1:2] +
		"::1"
}

func main() {
	fmt.Println(genPassword(mathId, activationKey))
}
function f1(n, byte, c) {
    for (var bitIndex = 0; bitIndex <= 7; bitIndex++) {
        var bit = (byte >> bitIndex) & 1;
        if (bit + ((n - bit) & ~1) === n) {
            n = (n - bit) >> 1;
        } else {
            n = ((c - bit) ^ n) >> 1;
        }
    }
    return n;
}

function genPassword(str) {
    var hash = 0xA439;
    for (var byteIndex = str.length - 1; byteIndex >= 0; byteIndex--) {
        hash = f1(hash, str.charCodeAt(byteIndex), 0x105C3);
    }
    var n1 = 0;
    while (f1(f1(hash, n1 & 0xFF, 0x105C3), n1 >> 8, 0x105C3) !== 0xA5B6) {
        if (++n1 >= 0xFFFF) {
            return "Error";
        }
    }
    n1 = Math.floor(((n1 + 0x72FA) & 0xFFFF) * 99999.0 / 0xFFFF);
    var n1str = ("0000" + n1.toString(10)).slice(-5);
    var temp = parseInt(n1str.slice(0, -3) + n1str.slice(-2) + n1str.slice(-3, -2), 10);
    temp = Math.ceil((temp / 99999.0) * 0xFFFF);
    temp = f1(f1(0, temp & 0xFF, 0x1064B), temp >> 8, 0x1064B);
    for (byteIndex = str.length - 1; byteIndex >= 0; byteIndex--) {
        temp = f1(temp, str.charCodeAt(byteIndex), 0x1064B);
    }
    var n2 = 0;
    while (f1(f1(temp, n2 & 0xFF, 0x1064B), n2 >> 8, 0x1064B) !== 0xA5B6) {
        if (++n2 >= 0xFFFF) {
            return "Error";
        }
    }
    n2 = Math.floor((n2 & 0xFFFF) * 99999.0 / 0xFFFF);
    var n2str = ("0000" + n2.toString(10)).slice(-5);
    return n2str.charAt(3) + n1str.charAt(3) + n1str.charAt(1) + n1str.charAt(0) + "-"
        + n2str.charAt(4) + n1str.charAt(2) + n2str.charAt(0) + "-"
        + n2str.charAt(2) + n1str.charAt(4) + n2str.charAt(1) + "::1";
}

console.log("%s", genPassword("6525-69419-44485"+"$1&"+"1234-4321-123456"))
$ go run keygen.go 6525-69419-44485 1234-4321-123456
0795-616-891::1

$ nodejs keygen.js                                                            
0795-616-891::1

Keygen Story


Github Keywords:


LEXUGE

LEXUGE also starred the same repo. Chatted with him for a while. (didn't know if will work for 11.3)


orsoon

Tempted to pay, but refrained. Noticed the combo

5109-97652-26690 // mathid
1234-4321-123456 // password
172******62::1   // activation key

Is it the same keygen as hexinal/mathematica-11.2-keygen? No. It's very likely they have a newer one! But where does that come from?


My Journey Begins

Suppose they use the same html template. The only thing different from mine is the hash. (which is the only actionable assumption I could make)

Given mathid, password I need 65536 hash values to generate all the possible activation keys.

If any key match the pattern 172******62::1 we can work back to the hash value

so I made the first brute forcer mma.js

it turned out the hash is 0xA439 !


Couldn't wait to show off my discovery!!!

but let me calm down and search for more interesting stuff...


a ustc student also mentioned 0xA439 in the comment. He said it's for 11.2

https://github.com/basavyr/ubuntu_vbox_dft/commit/1a632762d92fb39c210ef1743bf17e8674080035

How did he know that?

Another idea came to my mind...


mma11_2_keygen
mma11_2_keygen_32.exe
mma11_2_keygen_64.exe

I don't know who made it. But this generate 11 keys for a mathid:password combo

Maybe for a single mathematica version there exist multiple valid hashes?

Let me verify...


first step I generate 11 different activation keys using the mathId:activationKey combo from my archlinux host

6525-69419-44485 (archlinux host mathid)
1234-4321-123456 (hardcoded in binary)
5375-466-225::1
9094-108-092::1
4146-893-451::1
2260-689-169::1
5704-996-938::1
0795-616-891::1
6660-223-582::1
6103-942-608::1
6071-209-223::1
4511-125-476::1
1722-241-405::1

Results

0x29C2
0x29F8
0x2FDB
0x44F1
0x6A90
0xA439 <- this is the orsoon hash! (11.3 ok)
0xA68B
0xD226
0xDB74
0xE4A7
0xEE71
0x6A91 <- hexinal/mathematica-11.2-keygen (doesn't work in 11.3)

Older versions (unverified)

0x42DD <- 11.1 maybe
0x25DB <- Mathematica 11.0.1

0x6A91 mathematica

https://lfbg75wjgi4nzdio.tor.onl/f/crypto/17312

https://rutracker.org/forum/viewtopic.php?t=5454755 https://arlanow.ru/mathematica/ https://aeternumstudios.com/lukalafaye/Projects/Hacking/Mathematica11/Files/crack.html http://comsics.usm.my/tlyoon/teaching/ZCE111/1617SEM2/notes/briefing.pdf https://translate.googleusercontent.com/translate_c?act=url&depth=1&hl=en&ie=UTF8&prev=_t&rurl=translate.google.com&sl=auto&sp=nmt4&tl=en&u=https://arlanow.ru/mathematica/&xid=17259,15700002,15700021,15700105,15700124,15700149,15700168,15700173,15700186,15700201&usg=ALkJrhgfUH4iG7NMtfp1Gg7Un13k-f49CQ

github "// inverse of f2: f1(f2(x, y, c), c, y, c) == x"