|
1 | 1 | package dev.lavalink.youtube.cipher; |
2 | 2 |
|
3 | 3 | import org.jetbrains.annotations.NotNull; |
| 4 | +import org.slf4j.Logger; |
| 5 | +import org.slf4j.LoggerFactory; |
4 | 6 |
|
5 | 7 | import javax.script.Invocable; |
6 | 8 | import javax.script.ScriptEngine; |
7 | 9 | import javax.script.ScriptException; |
8 | 10 | import java.util.ArrayList; |
9 | 11 | import java.util.List; |
| 12 | +import java.util.regex.Matcher; |
| 13 | +import java.util.regex.Pattern; |
10 | 14 |
|
11 | 15 | /** |
12 | 16 | * Describes one signature cipher |
13 | 17 | */ |
14 | 18 | public class SignatureCipher { |
| 19 | + private static final Logger log = LoggerFactory.getLogger(SignatureCipher.class); |
| 20 | + private static final Pattern nFunctionTcePattern = Pattern.compile( |
| 21 | + "function\\s*\\((\\w+)\\)\\s*\\{var\\s*\\w+\\s*=\\s*\\1\\[\\w+\\[\\d+\\]\\]\\(\\w+\\[\\d+\\]\\)\\s*,\\s*\\w+\\s*=\\s*\\[.*?\\]\\;.*?catch\\(\\s*(\\w+)\\s*\\s*\\)\\s*\\{return\\s*\\w+\\[\\d+\\](\\+\\1)?\\}\\s*return\\s*\\w+\\[\\w+\\[\\d+\\]\\]\\(\\w+\\[\\d+\\]\\)\\}\\;", |
| 22 | + Pattern.DOTALL); |
| 23 | + private static final Pattern sigFunctionTcePattern = Pattern.compile("function\\(\\s*([a-zA-Z0-9$])\\s*\\)\\s*\\{" + |
| 24 | + "\\s*\\1\\s*=\\s*\\1\\[(\\w+)\\[\\d+\\]\\]\\(\\2\\[\\d+\\]\\);" + |
| 25 | + "([a-zA-Z0-9$]+)\\[\\2\\[\\d+\\]\\]\\(\\s*\\1\\s*,\\s*\\d+\\s*\\);" + |
| 26 | + "\\s*\\3\\[\\2\\[\\d+\\]\\]\\(\\s*\\1\\s*,\\s*\\d+\\s*\\);" + |
| 27 | + ".*?return\\s*\\1\\[\\2\\[\\d+\\]\\]\\(\\2\\[\\d+\\]\\)\\};"); |
| 28 | + private static final Pattern tceGlobalVarsPattern = Pattern.compile( |
| 29 | + "('use\\s*strict';)?" + |
| 30 | + "(?<code>var\\s*" + |
| 31 | + "(?<varname>[a-zA-Z0-9_$]+)\\s*=\\s*" + |
| 32 | + "(?<value>" + |
| 33 | + "(?:\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*')" + |
| 34 | + "\\.split\\(" + |
| 35 | + "(?:\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*')" + |
| 36 | + "\\)" + |
| 37 | + "|" + |
| 38 | + "\\[" + |
| 39 | + "(?:(?:\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*')" + |
| 40 | + "\\s*,?\\s*)*" + |
| 41 | + "\\]" + |
| 42 | + "|" + |
| 43 | + "\"[^\"]*\"\\.split\\(\"[^\"]*\"\\)" + |
| 44 | + ")" + |
| 45 | + ")"); |
| 46 | + private static final Pattern tceSigFunctionActionsPattern = Pattern.compile( |
| 47 | + "var\\s*[a-zA-Z0-9$_]+\\s*=\\s*\\{\\s*[a-zA-Z0-9$_]+\\s*:\\s*function\\((\\w+|\\s*\\w+\\s*,\\s*\\w+\\s*)\\)\\s*\\{\\s*(\\s*var\\s*\\w+=\\w+\\[\\d+\\];\\w+\\[\\d+\\]\\s*=\\s*\\w+\\[\\w+\\s*\\%\\s*\\w+\\[\\w+\\[\\d+\\]\\]\\];\\s*\\w+\\[\\w+\\s*%\\s*\\w+\\[\\w+\\[\\d+\\]\\]\\]\\s*=\\s*\\w+\\s*\\},|\\w+\\[\\w+\\[\\d+\\]\\]\\(\\)\\},)\\s*[a-zA-Z0-9$_]+\\s*:\\s*function\\((\\s*\\w+\\w*,\\s*\\w+\\s*|\\w+)\\)\\s*\\{(\\w+\\[\\w+\\[\\d+\\]\\]\\(\\)|\\s*var\\s*\\w+\\s*=\\s*\\w+\\[\\d+\\]\\s*;\\w+\\[\\d+\\]\\s*=\\w+\\[\\s*\\w+\\s*%\\s*\\w+\\[\\w+\\[\\d+\\]\\]\\]\\s*;\\w+\\[\\s*\\w+\\s*%\\s*\\w\\[\\w+\\[\\d+\\]\\]\\]\\s*=\\s*\\w+\\s*)\\},\\s*[a-zA-Z0-9$_]+\\s*:\\s*function\\s*\\(\\s*\\w+\\s*,\\s*\\w+\\s*\\)\\{\\w+\\[\\w+\\[\\d+\\]\\]\\(\\s*\\d+\\s*,\\s*\\w+\\s*\\)\\}\\};"); |
| 48 | + |
15 | 49 | private final List<CipherOperation> operations = new ArrayList<>(); |
16 | 50 | public final String nFunction; |
17 | 51 | public final String scriptTimestamp; |
18 | 52 | public final String rawScript; |
| 53 | + public final String sigFunction; |
| 54 | + public final String sigFunctionActions; |
19 | 55 |
|
20 | | - public final boolean tceScript; |
21 | | - public final String tceVars; |
| 56 | + public final TCEVariable tceVariable; |
22 | 57 |
|
23 | | - public SignatureCipher(@NotNull String nFunction, |
24 | | - @NotNull String timestamp, |
25 | | - @NotNull String rawScript, |
26 | | - boolean tceScript, |
27 | | - @NotNull String tceVars) { |
| 58 | + public SignatureCipher(@NotNull String nFunction, @NotNull String sigFunction, @NotNull String sigFunctionActions, |
| 59 | + @NotNull String timestamp, @NotNull String rawScript, @NotNull TCEVariable tceVariable) { |
28 | 60 | this.nFunction = nFunction; |
29 | 61 | this.scriptTimestamp = timestamp; |
30 | 62 | this.rawScript = rawScript; |
31 | | - this.tceScript = tceScript; |
32 | | - this.tceVars = tceVars; |
| 63 | + this.sigFunction = sigFunction; |
| 64 | + this.sigFunctionActions = sigFunctionActions; |
| 65 | + this.tceVariable = tceVariable; |
| 66 | + } |
| 67 | + |
| 68 | + public SignatureCipher(@NotNull String nFunction, @NotNull String timestamp, @NotNull String rawScript) { |
| 69 | + this.nFunction = nFunction; |
| 70 | + this.scriptTimestamp = timestamp; |
| 71 | + this.rawScript = rawScript; |
| 72 | + this.tceVariable = null; |
| 73 | + this.sigFunction = null; |
| 74 | + this.sigFunctionActions = null; |
| 75 | + } |
| 76 | + |
| 77 | + public static SignatureCipher fromRawScript(@NotNull String jsCode, @NotNull String timestamp) { |
| 78 | + log.debug("Finding tce global variable from the script..."); |
| 79 | + Matcher tceVariableMatcher = tceGlobalVarsPattern.matcher(jsCode); |
| 80 | + |
| 81 | + if (!tceVariableMatcher.find()) { |
| 82 | + log.warn("Failed to find the tce global variable..."); |
| 83 | + return null; |
| 84 | + } |
| 85 | + |
| 86 | + |
| 87 | + TCEVariable tce = new TCEVariable(tceVariableMatcher.group("varname"), tceVariableMatcher.group("code"), |
| 88 | + tceVariableMatcher.group("value")); |
| 89 | + |
| 90 | + Matcher nFunctionMatcher = nFunctionTcePattern.matcher(jsCode); |
| 91 | + |
| 92 | + if (!nFunctionMatcher.find()) { |
| 93 | + log.warn("Failed to find the tce variant n function..."); |
| 94 | + return null; |
| 95 | + } |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | + Matcher sigFunctionMatcher = sigFunctionTcePattern.matcher(jsCode); |
| 101 | + if (!sigFunctionMatcher.find()) { |
| 102 | + log.warn("Failed to find the tce variant sig function...."); |
| 103 | + return null; |
| 104 | + } |
| 105 | + |
| 106 | + Matcher sigFunctionActionsMatcher = tceSigFunctionActionsPattern.matcher(jsCode); |
| 107 | + if (!sigFunctionActionsMatcher.find()) { |
| 108 | + log.warn("Failed to find the tce variant sig function actions..."); |
| 109 | + return null; |
| 110 | + } |
| 111 | + |
| 112 | + String nFunction = nFunctionMatcher.group(0); |
| 113 | + Pattern shortCircuitPattern = Pattern.compile(String.format( |
| 114 | + ";\\s*if\\s*\\(\\s*typeof\\s+[a-zA-Z0-9_$]+\\s*===?\\s*(?:\"undefined\"|'undefined'|%s\\[\\d+\\])\\s*\\)\\s*return\\s+\\w+;", |
| 115 | + tce.getEscapedName())); |
| 116 | + Matcher tceShortCircuitMatcher = shortCircuitPattern.matcher(nFunction); |
| 117 | + if (tceShortCircuitMatcher.find()) { |
| 118 | + System.out.println("TCE global variable short circuit detected replacing nFunction..."); |
| 119 | + nFunction = nFunction.replaceAll(shortCircuitPattern.toString(), ";"); |
| 120 | + } |
| 121 | + |
| 122 | + return new SignatureCipher(nFunction, sigFunctionMatcher.group(0), sigFunctionActionsMatcher.group(0), timestamp, |
| 123 | + jsCode, tce); |
| 124 | + } |
| 125 | + |
| 126 | + public boolean isTceScript() { |
| 127 | + return this.tceVariable != null; |
| 128 | + |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * @param text Text to apply the cipher on |
| 133 | + * @return The result of the cipher on the input text |
| 134 | + */ |
| 135 | + public String apply(@NotNull String text, @NotNull ScriptEngine scriptEngine) |
| 136 | + throws ScriptException, NoSuchMethodException { |
| 137 | + String transformed; |
| 138 | + |
| 139 | + scriptEngine.eval("sig=" + sigFunction + sigFunctionActions + (isTceScript() ? tceVariable.getCode() : "")); |
| 140 | + transformed = (String) ((Invocable) scriptEngine).invokeFunction("sig", text); |
| 141 | + return transformed; |
33 | 142 | } |
34 | 143 |
|
35 | 144 | /** |
@@ -63,14 +172,15 @@ public String apply(@NotNull String text) { |
63 | 172 | } |
64 | 173 |
|
65 | 174 | /** |
66 | | - * @param text Text to transform |
| 175 | + * @param text Text to transform |
67 | 176 | * @param scriptEngine JavaScript engine to execute function |
68 | 177 | * @return The result of the n parameter transformation |
69 | 178 | */ |
70 | | - public String transform(@NotNull String text, @NotNull ScriptEngine scriptEngine) throws ScriptException, NoSuchMethodException { |
| 179 | + public String transform(@NotNull String text, @NotNull ScriptEngine scriptEngine) |
| 180 | + throws ScriptException, NoSuchMethodException { |
71 | 181 | String transformed; |
72 | 182 |
|
73 | | - scriptEngine.eval("n=" + nFunction + (tceScript ? tceVars : "")); |
| 183 | + scriptEngine.eval("n=" + nFunction + (isTceScript() ? tceVariable.getCode() : "")); |
74 | 184 | transformed = (String) ((Invocable) scriptEngine).invokeFunction("n", text); |
75 | 185 |
|
76 | 186 | return transformed; |
|
0 commit comments