corrade-nucleus-nucleons – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
2 | office | 1 | # |
2 | # Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier |
||
3 | # by Einar Lielmanis <einar@jsbeautifier.org> |
||
4 | # |
||
5 | # written by Stefano Sanfilippo <a.little.coder@gmail.com> |
||
6 | # |
||
7 | # usage: |
||
8 | # |
||
9 | # if detect(some_string): |
||
10 | # unpacked = unpack(some_string) |
||
11 | # |
||
12 | |||
13 | """Unpacker for Dean Edward's p.a.c.k.e.r""" |
||
14 | |||
15 | import re |
||
16 | import string |
||
17 | from jsbeautifier.unpackers import UnpackingError |
||
18 | |||
19 | PRIORITY = 1 |
||
20 | |||
21 | def detect(source): |
||
22 | """Detects whether `source` is P.A.C.K.E.R. coded.""" |
||
23 | return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,') |
||
24 | |||
25 | def unpack(source): |
||
26 | """Unpacks P.A.C.K.E.R. packed js code.""" |
||
27 | payload, symtab, radix, count = _filterargs(source) |
||
28 | |||
29 | if count != len(symtab): |
||
30 | raise UnpackingError('Malformed p.a.c.k.e.r. symtab.') |
||
31 | |||
32 | try: |
||
33 | unbase = Unbaser(radix) |
||
34 | except TypeError: |
||
35 | raise UnpackingError('Unknown p.a.c.k.e.r. encoding.') |
||
36 | |||
37 | def lookup(match): |
||
38 | """Look up symbols in the synthetic symtab.""" |
||
39 | word = match.group(0) |
||
40 | return symtab[unbase(word)] or word |
||
41 | |||
42 | source = re.sub(r'\b\w+\b', lookup, payload) |
||
43 | return _replacestrings(source) |
||
44 | |||
45 | def _filterargs(source): |
||
46 | """Juice from a source file the four args needed by decoder.""" |
||
47 | juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"), |
||
48 | (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"), |
||
49 | ] |
||
50 | for juicer in juicers: |
||
51 | args = re.search(juicer, source, re.DOTALL) |
||
52 | if args: |
||
53 | a = args.groups() |
||
54 | try: |
||
55 | return a[0], a[3].split('|'), int(a[1]), int(a[2]) |
||
56 | except ValueError: |
||
57 | raise UnpackingError('Corrupted p.a.c.k.e.r. data.') |
||
58 | |||
59 | # could not find a satisfying regex |
||
60 | raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)') |
||
61 | |||
62 | |||
63 | |||
64 | def _replacestrings(source): |
||
65 | """Strip string lookup table (list) and replace values in source.""" |
||
66 | match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL) |
||
67 | |||
68 | if match: |
||
69 | varname, strings = match.groups() |
||
70 | startpoint = len(match.group(0)) |
||
71 | lookup = strings.split('","') |
||
72 | variable = '%s[%%d]' % varname |
||
73 | for index, value in enumerate(lookup): |
||
74 | source = source.replace(variable % index, '"%s"' % value) |
||
75 | return source[startpoint:] |
||
76 | return source |
||
77 | |||
78 | |||
79 | class Unbaser(object): |
||
80 | """Functor for a given base. Will efficiently convert |
||
81 | strings to natural numbers.""" |
||
82 | ALPHABET = { |
||
83 | 53 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ', |
||
84 | 59 : '0123456789abcdefghijklmnopqrstuvwABCDEFGHIJKLMNOPQRSTUVWXYZ', |
||
85 | 62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', |
||
86 | 95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
||
87 | '[\]^_`abcdefghijklmnopqrstuvwxyz{|}~') |
||
88 | } |
||
89 | |||
90 | def __init__(self, base): |
||
91 | self.base = base |
||
92 | |||
93 | # If base can be handled by int() builtin, let it do it for us |
||
94 | if 2 <= base <= 36: |
||
95 | self.unbase = lambda string: int(string, base) |
||
96 | else: |
||
97 | # Build conversion dictionary cache |
||
98 | try: |
||
99 | self.dictionary = dict((cipher, index) for |
||
100 | index, cipher in enumerate(self.ALPHABET[base])) |
||
101 | except KeyError: |
||
102 | raise TypeError('Unsupported base encoding.') |
||
103 | |||
104 | self.unbase = self._dictunbaser |
||
105 | |||
106 | def __call__(self, string): |
||
107 | return self.unbase(string) |
||
108 | |||
109 | def _dictunbaser(self, string): |
||
110 | """Decodes a value to an integer.""" |
||
111 | ret = 0 |
||
112 | for index, cipher in enumerate(string[::-1]): |
||
113 | ret += (self.base ** index) * self.dictionary[cipher] |
||
114 | return ret |