Why decompiling LUA scripts doesn’t work all the time…

In one of my posts this year I presented a bunch of decompiled LUA scripts associated with FLAME malware. The scripts were decompiled using the Lua decompiler – and since the decompilation process is non-trivial – it brings us closer to the subject of this post – how to work with the tool that doesn’t work all the time.

First of all, the Lua Decompiler is only available as a source code and you need to compile it. This can be quite a big obstacle.

I won’t go into details on how to compile it, but will mention that on a plain vanilla Ubuntu ISO (v16.0) it worked like a charm, but only after updating the environment with the developers’ tools and fixing a few things here and there (think: 2h of research and work at least). Most of the required steps require to install additional (missing packages). If you never compiled open source stuff  you are in for a big fun and lots of googling (think: 4-8h of your life 😉

Secondly, the compiled LUA scripts are a pain in the neck.

Why?

They store the size of various types in the header of the compiled LUA script. These types affect the way decompiler works.

Yes, you hear that right.

To decompile the byte-coded LUA script you need a version of Lua Decomiler that _matches_ the settings inside the header of a compiled LUA script!

The below is a fragment of Lua Decompiler code that refers to this – the header of Lua compiled script is not fixed and it depends on the actual architecture of the CPU and compiler settings:

/*
* make header
*/
void luaU_header (char* h)
{
 int x=1;
 memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
 h+=sizeof(LUA_SIGNATURE)-1;
 *h++=(char)LUAC_VERSION;
 *h++=(char)LUAC_FORMAT;
 *h++=(char)*(char*)&x;                /* endianness */
 *h++=(char)sizeof(int);
 *h++=(char)sizeof(size_t);
 *h++=(char)sizeof(Instruction);
 *h++=(char)sizeof(lua_Number);
 *h++=(char)(((lua_Number)0.5)==0);        /* is lua_Number integral? */
}

An example of one of the Flame files (the header) is shown below:

You can quickly decipher that most of the structures are 4-bytes long i.e. 32-bit – as such you need a 32-bit version of LuaDec compiled for this particular version of compiled bytecode. In my tests I actually compiled various versions of LuaDec and preserved them for further use.

That’s it.

The best advice I can give you is to get the Luadec yourself and either compile it on a system with the architectural settings that match your compiled *.lua files, or tweak the compiler settings for Luadec to achieve the same result (I am not claiming this is possible as I have not tried it).

I am not sure why Lua scripts are compiled this way, but it’s pretty much nonsensical as it’s not very portable. But if the interpreter for the specific encoded Lua script is incorporated into the final malicious package the devs don’t really need to care – it simply works out of the box for them.

Reversers – as it’s often the case – don’t have it that easy…