diff --git a/pysoot/sootir/soot_block.py b/pysoot/sootir/soot_block.py index 4e1f91a..570c995 100644 --- a/pysoot/sootir/soot_block.py +++ b/pysoot/sootir/soot_block.py @@ -5,7 +5,7 @@ from .soot_statement import SootStmt -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootBlock: __slots__ = [ "label", diff --git a/pysoot/sootir/soot_class.py b/pysoot/sootir/soot_class.py index 7586bcb..cf987c5 100644 --- a/pysoot/sootir/soot_class.py +++ b/pysoot/sootir/soot_class.py @@ -8,7 +8,7 @@ from . import convert_soot_attributes -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootClass: __slots__ = [ "name", diff --git a/pysoot/sootir/soot_expr.py b/pysoot/sootir/soot_expr.py index a1b0177..8d9b061 100644 --- a/pysoot/sootir/soot_expr.py +++ b/pysoot/sootir/soot_expr.py @@ -5,23 +5,47 @@ from .soot_value import SootValue -@dataclass(unsafe_hash=True) -class SootExpr(SootValue): - NAME_TO_CLASS = {} +NAME_TO_CLASS: dict[str, type[SootExpr]] = {} +OP_TO_STR = { + "eq": "==", + "ge": ">=", + "gt": ">", + "le": "<=", + "lt": "<", + "ne": "!=", + "neg": "!", + "add": "+", + "and": "&", + "cmp": "cmp", + "cmpg": "cmpg", + "cmpl": "cmpl", + "div": "/", + "mul": "*", + "or": "|", + "rem": "%", + "shl": "<<", + "shr": ">>", + "sub": "-", + "ushr": ">>>", + "xor": "^", +} + +@dataclass(frozen=True) +class SootExpr(SootValue): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @staticmethod def from_ir(ir_expr): subtype = ir_expr.getClass().getSimpleName() - cls = SootExpr.NAME_TO_CLASS.get(subtype, None) + cls = NAME_TO_CLASS.get(subtype, None) if cls is None: raise NotImplementedError("Unsupported Soot expression type %s." % subtype) return cls.from_ir(str(ir_expr.getType()), subtype, ir_expr) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootBinopExpr(SootExpr): __slots__ = [ "op", @@ -35,7 +59,7 @@ class SootBinopExpr(SootExpr): def __str__(self): return "%s %s %s" % ( str(self.value1), - SootExpr.OP_TO_STR[self.op], + OP_TO_STR[self.op], str(self.value2), ) @@ -51,14 +75,14 @@ def from_ir(type_, expr_name, ir_subvalue): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootUnopExpr(SootExpr): __slots__ = ["op", "value"] # TODO: replace with dataclass in Python 3.10 op: str value: SootValue def __str__(self): - return "%s %s" % (SootExpr.OP_TO_STR[self.op], str(self.value)) + return "%s %s" % (OP_TO_STR[self.op], str(self.value)) @staticmethod def from_ir(type_, expr_name, ir_subvalue): @@ -66,7 +90,7 @@ def from_ir(type_, expr_name, ir_subvalue): return SootUnopExpr(type_, op, SootValue.from_ir(ir_subvalue.getOp())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootCastExpr(SootExpr): __slots__ = ["cast_type", "value"] # TODO: replace with dataclass in Python 3.10 cast_type: str @@ -84,7 +108,7 @@ def from_ir(type_, expr_name, ir_subvalue): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootConditionExpr(SootExpr): __slots__ = [ "op", @@ -98,7 +122,7 @@ class SootConditionExpr(SootExpr): def __str__(self): return "%s %s %s" % ( str(self.value1), - SootExpr.OP_TO_STR[self.op], + OP_TO_STR[self.op], str(self.value2), ) @@ -113,7 +137,7 @@ def from_ir(type_, expr_name, ir_subvalue): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootLengthExpr(SootExpr): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: SootValue @@ -126,7 +150,7 @@ def from_ir(type_, expr_name, ir_subvalue): return SootLengthExpr(type_, SootValue.from_ir(ir_subvalue.getOp())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootNewArrayExpr(SootExpr): __slots__ = ["base_type", "size"] # TODO: replace with dataclass in Python 3.10 base_type: str @@ -147,7 +171,7 @@ def from_ir(type_, expr_name, ir_subvalue): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootNewMultiArrayExpr(SootExpr): __slots__ = ["base_type", "sizes"] # TODO: replace with dataclass in Python 3.10 base_type: str @@ -168,7 +192,7 @@ def from_ir(type_, expr_name, ir_subvalue): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootNewExpr(SootExpr): __slots__ = ["base_type"] # TODO: replace with dataclass in Python 3.10 base_type: str @@ -181,10 +205,10 @@ def from_ir(type_, expr_name, ir_subvalue): return SootNewExpr(type_, str(ir_subvalue.getBaseType())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootPhiExpr(SootExpr): __slots__ = ["values"] # TODO: replace with dataclass in Python 3.10 - values: tuple[SootValue, ...] + values: tuple[tuple[SootValue, int], ...] def __str__(self): return "Phi(%s)" % ( @@ -193,14 +217,15 @@ def __str__(self): @staticmethod def from_ir(type_, expr_name, ir_subvalue): + # FIXME: this 0 is wrong, we need to figure out the right value! return SootPhiExpr( - type_, (SootValue.from_ir(v.getValue()) for v in ir_subvalue.getArgs()) + type_, ((SootValue.from_ir(v.getValue()), 0) for v in ir_subvalue.getArgs()) ) # every invoke type has a method signature (class + name + parameter types) and concrete arguments # all invoke types, EXCEPT static, have a base ("this" concrete instance) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootInvokeExpr(SootExpr): __slots__ = [ "class_name", @@ -225,7 +250,7 @@ def list_to_arg_str(args): return ", ".join([str(arg) for arg in args]) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootVirtualInvokeExpr(SootInvokeExpr): __slots__ = ["base"] # TODO: replace with dataclass in Python 3.10 base: SootValue @@ -254,7 +279,7 @@ def from_ir(type_, expr_name, ir_expr): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootDynamicInvokeExpr(SootInvokeExpr): __slots__ = [ "bootstrap_method", @@ -286,7 +311,7 @@ def from_ir(type_, expr_name, ir_expr): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootInterfaceInvokeExpr(SootInvokeExpr): __slots__ = ["base"] # TODO: replace with dataclass in Python 3.10 base: SootValue @@ -315,7 +340,7 @@ def from_ir(type_, expr_name, ir_expr): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootSpecialInvokeExpr(SootInvokeExpr): __slots__ = ["base"] # TODO: replace with dataclass in Python 3.10 base: SootValue @@ -344,7 +369,7 @@ def from_ir(type_, expr_name, ir_expr): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootStaticInvokeExpr(SootInvokeExpr): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -370,7 +395,7 @@ def from_ir(type_, expr_name, ir_expr): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootInstanceOfExpr(SootValue): __slots__ = ["check_type", "value"] # TODO: replace with dataclass in Python 3.10 check_type: str @@ -386,7 +411,7 @@ def from_ir(type_, expr_name, ir_expr): ) -SootExpr.NAME_TO_CLASS = { +NAME_TO_CLASS = { "JCastExpr": SootCastExpr, "JLengthExpr": SootLengthExpr, "JNewExpr": SootNewExpr, @@ -421,27 +446,3 @@ def from_ir(type_, expr_name, ir_expr): "JUshrExpr": SootBinopExpr, "JXorExpr": SootBinopExpr, } - -SootExpr.OP_TO_STR = { - "eq": "==", - "ge": ">=", - "gt": ">", - "le": "<=", - "lt": "<", - "ne": "!=", - "neg": "!", - "add": "+", - "and": "&", - "cmp": "cmp", - "cmpg": "cmpg", - "cmpl": "cmpl", - "div": "/", - "mul": "*", - "or": "|", - "rem": "%", - "shl": "<<", - "shr": ">>", - "sub": "-", - "ushr": ">>>", - "xor": "^", -} diff --git a/pysoot/sootir/soot_method.py b/pysoot/sootir/soot_method.py index df42e4d..7aebb7c 100644 --- a/pysoot/sootir/soot_method.py +++ b/pysoot/sootir/soot_method.py @@ -7,11 +7,13 @@ from frozendict import frozendict from jpype.types import JClass +from .soot_expr import SootPhiExpr, NAME_TO_CLASS from .soot_block import SootBlock +from .soot_value import SootValue from . import convert_soot_attributes -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootMethod: # TODO: replace with dataclass in Python 3.10 __slots__ = [ @@ -36,7 +38,7 @@ class SootMethod: exceptional_preds: frozendict[SootBlock, list[SootBlock]] @property - # @lru_cache(maxsize=1) + @lru_cache(maxsize=1) def block_by_label(self): return {b.label: b for b in self.blocks} @@ -98,8 +100,6 @@ def from_ir(class_name, ir_method): pred_soot_block = block_map[pred_idx] exceptional_preds[soot_block].append(pred_soot_block) - from .soot_value import SootValue - stmt_to_block_idx = {} for ir_block in cfg: for ir_stmt in ir_block: @@ -110,19 +110,18 @@ def from_ir(class_name, ir_method): if "Assign" in ir_stmt.getClass().getSimpleName(): ir_expr = ir_stmt.getRightOp() if "Phi" in ir_expr.getClass().getSimpleName(): - values = [ + values = tuple( ( SootValue.from_ir(v.getValue()), stmt_to_block_idx[v.getUnit()], ) for v in ir_expr.getArgs() - ] - - phi_expr = SootValue.IREXPR_TO_EXPR[ir_expr] - phi_expr.values = values + ) - # "Free" map - SootValue.IREXPR_TO_EXPR = {} + phi_expr = SootPhiExpr( + type=ir_expr.getClass().getSimpleName(), values=values + ) + # TODO: Are we supposed to do something with this? params = (str(p) for p in ir_method.getParameterTypes()) attrs = convert_soot_attributes(ir_method.getModifiers()) diff --git a/pysoot/sootir/soot_statement.py b/pysoot/sootir/soot_statement.py index 4b4127f..28a40f5 100644 --- a/pysoot/sootir/soot_statement.py +++ b/pysoot/sootir/soot_statement.py @@ -7,10 +7,11 @@ from .soot_value import SootValue -@dataclass(unsafe_hash=True) -class SootStmt: - NAME_TO_CLASS = {} +NAME_TO_CLASS = {} + +@dataclass(frozen=True) +class SootStmt: __slots__ = [ "label", "offset", @@ -21,7 +22,7 @@ class SootStmt: @staticmethod def from_ir(ir_stmt, stmt_map=None): stmt_type = ir_stmt.getClass().getSimpleName() - stmt_class = SootStmt.NAME_TO_CLASS.get(stmt_type, None) + stmt_class = NAME_TO_CLASS.get(stmt_type, None) if stmt_class is None: raise NotImplementedError( @@ -32,7 +33,7 @@ def from_ir(ir_stmt, stmt_map=None): return stmt_class.from_ir(stmt_map[ir_stmt], 0, ir_stmt, stmt_map) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class DefinitionStmt(SootStmt): __slots__ = ["left_op", "right_op"] # TODO: replace with dataclass in Python 3.10 left_op: SootValue @@ -46,7 +47,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): raise NotImplementedError() -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class AssignStmt(DefinitionStmt): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -63,7 +64,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class IdentityStmt(DefinitionStmt): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -80,7 +81,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class BreakpointStmt(SootStmt): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -92,7 +93,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return BreakpointStmt(label, offset) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class EnterMonitorStmt(SootStmt): __slots__ = ["obj"] # TODO: replace with dataclass in Python 3.10 obj: SootValue @@ -105,7 +106,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return EnterMonitorStmt(label, offset, SootValue.from_ir(ir_stmt.getOp())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class ExitMonitorStmt(SootStmt): __slots__ = ["obj"] # TODO: replace with dataclass in Python 3.10 obj: SootValue @@ -118,7 +119,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return ExitMonitorStmt(label, offset, SootValue.from_ir(ir_stmt.getOp())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class GotoStmt(SootStmt): __slots__ = ["target"] # TODO: replace with dataclass in Python 3.10 target: SootStmt @@ -131,7 +132,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return GotoStmt(label, offset, stmt_map[ir_stmt.getTarget()]) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class IfStmt(SootStmt): __slots__ = ["condition", "target"] # TODO: replace with dataclass in Python 3.10 condition: SootValue @@ -150,7 +151,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class InvokeStmt(SootStmt): __slots__ = ["invoke_expr"] # TODO: replace with dataclass in Python 3.10 invoke_expr: SootValue @@ -163,7 +164,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return InvokeStmt(label, offset, SootValue.from_ir(ir_stmt.getInvokeExpr())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class ReturnStmt(SootStmt): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: SootValue @@ -176,7 +177,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return ReturnStmt(label, offset, SootValue.from_ir(ir_stmt.getOp())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class ReturnVoidStmt(SootStmt): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -188,7 +189,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return ReturnVoidStmt(label, offset) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class LookupSwitchStmt(SootStmt): __slots__ = [ "key", @@ -221,7 +222,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class TableSwitchStmt(SootStmt): __slots__ = [ # TODO: replace with dataclass in Python 3.10 "key", @@ -265,7 +266,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class ThrowStmt(SootStmt): __slots__ = ["obj"] # TODO: replace with dataclass in Python 3.10 obj: SootValue @@ -278,7 +279,7 @@ def from_ir(label, offset, ir_stmt, stmt_map=None): return ThrowStmt(label, offset, SootValue.from_ir(ir_stmt.getOp())) -SootStmt.NAME_TO_CLASS = { +NAME_TO_CLASS = { "JAssignStmt": AssignStmt, "JBreakpointStmt": BreakpointStmt, "JEnterMonitorStmt": EnterMonitorStmt, diff --git a/pysoot/sootir/soot_value.py b/pysoot/sootir/soot_value.py index e50a35c..76253a0 100644 --- a/pysoot/sootir/soot_value.py +++ b/pysoot/sootir/soot_value.py @@ -3,11 +3,12 @@ from dataclasses import dataclass -@dataclass(unsafe_hash=True) -class SootValue: - NAME_TO_CLASS = {} - IREXPR_TO_EXPR = {} +# TODO: stop using globals +NAME_TO_CLASS: dict[str, type[SootValue]] = {} + +@dataclass(frozen=True) +class SootValue: __slots__ = ["type"] # TODO: replace with dataclass in Python 3.10 type: str @@ -23,10 +24,9 @@ def from_ir(ir_value): from .soot_expr import SootExpr expr = SootExpr.from_ir(ir_value) - SootValue.IREXPR_TO_EXPR[ir_value] = expr return expr - cls = SootValue.NAME_TO_CLASS.get(subtype, None) + cls = NAME_TO_CLASS.get(subtype, None) if cls is None: raise NotImplementedError("Unsupported SootValue type %s." % subtype) @@ -34,7 +34,7 @@ def from_ir(ir_value): return cls.from_ir(str(ir_value.getType()), ir_value) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootLocal(SootValue): __slots__ = ["name"] # TODO: replace with dataclass in Python 3.10 name: str @@ -47,7 +47,7 @@ def from_ir(type_, ir_value): return SootLocal(type_, str(ir_value.getName())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootArrayRef(SootValue): __slots__ = ["base", "index"] # TODO: replace with dataclass in Python 3.10 base: SootValue @@ -65,7 +65,7 @@ def from_ir(type_, ir_value): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootCaughtExceptionRef(SootValue): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -77,7 +77,7 @@ def from_ir(type_, ir_value): return SootCaughtExceptionRef(type_) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootParamRef(SootValue): __slots__ = ["index"] # TODO: replace with dataclass in Python 3.10 index: int @@ -90,7 +90,7 @@ def from_ir(type_, ir_value): return SootParamRef(type_, int(ir_value.getIndex())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootThisRef(SootValue): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -102,7 +102,7 @@ def from_ir(type_, ir_value): return SootThisRef(type_) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootStaticFieldRef(SootValue): __slots__ = ["field"] # TODO: replace with dataclass in Python 3.10 field: tuple[str, str] @@ -116,7 +116,7 @@ def from_ir(type_, ir_value): return SootStaticFieldRef(type_, (str(raw_field.getName()), str(raw_field.getDeclaringClass().getName()))) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootInstanceFieldRef(SootValue): __slots__ = ["base", "field"] # TODO: replace with dataclass in Python 3.10 base: SootValue @@ -135,7 +135,7 @@ def from_ir(type_, ir_value): ) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootClassConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: str @@ -148,7 +148,7 @@ def from_ir(type_, ir_value): return SootClassConstant(type_, str(ir_value.getValue())) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootDoubleConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: float @@ -161,7 +161,7 @@ def from_ir(type_, ir_value): return SootDoubleConstant(type_, float(ir_value.value)) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootFloatConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: float @@ -174,7 +174,7 @@ def from_ir(type_, ir_value): return SootFloatConstant(type_, float(ir_value.value)) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootIntConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: int @@ -187,7 +187,7 @@ def from_ir(type_, ir_value): return SootIntConstant(type_, int(ir_value.value)) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootLongConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: int @@ -200,7 +200,7 @@ def from_ir(type_, ir_value): return SootLongConstant(type_, int(ir_value.value)) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootNullConstant(SootValue): __slots__ = [] # TODO: replace with dataclass in Python 3.10 @@ -212,7 +212,7 @@ def from_ir(type_, ir_value): return SootNullConstant(type_) -@dataclass(unsafe_hash=True) +@dataclass(frozen=True) class SootStringConstant(SootValue): __slots__ = ["value"] # TODO: replace with dataclass in Python 3.10 value: str @@ -226,7 +226,7 @@ def from_ir(type_, ir_value): return SootStringConstant(type_, str(ir_value.value)) -SootValue.NAME_TO_CLASS = { +NAME_TO_CLASS = { "Local": SootLocal, "JArrayRef": SootArrayRef, "JCaughtExceptionRef": SootCaughtExceptionRef,