mirror of
https://github.com/bikini/patchwork
synced 2026-06-27 08:08:41 +00:00
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
from __future__ import annotations
|
|
import ast
|
|
import random
|
|
|
|
class NumberObfuscator(ast.NodeTransformer):
|
|
|
|
def __init__(self, rng: random.Random, prob: float=0.85):
|
|
self.rng = rng
|
|
self.prob = prob
|
|
|
|
def visit_Constant(self, node: ast.Constant) -> ast.AST:
|
|
v = node.value
|
|
if isinstance(v, bool):
|
|
return node
|
|
if isinstance(v, int):
|
|
if self.rng.random() <= self.prob:
|
|
return self._obfuscate(v)
|
|
return node
|
|
|
|
def visit_match_case(self, node: ast.match_case) -> ast.AST:
|
|
if node.guard is not None:
|
|
node.guard = self.visit(node.guard)
|
|
node.body = [self.visit(stmt) for stmt in node.body]
|
|
return node
|
|
|
|
def _obfuscate(self, n: int) -> ast.AST:
|
|
choices = ['xor', 'sum', 'shift', 'frombytes', 'affine', 'split']
|
|
kind = self.rng.choice(choices)
|
|
try:
|
|
return getattr(self, f'_t_{kind}')(n)
|
|
except Exception:
|
|
return ast.Constant(value=n)
|
|
|
|
def _t_xor(self, n: int) -> ast.AST:
|
|
k = self.rng.randrange(1, 1 << 32)
|
|
return self._parse_expr(f'(({n ^ k}) ^ {k})')
|
|
|
|
def _t_sum(self, n: int) -> ast.AST:
|
|
a = self.rng.randint(-(1 << 16), 1 << 16)
|
|
return self._parse_expr(f'(({n - a}) + ({a}))')
|
|
|
|
def _t_shift(self, n: int) -> ast.AST:
|
|
if abs(n) >= 1 << 60:
|
|
return self._t_xor(n)
|
|
s = self.rng.randint(1, 8)
|
|
return self._parse_expr(f'(({n} << {s}) >> {s})')
|
|
|
|
def _t_frombytes(self, n: int) -> ast.AST:
|
|
signed = n < 0
|
|
bit_len = max(1, n.bit_length() + (1 if signed else 0))
|
|
byte_len = (bit_len + 7) // 8
|
|
try:
|
|
data = n.to_bytes(byte_len, 'big', signed=signed)
|
|
except OverflowError:
|
|
return self._t_xor(n)
|
|
signed_arg = 'True' if signed else 'False'
|
|
return self._parse_expr(f"int.from_bytes({data!r}, 'big', signed={signed_arg})")
|
|
|
|
def _t_affine(self, n: int) -> ast.AST:
|
|
a = self.rng.randint(2, 64)
|
|
b = self.rng.randint(-(1 << 12), 1 << 12)
|
|
return self._parse_expr(f'((({n * a + b}) - ({b})) // {a})')
|
|
|
|
def _t_split(self, n: int) -> ast.AST:
|
|
p = self.rng.randint(-(1 << 16), 1 << 16)
|
|
q = n - p
|
|
return self._parse_expr(f'({p} + ({q}))')
|
|
|
|
def _parse_expr(self, src: str) -> ast.AST:
|
|
return ast.parse(src, mode='eval').body
|