diff --git a/anime_downloader/sites/helpers/unpacker.py b/anime_downloader/sites/helpers/unpacker.py index 86b3318..179f39b 100644 --- a/anime_downloader/sites/helpers/unpacker.py +++ b/anime_downloader/sites/helpers/unpacker.py @@ -1,170 +1,10 @@ -# -# taken from https://github.com/beautify-web/js-beautify/blob/main/python/jsbeautifier/unpackers/packer.py -# -# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier -# by Einar Lielmanis -# -# written by Stefano Sanfilippo -# -# usage: -# -# if detect_packed_js(some_string): -# unpacked = unpack_packed_js(some_string) -# +from jsbeautifier.unpackers import javascriptobfuscator, myobfuscate, packer -"""Unpacker for Dean Edward's p.a.c.k.e.r""" - -import re -import string -import sys +UNPACKERS = [javascriptobfuscator, myobfuscate, packer] -class UnpackingError(Exception): - """Badly packed source or general error. Argument is a - meaningful description.""" - - pass - - -PRIORITY = 1 - - -def detect_packed_js(source): - global beginstr - global endstr - beginstr = "" - endstr = "" - begin_offset = -1 - """Detects whether `source` is P.A.C.K.E.R. coded.""" - mystr = re.search( - "eval[ ]*\([ ]*function[ ]*\([ ]*p[ ]*,[ ]*a[ ]*,[ ]*c[" - " ]*,[ ]*k[ ]*,[ ]*e[ ]*,[ ]*", - source, - ) - if mystr: - begin_offset = mystr.start() - beginstr = source[:begin_offset] - if begin_offset != -1: - """ Find endstr""" - source_end = source[begin_offset:] - if source_end.split("')))", 1)[0] == source_end: - try: - endstr = source_end.split("}))", 1)[1] - except IndexError: - endstr = "" - else: - endstr = source_end.split("')))", 1)[1] - return mystr is not None - - -def unpack_packed_js(source): - """Unpacks P.A.C.K.E.R. packed js code.""" - payload, symtab, radix, count = _filterargs(source) - - if count != len(symtab): - raise UnpackingError("Malformed p.a.c.k.e.r. symtab.") - - try: - unbase = Unbaser(radix) - except TypeError: - raise UnpackingError("Unknown p.a.c.k.e.r. encoding.") - - def lookup(match): - """Look up symbols in the synthetic symtab.""" - word = match.group(0) - return symtab[unbase(word)] or word - - payload = payload.replace("\\\\", "\\").replace("\\'", "'") - if sys.version_info.major == 2: - source = re.sub(r"\b\w+\b", lookup, payload) - else: - source = re.sub(r"\b\w+\b", lookup, payload, flags=re.ASCII) - return _replacestrings(source) - - -def _filterargs(source): - """Juice from a source file the four args needed by decoder.""" - juicers = [ - (r"}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"), - (r"}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)"), - ] - for juicer in juicers: - args = re.search(juicer, source, re.DOTALL) - if args: - a = args.groups() - if a[1] == "[]": - a = list(a) - a[1] = 62 - a = tuple(a) - try: - return a[0], a[3].split("|"), int(a[1]), int(a[2]) - except ValueError: - raise UnpackingError("Corrupted p.a.c.k.e.r. data.") - - # could not find a satisfying regex - raise UnpackingError( - "Could not make sense of p.a.c.k.e.r data (unexpected code structure)" - ) - - -def _replacestrings(source): - global beginstr - global endstr - """Strip string lookup table (list) and replace values in source.""" - match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL) - - if match: - varname, strings = match.groups() - startpoint = len(match.group(0)) - lookup = strings.split('","') - variable = "%s[%%d]" % varname - for index, value in enumerate(lookup): - source = source.replace(variable % index, '"%s"' % value) - return source[startpoint:] - return beginstr + source + endstr - - -class Unbaser(object): - """Functor for a given base. Will efficiently convert - strings to natural numbers.""" - - ALPHABET = { - 62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - 95: ( - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - ), - } - - def __init__(self, base): - self.base = base - - # fill elements 37...61, if necessary - if 36 < base < 62: - if not hasattr(self.ALPHABET, self.ALPHABET[62][:base]): - self.ALPHABET[base] = self.ALPHABET[62][:base] - # attrs = self.ALPHABET - # print ', '.join("%s: %s" % item for item in attrs.items()) - # If base can be handled by int() builtin, let it do it for us - if 2 <= base <= 36: - self.unbase = lambda string: int(string, base) - else: - # Build conversion dictionary cache - try: - self.dictionary = dict( - (cipher, index) for index, cipher in enumerate(self.ALPHABET[base]) - ) - except KeyError: - raise TypeError("Unsupported base encoding.") - - self.unbase = self._dictunbaser - - def __call__(self, string): - return self.unbase(string) - - def _dictunbaser(self, string): - """Decodes a value to an integer.""" - ret = 0 - for index, cipher in enumerate(string[::-1]): - ret += (self.base ** index) * self.dictionary[cipher] - return ret +def unpack(js): + for unpacker in UNPACKERS: + if unpacker.detect(js): + return unpacker.unpack(js) + return js