Update unpacker.py
parent
c75e471994
commit
f273f36cf5
|
@ -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 <einar@beautifier.io>
|
||||
#
|
||||
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
|
||||
#
|
||||
# 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
|
||||
|
|
Loading…
Reference in New Issue