pof icon indicating copy to clipboard operation
pof copied to clipboard

Import Submodule NamesObfuscator error

Open loqpa opened this issue 1 year ago • 2 comments

While playing around with example python API provided by the docs I have encountered an error in NamesObfuscator.

Take following source as example:

import urllib.request
from urllib.error import URLError, HTTPError

url = 'http://example.com/'

with urllib.request.urlopen(url) as response:
    html = response.read()
    html = html.decode('utf-8')
print(html)

While trying to obfuscate the code the following error is thrown:

Traceback (most recent call last):
  File "pof_obfuscator.py", line 117, in <module>
    open('pof_testing.py','w').write(obfuscate(source))
                                  ~~~~~~~~~^^^^^^^^
  File "pof_obfuscator.py", line 101, in obfuscate
    tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)
  File "venv/lib/python3.13/site-packages/pof/obfuscator/names.py", line 493, in obfuscate_tokens
    return self.import_as(result, new_names)
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "venv/lib/python3.13/site-packages/pof/obfuscator/names.py", line 343, in import_as
    raise e
  File "venv/lib/python3.13/site-packages/pof/obfuscator/names.py", line 320, in import_as
    n.name = inew_names[n.name]
             ~~~~~~~~~~^^^^^^^^
KeyError: 'oL_9_VR62F._238'

To understand the issue a little more I added couple of debug prints, here is the contents of new_names variable:

{'urllib': 'oL_9_VR62F', 'request': '_238', 'error': 'errnos', 'URLError': 'blob', 'HTTPError': 'mod_name', 'url': 'stdev', 'urlopen': 'tag_remove', 'response': 'listobj', 'html': 'writeHeader'}

It looks like AST library threats urllib.request as a single token, while the pof code splits it into two.

loqpa avatar Feb 07 '25 16:02 loqpa

The error is coming from the line in the Python API example:

okens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)

This is a known bug in POF and there is some explanations in pof/main.py.

Here is a full example (fixed) of the API usage:

import random

from pof import BaseObfuscator
from pof.obfuscator import (
    BuiltinsObfuscator,
    CommentsObfuscator,
    ConstantsObfuscator,
    ExceptionObfuscator,
    GlobalsObfuscator,
    LoggingObfuscator,
    # NamesObfuscator,
    NumberObfuscator,
    PrintObfuscator,
    StringsObfuscator,
)
from pof.utils.extract_names import NameExtract
from pof.utils.generator import AdvancedGenerator, BaseGenerator, BasicGenerator


class ExampleObfuscator(BaseObfuscator):
    def obfuscate(self, source):
        tokens = self._get_tokens(source)

        # get all the names and add them to the RESERVED_WORDS for the generators
        reserved_words_add = NameExtract.get_names(tokens)
        BaseGenerator.extend_reserved(reserved_words_add)

        tokens = CommentsObfuscator().obfuscate_tokens(tokens)
        tokens = LoggingObfuscator().obfuscate_tokens(tokens)
        tokens = PrintObfuscator().obfuscate_tokens(tokens)
        ex_generator = BasicGenerator.number_name_generator()
        tokens = ExceptionObfuscator(
            add_codes=True,
            generator=ex_generator,
        ).obfuscate_tokens(tokens)

        # configure generator
        gen_dict = {
            86: AdvancedGenerator.realistic_generator(),
            10: BasicGenerator.alphabet_generator(),
            4: BasicGenerator.number_name_generator(length=random.randint(2, 5)),
        }
        generator = AdvancedGenerator.multi_generator(gen_dict)

        # core obfuscation
        tokens = ConstantsObfuscator(
            generator=generator,
            obf_number_rate=0.7,
            obf_string_rate=0.1,
            obf_builtins_rate=0.3,
        ).obfuscate_tokens(tokens)

        # FIXME: broken for the moment
        # tokens = NamesObfuscator(generator=generator).obfuscate_tokens(tokens)

        tokens = GlobalsObfuscator().obfuscate_tokens(tokens)
        tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)

        b64decode_name = next(generator)
        b85decode_name = next(generator)
        string_obfuscator = StringsObfuscator(
            import_b64decode=True,
            import_b85decode=True,
            b64decode_name=b64decode_name,
            b85decode_name=b85decode_name,
        )
        tokens = string_obfuscator.obfuscate_tokens(tokens)
        string_obfuscator.import_b64decode = False
        string_obfuscator.import_b85decode = False

        for _ in range(2):
            tokens = NumberObfuscator().obfuscate_tokens(tokens)
        tokens = BuiltinsObfuscator().obfuscate_tokens(tokens)
        for _ in range(2):
            tokens = string_obfuscator.obfuscate_tokens(tokens)

        return self._untokenize(tokens)


print(ExampleObfuscator().obfuscate(open("source.py", "r").read()))

Hope this helps.

deoktr avatar Feb 16 '25 10:02 deoktr

README is fixed in 90036e895fabc0332a6a5b5dc90f00bd03f4d0ef

deoktr avatar Feb 16 '25 10:02 deoktr