1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
#
# 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(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
import re
import string
import sys
from jsbeautifier.unpackers import UnpackingError
PRIORITY = 1
def detect(source):
global beginstr
global endstr
beginstr = ""
endstr = ""
begin_offset = -1
"""Detects whether `source` is P.A.C.K.E.R. coded."""
mystr = re.search(
r"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(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
|