From 1d314e512655bdf978b0259c2eda4f40abaa7971 Mon Sep 17 00:00:00 2001 From: zu1k Date: Tue, 16 Jun 2020 08:52:43 +0800 Subject: [PATCH 1/7] Init --- main.go | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 main.go diff --git a/main.go b/main.go new file mode 100644 index 0000000..e11362e --- /dev/null +++ b/main.go @@ -0,0 +1,242 @@ +package main + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "io/ioutil" + "strings" + "time" +) + +var ( + originPubKeyPem = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2K+PRbp/yumhaVnN92JS +GuQiwj7df64jHAo8MvXLWjYxU/yvqB4LbGty8ymKQy33qaDNpu9jgE2s8cXrtftm +/UcvwDb8sTqWXpDhxYhcvJM30agxz3/8VwNJ4JOvhk9Gn+msYIUz+gXZMBuUFKhi +BOd6C2Pro03GYwVTNjfwH/Y9C5EfPKIKNU/5t2cYo+TuOBk5ooP+NTaDzB6rb7fd +E5uuNnF21x3rdiI9rZcKPbuU97/0OWNcIUh5wfxPNWwcmjYmFuZcxk/7dOUD65s4 +pTplCoMLOelacB0l442dM4w2xNpn+Yg7i/ujmg37F+VguCZJWnoyImdhp/raccNG ++wIDAQAB +-----END PUBLIC KEY-----` + newPublicKeyPem = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtKvyFB24hIEVwMs4Xi00 +FCW41tqELGYb7f63A/lAsBPVSOvGrQ5UzuKmttatQF/IDD9UcHqqbi+B80pydiGS +eKJOaly0GuX6hfDd51/uo7E44LyzJSSBhTc1vtbL5JbNcapnxo4P6rJ1Uh9V7y8z +pRvc1G2da00mQSYoIg/9ty21j4So+Fz/v37qhK50EEIeXGJZb4uz9I9iKCHaazjI +Lf293Gzvp7EFEpZkKrh2VktKaERh+jHmJqEe0z7U/sz0cCa9ohS+TF5nxmkAZBel +CwEMXjkjGnCWO3wXJoyrXMn1GY/ilNPDFT7rSZBKLEIi7PrBD1pVLGdq2zTboenV +6wIDAQAB +-----END PUBLIC KEY-----` + newPrivateKeyPem = `-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtKvyFB24hIEVwMs4Xi00FCW41tqELGYb7f63A/lAsBPVSOvG +rQ5UzuKmttatQF/IDD9UcHqqbi+B80pydiGSeKJOaly0GuX6hfDd51/uo7E44Lyz +JSSBhTc1vtbL5JbNcapnxo4P6rJ1Uh9V7y8zpRvc1G2da00mQSYoIg/9ty21j4So ++Fz/v37qhK50EEIeXGJZb4uz9I9iKCHaazjILf293Gzvp7EFEpZkKrh2VktKaERh ++jHmJqEe0z7U/sz0cCa9ohS+TF5nxmkAZBelCwEMXjkjGnCWO3wXJoyrXMn1GY/i +lNPDFT7rSZBKLEIi7PrBD1pVLGdq2zTboenV6wIDAQABAoIBAQCu3PSxr4pVBLLP +JGFsFQggr9nUaS4f4rwJfswXlnibcratmzVxbTt7+TYuJF0OvyVZZToOm0q01lpJ +5LYfy6J+C2kl3I+csRXl6Rh8xgase+x252vj+Q86phLon/A7UBGLf8htDjYti4et +chK0KtUramozV9xSbBsoVwvk2+FOFdiLsc+B3PyuydB0Lvov5EDBtZJ1GbnyWk/3 +c++aL+lkjQbIs11A4Nwp7hUdPmM/Va8VK+DqWxbFCIr6rli5d9VOE//EHJ7S7aPp ++fxV9gyv1d0WBRNktH2t8O2JVn90379/EgWuonSlRG+HrhqZKrXIKuIFJEUmGUjs +8qJNzoERAoGBAOLjiGbXuOQHkshDqdB4xF1b4rvCBrr881dBAOnYqSyEUWuUD3et +4qX/7GaxWlSU2IteB+r5FyfEpmqUNmKVsgLkGh3lgeTU0Mss2+2xIAODNXvba8MV +UIawpvDFnLN2HEY/d+LYycBjWDk+6B1+dGlPZIxXF+8HqGnlqyBNFjP/AoGBAMva +WVB02FK4oXa8APTtvuQ2MP67Q95WdhZXdy8CEWnwJaknSTE3dXJ9nZZmFgHt54lo +KjbGfIOSCLeCqXm3ZGs5HQr2kY/xJXDJga6uNh71w66/q/W2z+30FFzta6BjYE/8 +3pB+P4vUUsp/vb3SkNfRKdcNrtoL29UYdXM7QG4VAoGAQqLw/MN+2fofchHtXf0a +LxE9lkd2EpUYIxhEXGn1xc1W3HGv2UaIuphfpgmQribJMqV7Tde6pUNsXQEKuAmf +Lpov0XgGnl6itAmIzlanQGDY5HedPr6T1/sqDKz9SPf3depOG6HwH0EOOEHxijgJ +mKRos48gyGNHY1LA38vEKaECgYEAmu8fRsknyOdOwMFvMLiphyWw40pM8OVh5uUf +TnkR5ySAWynitSdjelsCtNZuD5VTjtm+i9cbt5v8SA1k5X9/MQc9jaGNTIuJW0mr +6Km7tJgx29UNyzjgnAgQmfhQ/pvJDcIxHjz16z66lfG0slshfwYX+L0LkenFcRaf +3a7A72kCgYEAhSSGHVkCTGteSyKxhbMVqTlxQQQWZKv4b+usqss00CKgs3CAKL8H +Crds7fq96xVDVCvxJGYMKQzG61MBa+e1f8YSdhl5EY1IltlHkZstgts7avG6MP6A +xMNjyLp1b84s2VVXTpSFA7i6KEUhl4NjqhZTslJht5Dfiy2Mmvfk2so= +-----END RSA PRIVATE KEY-----` + pre2Bytes = []byte{0x00, 0x01} + pre17Bytes, _ = hex.DecodeString("01710d238a47f02c748d11d1ed906b4e1f") + aesKey = []byte{0x12, 0x23, 0x45, 0x56, 0x00, 0x87, 0x36, 0x93, 0x23, 0x86, 0x64, 0x76, 0x78, 0x67, 0x78, 0x00} + aesIv = []byte{0x71, 0x0d, 0x23, 0x8a, 0x47, 0xf0, 0x2c, 0x74, 0x8d, 0x11, 0xd1, 0xed, 0x90, 0x6b, 0x4e, 0x1f} +) + +func main() { + //parseAlready("xray-license.lic") + genNew() +} + +func parseAlready(licenseFile string) { + // 加载公钥 + pubKey := importPublicKey(originPubKeyPem) + + // 解析 xray-license.lic 文件 + licenseFileData, err := ioutil.ReadFile(licenseFile) + if err != nil { + panic(err.Error()) + } + licenseString := string(licenseFileData) + tmpStrings := strings.Split(licenseString, "\n") + licenseString = "" + for _, line := range tmpStrings { + if !strings.HasPrefix(line, "#") && line != "" { + licenseString += line + } + } + //fmt.Println("your license:", licenseString) + + decode_data, err := base64.StdEncoding.DecodeString(licenseString) + if err != nil { + panic(err) + } + //fmt.Printf("pre 17: %x\n", decode_data[:17]) + //fmt.Printf("pre 17: %x\n", pre17Bytes) + + // aes解密license + // 总长度 487,前面17个字节是单独加上的,所以总共解密出 480个字节的数据 + aesDecData, err := Decrypt(decode_data[17:]) + if err != nil { + panic(err) + } + //fmt.Printf("AES DEC: %x\n", aesDecData) + //fmt.Println(string(aesDecData)) + + // 后半部分是明文的json + licensePlainJsonBytes := aesDecData[0x102:] + //fmt.Println("license info json:", string(licensePlainJsonBytes)) + + license := License{} + err = json.Unmarshal([]byte(licensePlainJsonBytes), &license) + if err != nil { + panic(err) + } + //fmt.Println("license parsed:", license) + + // rsa 验证签名 pss + sum := sha256.Sum256(licensePlainJsonBytes) + //fmt.Println(sum) + + // rsa使用 sha256算法,对 aes解密后的数据第三个字节开始,到后面json明文前面为止是签名 + //fmt.Println("解析出来的签名:", aesDecData[2:0x102]) + + err = rsa.VerifyPSS(pubKey, crypto.SHA256, sum[:], aesDecData[2:0x102], nil) + if err != nil { + fmt.Println(err.Error()) + //panic(err.Error()) + } else { + fmt.Println("varify success") + } +} + +func genNew() { + validTime, err := time.Parse("2006-01-02 15:04:05", "2099-01-01 00:00:00") + + license := License{ + LicenseId: "ee2bf288bbbe829be29a49830f2c38ec", + UserId: "490517af5242801ed981b37ff6987c18", + UserName: "zu1k", + Distribution: "COMMUNITY-ADVANCED", + NotValidBefore: 1591891200, + NotValidAfter: validTime.Unix(), + } + + licensePlainJsonBytes, _ := json.Marshal(license) + licensePlainJson := string(licensePlainJsonBytes) + + fmt.Println("明文license信息:", licensePlainJson) + + // rsa sign + priKey := importPrivateKey(newPrivateKeyPem) + //fmt.Println(priKey) + //sha256sum + sum := sha256.Sum256(licensePlainJsonBytes) + signature, err := rsa.SignPSS(rand.Reader, priKey, crypto.SHA256, sum[:], nil) + if err != nil { + panic(err) + } + + //fmt.Println("新签名", len(signature), signature) + + licenseInfoWithSign := append(signature, licensePlainJsonBytes...) + aesEnc, err := Encrypt(append(pre2Bytes, licenseInfoWithSign...)) + if err != nil { + panic(err) + } + //fmt.Println(aesEnc) + + allBytes := append(pre17Bytes, aesEnc...) + + // 增加前17个字节的不知道干啥用的信息 + licenseText := base64.StdEncoding.EncodeToString(allBytes) + fmt.Println("你的新证书:\n", licenseText) +} + +func Decrypt(decode_data []byte) ([]byte, error) { + block, _ := aes.NewCipher(aesKey) + blockMode := cipher.NewCBCDecrypter(block, aesIv) + origin_data := make([]byte, len(decode_data)) + blockMode.CryptBlocks(origin_data, decode_data) + return unpad(origin_data), nil +} + +func unpad(ciphertext []byte) []byte { + length := len(ciphertext) + //去掉最后一次的padding + unpadding := int(ciphertext[length-1]) + return ciphertext[:(length - unpadding)] +} + +func Encrypt(text []byte) ([]byte, error) { + block, _ := aes.NewCipher(aesKey) + blockSize := block.BlockSize() + originData := pad(text, blockSize) + blockMode := cipher.NewCBCEncrypter(block, aesIv) + crypted := make([]byte, len(originData)) + blockMode.CryptBlocks(crypted, originData) + //fmt.Println(len(originData)) + return crypted, nil +} + +func pad(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +type License struct { + LicenseId string `json:"license_id"` + UserId string `json:"user_id"` + UserName string `json:"user_name"` + Distribution string `json:"distribution"` + NotValidBefore int64 `json:"not_valid_before"` + NotValidAfter int64 `json:"not_valid_after"` +} + +func importPublicKey(key string) *rsa.PublicKey { + block, _ := pem.Decode([]byte(key)) + if block == nil { + panic("unable to decode publicKey to request") + } + + pub, _ := x509.ParsePKIXPublicKey(block.Bytes) + return pub.(*rsa.PublicKey) +} + +func importPrivateKey(key string) *rsa.PrivateKey { + block, _ := pem.Decode([]byte(key)) + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } + return privateKey +} From 2f6c2b97fcc60472de7a6ebd25e60a87e62e47d1 Mon Sep 17 00:00:00 2001 From: zu1k Date: Tue, 16 Jun 2020 17:45:27 +0800 Subject: [PATCH 2/7] Add xray crack and license gen --- go.mod | 3 +++ main.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..48104d7 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/zu1k/xray-crack + +go 1.14 diff --git a/main.go b/main.go index e11362e..c30db74 100644 --- a/main.go +++ b/main.go @@ -13,8 +13,10 @@ import ( "encoding/hex" "encoding/json" "encoding/pem" + "flag" "fmt" "io/ioutil" + "os" "strings" "time" ) @@ -71,9 +73,29 @@ xMNjyLp1b84s2VVXTpSFA7i6KEUhl4NjqhZTslJht5Dfiy2Mmvfk2so= aesIv = []byte{0x71, 0x0d, 0x23, 0x8a, 0x47, 0xf0, 0x2c, 0x74, 0x8d, 0x11, 0xd1, 0xed, 0x90, 0x6b, 0x4e, 0x1f} ) +var ( + genLicense bool + crackXray bool + xrayFile string +) + func main() { + fmt.Println(`破解xray高级版证书,使用 -h 参数查看使用帮助`) + + flag.BoolVar(&genLicense, "g", false, "生成一个永久license") + flag.StringVar(&xrayFile, "c", "", "替换xray程序内置公钥,需要指定xray程序文件路径") + + flag.Parse() + if genLicense { + genNew() + } + + if xrayFile != "" { + replacePublicKeyInXray(xrayFile) + } + //parseAlready("xray-license.lic") - genNew() + //genNew() } func parseAlready(licenseFile string) { @@ -139,21 +161,21 @@ func parseAlready(licenseFile string) { } func genNew() { - validTime, err := time.Parse("2006-01-02 15:04:05", "2099-01-01 00:00:00") + validTime, err := time.Parse("2006-01-02 15:04:05", "2099-09-09 01:09:09") license := License{ - LicenseId: "ee2bf288bbbe829be29a49830f2c38ec", - UserId: "490517af5242801ed981b37ff6987c18", - UserName: "zu1k", + LicenseId: "00000000000000000000000000000000", + UserId: "00000000000000000000000000000000", + UserName: "Chinese", Distribution: "COMMUNITY-ADVANCED", NotValidBefore: 1591891200, NotValidAfter: validTime.Unix(), } licensePlainJsonBytes, _ := json.Marshal(license) - licensePlainJson := string(licensePlainJsonBytes) + //licensePlainJson := string(licensePlainJsonBytes) - fmt.Println("明文license信息:", licensePlainJson) + //fmt.Println("明文license信息:", licensePlainJson) // rsa sign priKey := importPrivateKey(newPrivateKeyPem) @@ -178,7 +200,23 @@ func genNew() { // 增加前17个字节的不知道干啥用的信息 licenseText := base64.StdEncoding.EncodeToString(allBytes) - fmt.Println("你的新证书:\n", licenseText) + //fmt.Println("你的新证书:\n") + //fmt.Println(licenseText) + + fileText := `# xray license +# 需要重命名为 xray-license.lic 和 xray 可执行程序放在同一个文件夹中 +# user_name: Chinese +# distribution: COMMUNITY-ADVANCED +# 仅对修改后的xray有效 + + +` + licenseText + ` +` + + err = ioutil.WriteFile("xray-license.lic", []byte(fileText), os.ModePerm) + if err == nil { + fmt.Println("证书已写入文件:xray-license.lic") + } } func Decrypt(decode_data []byte) ([]byte, error) { @@ -240,3 +278,24 @@ func importPrivateKey(key string) *rsa.PrivateKey { } return privateKey } + +func replacePublicKeyInXray(filePath string) { + origin, err := ioutil.ReadFile(filePath) + if err != nil { + panic(err) + } + + //originPubKeyPem = strings.ReplaceAll(originPubKeyPem, "\r\n", "\n") + originPubKeyPemBytes := []byte(originPubKeyPem) + newPubKeyPemBytes := []byte(newPublicKeyPem) + //fmt.Printf("%x\n", originPubKeyPemBytes) + + loc := bytes.Index(origin, originPubKeyPemBytes) + fmt.Println("public key index:", loc) + + newFile := bytes.ReplaceAll(origin, originPubKeyPemBytes, newPubKeyPemBytes) + err = ioutil.WriteFile(filePath, newFile, os.ModePerm) + if err == nil { + fmt.Println("文件写入成功:", filePath) + } +} From cc589ae6703203a46a22f71ebd8ce3a884dbfc89 Mon Sep 17 00:00:00 2001 From: zu1k Date: Thu, 18 Jun 2020 09:23:27 +0800 Subject: [PATCH 3/7] add readme --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 37 +++++++++++++++-------- 2 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..df8a469 --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# XRAY CRACKER + +前几天长亭官方有个活动,可以领2个月的xray社区高级版证书,正好趁这个机会逆向分析了一下xray的证书算法,写了一个证书生成器 + +因为xray证书用到了rsa算法,所以需要替换xray程序中的公钥,将该功能也集成在工具中了 + +相关算法分析文章后面有空再写,这里先放出写好的工具 + +## 工具使用 + +### 查看帮助 + +使用 `-h` 查看帮助 + +``` +PS > .\xray-cracker -h +破解xray高级版证书,使用 -h 参数查看使用帮助 + +Usage of xray-cracker: + -c string + 替换xray程序内置公钥,需要指定xray程序文件路径 + -g string + 生成一个永久license,需要指定用户名 + -p string + 解析官方证书,需要指定证书路径 +``` + +### 生成证书 + +使用 `-g username` 生成永久证书 + +``` +PS > .\xray-cracker -g "我叫啥" +破解xray高级版证书,使用 -h 参数查看使用帮助 + +证书已写入文件:xray-license.lic +``` + +### 破解xray + +使用 `-c path-to-xray` 修改xray内置公钥 + +``` +PS > .\xray-cracker -c .\xray_windows_amd64.exe +破解xray高级版证书,使用 -h 参数查看使用帮助 + +public key index: 16741321 +文件写入成功: .\xray_windows_amd64.exe +``` + +> 工具虽然是windows平台下运行,但是照样可以破解其他平台xray +> 目前xray最新版是1.0.0,现在全平台全版本通杀 + + +## 破解效果 + +使用修改版xray和永久证书后,效果如下 + +``` +PS > .\xray_windows_amd64.exe version + + __ __ _____ __ __ + \ \ / / | __ \ /\ \ \ / / + \ V / | |__) | / \ \ \_/ / + > < | _ / / /\ \ \ / + / . \ | | \ \ / ____ \ | | + /_/ \_\ |_| \_\ /_/ \_\ |_| + + +Version: 1.0.0/62161168/COMMUNITY-ADVANCED +Licensed to 我叫啥, license is valid until 2099-09-09 08:00:00 + +[xray 1.0.0/62161168] +Build: [2020-06-13] [windows/amd64] [RELEASE/COMMUNITY-ADVANCED] +Compiler Version: go version go1.14.1 linux/amd64 +License ID: 00000000000000000000000000000000 +User Name: 我叫啥/00000000000000000000000000000000 +Not Valid Before: 2020-06-12 00:00:00 +Not Valid After: 2099-09-09 08:00:00 +``` + +## 工具下载 + +下载链接: {% asset_link xray-cracker.zip [xray-cracker.zip] %} + +> 解压密码请邮件联系我索取 + +为了不至于过于泛滥,给工具增加了一个有效期,在 2021年以前有效 + diff --git a/main.go b/main.go index c30db74..c96e7ef 100644 --- a/main.go +++ b/main.go @@ -74,20 +74,33 @@ xMNjyLp1b84s2VVXTpSFA7i6KEUhl4NjqhZTslJht5Dfiy2Mmvfk2so= ) var ( - genLicense bool - crackXray bool - xrayFile string + licenseName string + xrayFile string + originLicense string ) func main() { - fmt.Println(`破解xray高级版证书,使用 -h 参数查看使用帮助`) + fmt.Println(`破解xray高级版证书,使用 -h 参数查看使用帮助 +本工具有效期到 2021-01-01 +`) + validTime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + nowTime := time.Now() + if nowTime.After(validTime) { + panic("本工具已失效") + } - flag.BoolVar(&genLicense, "g", false, "生成一个永久license") + flag.StringVar(&licenseName, "g", "", "生成一个永久license,需要指定用户名") flag.StringVar(&xrayFile, "c", "", "替换xray程序内置公钥,需要指定xray程序文件路径") + flag.StringVar(&originLicense, "p", "", "解析官方证书,需要指定证书路径") flag.Parse() - if genLicense { - genNew() + + if originLicense != "" { + parseAlready(originLicense) + } + + if licenseName != "" { + genNew(licenseName) } if xrayFile != "" { @@ -142,7 +155,7 @@ func parseAlready(licenseFile string) { if err != nil { panic(err) } - //fmt.Println("license parsed:", license) + fmt.Println("license parsed:", license) // rsa 验证签名 pss sum := sha256.Sum256(licensePlainJsonBytes) @@ -153,20 +166,20 @@ func parseAlready(licenseFile string) { err = rsa.VerifyPSS(pubKey, crypto.SHA256, sum[:], aesDecData[2:0x102], nil) if err != nil { - fmt.Println(err.Error()) + //fmt.Println(err.Error()) //panic(err.Error()) } else { fmt.Println("varify success") } } -func genNew() { - validTime, err := time.Parse("2006-01-02 15:04:05", "2099-09-09 01:09:09") +func genNew(name string) { + validTime, err := time.Parse("2006-01-02 15:04:05", "2099-09-09 00:00:00") license := License{ LicenseId: "00000000000000000000000000000000", UserId: "00000000000000000000000000000000", - UserName: "Chinese", + UserName: name, Distribution: "COMMUNITY-ADVANCED", NotValidBefore: 1591891200, NotValidAfter: validTime.Unix(), From 18ebac2b3729d543dfc81988a5bdd5f45a7d2615 Mon Sep 17 00:00:00 2001 From: zu1k <42370281+zu1k@users.noreply.github.com> Date: Thu, 18 Jun 2020 09:26:11 +0800 Subject: [PATCH 4/7] Update README.md --- README.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/README.md b/README.md index df8a469..c083122 100644 --- a/README.md +++ b/README.md @@ -78,12 +78,3 @@ User Name: 我叫啥/00000000000000000000000000000000 Not Valid Before: 2020-06-12 00:00:00 Not Valid After: 2099-09-09 08:00:00 ``` - -## 工具下载 - -下载链接: {% asset_link xray-cracker.zip [xray-cracker.zip] %} - -> 解压密码请邮件联系我索取 - -为了不至于过于泛滥,给工具增加了一个有效期,在 2021年以前有效 - From 7e40c1813c9ecb89b82e386113756e3064ac962a Mon Sep 17 00:00:00 2001 From: zu1k <42370281+zu1k@users.noreply.github.com> Date: Thu, 27 Aug 2020 17:55:57 +0800 Subject: [PATCH 5/7] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c083122..dd21686 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # XRAY CRACKER +> 支持到 1.2.0 版本,官方在 1.3.0 版本中更换了授权验证机制 + 前几天长亭官方有个活动,可以领2个月的xray社区高级版证书,正好趁这个机会逆向分析了一下xray的证书算法,写了一个证书生成器 因为xray证书用到了rsa算法,所以需要替换xray程序中的公钥,将该功能也集成在工具中了 From d13166b3cd012ec1fa6c8c76e346b47cee340597 Mon Sep 17 00:00:00 2001 From: zu1k Date: Wed, 30 Sep 2020 15:08:01 +0800 Subject: [PATCH 6/7] new keygen --- README.md | 83 +++++++++++------------------------ main.go | 127 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 92 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index dd21686..a4d974e 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,49 @@ # XRAY CRACKER -> 支持到 1.2.0 版本,官方在 1.3.0 版本中更换了授权验证机制 - -前几天长亭官方有个活动,可以领2个月的xray社区高级版证书,正好趁这个机会逆向分析了一下xray的证书算法,写了一个证书生成器 - -因为xray证书用到了rsa算法,所以需要替换xray程序中的公钥,将该功能也集成在工具中了 - -相关算法分析文章后面有空再写,这里先放出写好的工具 - -## 工具使用 - -### 查看帮助 - -使用 `-h` 查看帮助 - -``` -PS > .\xray-cracker -h -破解xray高级版证书,使用 -h 参数查看使用帮助 - -Usage of xray-cracker: - -c string - 替换xray程序内置公钥,需要指定xray程序文件路径 - -g string - 生成一个永久license,需要指定用户名 - -p string - 解析官方证书,需要指定证书路径 -``` - ### 生成证书 使用 `-g username` 生成永久证书 ``` -PS > .\xray-cracker -g "我叫啥" -破解xray高级版证书,使用 -h 参数查看使用帮助 +# ./xray-cracker -g kali 证书已写入文件:xray-license.lic ``` -### 破解xray - -使用 `-c path-to-xray` 修改xray内置公钥 - -``` -PS > .\xray-cracker -c .\xray_windows_amd64.exe -破解xray高级版证书,使用 -h 参数查看使用帮助 +### 破解 xray -public key index: 16741321 -文件写入成功: .\xray_windows_amd64.exe -``` +目前 PubKey 是加密过的,加密算法很简单,但是那个函数是硬编码了几百个局部变量,替换一波后,两个一对进行加、减、异或等操作进行还原,看样子是用代码生成的加密函数代码然后编译的,如果花时间在这上面可能是个死局,因为可以在每一次编译前都重新生成一下代码 -> 工具虽然是windows平台下运行,但是照样可以破解其他平台xray -> 目前xray最新版是1.0.0,现在全平台全版本通杀 +所以我选择从其他地方入手,很明显公钥是用来验证签名的,如此看来直接修改签名验证函数的返回值就行,golang 中 VerifyPSS 返回一个 err,如果`err==nil`就代表签名没问题,放在汇编里就是 `test 某寄存器` 然后 `setz`或`setnz`,改一下就行 +我目前的能力还无法实现用程序对不同平台不同系统的二进制进行这个修改,还没学习怎么写,请大神们各显神通吧 ## 破解效果 -使用修改版xray和永久证书后,效果如下 +使用修改版 xray 和永久证书后,效果如下 ``` -PS > .\xray_windows_amd64.exe version +# ./xray_linux_amd64 version - __ __ _____ __ __ - \ \ / / | __ \ /\ \ \ / / - \ V / | |__) | / \ \ \_/ / - > < | _ / / /\ \ \ / - / . \ | | \ \ / ____ \ | | - /_/ \_\ |_| \_\ /_/ \_\ |_| +____ ___.________. ____. _____.___. +\ \/ /\_ __ \ / _ \ \__ | | + \ / | _ _/ / /_\ \ / | | + / \ | | \/ | \ \____ | +\___/\ \ |____| /\____|_ / / _____/ + \_/ \_/ \_/ \/ +Version: 1.3.3/1d166d72/COMMUNITY-ADVANCED +Licensed to kali, license is valid until 2099-09-08 19:00:00 -Version: 1.0.0/62161168/COMMUNITY-ADVANCED -Licensed to 我叫啥, license is valid until 2099-09-09 08:00:00 - -[xray 1.0.0/62161168] -Build: [2020-06-13] [windows/amd64] [RELEASE/COMMUNITY-ADVANCED] -Compiler Version: go version go1.14.1 linux/amd64 +[INFO] 2020-09-29 00:20:20 [default:entry.go:122] set file descriptor limit to 10000 +[INFO] 2020-09-29 00:20:20 [default:entry.go:157] loading config file from /home/kali/tools/scan/xray/config.yaml +[xray 1.3.3/1d166d72] +Build: [2020-09-17] [linux/amd64] [RELEASE/COMMUNITY-ADVANCED] +Compiler Version: go version go1.14.4 linux/amd64 License ID: 00000000000000000000000000000000 -User Name: 我叫啥/00000000000000000000000000000000 -Not Valid Before: 2020-06-12 00:00:00 -Not Valid After: 2099-09-09 08:00:00 +User Name: kali/00000000000000000000000000000000 +Not Valid Before: 2020-06-11 12:00:00 +Not Valid After: 2099-09-08 19:00:00 + +To show open source licenses, please use `osslicense` sub-command. ``` diff --git a/main.go b/main.go index c96e7ef..408bb5e 100644 --- a/main.go +++ b/main.go @@ -67,22 +67,17 @@ TnkR5ySAWynitSdjelsCtNZuD5VTjtm+i9cbt5v8SA1k5X9/MQc9jaGNTIuJW0mr Crds7fq96xVDVCvxJGYMKQzG61MBa+e1f8YSdhl5EY1IltlHkZstgts7avG6MP6A xMNjyLp1b84s2VVXTpSFA7i6KEUhl4NjqhZTslJht5Dfiy2Mmvfk2so= -----END RSA PRIVATE KEY-----` - pre2Bytes = []byte{0x00, 0x01} - pre17Bytes, _ = hex.DecodeString("01710d238a47f02c748d11d1ed906b4e1f") - aesKey = []byte{0x12, 0x23, 0x45, 0x56, 0x00, 0x87, 0x36, 0x93, 0x23, 0x86, 0x64, 0x76, 0x78, 0x67, 0x78, 0x00} - aesIv = []byte{0x71, 0x0d, 0x23, 0x8a, 0x47, 0xf0, 0x2c, 0x74, 0x8d, 0x11, 0xd1, 0xed, 0x90, 0x6b, 0x4e, 0x1f} + licenseVersion2Byte = []byte{0x02} + pre2Bytes = []byte{0x00, 0x01} + aesKeyNew, _ = hex.DecodeString("B293C506E0C7F60353C604961837B810") ) var ( licenseName string - xrayFile string originLicense string ) func main() { - fmt.Println(`破解xray高级版证书,使用 -h 参数查看使用帮助 -本工具有效期到 2021-01-01 -`) validTime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") nowTime := time.Now() if nowTime.After(validTime) { @@ -90,7 +85,6 @@ func main() { } flag.StringVar(&licenseName, "g", "", "生成一个永久license,需要指定用户名") - flag.StringVar(&xrayFile, "c", "", "替换xray程序内置公钥,需要指定xray程序文件路径") flag.StringVar(&originLicense, "p", "", "解析官方证书,需要指定证书路径") flag.Parse() @@ -102,13 +96,6 @@ func main() { if licenseName != "" { genNew(licenseName) } - - if xrayFile != "" { - replacePublicKeyInXray(xrayFile) - } - - //parseAlready("xray-license.lic") - //genNew() } func parseAlready(licenseFile string) { @@ -130,25 +117,52 @@ func parseAlready(licenseFile string) { } //fmt.Println("your license:", licenseString) - decode_data, err := base64.StdEncoding.DecodeString(licenseString) + base64DecodeData, err := base64.StdEncoding.DecodeString(licenseString) if err != nil { panic(err) } - //fmt.Printf("pre 17: %x\n", decode_data[:17]) + + //fmt.Println("base64 decode data:", hex.EncodeToString(base64DecodeData)) + + licenseVersion := base64DecodeData[0] + if licenseVersion == 2 { + fmt.Println("version ok: 2") + } + + //fmt.Printf("pre 17: %x\n", base64DecodeData[:17]) //fmt.Printf("pre 17: %x\n", pre17Bytes) + //解密前有一个简单的变换处理 + right := len(base64DecodeData) - 1 + for l := 1; l < right; l++ { + r := right - l + if l >= r { + break + } + base64DecodeData[l], base64DecodeData[r] = base64DecodeData[r], base64DecodeData[l] + } + //fmt.Println("trans bytes:", hex.EncodeToString(base64DecodeData)) + // aes解密license // 总长度 487,前面17个字节是单独加上的,所以总共解密出 480个字节的数据 - aesDecData, err := Decrypt(decode_data[17:]) + aesDecData, err := Decrypt(base64DecodeData[17:], base64DecodeData[1:17]) if err != nil { panic(err) } //fmt.Printf("AES DEC: %x\n", aesDecData) //fmt.Println(string(aesDecData)) + //另一个异或变换 + for i := 0; i < len(aesDecData); i++ { + aesDecData[i] = aesDecData[i] ^ 0x44 + } + //fmt.Println("trans 2 :", hex.EncodeToString(aesDecData)) + //fmt.Println("trans 2 string:", string(aesDecData)) + // 后半部分是明文的json licensePlainJsonBytes := aesDecData[0x102:] //fmt.Println("license info json:", string(licensePlainJsonBytes)) + //fmt.Println("pre2bytes:", hex.EncodeToString(aesDecData[:0x2])) license := License{} err = json.Unmarshal([]byte(licensePlainJsonBytes), &license) @@ -166,8 +180,7 @@ func parseAlready(licenseFile string) { err = rsa.VerifyPSS(pubKey, crypto.SHA256, sum[:], aesDecData[2:0x102], nil) if err != nil { - //fmt.Println(err.Error()) - //panic(err.Error()) + fmt.Println(err.Error()) } else { fmt.Println("varify success") } @@ -187,12 +200,11 @@ func genNew(name string) { licensePlainJsonBytes, _ := json.Marshal(license) //licensePlainJson := string(licensePlainJsonBytes) - //fmt.Println("明文license信息:", licensePlainJson) // rsa sign priKey := importPrivateKey(newPrivateKeyPem) - //fmt.Println(priKey) + //sha256sum sum := sha256.Sum256(licensePlainJsonBytes) signature, err := rsa.SignPSS(rand.Reader, priKey, crypto.SHA256, sum[:], nil) @@ -200,21 +212,38 @@ func genNew(name string) { panic(err) } - //fmt.Println("新签名", len(signature), signature) - licenseInfoWithSign := append(signature, licensePlainJsonBytes...) - aesEnc, err := Encrypt(append(pre2Bytes, licenseInfoWithSign...)) + data2Enc := append(pre2Bytes, licenseInfoWithSign...) + + // 加密前一次异或 + for i := 0; i < len(data2Enc); i++ { + data2Enc[i] = data2Enc[i] ^ 0x44 + } + + // session iv + iv := make([]byte, 16) + _, _ = rand.Read(iv) + fmt.Println("temp aes iv:", hex.EncodeToString(iv)) + aesEnc, err := Encrypt(data2Enc, iv) if err != nil { panic(err) } //fmt.Println(aesEnc) - allBytes := append(pre17Bytes, aesEnc...) + allBytes := append(iv, aesEnc...) + allBytes = append(licenseVersion2Byte, allBytes...) + + // 左右交换 + right := len(allBytes) - 1 + for l := 1; l < right; l++ { + r := right - l + if l >= r { + break + } + allBytes[l], allBytes[r] = allBytes[r], allBytes[l] + } - // 增加前17个字节的不知道干啥用的信息 licenseText := base64.StdEncoding.EncodeToString(allBytes) - //fmt.Println("你的新证书:\n") - //fmt.Println(licenseText) fileText := `# xray license # 需要重命名为 xray-license.lic 和 xray 可执行程序放在同一个文件夹中 @@ -232,26 +261,25 @@ func genNew(name string) { } } -func Decrypt(decode_data []byte) ([]byte, error) { - block, _ := aes.NewCipher(aesKey) - blockMode := cipher.NewCBCDecrypter(block, aesIv) - origin_data := make([]byte, len(decode_data)) - blockMode.CryptBlocks(origin_data, decode_data) +func Decrypt(decodeData []byte, iv []byte) ([]byte, error) { + block, _ := aes.NewCipher(aesKeyNew) + blockMode := cipher.NewCBCDecrypter(block, iv) + origin_data := make([]byte, len(decodeData)) + blockMode.CryptBlocks(origin_data, decodeData) return unpad(origin_data), nil } func unpad(ciphertext []byte) []byte { length := len(ciphertext) - //去掉最后一次的padding unpadding := int(ciphertext[length-1]) return ciphertext[:(length - unpadding)] } -func Encrypt(text []byte) ([]byte, error) { - block, _ := aes.NewCipher(aesKey) +func Encrypt(text []byte, iv []byte) ([]byte, error) { + block, _ := aes.NewCipher(aesKeyNew) blockSize := block.BlockSize() originData := pad(text, blockSize) - blockMode := cipher.NewCBCEncrypter(block, aesIv) + blockMode := cipher.NewCBCEncrypter(block, iv) crypted := make([]byte, len(originData)) blockMode.CryptBlocks(crypted, originData) //fmt.Println(len(originData)) @@ -291,24 +319,3 @@ func importPrivateKey(key string) *rsa.PrivateKey { } return privateKey } - -func replacePublicKeyInXray(filePath string) { - origin, err := ioutil.ReadFile(filePath) - if err != nil { - panic(err) - } - - //originPubKeyPem = strings.ReplaceAll(originPubKeyPem, "\r\n", "\n") - originPubKeyPemBytes := []byte(originPubKeyPem) - newPubKeyPemBytes := []byte(newPublicKeyPem) - //fmt.Printf("%x\n", originPubKeyPemBytes) - - loc := bytes.Index(origin, originPubKeyPemBytes) - fmt.Println("public key index:", loc) - - newFile := bytes.ReplaceAll(origin, originPubKeyPemBytes, newPubKeyPemBytes) - err = ioutil.WriteFile(filePath, newFile, os.ModePerm) - if err == nil { - fmt.Println("文件写入成功:", filePath) - } -} From 61b3cbdf737a2a1c0530dbbf6ee567c05f1173a6 Mon Sep 17 00:00:00 2001 From: Lz1y Date: Sat, 3 Oct 2020 14:36:33 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=87=AA?= =?UTF-8?q?=E5=8A=A8patch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 57 +++++++++++++++++++++++++++++++++---------------------- main.go | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a4d974e..a0766d5 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,37 @@ 使用 `-g username` 生成永久证书 -``` -# ./xray-cracker -g kali - -证书已写入文件:xray-license.lic -``` - -### 破解 xray - -目前 PubKey 是加密过的,加密算法很简单,但是那个函数是硬编码了几百个局部变量,替换一波后,两个一对进行加、减、异或等操作进行还原,看样子是用代码生成的加密函数代码然后编译的,如果花时间在这上面可能是个死局,因为可以在每一次编译前都重新生成一下代码 - -所以我选择从其他地方入手,很明显公钥是用来验证签名的,如此看来直接修改签名验证函数的返回值就行,golang 中 VerifyPSS 返回一个 err,如果`err==nil`就代表签名没问题,放在汇编里就是 `test 某寄存器` 然后 `setz`或`setnz`,改一下就行 - -我目前的能力还无法实现用程序对不同平台不同系统的二进制进行这个修改,还没学习怎么写,请大神们各显神通吧 +### Patch Xray +使用 `-c xray_path` 修改xray ## 破解效果 -使用修改版 xray 和永久证书后,效果如下 - ``` -# ./xray_linux_amd64 version +H:\xray-crack (master -> origin) +λ go build . + +H:\xray-crack (master -> origin) +λ xray-crack.exe -h +Usage of xray-crack.exe: + -c string + patch xray,需要指定xray程序文件路径 + -g string + 生成一个永久license,需要指定用户名 + -p string + 解析官方证书,需要指定证书路径 + +H:\xray-crack (master -> origin) +λ xray-crack.exe -c xray_windows_386.exe +Signature index: 0x92919b +Patch success: xray_windows_386.exe + +H:\xray-crack (master -> origin) +λ xray-crack.exe -g Lz1y +temp aes iv: 0f79b747bb60a29c82d8f4da079a767b +证书已写入文件:xray-license.lic + +H:\xray-crack (master -> origin) +λ xray_windows_386.exe version ____ ___.________. ____. _____.___. \ \/ /\_ __ \ / _ \ \__ | | @@ -33,17 +44,17 @@ ____ ___.________. ____. _____.___. \_/ \_/ \_/ \/ Version: 1.3.3/1d166d72/COMMUNITY-ADVANCED -Licensed to kali, license is valid until 2099-09-08 19:00:00 +Licensed to Lz1y, license is valid until 2099-09-09 08:00:00 -[INFO] 2020-09-29 00:20:20 [default:entry.go:122] set file descriptor limit to 10000 -[INFO] 2020-09-29 00:20:20 [default:entry.go:157] loading config file from /home/kali/tools/scan/xray/config.yaml +Generate default configurations to config.yaml +[INFO] 2020-10-03 14:33:14 [default:entry.go:157] loading config file from H:\xray-crack\config.yaml [xray 1.3.3/1d166d72] -Build: [2020-09-17] [linux/amd64] [RELEASE/COMMUNITY-ADVANCED] +Build: [2020-09-17] [windows/386] [RELEASE/COMMUNITY-ADVANCED] Compiler Version: go version go1.14.4 linux/amd64 License ID: 00000000000000000000000000000000 -User Name: kali/00000000000000000000000000000000 -Not Valid Before: 2020-06-11 12:00:00 -Not Valid After: 2099-09-08 19:00:00 +User Name: Lz1y/00000000000000000000000000000000 +Not Valid Before: 2020-06-12 00:00:00 +Not Valid After: 2099-09-09 08:00:00 To show open source licenses, please use `osslicense` sub-command. ``` diff --git a/main.go b/main.go index 408bb5e..41e9e00 100644 --- a/main.go +++ b/main.go @@ -75,6 +75,7 @@ xMNjyLp1b84s2VVXTpSFA7i6KEUhl4NjqhZTslJht5Dfiy2Mmvfk2so= var ( licenseName string originLicense string + xrayPath string ) func main() { @@ -86,6 +87,7 @@ func main() { flag.StringVar(&licenseName, "g", "", "生成一个永久license,需要指定用户名") flag.StringVar(&originLicense, "p", "", "解析官方证书,需要指定证书路径") + flag.StringVar(&xrayPath, "c", "", "patch xray,需要指定xray程序文件路径") flag.Parse() @@ -96,6 +98,48 @@ func main() { if licenseName != "" { genNew(licenseName) } + + if xrayPath != "" { + patchXray(xrayPath) + } +} + +func patchXray(filePath string) { + var ( + originBytes []byte + newBytes []byte + ) + origin, err := ioutil.ReadFile(filePath) + if err != nil { + panic(err) + } + //x86特征码: 0F 95 C0 83 F0 01 88 44 24 50 83 C4 30 C3 + originX86Bytes := []byte{0x0F, 0x95, 0xC0, 0x83, 0xF0, 0x01, 0x88, 0x44, 0x24, 0x50, 0x83, 0xC4, 0x30, 0xC3} + newX86Bytes := []byte{0x0F, 0x94, 0xC0, 0x83, 0xF0, 0x01, 0x88, 0x44, 0x24, 0x50, 0x83, 0xC4, 0x30, 0xC3} + loc := bytes.LastIndex(origin, originX86Bytes) + //x64特征码: 0F 94 84 24 A8 00 00 00 + if loc == -1 { + originX64Bytes := []byte{0x0F, 0x94, 0x84, 0x24, 0xA8, 0x00, 0x00, 0x00} + newX64Bytes := []byte{0x0F, 0x95, 0x84, 0x24, 0xA8, 0x00, 0x00, 0x00} + loc = bytes.LastIndex(origin, originX64Bytes) + if loc == -1 { + fmt.Println("Patch failed: maybe already patched.") + os.Exit(0) + } + originBytes = originX64Bytes + newBytes = newX64Bytes + } else { + originBytes = originX86Bytes + newBytes = newX86Bytes + } + + fmt.Printf("Signature index: %#x\n", loc) + + newFile := bytes.ReplaceAll(origin, originBytes, newBytes) + err = ioutil.WriteFile(filePath, newFile, os.ModePerm) + if err == nil { + fmt.Println("Patch success:", filePath) + } } func parseAlready(licenseFile string) {