Lua File Encyption + Security

Hi all, I come today to talk about Lua encyption and security. I have recently seen quite a few threads pop up about decrypting of lua cache files (which has been around since the beginning of lua cache files in gmod), and an (bad) attempt to secure lua files using some default lua functions.

So after some thought, I have come up with a few things that would seem like a fairly good attempt at securing our client/shared files, but some problems exist with them.

Solution 1: (XOR)

I have found that we can actually use xor encyption in glua, manipulating string.byte and bit.bxor. This way we can make our own unique keys and own unique ways of encrypting data. Here is a simple function that allows you to pass a string and a key to it, and it will return the xor'd version:

function xorstring(toenc, key)
	local endstr = "";
	for i = 1, #toenc do
		local keyind = ((i - 1) % #key) + 1
		endstr = endstr..string.char(bit.bxor(string.byte(toenc*), string.byte(key[keyind])))
	return endstr;

This function paired up with these next two can do a lot:

function fillleft(str, amt, rep) -- not sure if there is a default function for this
	local rep = tostring(rep);
	local str = tostring(str);
	for i = #str, amt - 1 do
		str = rep..str;
	return str;

function readbytes(str)
	local ends = "";
	for i = 1, #str do
		ends = string.format("%s%s ", ends, fillleft(string.byte(str*), 2, 0));
	return ends

For example if we were to do this:

print(readbytes(xorstring("Blob Dillon", "Alphabet")));

It would print: 03 00 31 10 65 38 12 24 45 03 30
Calling this function again with the same key would output the original function:

Solution 2: (Running Bytecode)
I have found out that some default lua functions allow you to read and run bytecode, but unfortunately the running of bytecode is not supported in glua.

I have researched this a bit and found out that there could be a risk for hacks when allowing people to load bytecode, but, this simply **does not make sense**. People can easily load hacks without using bytecode, why limit the possibilities just because of a really low risk of someone having to load bytecode instead of regular lua hacks? (garry pls fix it the way it is meant to be? :D)

Now, instead of me getting sidetracked into this hacking area of glua, I will explain how bytecode can secure your client and shared lua files.

Think of it this way, Lua reads your text data and converts it into bytes that can be interpreted to do something else, much like any other language (C++, C, .NET). string.dump allows you to dump the function you pass to it's bytes into a string. You could pass this to a client and call loadstring or load with this string. This would load the function without having any text with it, thus making it **harder** (not impossible) for many people to get the text associated with it, and make the need for encoding the plain text not needed. 

What are your thoughts on the matter? Do you have any ideas for securing lua files/code? Am I trying too hard? Post below!

When will people realize encryption is useless if all it takes is RunString = print?

You do realize that can be easily bypassed if one wanted it to be, in init.lua do:

SomeOtherFuncName = RunString;
RunString = function() SomeOtherFuncName() end -- in case of emergency!

But what do you think about the second encyption method? It does not need the use for running any string at all.

If I override RunString first then you’ll just be declaring the new var as whatever I changed RunString to. I’ll address your other stuff when I get home.

Changing RunString can also be detected by just using string.dump and comparing it to the real value of string.dump(RunString), but yes that can be detoured to and, yes, everything that can detect everything can be detoured successfully, so it is only a matter of “who can be the biggest no lifer” when it comes to detecting it.

You can’t dump RunString or print. They’re declared in C. Go try it.

These aren’t solutions. But let’s assume that for some reason, we couldn’t detour RunString, The client would still need to know the key, and thus anyone can still decrypt it. We went over this in the last thread.

At the end of the day, the client side code is not the important stuff when it comes to how the game works.

There are a handful of bugs in bytecode that allow essentially arbitrary memory access in the 5.1 VM. I would assume such bugs also exist in some form in the LuaJIT 5.1 VM, and so it would be very unsafe to allow servers to run completely unknown bytecode on a client.

Peter Cawley did quite a bit of research on this for the 5.1 VM:


The problem with your XOR obfuscation is that say all your client files are leaked without the key but obfuscated. The key is much shorter than the source material, so brute forcing it would be very simple (especially if it had a limited character set). You could mitigate this by using a stronger cryptographic algorithm such as AES, but the weakness is still intercepting the key or the execution on the client. I don’t agree with the premise of obfuscation and sending to the client post-init, since you’re just wasting bandwidth, missing critical setup windows, and it’s just generally inefficient.

You’re not going to stop people getting your code, Lua’s a scripting language. The best you can do is obfuscate it, anything else is a pointless exercise, even compiling to byte code: writing decompilers is just trivial for that.

No form of “encryption” will block people from using at your code. If you use net messages, they can be captured. If the clientside code has some form of actual encryption applied to it, someone here will be able to decrypt it within hours; as there has to be a decryption method in lua as well (and from my knowledge lua is extremely limited).

It’s impossible to completely secure clientside code, as long as it’s still on the client it’s vulnerable. I think instead of encrypting your code and hoping people won’t use it, you should file for a copyright on your code/get DMCA protection and take legal action if someone uses it. This is the best way to ensure it’s security, if you’re that worried. Sure, I could steal’s frontend but I’ll be infringing their site.

At least this way, people that legitimately need to view code for educational purposes have the ability to.

local oldfenv = getfenv(1)

local t = {}
t.__index = function( tabl , key )
	if key == 'RunString' then
		return oldfenv['print']
	return oldfenv

local new_G = setmetatable( t , {_G = oldfenv} )

setfenv(1, new_G )

Untested but should work.

I forgot about that, you are correct. Still, there are many other ways to detect it, and in the end it is still a matter of who is more of a no life…

That was an interesting read. That would make clients able to be exploited to read personal data, and other things, correct? That’d make my bytecode possibility out of the question.

Imagine having a random amount (up to however long the way you are sending data allows) of a set of 255 different characters, would you be able to forcebrute that effectively?

as long as no one releases something for that (whistles while staring at you), it will be fine for most people.

Unfortunately, bad people always exist. So relying on your trust that nobody will write a decompiler is a little silly.

I’m not relying on people not to write a decompiler, I’m actually expecting people to.

I’ve had a file stealer for years, many many years, and many other people have had them too. The people who make them are usually smart enough not to release them, though.

I guess I’m too dumb, but in all honesty the files were never ment to be hidden from the public in the first place. They’ve always just been a zip file.

RunString(“bytecode”) is possible FYI, and on top of that

If you detour RunStringEx in c++, you can catch all lua ran, decrypted, and it’s completely readable.

Encrypting your cache only stops people from using a 30 line decompiler, instead, they’ll need to use a 60-70 line runstringex detour

The only reason bytecode loading in this way doesn’t work every time is because garry essentially uses strlen to get the length of the input string, which means that it’s truncated after the first null byte. Bytecode loading is not disabled anywhere - it’s literally only stopped by the behaviour of strlen.

Where’s Acecool? I’m sure he’d be having a field day at all this shit.

Would it be possible to execute code without RunString? For instance, using patterns, to execute a string based off its contents and completely avoid predefined Lua compilation?

Regardless I find that string encryption (not just Lua files to be executed) is a valuable tool for data storage.

Not possible to execute Lua without RunString.

This thread is pretty much done as zero said above, all it takes is a C detour on RunString and there is no way to stop it.

You can obfuscate your clientside Lua, but you will never be able to encrypt it. /thread

is the hip new glua thing to do encryption? why does everybody want to hide their shit so bad?