Skip to content

Commit

Permalink
Add method and attribute randomization
Browse files Browse the repository at this point in the history
  • Loading branch information
mad-cat-lon committed Aug 7, 2024
1 parent f1aaa64 commit 9e35261
Show file tree
Hide file tree
Showing 5 changed files with 414 additions and 460 deletions.
7 changes: 6 additions & 1 deletion jargonaut.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ def main():
control.InsertStaticOpaqueMBAPredicates(inference=do_inference),
# Randomize names
layout.RandomizeNames(),
# Randomize methods and attributes
layout.RandomizeAttributes(),
# Remove annotations
layout.RemoveAnnotations()

Expand All @@ -182,7 +184,10 @@ def main():
else:
wrapper = cst.MetadataWrapper(tree)
tree = wrapper.visit(t)
t.spinner.ok()
try:
t.spinner.ok()
except Exception:
pass
obfus = tree.code
with open(os.path.join(output_dir, args.out_file), "w", encoding="utf-8") as out_file:
# Most specify encoding in output file due to binary string obfuscation
Expand Down
3 changes: 2 additions & 1 deletion jargonaut/transformations/layout/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from jargonaut.transformations.layout.randomize_names import RandomizeNames
from jargonaut.transformations.layout.convert_unicode import ConvertUnicode
from jargonaut.transformations.layout.remove_comments import RemoveComments
from jargonaut.transformations.layout.remove_annotations import RemoveAnnotations
from jargonaut.transformations.layout.remove_annotations import RemoveAnnotations
from jargonaut.transformations.layout.randomize_attributes import RandomizeAttributes
144 changes: 144 additions & 0 deletions jargonaut/transformations/layout/randomize_attributes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
from libcst.metadata import (
QualifiedNameProvider,
QualifiedNameSource,
ScopeProvider,
ClassScope,
TypeInferenceProvider
)
import libcst as cst
import random
from typing import Optional
from yaspin.spinners import Spinners
from yaspin import kbi_safe_yaspin
import builtins
from collections import defaultdict


class RandomizeAttributes(cst.CSTTransformer):
"""
Replaces class methods and attributes with a randomized string.
Only does so for user-defined classes.
"""

METADATA_DEPENDENCIES = (
QualifiedNameProvider,
ScopeProvider,
TypeInferenceProvider
)

def __init__(self):
self.randomize_map = defaultdict(self.rand_name)
self.avoid_names = ["self", "__init__", "main", "super"]
self.avoid_names.extend(dir(builtins))
self.ignore = False
self.first_visit = True
self.progress_msg = "Randomizing methods and attributes..."
self.spinner = None

class DummyClass:
pass

def dummyFunc():
pass
types_to_inspect = [
object,
int,
float,
str,
list,
dict,
set,
tuple,
DummyClass,
dummyFunc,
# Avoid messing with the lambda strings which use
# __code__ as well
dummyFunc.__code__
]
for t in types_to_inspect:
self.avoid_names.extend(method for method in dir(t))

def rand_name(self):
keys = [
"I",
"l",
"i",
"Î",
"IJ",
"ij",
"lj",
"Ḭ",
"j",
"J",
"Ĵ",
"ⅉ",
"j"
]
return ''.join(random.choices(keys, k=30))

def visit_Node(self, node):
if self.first_visit is True:
self.spinner = kbi_safe_yaspin(
Spinners.simpleDotsScrolling,
text=self.progress_msg,
timer=True
)
self.spinner.start()
self.first_visit = False

def leave_Attribute(
self,
original_node: cst.Attribute,
updated_node: cst.Attribute
) -> Optional[bool]:
scope = self.get_metadata(ScopeProvider, original_node)
qualified_names = list(scope.get_qualified_names_for(original_node))
qualified_name = qualified_names[0]
# print(f"scope: {scope} qualified_name: {qualified_name}")
if isinstance(scope, ClassScope):
if qualified_name.source == QualifiedNameSource.LOCAL:
if original_node.attr.value not in self.avoid_names:
return updated_node.with_changes(
attr=cst.Name(
value=self.randomize_map[original_node.attr.value]
)
)
else:
return updated_node
else:
return updated_node
else:
# The user didn't define this attribute, so leave it unchanged so
# we don't break anything
if original_node.attr.value not in self.randomize_map:
return updated_node
else:
return updated_node.with_changes(
attr=cst.Name(
value=self.randomize_map[original_node.attr.value]
)
)

def leave_FunctionDef(
self,
original_node: cst.FunctionDef,
updated_node: cst.FunctionDef
):
# Only target methods as RandomizeNames skips them
scope = self.get_metadata(ScopeProvider, original_node)
if isinstance(scope, ClassScope):
if original_node.name.value not in self.avoid_names:
if original_node.name.value in self.randomize_map:
new_value = self.randomize_map[original_node.name.value]
else:
new_value = self.rand_name()
self.randomize_map[original_node.name.value] = new_value
return updated_node.with_changes(
name=cst.Name(
value=str(new_value)
)
)
else:
return updated_node
else:
return updated_node
2 changes: 1 addition & 1 deletion jargonaut/transformations/layout/randomize_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@ def leave_Name(
return updated_node.with_changes(
value=self.randomize_map[qualified_name.name]
)
return updated_node
return updated_node
Loading

0 comments on commit 9e35261

Please sign in to comment.