Well, since there was no option for "tutorial" in the type of post, it has been created as a simple thread...
Obfuscation - confusion resulting from failure to understand
obfuscated code - Code that is hard to read
In this tutorial, I will show you how to convert something readable, to something completely unreadable, but it still works, exactly the same. There are multiple thing you can do in Python to achieve obfuscation, one of the main ones being one-liners and exec(). I have not understood the lambda function yet, but you can still pull off loops and if statements that have one statement, by putting them on the same line; I.e.:
x = 1
# This:
if x == 1:
print("x is equal to 1")
# Becomes this:
if x == 1: print("x is equal to 1")
Both work exactly the same.
Next, we shall take a look at a string:
toexec = '''x = 0
if x == 0:
print("Hello world")
'''
You might be asking, well, what does this have to do with anything? Here's the answer, the exec()
can execute this string as actual code. If you type in toexec
in the interpreter, you're shown this: 'x = 0\nif x == 0:\n print("Hello world")\n'
Now, if you type this: exec(toexec)
You are now presented with this: 'Hello world'
Nifty, eh? Another way of obfuscating your code is through use of ord() and chr(). That toexec code can also be represented this way: chr(120)+chr(32)+chr(61)+chr(32)+chr(48)+chr(10)+chr(105)+chr(102)+chr(32)+chr(120)+chr(32)+chr(61)+chr(61)+chr(32)+chr(48)+chr(58)+chr(10)+chr(32)+chr(32)+chr(32)+chr(32)+chr(112)+chr(114)+chr(105)+chr(110)+chr(116)+chr(40)+chr(34)+chr(72)+chr(101)+chr(108)+chr(108)+chr(111)+chr(32)+chr(119)+chr(111)+chr(114)+chr(108)+chr(100)+chr(34)+chr(41)+chr(10)
Any idea of what that is? That is a numerical representation of the string toexec. If you type in exec(chr(120)+chr(32)+chr(61)+chr(32)+chr(48)+chr(10)+chr(105)+chr(102)+chr(32)+chr(120)+chr(32)+chr(61)+chr(61)+chr(32)+chr(48)+chr(58)+chr(10)+chr(32)+chr(32)+chr(32)+chr(32)+chr(112)+chr(114)+chr(105)+chr(110)+chr(116)+chr(40)+chr(34)+chr(72)+chr(101)+chr(108)+chr(108)+chr(111)+chr(32)+chr(119)+chr(111)+chr(114)+chr(108)+chr(100)+chr(34)+chr(41)+chr(10))
, you are presented with "Hello world"
once again. Again, pretty nifty. However, you could also type in print(chr(120)+chr(32)+chr(61)+chr(32)+chr(48)+chr(10)+chr(105)+chr(102)+chr(32)+chr(120)+chr(32)+chr(61)+chr(61)+chr(32)+chr(48)+chr(58)+chr(10)+chr(32)+chr(32)+chr(32)+chr(32)+chr(112)+chr(114)+chr(105)+chr(110)+chr(116)+chr(40)+chr(34)+chr(72)+chr(101)+chr(108)+chr(108)+chr(111)+chr(32)+chr(119)+chr(111)+chr(114)+chr(108)+chr(100)+chr(34)+chr(41)+chr(10)) and be presented with your code. D: Pretty bad. So, what we do, is turn those \n into \x01, and then make another function to turn the \x01 into \n, and then execute that. D: Sounds, difficult, right? Yeah, it kinda is. I would actually stay away from the chr(i) way of obfuscating for right now, it takes up a lot of space. However, we still need to replace those \n's within our code to make sure we cannot just print(toexec) to see what it is. So, we do this before saving our source code:
toexec = toexec.replace('\n', '\x01')
Now, we cannot just do a print(). Although we could, but it would still be on one line. Here is where we can use the chr() to run that code to replace the '\x01' with '\n' when running the code:
s = ''
for x in "toexec = toexec.replace('\x01', '\n')":
s += 'chr(' + str(ord(x)) + ')+'
So, we now have this code: toexec = chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(32)+chr(61)+chr(32)+chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(46)+chr(114)+chr(101)+chr(112)+chr(108)+chr(97)+chr(99)+chr(101)+chr(40)+chr(39)+chr(1)+chr(39)+chr(44)+chr(32)+chr(99)+chr(104)+chr(114)+chr(40)+chr(49)+chr(48)+chr(41)+chr(41)
, which will convert our toexec code's \x01's back to \n.
Now, here is our almost completely obfuscated code, which we can place all on one line, thanks to the semicolon for defining things:
toexec = 'x = 0\x01if x == 0:\x01 print("Hello world")\x01'; exec(chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(32)+chr(61)+chr(32)+chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(46)+chr(114)+chr(101)+chr(112)+chr(108)+chr(97)+chr(99)+chr(101)+chr(40)+chr(39)+chr(1)+chr(39)+chr(44)+chr(32)+chr(99)+chr(104)+chr(114)+chr(40)+chr(49)+chr(48)+chr(41)+chr(41)); exec(toexec)
This will run exactly the same as this:
x = 0
if x == 0:
print("Hello world")
Ah, but wait! There's more!
We can now go and replace all of the variables with an underscore! So, our x will now be '_'. If we had more, each one would be '_'*number of the variable. So if we had 'x', 'y', 'z', then they would now be: '_', '__', '___'. This also works for classes and functions as well. So, our code is now this:
toexec = '_ = 0\x01if _ == 0:\x01 print("Hello world")\x01'; exec(chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(32)+chr(61)+chr(32)+chr(116)+chr(111)+chr(101)+chr(120)+chr(101)+chr(99)+chr(46)+chr(114)+chr(101)+chr(112)+chr(108)+chr(97)+chr(99)+chr(101)+chr(40)+chr(39)+chr(1)+chr(39)+chr(44)+chr(32)+chr(99)+chr(104)+chr(114)+chr(40)+chr(49)+chr(48)+chr(41)+chr(41)); exec(toexec)
Even more unreadable, right!? Well, put that in your interpreter, it still runs :D Amazing, right?
Well, for those who think it's a bit unnecessary to obfuscate printing "Hello world", I have attached two files that run exactly the same, and one has been obfuscated. You tell me if you can read the obfuscated version :D Of course, this won't stop the truly determined who want to read your source code, it should scare off a lot of people xDDD Not to mention that if you compile it, making it into a .pyc, if someone has an uncompiler, they'll think it's broken xD
Anyways, that concludes my highly informal tutorial, if you have any questions, feel free to ask :)