How security plugins for Autoplay Media Studio fail


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.

Introducing DCrypt

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: 31F69FFF699F22FACB09FB28542F0385B8EDBA49)

After you unpack Installer.exe, you should get file AutoPlay\Plugins\DCrypt\DCrypt.lmd which is version 1.4 of the plugin.

Plugin design

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 (

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[4]>255)endfunction();end"local a=loadstring((function(b,c)function bxor....

Ok, I will study!. πŸ˜€

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.

Soon enough you'll notice that it's calling functions from AMS runtime and from Lua runtime. Something like this (click on the image to see it in full size):

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! πŸ˜€

LuaEncrypt plugin

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.

From attacker's perspective, it looks like this.

All you need is one simple breakpoint and the protection is defeated. πŸ™‚

Can it be fixed?

In previous sections, we identified several easy points for attacks. Would it be possible to fix them all?

Hardcoded password

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? πŸ™‚

Improved AMS unpacker

I added a very simple support for DCrypt into my AMS unpacker. It will try to find DCrypt usage in project.dat file, and decrypt the Lua code.

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?

Well, no.

There are lots of broken Blowfish implementations out there. JavaScript had one. Molebox Virtualization Studio has one. And - surprise, surprise - Autoplay Media Studio has another one!

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. πŸ˜€

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:
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!

15 thoughts on “How security plugins for Autoplay Media Studio fail

  1. using System.Diagnostics;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;

    [assembly: AssemblyCompany("kao")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCopyright("Copyright Β© kao 2015-2022")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyFileVersion("")]
    [assembly: AssemblyProduct("AMSUnpacker")]
    [assembly: AssemblyTitle("AMSUnpacker")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyVersion("")]
    [assembly: CompilationRelaxations(8)]
    [assembly: ComVisible(false)]
    [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
    [assembly: Guid("c53876b2-0428-47d9-b704-c3b84ac89b3d")]
    [assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)]
    [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName=".NET Framework 4.7.2"

    1. And what is your question? πŸ™‚

      My unpacker is not protected in any way. If someone wants to look at the decompiled code, he/she can do it any time.

  2. This article is very relatable. It sometimes really seems there is some form of illiteracy when it comes to software protection. A lot of people don't fundamentally understand what an obfuscator does and what it protects against. For example, I've seen many cases (especially in the .NET RE world) where people just add layers and layers of latest technology obfuscation and virtualization on top of each other. However, before they obfuscate, they used a packer/compressor first. This way they end up just protecting the unpacker stub instead of the actual code that matters. Like you said, just one or two breakpoints and Bob's your uncle! πŸ™‚

  3. bro My question is
    {hidden link}
    {hidden link}
    {hidden link}
    {hidden link}
    {hidden link}
    {hidden link}

  4. Nice article like always, kao.
    I agree: sometimes crypto gives a [false] sense of security. πŸ™‚

    Thank you for sharing your experience and your adapted blowfish code: I can only imagine the headaches you had. Couple of times I had to deal with "different" implementations of Base32 and it took me some time to understand why I couldn't match my results with theirs (me being, basically, a crypto noob ), LoL.

    Keep rocking.

    1. You're struggling to deobfuscate LUA code, which is a totally different problem, and not related to my unpacker.

      First, you should use new version of unluac. I used unluac_2022_01_12.jar and the result looks like this

      local L0_1, L1_1, L2_1, L3_1, L4_1, L5_1, L6_1, L7_1, ...
      L0_1 = "D~L@lPEQW_]e'hm~V'Q!Xb4{axkqi-Agz0@H};nJSVYSH=cXj%"
      _Crypto = L0_1
      L0_1 = loadstring
      L3_1 = _Crypto
      L1_1, L2_1, L3_1, ... = L1_1(L2_1, L3_1)
      L0_1 = L0_1(L1_1, L2_1, L3_1, L4_1, L5_1, L6_1, ...)
      if L0_1 then
        L1_1 = L0_1

      Warning: I replaced parts of code with "..." to make this comment more readable!

      To deobfuscate this LUA code, replace last 5 lines with a simple print statement.


      Now the code should look like this:

      local L0_1, L1_1, L2_1, L3_1, L4_1, L5_1, L6_1, L7_1, ...
      L0_1 = "D~L@lPEQW_]e'hm~V'Q!Xb4{axkqi-Agz0@H};nJSVYSH=cXj%"
      _Crypto = L0_1
      L0_1 = loadstring
      L3_1 = _Crypto
      L1_1, L2_1, L3_1, ... = L1_1(L2_1, L3_1)

      Run your LUA file and it will print out the deobfuscated code:

       function Sloock_db()
        s_Handl = Application.GetWndHandle()
        DLL.CallFunction(_SystemFolder .. "\\User32.dll", "SetClassLongA", 
  5. This stuff is amusing to read, pt2 as well. It's time people understand that obfuscation is just obfuscation and that it's a waste of time. A VM + a luaL_loadbuffer or luaL_loadfile hook beats pretty much all of them πŸ˜€

    1. That was exactly the point I was trying to make! πŸ™‚

      However, some people are lazy and they demand ready-made tools..

Leave a Reply

  • Be nice to me and everyone else.
  • If you are reporting a problem in my tool, please upload the file which causes the problem.
    I can`t help you without seeing the file.
  • Links in comments are visible only to me. Other visitors cannot see them.

9  +   =  eighteen