diff --git a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs index e5ac261..bdf396c 100644 --- a/YoutubeExtractor/YoutubeExtractor/Decipherer.cs +++ b/YoutubeExtractor/YoutubeExtractor/Decipherer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -12,132 +13,116 @@ public static string DecipherWithVersion(string cipher, string cipherVersion) string jsUrl = string.Format("http://s.ytimg.com/yts/jsbin/player-{0}.js", cipherVersion); string js = HttpHelper.DownloadString(jsUrl); - //Find "C" in this: var A = B.sig||C (B.s) - string functNamePattern = @"\""signature"",\s?([a-zA-Z0-9\$]+)\("; //Regex Formed To Find Word or DollarSign + var decodeArray = FindSignatureCode(js); - var funcName = Regex.Match(js, functNamePattern).Groups[1].Value; - - if (funcName.Contains("$")) - { - funcName = "\\" + funcName; //Due To Dollar Sign Introduction, Need To Escape - } + return DecryptSignature(cipher, decodeArray); + } - string funcPattern = @"(?!h\.)" + @funcName + @"=function\(\w+\)\{.*?\}"; //Escape funcName string - var funcBody = Regex.Match(js, funcPattern, RegexOptions.Singleline).Value; //Entire sig function - var lines = funcBody.Split(';'); //Each line in sig function + private static List FindSignatureCode(string sourceCode) + { + var signatureFunctionName = FindMatch(sourceCode, @"\.set\s*\(""signature""\s*,\s*([a-zA-Z0-9_$][\w$]*)\("); - string idReverse = "", idSlice = "", idCharSwap = ""; //Hold name for each cipher method - string functionIdentifier = ""; - string operations = ""; + //Optimization of Gantt's technique - functionName not needed + var regExp = @"\s*\([\w$]*\)\s*{[\w$]*=[\w$]*\.split\(""""\);\n*(.+);return [\w$]*\.join"; - foreach (var line in lines.Skip(1).Take(lines.Length - 2)) //Matches the funcBody with each cipher method. Only runs till all three are defined. - { - if (!string.IsNullOrEmpty(idReverse) && !string.IsNullOrEmpty(idSlice) && - !string.IsNullOrEmpty(idCharSwap)) - { - break; //Break loop if all three cipher methods are defined - } + var reverseFunctionName = FindMatch(sourceCode, @"([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.reverse\s*\(\s*\)\s*}"); + var sliceFunctionName = FindMatch(sourceCode, @"([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*,\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.(?:slice|splice)\(.+\)\s*}"); - functionIdentifier = GetFunctionFromLine(line); - string reReverse = string.Format(@"{0}:\bfunction\b\(\w+\)", functionIdentifier); //Regex for reverse (one parameter) - string reSlice = string.Format(@"{0}:\bfunction\b\([a],b\).(\breturn\b)?.?\w+\.", functionIdentifier); //Regex for slice (return or not) - string reSwap = string.Format(@"{0}:\bfunction\b\(\w+\,\w\).\bvar\b.\bc=a\b", functionIdentifier); //Regex for the char swap. + var functionCode = FindMatch(sourceCode, regExp); + functionCode = functionCode.Replace(reverseFunctionName, "reverse"); + functionCode = functionCode.Replace(sliceFunctionName, "slice"); + var functionCodePieces = functionCode.Split(';'); - if (Regex.Match(js, reReverse).Success) - { - idReverse = functionIdentifier; //If def matched the regex for reverse then the current function is a defined as the reverse - } + List decodeArray = new List(); - if (Regex.Match(js, reSlice).Success) - { - idSlice = functionIdentifier; //If def matched the regex for slice then the current function is defined as the slice. - } + var regSlice = new Regex("slice\\s*\\(\\s*.+([0-9]+)\\s*\\)"); + string regSwap = "\\w+\\s*\\(\\s*\\w+\\s*,\\s*([0-9]+)\\s*\\)"; + string regInline = "\\w+\\[0\\]\\s*=\\s*\\w+\\[([0-9]+)\\s*%\\s*\\w+\\.length\\]"; - if (Regex.Match(js, reSwap).Success) - { - idCharSwap = functionIdentifier; //If def matched the regex for charSwap then the current function is defined as swap. - } - } - - foreach (var line in lines.Skip(1).Take(lines.Length - 2)) + for (var i = 0; i < functionCodePieces.Length; i++) { - Match m; - functionIdentifier = GetFunctionFromLine(line); - if ((m = Regex.Match(line, @"\(\w+,(?\d+)\)")).Success && functionIdentifier == idCharSwap) - { - operations += "w" + m.Groups["index"].Value + " "; //operation is a swap (w) - } + functionCodePieces[i] = functionCodePieces[i].Trim(); - if ((m = Regex.Match(line, @"\(\w+,(?\d+)\)")).Success && functionIdentifier == idSlice) - { - operations += "s" + m.Groups["index"].Value + " "; //operation is a slice - } + var codeLine = functionCodePieces[i]; - if (functionIdentifier == idReverse) //No regex required for reverse (reverse method has no parameters) + if (codeLine.Length > 0) { - operations += "r "; //operation is a reverse - } - } - operations = operations.Trim(); + var arrSlice = regSlice.Match(codeLine); - return DecipherWithOperations(cipher, operations); - } - - private static string ApplyOperation(string cipher, string op) - { - switch (op[0]) - { - case 'r': - return new string(cipher.ToCharArray().Reverse().ToArray()); - - case 'w': + if (arrSlice.Success && arrSlice.Length >= 2) { - int index = GetOpIndex(op); - return SwapFirstChar(cipher, index); + var slice = int.Parse(arrSlice.Groups[1].Value); + decodeArray.Add(-slice); } - - case 's': + else if (functionCodePieces[i].IndexOf("reverse") >= 0) { - int index = GetOpIndex(op); - return cipher.Substring(index); + decodeArray.Add(0); + } + else if (codeLine.IndexOf("[0]") >= 0) + { // inline swap + + if (i + 2 < functionCodePieces.Length && functionCodePieces[i + 1].IndexOf(".length") >= 0 && functionCodePieces[i + 1].IndexOf("[0]") >= 0) + { + + var inline = FindMatch(functionCodePieces[i + 1], regInline); + decodeArray.Add(int.Parse(inline)); + + i += 2; + + } } + else if (codeLine.IndexOf(',') >= 0) + { // swap + var swap = FindMatch(codeLine, regSwap); + int swapVal = int.Parse(swap); - default: - throw new NotImplementedException("Couldn't find cipher operation."); + if (swapVal > 0) + { + decodeArray.Add(swapVal); + } + + } + } } + return decodeArray; } - private static string DecipherWithOperations(string cipher, string operations) + private static string DecryptSignature(string sig, List arr) { - return operations.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries) - .Aggregate(cipher, ApplyOperation); - } + var sigA = sig; - private static string GetFunctionFromLine(string currentLine) - { - Regex matchFunctionReg = new Regex(@"\w+\.(?\w+)\("); //lc.ac(b,c) want the ac part. - Match rgMatch = matchFunctionReg.Match(currentLine); - string matchedFunction = rgMatch.Groups["functionID"].Value; - return matchedFunction; //return 'ac' + for (var i = 0; i < arr.Count; i++) + { + var act = arr[i]; + sigA = (act > 0) ? Swap(sigA.ToCharArray(), act) : ((act == 0) ? Reverse(sigA) : sigA.Substring(-act)); + } + + return sigA; } - private static int GetOpIndex(string op) + private static string Swap(char[] a, int b) { - string parsed = new Regex(@".(\d+)").Match(op).Result("$1"); - int index = Int32.Parse(parsed); + var c = a[0]; + a[0] = a[b % a.Length]; + a[b] = c; - return index; + return new string(a); } - private static string SwapFirstChar(string cipher, int index) + private static string Reverse(string s) + { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + private static string FindMatch(string text, string regexp) { - var builder = new StringBuilder(cipher); - builder[0] = cipher[index]; - builder[index] = cipher[0]; + Regex rgx = new Regex(regexp); + var matches = rgx.Matches(text); - return builder.ToString(); + return matches.Count > 0 ? matches[0].Groups[1].Value : string.Empty; } } } \ No newline at end of file