Old Flame Never Dies (a.k.a. decompiling LUA)

When the news about Flame exploded all over the media, I remember grabbing available samples and like many other researchers started poking around. Pretty quickly, I extracted a large number of very unique strings from various Flame samples and posted them online.

Recently, I accidentally came across that old post and started wondering if anyone ever posted the decompiled Lua scripts for the malware. I googled around for some of the strings I posted on my blog back then and to my surprise – my blog was the only one showing up!

I guess there must be some conspiracy theory that will explain that…

Back in 2012, I didn’t have all the samples, but I did run them through a quick analysis process which I will describe below. The procedure for obtaining the strings was extremely crude, but like many quick&dirty solutions – it worked pretty well (and it was fast!).

  • For each DLL, load it via rundll32
    • For each exported function, execute it
      • For ever every single execution, delay for some time
      • Grab memory dumps for rundll32
      • Kill rundll32

Interestingly, I still have the memory dumps I used to extract the strings from, so… since I suddenly thought of these Lua scripts I re-used the memdumps to extract over 60 Lua bytecoded scripts (from both static files and memory dumps to be precise).

And here comes the real purpose of the thread – document how to obtain decompiled Lua scripts from Flame:

  • I wrote a quick carving tool in perl to extract Lua bytecode from both static files, and memory dumps
    • This was pretty easy, since the compiled Lua always starts with a header “\x1BLua”
    • For each extracted file, I wrote another quick&dirty script to rename it to the name embedded inside the bytecoded Lua script
    • That’s how we get the ‘original’ name of the files f.ex. ‘MUNCH_ATTACKED_ACTION.lua’ is embedded inside the bytecoded Lua script

munch_attacked_action-lua

  • With all the files preprocessed, I ran them through a Lua decompiler
    • For many files, it worked like a charm; for some, it failed

If you remember Kaspersky’s Flame code from 2012:

kaspersky0

you can find the code inside the flame_props.lua.dec file (you need to remove decompiler’s comments):

kaspersky1

The collection of all decompiled scripts can be found here.

The password is: old_flame_never_dies

List of all scripts:

  • ___kaspersky.dec
  • attackop_base_prods.lua.dec
  • attackop_base_sendfile.lua.dec
  • ATTACKOP_FLAME.lua.dec
  • ATTACKOP_FLAME_PRODS.lua.dec
  • ATTACKOP_FLAME_STARTLEAK.lua.dec
  • ATTACKOP_FLASK.lua.dec
  • ATTACKOP_FLASK_PRODS.lua.dec
  • ATTACKOP_JIMMY.lua.dec
  • ATTACKOP_JIMMY_PRODS.lua.dec
  • ATTACKOP_MOVEFILE.lua.dec
  • ATTACKOP_RUNDLL.lua.dec
  • basic_info_app.lua.dec
  • casafety.lua.dec
  • clan_entities.lua.dec
  • clan_seclog.lua.dec
  • CRUISE_CRED.lua.dec
  • euphoria_app.lua.dec
  • event_writer.lua.dec
  • fio.lua.dec
  • flame_props.lua.dec
  • get_cmd_app.lua.dec
  • IMMED_ATTACK_ACTION.lua.dec
  • json.lua.dec
  • leak_app.lua.dec
  • libclanattack.lua.dec
  • libclandb.lua.dec
  • libcommon.lua.dec
  • libdb.lua.dec
  • libflamebackdoor.lua.dec
  • liblog.lua.dec
  • libmmio.lua.dec
  • libmmstr.lua.dec
  • libnetutils.lua.dec
  • libplugins.lua.dec
  • libwmi.lua.dec
  • main_app.lua.dec
  • MUNCH_ATTACKED_ACTION.lua.dec
  • MUNCH_SHOULD_ATTACK.lua.dec
  • NETVIEW_HANDLER.lua.dec
  • NETVIEW_SPOTTER.lua.dec
  • payload_logger.lua.dec
  • post_cmd_app.lua.dec
  • REG_SAFETY.lua.dec
  • RESCH_EXEC.lua.dec
  • rts_common.lua.dec
  • SECLOG_HANDLER.lua.dec
  • SECLOG_SPOTTER.lua.dec
  • SNACK_BROWSER_HANDLER.lua.dec
  • SNACK_ENTITY_ACTION.lua.dec
  • SNACK_NBNS_HANDLER.lua.dec
  • STD.lua.dec
  • storage_manager.lua.dec
  • SUCCESS_FLAME.lua.dec
  • SUCCESS_FLAME_STARTLEAK.lua.dec
  • SUCCESS_GET_PRODS.lua.dec
  • table_ext.lua.dec
  • transport_nu_base.lua.dec
  • TRANSPORT_NU_DUSER.lua.dec
  • TRANSPORT_NUSYSTEM.lua.dec
  • USERPASS_CRED.lua.dec
  • WMI_EXEC.lua.dec
  • WMI_SAFETY.lua.dec

Returning the call – ‘moshi moshi’, the API way (a.k.a. API cold calling)

Software development is quite easy today. There is no longer a need to write your own libraries for everything (a mouse handler, a proprietary database, a graphics library, or your own search and caching algorithms, etc.). What’s more, a very complex functionality can be now delivered with just a few lines of script.

Yup. We are indeed very lucky.

While we depend on these modules to do the hard work we happily focus on the business logic and simply getting things done. But yes, of course, this privilege relies heavily on a tremendous work of other programmers who developed and tested modules which we use every day. Modules that are the foundation of Windows as we see it today have been written over a few decades (maybe with the exception of telemetry modules ;)) and it’s not a surprise that many of them contain legacy code, vulnerabilities, and… dummy, retired, or unfinished code.

In this post I will focus on the latter – the APIs that are pretty much doing nothing, but returning an error or a predictable constant value (I will talk about 32-bit Windows, but same applies to 64-bit version). Either indicating that the function is not implemented yet, or is just a dummy function kept for compatibility reasons, or perhaps for some other reason. I won’t cover all of them, but will show a few examples of how these could be used.

  • Probably the best known function that returns a constant value is kernel32.dll!GetCurrentProcess. It simply returns -1 (0xFFFFFFFF on win32). For this reason many programmers hard-code this value in their programs (one API call less).
  • Its close cousin, kernel32.dll!GetCurrentThread always returns -2. Same, some coders hard-code it to avoid superfluous API calls.
  • crypt32.dll!DbgPrintf always returns 0. So does imm32.dll!CtfImmIsTextFrameServiceDisabled. And tones of other APIs. A nice ‘obfuscated’ replacement for the worn out ‘xor eax, eax’.
  • gdi32.dll!GdiSupportsFontChangeEvent always returns 1. So does kernel32.dll!LZStart. And again, lots of other APIs.
  • GdiPlus.dll!GdipCreateStreamOnFile aways returns 6.
  • rasman.dll!RasRpcUnloadDll always returns 50.
  • atl.dll!AtlGetVersion returns 768 on Win10..
  • Many unimplemented APIs return ERROR_INVALID_PARAMETER which is equal to 87 f.ex. dnsapi.dll!DnsUpdate and dnsapi.dll!Dns_UpdateLibEx.

This can be obviously extended to COM and its methods that do nothing, but returning ‘not implemented’ constants.

The other variants of this technique can utilize fully implemented APIs which are called with incorrect arguments producing a predictable last error value.

There are a number of potential tricks we can pull using APIs returning predictable, or very specific values, or behaving in a predictable way:

  • anti-emulation tricks
    • initialization of register values – if an emulator fails to emulate the function it may not get the proper value into the eax register and as such a code dependent on it may fail (f.ex. a decryption routine, a routine using the return value as an index to a lookup table, etc.)
    • passing data by storing it inside the internal OS structures f.ex. SetLastError/ SetLastErrorEx  / GetLastError combo
  • detecting the version of OS
    • historically, some APIs changed the behavior and newer versions may simply return a constant f.ex. TRUE (1); a simple example is GdiSetAttrs (probably not very practical example as its behavior changed long time ago, but always…)
    • new APIs can help to detect OS version w/o using ‘traditional’ APIs (f.ex. Get Version, etc.) – relatively new ole32.dll’s CoBuildVersion function could be used to determine the version of ole32.dll (and indirectly, OS version); same goes for UrlMkBuildVersion exported by urlmon.dll
  • anti-debugging tricks
    • if the function does nothing, but calling DebugBreak (int 3), it may fool some analysts by triggering the exception handler w/o them noticing it (lame, but always)
  • perhaps others…