Skip to content

Commit 261fc7e

Browse files
committed
add: bitwise operations for number and boolean obfuscators
1 parent 39658ef commit 261fc7e

6 files changed

Lines changed: 233 additions & 31 deletions

File tree

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -373,30 +373,38 @@ print(len('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'))
373373

374374
# Boolean
375375
print((True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True))
376+
377+
# Bitwise XOR
378+
print((53^31))
379+
380+
# Bitwise shift
381+
print((21<<1))
382+
383+
# Bitwise NOT complement
384+
print((~(~42)))
376385
```
377386

378387
#### BooleanObfuscator
379388

380389
Source: `print(True)`
381390

382391
```python
383-
# not False
384392
print(not False)
385-
386-
# all([])
387393
print(all([]))
388-
389-
# any([True])
390394
print(any([True]))
391-
392-
# not not True
393395
print(not not True)
394-
395-
# '' in ''
396396
print('' in '')
397-
398-
# bool(1)
399397
print(bool(1))
398+
print(bool(1&1))
399+
print(bool(~0))
400+
```
401+
402+
Source: `print(False)`
403+
404+
```python
405+
print(bool(1&0))
406+
print(bool(1^1))
407+
print(bool(0|0))
400408
```
401409

402410
#### ConstantsObfuscator

pof/obfuscator/boolean.py

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

1717
import random
18-
from tokenize import LPAR, LSQB, NAME, NUMBER, RPAR, RSQB, STRING
18+
from tokenize import LPAR, LSQB, NAME, NUMBER, OP, RPAR, RSQB, STRING
1919

2020

2121
class BooleanObfuscator:
2222
"""Obfuscate booleans with multiple methods."""
2323

2424
@staticmethod
25-
def obf_true():
26-
match random.randint(1, 6):
25+
def obf_true(): # noqa: PLR0911
26+
match random.randint(1, 9):
2727
case 1:
2828
# all([])
2929
return [
@@ -77,10 +77,39 @@ def obf_true():
7777
(NUMBER, "1"),
7878
(RPAR, ")"),
7979
]
80+
case 7:
81+
# bool(1&1)
82+
return [
83+
(NAME, "bool"),
84+
(LPAR, "("),
85+
(NUMBER, "1"),
86+
(OP, "&"),
87+
(NUMBER, "1"),
88+
(RPAR, ")"),
89+
]
90+
case 8:
91+
# bool(1|0)
92+
return [
93+
(NAME, "bool"),
94+
(LPAR, "("),
95+
(NUMBER, "1"),
96+
(OP, "|"),
97+
(NUMBER, "0"),
98+
(RPAR, ")"),
99+
]
100+
case 9:
101+
# bool(~0)
102+
return [
103+
(NAME, "bool"),
104+
(LPAR, "("),
105+
(OP, "~"),
106+
(NUMBER, "0"),
107+
(RPAR, ")"),
108+
]
80109

81110
@staticmethod
82-
def obf_false():
83-
match random.randint(1, 6):
111+
def obf_false(): # noqa: PLR0911
112+
match random.randint(1, 9):
84113
case 1:
85114
# False = all([[]])
86115
return [
@@ -137,6 +166,36 @@ def obf_false():
137166
(NUMBER, "0"),
138167
(RPAR, ")"),
139168
]
169+
case 7:
170+
# bool(1&0)
171+
return [
172+
(NAME, "bool"),
173+
(LPAR, "("),
174+
(NUMBER, "1"),
175+
(OP, "&"),
176+
(NUMBER, "0"),
177+
(RPAR, ")"),
178+
]
179+
case 8:
180+
# bool(0|0)
181+
return [
182+
(NAME, "bool"),
183+
(LPAR, "("),
184+
(NUMBER, "0"),
185+
(OP, "|"),
186+
(NUMBER, "0"),
187+
(RPAR, ")"),
188+
]
189+
case 9:
190+
# bool(1^1)
191+
return [
192+
(NAME, "bool"),
193+
(LPAR, "("),
194+
(NUMBER, "1"),
195+
(OP, "^"),
196+
(NUMBER, "1"),
197+
(RPAR, ")"),
198+
]
140199

141200
def obfuscate_boolean(self, tokval):
142201
if tokval == "True":

pof/obfuscator/numbers.py

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,21 @@ class NStrats(Enum):
4343
HEX = 3
4444
BOOLEAN = 4
4545
LEN = 5
46+
BITWISE = 6
4647

4748
INT_STRATS = ( # positive int obfuscation strategies
4849
NStrats.STRING,
4950
NStrats.ADDITION,
5051
NStrats.HEX,
5152
NStrats.LEN,
53+
NStrats.BITWISE,
5254
)
5355
NEG_INT_STRATS = ( # negative int obfuscation strategies
5456
NStrats.STRING,
5557
NStrats.ADDITION,
5658
NStrats.HEX,
5759
NStrats.LEN,
60+
NStrats.BITWISE,
5861
)
5962
FLOAT_STRATS = ( # positive float obfuscation strategies
6063
NStrats.STRING,
@@ -203,6 +206,133 @@ def obf_len_random(tokval):
203206
)
204207
return t
205208

209+
@staticmethod
210+
def obf_bitwise(tokval): # noqa: C901, PLR0912
211+
"""Obfuscate integer using bitwise operations."""
212+
n = int(tokval)
213+
214+
# choose a sub-strategy randomly
215+
strategies = ["xor", "shift", "not"]
216+
if n != 0:
217+
strategies.append("or_compose")
218+
strategy = random.choice(strategies)
219+
220+
if strategy == "xor":
221+
# n = a ^ b where a is random, b = n ^ a
222+
mask = random.randint(1, max(abs(n) + 100, 255))
223+
other = n ^ mask
224+
tokens = [
225+
(NUMBER, str(mask)),
226+
(OP, "^"),
227+
]
228+
if other < 0:
229+
tokens.extend(
230+
[
231+
(LPAR, "("),
232+
(NUMBER, str(other)),
233+
(RPAR, ")"),
234+
],
235+
)
236+
else:
237+
tokens.append((NUMBER, str(other)))
238+
239+
elif strategy == "shift":
240+
if n == 0:
241+
# 1 >> 1 = 0
242+
tokens = [
243+
(NUMBER, "1"),
244+
(OP, ">>"),
245+
(NUMBER, "1"),
246+
]
247+
elif n > 0:
248+
# find a valid shift amount
249+
k = random.randint(1, 3)
250+
shifted = n >> k
251+
remainder = n - (shifted << k)
252+
if remainder == 0:
253+
tokens = [
254+
(NUMBER, str(shifted)),
255+
(OP, "<<"),
256+
(NUMBER, str(k)),
257+
]
258+
else:
259+
tokens = [
260+
(LPAR, "("),
261+
(NUMBER, str(shifted)),
262+
(OP, "<<"),
263+
(NUMBER, str(k)),
264+
(RPAR, ")"),
265+
(OP, "|"),
266+
(NUMBER, str(remainder)),
267+
]
268+
else:
269+
# for negatives, use ~(~n)
270+
complement = ~n
271+
tokens = [
272+
(OP, "~"),
273+
(NUMBER, str(complement)),
274+
]
275+
276+
elif strategy == "not":
277+
# ~(~n) = n, but render as ~m where m = ~n
278+
complement = ~n
279+
if complement >= 0:
280+
tokens = [
281+
(OP, "~"),
282+
(NUMBER, str(complement)),
283+
]
284+
else:
285+
tokens = [
286+
(OP, "~"),
287+
(LPAR, "("),
288+
(NUMBER, str(complement)),
289+
(RPAR, ")"),
290+
]
291+
292+
else:
293+
# or_compose: split n into disjoint bit groups
294+
# a | b where a & b == 0 and a | b == n
295+
if n > 0:
296+
bit_len = n.bit_length()
297+
split = random.randint(1, max(bit_len - 1, 1))
298+
mask = (1 << split) - 1
299+
a = n & mask
300+
b = n & ~mask
301+
else:
302+
# for negatives, use XOR as fallback
303+
mask = random.randint(1, 255)
304+
other = n ^ mask
305+
tokens = [
306+
(NUMBER, str(mask)),
307+
(OP, "^"),
308+
]
309+
if other < 0:
310+
tokens.extend(
311+
[
312+
(LPAR, "("),
313+
(NUMBER, str(other)),
314+
(RPAR, ")"),
315+
],
316+
)
317+
else:
318+
tokens.append((NUMBER, str(other)))
319+
return [
320+
(LPAR, "("),
321+
*tokens,
322+
(RPAR, ")"),
323+
]
324+
tokens = [
325+
(NUMBER, str(a)),
326+
(OP, "|"),
327+
(NUMBER, str(b)),
328+
]
329+
330+
return [
331+
(LPAR, "("),
332+
*tokens,
333+
(RPAR, ")"),
334+
]
335+
206336
@staticmethod
207337
def verify_number_obfuscation(tokval, tokens):
208338
# if only one token then it's the same
@@ -217,7 +347,7 @@ def verify_number_obfuscation(tokval, tokens):
217347
logger.error(msg)
218348
return False
219349

220-
def obfuscate_number(self, toknum, tokval): # noqa: C901 PLR0912
350+
def obfuscate_number(self, toknum, tokval): # noqa: C901, PLR0912, PLR0915
221351
unobfuscated = [(toknum, tokval)]
222352

223353
# get token type
@@ -268,6 +398,8 @@ def obfuscate_number(self, toknum, tokval): # noqa: C901 PLR0912
268398
tokens = self.obf_boolean_conversion(tokval)
269399
elif strategy == self.NStrats.LEN:
270400
tokens = self.obf_len_random(tokval)
401+
elif strategy == self.NStrats.BITWISE:
402+
tokens = self.obf_bitwise(tokval)
271403
else:
272404
msg = f"Strategy {strategy} not found"
273405
raise PofError(msg) # noqa: TRY301

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "python-obfuscation-framework"
7-
version = "1.12.0"
7+
version = "1.12.1"
88
description = "Python Obfuscation Framework"
99
readme = "README.md"
1010
requires-python = ">=3.12"

tests/obfuscator/fixtures/booleans.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,15 @@
1010
print(2)
1111

1212
# obfuscated
13-
14-
# not False
1513
print(not False)
16-
17-
# all([])
1814
print(all([]))
19-
20-
# any([True])
2115
print(any([True]))
22-
23-
# not not True
2416
print(not not True)
25-
26-
# '' in ''
27-
print("" in "")
28-
29-
# bool(1)
17+
print('' in '')
3018
print(bool(1))
19+
print(bool(1&1))
20+
print(bool(~0))
21+
22+
print(bool(1&0))
23+
print(bool(1^1))
24+
print(bool(0|0))

tests/obfuscator/fixtures/numbers.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@
2727

2828
# Boolean
2929
print((True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True+True))
30+
31+
# Bitwise XOR
32+
print((53^31))
33+
34+
# Bitwise shift
35+
print((21<<1))
36+
37+
# Bitwise NOT complement
38+
print((~(~42)))

0 commit comments

Comments
 (0)