How to write a Javascript obfuscator in Python?

An obfuscator is a tool or program designed to make source code difficult to understand and reverse-engineer. It transforms the original code into a version that is functionally equivalent but significantly more challenging to read and comprehend. This process is often used to protect intellectual property, prevent unauthorized access, and hinder reverse engineering.

Obfuscator and a minifier are not the same. An obfuscator makes the code difficult to understand and reverse-engineer, focusing on security. However, a minifier reduces the code size by removing unnecessary characters (like whitespace and comments) to improve load times without altering functionality.

Key responsibilities of an obfuscator

An obfuscator is for

  • Code Protection: Protecting proprietary algorithms and business logic from being copied or tampered with.

  • Security: Making it harder for attackers to find vulnerabilities or understand the code's functionality.

  • Intellectual Property: Safeguarding the intellectual property embedded in the code by making it difficult to reverse-engineer.

Common techniques

Common techniques used by obfuscators include:

  • Renaming: Changing variable, function, and class names to meaningless sequences of characters.

  • Control Flow Alteration: Modifying the control flow to make the code's logic harder to follow.

  • Inlining and Outlining: Combining multiple functions into one or splitting a function into multiple parts.

  • String Encryption: Encrypting strings within the code and decrypting them at runtime.

  • Dead Code Insertion: Adding unnecessary code that doesn't affect the program's functionality but confuses anyone trying to understand the code.

Obfuscators are used in various programming languages, including JavaScript, Java, C#, and others, especially in environments where source code is distributed or easily accessible, such as web applications and mobile apps.

Javascript obfuscator written in Python

To run this Python code for Javascript obfuscator, you first need to install pyjsparser to parse the Javascript code:

pip install pyjsparser

Here is the Python code:

from pyjsparser import PyJsParser
import random
import string



def generate_random_name(length=8):
    return ''.join(random.choices(string.ascii_letters, k=length))


def obfuscate(js_code, exclude_functions):
    parser = PyJsParser()
    ast = parser.parse(js_code)

    # List of built-in objects and functions to exclude
    builtins = [
        'console', 'log', 'error', 'warn', 'info', 'alert', 'document', 
        'window', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Math',
        'Date', 'RegExp', 'JSON', 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
        'eval', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval',
        'Function', 'Promise', 'Symbol', 'Map', 'Set', 'WeakMap', 'WeakSet',
        'Proxy', 'Reflect', 'Intl'
    ]
    exclude_functions = set(exclude_functions + builtins)

    # Collect all identifiers in the code
    identifiers = set()

    def collect_identifiers(node):
        if isinstance(node, dict):
            if node.get('type') == 'Identifier':
                identifiers.add(node['name'])
            for key, value in node.items():
                if isinstance(value, (dict, list)):
                    collect_identifiers(value)
        elif isinstance(node, list):
            for item in node:
                collect_identifiers(item)

    collect_identifiers(ast)

    # Generate a mapping for obfuscation
    obfuscation_map = {}
    for identifier in identifiers:
        if identifier not in exclude_functions:
            obfuscation_map[identifier] = generate_random_name()

    # Obfuscate the code
    def obfuscate_node(node):
        if isinstance(node, dict):
            if node.get('type') == 'Identifier' and node['name'] in obfuscation_map:
                node['name'] = obfuscation_map[node['name']]
            for key, value in node.items():
                if isinstance(value, (dict, list)):
                    obfuscate_node(value)
        elif isinstance(node, list):
            for item in node:
                obfuscate_node(item)

    obfuscate_node(ast)

    # Convert AST back to JavaScript code
    def ast_to_js(node):
        if isinstance(node, dict):
            if node['type'] == 'Program':
                return '\n'.join(ast_to_js(item) for item in node['body'])
            if node['type'] == 'FunctionDeclaration':
                params = ', '.join(ast_to_js(param) for param in node['params'])
                body = ast_to_js(node['body'])
                return f"function {node['id']['name']}({params}) {body}"
            if node['type'] == 'BlockStatement':
                body = '\n'.join(ast_to_js(item) for item in node['body'])
                return f"{{\n{body}\n}}"
            if node['type'] == 'VariableDeclaration':
                declarations = ', '.join(ast_to_js(decl) for decl in node['declarations'])
                return f"{node['kind']} {declarations};"
            if node['type'] == 'VariableDeclarator':
                id_ = ast_to_js(node['id'])
                init = ast_to_js(node['init']) if node['init'] else ''
                return f"{id_} = {init}" if init else id_
            if node['type'] == 'Identifier':
                return node['name']
            if node['type'] == 'Literal':
                return repr(node['value'])
            if node['type'] == 'ExpressionStatement':
                return f"{ast_to_js(node['expression'])};"
            if node['type'] == 'CallExpression':
                callee = ast_to_js(node['callee'])
                args = ', '.join(ast_to_js(arg) for arg in node['arguments'])
                return f"{callee}({args})"
            if node['type'] == 'MemberExpression':
                obj = ast_to_js(node['object'])
                prop = ast_to_js(node['property'])
                return f"{obj}.{prop}"
            if node['type'] == 'BinaryExpression':
                left = ast_to_js(node['left'])
                right = ast_to_js(node['right'])
                return f"{left} {node['operator']} {right}"
            if node['type'] == 'ReturnStatement':
                return f"return {ast_to_js(node['argument'])};"
            if node['type'] == 'FunctionExpression':
                params = ', '.join(ast_to_js(param) for param in node['params'])
                body = ast_to_js(node['body'])
                return f"function({params}) {body}"
            # Add more cases as needed
            return ''
        elif isinstance(node, list):
            return '\n'.join(ast_to_js(item) for item in node)
        return ''

    return ast_to_js(ast)

# Your JavaScript code
js_code = """
function helloWorld() {
    console.log('Hello, World!');
    var longVariableName = 42;
    return longVariableName;
}
function doNotObfuscateThis() {
    console.log('This function should not be obfuscated.');
}
"""

# Functions to exclude from obfuscation
exclude_functions = ['doNotObfuscateThis']

# Obfuscate the JavaScript code
obfuscated_js = obfuscate(js_code, exclude_functions)

print(obfuscated_js)

Running this script will lead to the following output:

function fKbzQQWA() {
console.log('Hello, World!');
var ABcMZYif = 42.0;
return ABcMZYif;
}
function doNotObfuscateThis() {
console.log('This function should not be obfuscated.');
}

JS obfuscation challenges

JavaScript obfuscation faces several challenges due to the nature of the language and its execution environment:

  1. Readability vs. Functionality: Ensuring that the obfuscated code remains functionally equivalent to the original while making it difficult to read and understand.
  2. Performance Impact: Obfuscated code can sometimes lead to performance degradation due to increased complexity and size.
  3. Browser Compatibility: Ensuring that the obfuscated code works consistently across different browsers and JavaScript engines.
  4. Debugging Difficulties: Troubleshooting issues in obfuscated code is challenging, as the obfuscated code is much harder to read.
  5. De-obfuscation Tools: There are tools available that can attempt to reverse the obfuscation process, making it less effective.
  6. Source Maps: For debugging purposes, source maps are often used, but they can also be exploited to reverse-engineer the original code.
  7. Dynamic Code Execution: JavaScript’s ability to execute code dynamically (e.g., eval) can complicate the obfuscation process and make it less secure.

Balancing these challenges requires careful consideration of the obfuscation techniques and tools used.

javascript
python
python3
obfuscation
Software and digital electronics / Coding
Posted by admin
2024-07-20 08:11
×

Login

No account?
Terms of use
Forgot password?