This commit is contained in:
@@ -1 +1,7 @@
|
||||
# This file ensures that the transformers directory is treated as a Python package
|
||||
"""
|
||||
@file transformers/__init__.py
|
||||
@brief Package initializer for the transformers module.
|
||||
@details Groups AST transformers and analyzers used by OMG-Fuscator, including
|
||||
renaming, control-flow flattening, class mapping, attribute handling,
|
||||
and symbol tree utilities.
|
||||
"""
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
"""
|
||||
@file transformers/attribute_transformer.py
|
||||
@brief Attribute access transformer for consistent class member renaming.
|
||||
@details Ensures references like self.method and self.attr are kept consistent
|
||||
with class and attribute mappings produced by analysis.
|
||||
"""
|
||||
|
||||
import ast
|
||||
from typing import Dict, Optional
|
||||
|
||||
class AttributeTransformer(ast.NodeTransformer):
|
||||
"""
|
||||
Transforms attribute access expressions (obj.attr) consistently,
|
||||
especially for class method calls and attribute accesses.
|
||||
@brief Transform attribute access expressions for consistency.
|
||||
@details Handles self.method and self.attr translations based on provided
|
||||
mappings to keep calls and attributes aligned with obfuscated names.
|
||||
"""
|
||||
|
||||
def __init__(self, class_attr_mapping: Dict[str, Dict[str, str]], class_renames: Dict[str, str]):
|
||||
"""
|
||||
Initialize with mapping dictionaries.
|
||||
|
||||
Args:
|
||||
class_attr_mapping: Maps class_name -> {attr_name -> obfuscated_attr_name}
|
||||
class_renames: Maps original_class_name -> obfuscated_class_name
|
||||
@brief Initialize transformer with mapping dictionaries.
|
||||
@param class_attr_mapping Maps class_name -> {attr_name -> obfuscated_attr_name}
|
||||
@param class_renames Maps original_class_name -> obfuscated_class_name
|
||||
"""
|
||||
self.class_attr_mapping = class_attr_mapping
|
||||
self.class_renames = class_renames
|
||||
self.current_class: Optional[str] = None
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
"""Keep track of the current class being processed"""
|
||||
"""
|
||||
@brief Track the current class and process its body.
|
||||
@param node ast.ClassDef being visited.
|
||||
@return ast.ClassDef Potentially modified node.
|
||||
"""
|
||||
old_class = self.current_class
|
||||
|
||||
# Get the obfuscated name for this class
|
||||
@@ -38,7 +48,11 @@ class AttributeTransformer(ast.NodeTransformer):
|
||||
return node
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
"""Transform attribute access like self.method to use consistent names"""
|
||||
"""
|
||||
@brief Transform attribute access for consistency within a class.
|
||||
@param node ast.Attribute node to transform.
|
||||
@return ast.AST Updated attribute node.
|
||||
"""
|
||||
# First process any nested attributes
|
||||
node = self.generic_visit(node)
|
||||
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
"""
|
||||
@file transformers/class_analyzer.py
|
||||
@brief Class analysis utilities for consistent renaming.
|
||||
@details Pre-analyzes classes, methods, attributes, and inheritance to build
|
||||
consistent mappings used by obfuscation transformers.
|
||||
"""
|
||||
import ast
|
||||
from typing import Dict, Set, Tuple, List
|
||||
|
||||
|
||||
class ClassMethodMap:
|
||||
"""Stores method name mappings for all classes in the code."""
|
||||
"""
|
||||
@brief Stores method and attribute mappings for all classes.
|
||||
"""
|
||||
def __init__(self):
|
||||
# Maps: original_class_name -> {original_method_name -> obfuscated_method_name}
|
||||
self.class_methods: Dict[str, Dict[str, str]] = {}
|
||||
@@ -20,8 +27,8 @@ class ClassMethodMap:
|
||||
|
||||
class ClassAnalyzer(ast.NodeVisitor):
|
||||
"""
|
||||
Pre-analyzes classes to ensure consistent renaming of methods and attributes.
|
||||
This is crucial for making self.method() calls match def method() definitions.
|
||||
@brief Analyze classes for consistent method/attribute renaming.
|
||||
@details Ensures self.method() calls match def method() definitions and records inheritance.
|
||||
"""
|
||||
def __init__(self, name_generator):
|
||||
self.name_generator = name_generator
|
||||
@@ -35,14 +42,21 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
self.method_calls: Dict[str, Set[str]] = {}
|
||||
|
||||
def analyze(self, tree: ast.AST) -> ClassMethodMap:
|
||||
"""Analyzes the entire AST and returns populated method mappings."""
|
||||
"""
|
||||
@brief Analyze the entire AST and produce mappings.
|
||||
@param tree Parsed AST of the input module.
|
||||
@return ClassMethodMap Populated mappings.
|
||||
"""
|
||||
self.visit(tree)
|
||||
self._resolve_inheritance()
|
||||
self._ensure_consistent_method_mapping()
|
||||
return self.method_map
|
||||
|
||||
def visit_ClassDef(self, node: ast.ClassDef):
|
||||
"""Process a class definition and map its methods."""
|
||||
"""
|
||||
@brief Process a class definition and map methods/attrs.
|
||||
@param node ast.ClassDef node being visited.
|
||||
"""
|
||||
prev_class = self.current_class
|
||||
self.current_class = node.name
|
||||
|
||||
@@ -105,7 +119,10 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
self.current_class = prev_class
|
||||
|
||||
def visit_attribute_assign(self, node):
|
||||
"""Process attribute assignments like self.attr = value"""
|
||||
"""
|
||||
@brief Process self.attr assignments within a class.
|
||||
@param node ast.Assign node of the assignment.
|
||||
"""
|
||||
if not self.current_class:
|
||||
return
|
||||
|
||||
@@ -122,7 +139,10 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
"""Track self.method references to ensure consistent naming"""
|
||||
"""
|
||||
@brief Track self.method references for consistency checks.
|
||||
@param node ast.Attribute being visited.
|
||||
"""
|
||||
if self.current_class:
|
||||
is_self_method, method_name = get_method_name(node)
|
||||
if is_self_method:
|
||||
@@ -134,8 +154,7 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
|
||||
def _ensure_consistent_method_mapping(self):
|
||||
"""
|
||||
Make sure that all methods called via self.method() have a mapping,
|
||||
even if they're not defined in the class.
|
||||
@brief Ensure all self.method() calls have mappings, even if undefined in class.
|
||||
"""
|
||||
for class_name, method_calls in self.method_calls.items():
|
||||
if class_name not in self.method_map.class_methods:
|
||||
@@ -153,8 +172,8 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
|
||||
def _resolve_inheritance(self):
|
||||
"""
|
||||
Ensure child classes inherit method mappings from parent classes.
|
||||
This ensures that overridden methods use the same obfuscated name.
|
||||
@brief Propagate method mappings along inheritance hierarchies.
|
||||
@details Ensures overridden methods reuse the same obfuscated name.
|
||||
"""
|
||||
# Process inheritance depth-first to handle multi-level inheritance
|
||||
def process_inheritance(class_name):
|
||||
@@ -181,8 +200,9 @@ class ClassAnalyzer(ast.NodeVisitor):
|
||||
|
||||
def get_method_name(node: ast.Attribute) -> Tuple[bool, str]:
|
||||
"""
|
||||
Helper function to determine if an attribute is a self.method() call.
|
||||
Returns (is_self_method, method_name)
|
||||
@brief Determine if an attribute is a self.method() call.
|
||||
@param node Attribute node to check.
|
||||
@return Tuple[bool, str] (is_self_method, method_name)
|
||||
"""
|
||||
if isinstance(node.value, ast.Name) and node.value.id == 'self':
|
||||
return True, node.attr
|
||||
@@ -191,8 +211,9 @@ def get_method_name(node: ast.Attribute) -> Tuple[bool, str]:
|
||||
|
||||
def update_obfuscator_with_class_mappings(obfuscator, class_map: ClassMethodMap):
|
||||
"""
|
||||
Updates the main obfuscator with class method and attribute mappings
|
||||
to ensure consistent renaming across the codebase.
|
||||
@brief Update obfuscator with class/method/attribute mappings.
|
||||
@param obfuscator AdvancedObfuscator instance to update.
|
||||
@param class_map ClassMethodMap mappings produced by analysis.
|
||||
"""
|
||||
# Update class name mappings in global_var_renames
|
||||
for orig_name, obf_name in class_map.class_renames.items():
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
"""
|
||||
@file transformers/class_mapper.py
|
||||
@brief Class, method, and attribute mapping analyzer and transformer.
|
||||
@details Builds consistent rename mappings across classes, resolves inheritance,
|
||||
and applies the mappings back to the AST for coherent obfuscation.
|
||||
"""
|
||||
|
||||
import ast
|
||||
from typing import Dict, Set, List, Tuple, Optional
|
||||
import logging
|
||||
@@ -7,7 +14,11 @@ logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("ClassMapper")
|
||||
|
||||
class ClassMapping:
|
||||
"""Stores all mappings related to classes in a centralized way."""
|
||||
"""
|
||||
@brief Stores all mappings related to classes in a centralized way.
|
||||
@details Tracks class renames, method and attribute mappings, inheritance structure,
|
||||
and seen method calls to ensure complete coverage.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Original class name -> obfuscated class name
|
||||
@@ -49,7 +60,11 @@ class ClassMapping:
|
||||
|
||||
|
||||
class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
"""Analyzes the AST to create a complete class mapping."""
|
||||
"""
|
||||
@brief Analyze the AST to create a complete class mapping.
|
||||
@details Performs multi-pass analysis to collect classes, methods, attributes,
|
||||
inheritance, and method-call references and builds consistent mappings.
|
||||
"""
|
||||
|
||||
def __init__(self, name_generator):
|
||||
self.name_generator = name_generator
|
||||
@@ -59,7 +74,11 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
self.processed_classes: Set[str] = set()
|
||||
|
||||
def analyze(self, tree: ast.AST) -> ClassMapping:
|
||||
"""Perform a complete analysis of the AST."""
|
||||
"""
|
||||
@brief Perform a complete analysis of the AST.
|
||||
@param tree Parsed AST to analyze.
|
||||
@return ClassMapping Aggregated mappings for class renaming and members.
|
||||
"""
|
||||
# First pass: collect all class definitions, methods, and inheritance
|
||||
self.visit(tree)
|
||||
|
||||
@@ -73,7 +92,10 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
return self.mapping
|
||||
|
||||
def visit_ClassDef(self, node: ast.ClassDef):
|
||||
"""Process a class definition."""
|
||||
"""
|
||||
@brief Process a class definition.
|
||||
@param node ast.ClassDef node.
|
||||
"""
|
||||
prev_class = self.current_class
|
||||
self.current_class = node.name
|
||||
|
||||
@@ -125,7 +147,10 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
self.current_class = prev_class
|
||||
|
||||
def visit_method_def(self, node: ast.FunctionDef):
|
||||
"""Process a method definition in a class."""
|
||||
"""
|
||||
@brief Process a method definition in a class.
|
||||
@param node ast.FunctionDef node.
|
||||
"""
|
||||
if not self.current_class:
|
||||
return
|
||||
|
||||
@@ -147,7 +172,10 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
self.current_method = prev_method
|
||||
|
||||
def visit_assign_in_class(self, node: ast.Assign):
|
||||
"""Process assignments in class body or methods."""
|
||||
"""
|
||||
@brief Process assignments in class body or methods.
|
||||
@param node ast.Assign possibly containing self.attr writes.
|
||||
"""
|
||||
if not self.current_class:
|
||||
return
|
||||
|
||||
@@ -166,7 +194,10 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
self.visit(node.value)
|
||||
|
||||
def visit_Attribute(self, node: ast.Attribute):
|
||||
"""Process attribute access like self.method or self.attr."""
|
||||
"""
|
||||
@brief Process attribute access like self.method or self.attr.
|
||||
@param node ast.Attribute node.
|
||||
"""
|
||||
if self.current_class and isinstance(node.value, ast.Name) and node.value.id == 'self':
|
||||
# Record this access for later processing
|
||||
method_name = node.attr
|
||||
@@ -177,7 +208,10 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Assign(self, node: ast.Assign):
|
||||
"""Process assignments that might contain self.attr references."""
|
||||
"""
|
||||
@brief Process assignments that might contain self.attr references.
|
||||
@param node ast.Assign node.
|
||||
"""
|
||||
# Visit both sides of the assignment
|
||||
for target in node.targets:
|
||||
self.visit(target)
|
||||
@@ -185,7 +219,8 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
|
||||
def _resolve_inheritance(self):
|
||||
"""
|
||||
Ensure child classes inherit method mappings from parent classes.
|
||||
@brief Ensure child classes inherit method mappings from parent classes.
|
||||
@details Copies parent method mappings into children when not overridden.
|
||||
"""
|
||||
def process_inheritance(class_name):
|
||||
if class_name not in self.mapping.inheritance:
|
||||
@@ -211,8 +246,8 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
|
||||
def _ensure_complete_method_mapping(self):
|
||||
"""
|
||||
Make sure all method calls have corresponding mappings.
|
||||
This handles methods called but not defined in the class.
|
||||
@brief Ensure all method calls have corresponding mappings.
|
||||
@details Handles methods called but not defined in the class.
|
||||
"""
|
||||
for class_name, method_calls in self.mapping.seen_method_calls.items():
|
||||
if class_name not in self.mapping.class_methods:
|
||||
@@ -231,14 +266,22 @@ class ClassMapAnalyzer(ast.NodeVisitor):
|
||||
|
||||
|
||||
class ClassTransformer(ast.NodeTransformer):
|
||||
"""Transforms class-related nodes using the mapping."""
|
||||
"""
|
||||
@brief Transform class-related nodes using the mapping.
|
||||
@details Renames class names, methods, and self.attr/self.method references
|
||||
according to the analyzed mappings.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping: ClassMapping):
|
||||
self.mapping = mapping
|
||||
self.current_class: Optional[str] = None
|
||||
|
||||
def visit_ClassDef(self, node: ast.ClassDef):
|
||||
"""Transform class name and process its body."""
|
||||
"""
|
||||
@brief Transform class name and process its body.
|
||||
@param node ast.ClassDef node.
|
||||
@return ast.ClassDef Transformed class node.
|
||||
"""
|
||||
prev_class = self.current_class
|
||||
orig_name = node.name
|
||||
self.current_class = orig_name
|
||||
@@ -255,7 +298,11 @@ class ClassTransformer(ast.NodeTransformer):
|
||||
return node
|
||||
|
||||
def visit_FunctionDef(self, node: ast.FunctionDef):
|
||||
"""Transform method name."""
|
||||
"""
|
||||
@brief Transform method name.
|
||||
@param node ast.FunctionDef node.
|
||||
@return ast.FunctionDef Transformed method node.
|
||||
"""
|
||||
if self.current_class and node.name in self.mapping.class_methods.get(self.current_class, {}):
|
||||
orig_name = node.name
|
||||
node.name = self.mapping.class_methods[self.current_class][node.name]
|
||||
@@ -266,7 +313,11 @@ class ClassTransformer(ast.NodeTransformer):
|
||||
return node
|
||||
|
||||
def visit_Attribute(self, node: ast.Attribute):
|
||||
"""Transform self.method and self.attr references."""
|
||||
"""
|
||||
@brief Transform self.method and self.attr references.
|
||||
@param node ast.Attribute node.
|
||||
@return ast.Attribute Transformed attribute node.
|
||||
"""
|
||||
# Process any child nodes first (for nested attributes)
|
||||
node.value = self.visit(node.value)
|
||||
|
||||
@@ -288,7 +339,12 @@ class ClassTransformer(ast.NodeTransformer):
|
||||
|
||||
# Helper function to apply the class mapping transformation
|
||||
def apply_class_mapping(tree: ast.AST, name_generator) -> ast.AST:
|
||||
"""Analyze and transform classes consistently."""
|
||||
"""
|
||||
@brief Analyze and transform classes consistently.
|
||||
@param tree Input AST.
|
||||
@param name_generator Name generator used to create obfuscated identifiers.
|
||||
@return Tuple[ast.AST, ClassMapping] Transformed AST and the mapping produced.
|
||||
"""
|
||||
# First pass: analyze all classes
|
||||
analyzer = ClassMapAnalyzer(name_generator)
|
||||
mapping = analyzer.analyze(tree)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
"""
|
||||
@file transformers/control_flow.py
|
||||
@brief Control-flow flattening transformer.
|
||||
@details Converts structured control flow into a state-machine with a while/dispatch
|
||||
loop to hinder static analysis, and can emit debug telemetry in debug mode.
|
||||
"""
|
||||
import ast
|
||||
import random
|
||||
from typing import Dict, List, Set, Tuple, Any, Optional
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
"""
|
||||
@file transformers/rename.py
|
||||
@brief Identifier renaming and string literal encryption transformer.
|
||||
@details Performs scoped variable/function renaming, consistent class/method/attribute
|
||||
remapping, and in-place string encryption producing runtime decryption code.
|
||||
"""
|
||||
|
||||
import ast
|
||||
from utils.encryption import StringEncryptor
|
||||
from utils.name_gen import NameGenerator
|
||||
@@ -8,6 +15,11 @@ logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("RenameTransformer")
|
||||
|
||||
class RenameTransformer(ast.NodeTransformer):
|
||||
"""
|
||||
@brief AST transformer for renaming and string encryption.
|
||||
@details Maintains scope stacks, global mappings, class method/attribute maps, and
|
||||
collects key-setup code for runtime decryption. Can emit debug telemetry.
|
||||
"""
|
||||
def __init__(self, name_generator, global_var_renames, class_attr_mapping,
|
||||
primary_key, secondary_key, salt, debug_mode=False):
|
||||
self.name_generator = name_generator
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
"""
|
||||
@file transformers/symbol_tree.py
|
||||
@brief Global symbol tree and scope tracking.
|
||||
@details Builds a hierarchical representation of scopes (module, class, function),
|
||||
tracks symbols, imports, references, and inheritance; generates rename
|
||||
mappings consumed by the obfuscation pipeline.
|
||||
"""
|
||||
import ast
|
||||
from typing import Dict, Set, List, Optional, Union, Tuple
|
||||
import logging
|
||||
|
||||
Reference in New Issue
Block a user