Skip to content
This repository has been archived by the owner on Sep 28, 2020. It is now read-only.

Update Decipherer #245

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 78 additions & 93 deletions YoutubeExtractor/YoutubeExtractor/Decipherer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
Expand All @@ -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<int> 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<int> decodeArray = new List<int>();

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+,(?<index>\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+,(?<index>\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<int> arr)
{
return operations.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(cipher, ApplyOperation);
}
var sigA = sig;

private static string GetFunctionFromLine(string currentLine)
{
Regex matchFunctionReg = new Regex(@"\w+\.(?<functionID>\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;
}
}
}