Skip to content

Commit

Permalink
fix(kuwo): use node to calc secret (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
foamzou committed Mar 2, 2024
1 parent e35a09b commit e859c1b
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 113 deletions.
2 changes: 1 addition & 1 deletion LATEST_VERSION
@@ -1 +1 @@
0.2.12
0.2.13
170 changes: 61 additions & 109 deletions processor/kuwo/js.go
@@ -1,144 +1,96 @@
package kuwo

import (
"fmt"
"crypto/md5"
"encoding/hex"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/dop251/goja"
"github.com/foamzou/audio-get/logger"
"github.com/foamzou/audio-get/utils"
)

var codeJS = `
// the function is for fix 1.7118116959910114e+99 -> 1.71181169599101e+99
function normalizeParseIntToString(numberString) {
const match = numberString.match(/^(\d+\.\d{14})(\d*)e([+-]?\d+)$/);
if (match) {
return match[1] + "e" + match[3];
}
return numberString;
}
function encryptMessage(input, password) {
const debugList = [];
const dl = (m) => {
debugList.push(m);
}
const ed = () => {
return debugList.join("\n");
}
if (password == null || password.length === 0) {
function h(t, e) {
if (e == null || e.length <= 0) {
console.log("Please enter a password with which to encrypt the message.");
return null;
}
let numericPassword = "";
for (let i = 0; i < password.length; i++) {
numericPassword += password.charCodeAt(i).toString();
for (var n = "", i = 0; i < e.length; i++) {
n += e.charCodeAt(i).toString();
}
const r = Math.floor(numericPassword.length / 5);
const o = parseInt(
numericPassword.charAt(r) +
numericPassword.charAt(2 * r) +
numericPassword.charAt(3 * r) +
numericPassword.charAt(4 * r) +
numericPassword.charAt(5 * r)
);
const l = Math.ceil(password.length / 2);
const c = Math.pow(2, 31) - 1;
var r = Math.floor(n.length / 5);
var o = parseInt(n.charAt(r) + n.charAt(r * 2) + n.charAt(r * 3) + n.charAt(r * 4) + n.charAt(r * 5));
var l = Math.ceil(e.length / 2);
var c = Math.pow(2, 31) - 1;
if (o < 2) {
console.log(
"Algorithm cannot find a suitable hash. Please choose a different password.\nPossible considerations are to choose a more complex or longer password."
);
console.log("Algorithm cannot find a suitable hash. Please choose a different password. \nPossible considerations are to choose a more complex or longer password.");
return null;
}
let randomNum = Math.round(1e9 * Math.random()) % 1e8;
randomNum = 91610361; // fixed this value
numericPassword += randomNum.toString();
let counter = 0;
while (numericPassword.length > 10) {
dl("sss|" + numericPassword)
numericPassword = normalizeParseIntToString((
parseInt(numericPassword.substring(0, 10)) +
parseInt(numericPassword.substring(10))
).toString());
dl("eee|" + numericPassword)
if (counter++ > 20) {
break;
}
}
dl(numericPassword.toString())
numericPassword = (o * numericPassword + l) % c;
dl(numericPassword.toString())
let encryptedMessage = "";
let hexValue = "";
for (let i = 0; i < input.length; i++) {
hexValue = parseInt(input.charCodeAt(i) ^ Math.floor((numericPassword / c) * 255));
if (hexValue < 16) {
encryptedMessage += "0" + hexValue.toString(16);
} else {
encryptedMessage += hexValue.toString(16);
}
numericPassword = (o * numericPassword + l) % c;
var d = Math.round(Math.random() * 1000000000) % 100000000;
for (n += d; n.length > 10;) {
n = (parseInt(n.substring(0, 10)) + parseInt(n.substring(10, n.length))).toString();
}
randomNum = randomNum.toString(16);
while (randomNum.length < 8) {
randomNum = "0" + randomNum;
n = (o * n + l) % c;
var h = "";
var f = "";
for (i = 0; i < t.length; i++) {
f += (h = parseInt(t.charCodeAt(i) ^ Math.floor(n / c * 255))) < 16 ? "0" + h.toString(16) : h.toString(16);
n = (o * n + l) % c;
}
dl(randomNum)
//return ed();
return encryptedMessage + randomNum;
for (d = d.toString(16); d.length < 8;) {
d = "0" + d;
}
return f += d;
}
function findCookieValue(cookie, name) {
const cookieString = cookie;
const cookieName = name + '=';
let startIndex = cookieString.indexOf(cookieName);
if (startIndex !== -1) {
startIndex = startIndex + cookieName.length;
let endIndex = cookieString.indexOf(";", startIndex);
if (endIndex === -1) {
endIndex = cookieString.length;
}
return decodeURIComponent(cookieString.substring(startIndex, endIndex));
function v(t, cookie) {
const e = cookie;
let n = e.indexOf(t + "=");
if (n !== -1) {
n = n + t.length + 1;
let r = e.indexOf(";", n);
return r === -1 && (r = e.length), unescape(e.substring(n, r));
}
return null;
}
function main(cookie, hmCookie) {
return encryptMessage(findCookieValue(cookie, hmCookie), hmCookie);
}
function main(cookie, hmCookie) {
dd = v(hmCookie, cookie)
return h(dd, hmCookie);
}
console.log(main(process.argv[2], process.argv[3]));
`

// genSecretHeader
// hmCookie format: Hm_Iuvt_cdb524f42f0cer9b268e4v7y735ewrq2324
func genSecretHeader(cookie string, hmCookie string) string {
vm := goja.New()
// 计算jsCode的MD5哈希值
hasher := md5.New()
hasher.Write([]byte(codeJS))
// 将哈希值转换为十六进制并得到文件名
md5Hash := hex.EncodeToString(hasher.Sum(nil))
filename := filepath.Join(os.TempDir(), "media-get-"+md5Hash+".js")

_, err := vm.RunString(codeJS) // executes a script on the global context
if err != nil {
logger.Warn("genSecretHeader failed", err)
return ""
// 如果文件不存在则创建
if _, err := os.Stat(filename); os.IsNotExist(err) {
err := ioutil.WriteFile(filename, []byte(codeJS), 0644)
if err != nil {
logger.Error("Failed to create file", filename, ":", err)
return ""
}
}
val, err := vm.RunString(fmt.Sprintf("main('%s', '%s')", cookie, hmCookie))
secret, err := utils.ExecCmd("node", filename, cookie, hmCookie)
if err != nil {
logger.Warn("genSecretHeader failed", err)
logger.Error("Failed to execute js code:", err)
return ""
}
return strings.Trim(secret, "\n")
}

//nolint
return val.Export().(string)
func nodeEnvAvailable() bool {
_, err := utils.ExecCmd("node", "-v")
return err == nil
}
5 changes: 4 additions & 1 deletion processor/kuwo/kuwo.go
Expand Up @@ -13,7 +13,7 @@ import (

const (
InfoAPI = "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=%d&httpsStatus=1&reqId=%s"
PlayAPI = "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1&reqId=%s"
PlayAPI = "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1&reqId=%s&plat=web_www"
)

type Core struct {
Expand All @@ -33,6 +33,9 @@ func (c *Core) GetSourceName() string {
}

func (c *Core) FetchMetaAndResourceInfo() (mediaMeta *meta.MediaMeta, err error) {
if !nodeEnvAvailable() {
return nil, errors.New("node env is not available")
}
songID, err := getSongID(c.Opts.Url)
if err != nil {
return nil, err
Expand Down
14 changes: 14 additions & 0 deletions utils/cmd.go
@@ -0,0 +1,14 @@
package utils

import (
"fmt"
"os/exec"
)

func ExecCmd(program string, arg ...string) (string, error) {
out, err := exec.Command(program, arg...).CombinedOutput()
if err != nil {
return "", fmt.Errorf("error: %w, output: %s", err, out)
}
return string(out), nil
}
42 changes: 42 additions & 0 deletions utils/cmd_test.go
@@ -0,0 +1,42 @@
package utils

import "testing"

func TestExecCmd(t *testing.T) {
type args struct {
cmd string
arg1 string
arg2 string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "Test echo",
args: args{cmd: "echo", arg1: "hello", arg2: "foam"},
want: "hello foam\n",
wantErr: false,
},
{
name: "Test error",
args: args{cmd: "error"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ExecCmd(tt.args.cmd, tt.args.arg1, tt.args.arg2)
if (err != nil) != tt.wantErr {
t.Errorf("ExecCmd() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ExecCmd() got = %v, want %v", got, tt.want)
}
})
}
}
4 changes: 2 additions & 2 deletions version/version.go
Expand Up @@ -5,8 +5,8 @@ import (
)

const (
BuildCode = 17
BuildName = "0.2.12"
BuildCode = 18
BuildName = "0.2.13"
Repo = "https://github.com/foamzou/media-get"
)

Expand Down

0 comments on commit e859c1b

Please sign in to comment.