Every once in a while I encounter a strange anti-reverse engineering protection. Protection authors are so focused on improving one specific aspect of the protection that completely overlook other, much easier ways how the system can be defeated.
Their logic is like this - someone stole my code, I better protect it. I've heard that cryptography is good, so I'll use that. Oh no, someone stole my code again! Let me add another layer of encryption over it! Few days/weeks/months later - Those bloody hackers won't stop! Let me protect my encryption code with another encryption!
What the authors should do instead is stop and think. What do I want to protect? Against whom? For how long? What kind of loss is acceptable to me?
There is a fancy term for that - threat modeling.
The concept is nothing new, Bruce Schneier wrote a paper about "Attack Trees" back in 1999. In the paper he provides an example how safe security could be analyzed.
Once you know what types of attacks are actually possible, you know where to focus your efforts to protect your valuables.
In the next sections, I'll describe two Autoplay Media Studio plugins that have failed to do proper threat analysis and focus on totally irrelevant parts of protection.
DCrypt is a custom plugin for Autoplay Media Studio. This plugin made by a company called Dindroid and, from what I can tell, it's only used to protect their own products.
To obtain the plugin, you can download some of the newest Dindroid products. My research was done on TextExtractor installer which you can download from Dropbox (SHA1:
After you unpack Installer.exe, you should get file AutoPlay\Plugins\DCrypt\DCrypt.lmd which is version 1.4 of the plugin.
If you examine DCrypt.lmd using hex editor, you'll notice that it contains some encrypted data.
Once you decrypt the data, you'll see that it's a Lua code with a big warning message:
dindroid = [[
Contato: +55 011 97399-5031
Criado e desenvolvido por Anderson M Santos
Copyright © 2011-2022 Anderson M Santos (www.dindroid.com)
Não foi roubando código dos outros que cheguei até aqui seu ladranzinho de segunada. "Vai estudar"
It wasn't stealing code from others that got me here, you second-rate little thief. "Will study"
indc="else if V2==0 then if not((r2==13 or U2)and h2>255)endfunction();end"local a=loadstring((function(b,c)function bxor....
Ok, I will study!. bigsmile
Once you decrypt this layer, you'll find some compiled Lua code that is protected by Luraph.
Luraph is a really good obfuscator for Lua code. Some very smart people tried to devirtualize Luraph and decided that it's hard. And a huge waste of time.
So, there are plenty of protection layers but are they actually useful for protecting your code?
Think like an attacker
Let's pretend that DCrypt plugin is a big black box and observe how it interacts with the outside world.
First, it calls AMS function BlowfishDecryptString using a hardcoded password. This function decrypts and returns original Lua code.
Then Lua code is written to disk using AMS function WriteFromString. You can use OllyDbg to put breakpoint there, or on kernel32.dll functions CreateFileA and WriteFile.
And finally, the Lua code is executed via call to luaL_loadfile. At this moment you can just copy the source file from disk.
Just like that - you can ignore all of the well protected DCrypt internals and have not one, but 4 great opportunities to obtain original Lua code. Fantastic! bigsmile
This week someone sent me a link to LuaEncrypt plugin. You can download it from MediaFire (SHA1:
547CEEB94405FADF3AF7216BF6248AA07F8C3DC6). As far as I know, it's a completely new plugin and is not used by anyone yet.
Security wise, it's just as bad as DCrypt.
During protection phase, it takes Lua code and compiles it to Lua P-Code. Then it "encrypts" it using XOR, and places it into a password-protected ZIP file. During execution phase, it unpacks ZIP file with password, uses XOR to decode the Lua P-Code and then runs it.
All you need is one simple breakpoint and the protection is defeated. smile
Can it be fixed?
In previous sections, we identified several easy points for attacks. Would it be possible to fix them all?
This one is easy to fix. One can use a random alphanumeric password and just add it at the front of encrypted data. If decryption code is a black box, nobody will know where to look for the password.
Call to BlowfishDecryptString
There are several encryption algorithms that are small and could be easily implemented in Lua. For example, RC4 or IDEA. Again, if decryption code is a black box, this is safe enough.
Writing decrypted code to a file
Easy to fix - just use luaL_loadbuffer or luaL_loadstring instead. No additional file needed.
Decrypting the original code
Oops. This design issue cannot be fixed. If you use some of the Lua functions to load Lua sourcecode, or compiled P-Code, it will always be possible to obtain this code from memory.
If your threat model says that you want to prevent newbies from looking at your code, both plugins are good enough. But if your goal is protecting your source code, it would be much smarter to get rid of DCrypt and LuaEncrypt. Take your original code of the application, apply a good Lua obfuscator like Luraph and you're done. All your code will become a big black box, very hard to analyze and decompile.
How hard is that? smile
Improved AMS unpacker
It works for all files I was able to find - but I am not planning to improve it any further. If it stops working for whatever reason, please read the article, choose your own way to attack DCrypt protection and make your own tools for it.
Bonus material - broken Blowfish
As mentioned earlier, DCrypt is calling function BlowfishDecryptString to decrypt the original code. Adding Blowfish code to my unpacker should be very simple, right?
I'm guessing that the original specification was not exactly clear whether integers are big-endian or little endian. And software authors did not bother to use official test vectors to test their code. bigsmile
Once I realized the cause of the issue, I was able to create AMS-compatible Blowfish implementation in C#. I took the first available Blowfish implementation, stripped it to bare minimum, added support for PKCS#5 padding and "fixed" the endianity issue.
Here is the source code: https://bitbucket.org/kao/blowfish/src/master/Blowfish.cs.
And some example usage:
string password = "password";
string encryptedData = "AD0r0JAQIXSxYJ7KNJVS4Q==";
Blowfish blowfish = new Blowfish(password);
blowfish.Mode = BlowfishMode.AMS;
Console.WriteLine(Encoding.ASCII.GetBytes(blowfish.Decrypt_ECB(encryptedData))); // "Hello world!"
That's all for today, have fun!