Malware Technology

Personal Journal on Defensive Cybersecurity, DevOps, and anything that comes to mind


An Introduction to Detect-It-Easy (DIE) Script

Introduction

After completing my BAD BASS experiment, I have become more interested in ways to identify, extract from memory, and unpack “shellcode-compiled” PE files. Tools such as Monoxgas’s sRDI, TheWover’s Donut, and hashezerade’s pe_to_shellcode make it easy to generate shellcode loaders for PEs that eliminate the need to write code for manual mapping, handling imports, relocations, TLS callbacks, etc. OffSec-minded security folk can simply integrate these tools with their own projects or C2 frameworks, write shellcode to a local or remote process, and trigger a thread to start execution.

In fact, that’s exactly what the popular Sliver C2 does!

Tools exist such as Hashezerade’s pe-sieve or hollows-hunter that can automate the identification and extraction of shellcode from a process’s memory. However, once the shellcode is extracted, how can we figure out what it does?

Emulation tools exist that can run shellcode in a sandbox to help determine its nature. However, attackers are people too, and so may use off-the-shelf or open-source tooling in order to lessen expenditures in developing their TTPs. If we could simply identify if they’re using a known shellcode-generator, we might be able to shorten our own timeline to unpack it for easier analysis.

Detect-It-Easy (DIE) is one of my favorite tools for the initial triage of a malware sample. In addition to a plethora of other features, it has a large signature database to identify the compiler, linker, CPU architecture, format, and any packers/obfuscators that may relate to a given file.

Writing a signature for DIE is a relatively simple process, but it is largely undocumented. So, how can we start?


DIE Script


DIE Script is a simple language based upon Qt Script. Making heavy usage of the Qt framework for development, this makes sense as it allows for easy construction of a domain-specific language tailored towards your application.

Qt Script itself is based upon ECMAScript, a predecessor to Javascript. As such, it’s easy to pick up for anybody already familiar with programming.

The codebase for DIE Script can be found here. Code documentation is really the only documentation that I have found for it. Different objects exist to read and interpret part of the input file given to DIE, such as those for PE files, Mach (OSX) binaries, ELF, and Binary (simply reading and finding byte-values).

To better understand how to write a DIE script, let’s walk through a simple one that I have developed for identifying Hashezerade’s pe_to_shellcode.

Example Signature


Example pe_to_shellcode detected



Code

// DIE's signature file
// Author: nicholasmckinney


// Detects hashezerade's reflective pe_to_shellcode generator
// https://github.com/hasherezade/pe_to_shellcode

init("shellcode","pe_to_shellcode");


function detect(bShowType,bShowVersion,bShowOptions)
{
    bDetected = 0;
    
    // https://github.com/hasherezade/pe_to_shellcode/blob/master/pe2shc/stub2/stub32.bin
    var stub32 = "558BEC51518D45F85350E8AA0100005984C0750883C8FFE9A4000000\
    56578B7D08B84D5A00006639070F858C0000008B773C03F7813E50450000757F8D86A0\
    00000033DB391875046AFDEB71FF76345750E8FB00000083C40C84C075046AFCEB5C39\
    9E80000000742357FFB684000000FFB680000000FF75FCFF75F8E86F00000083C41484\
    C075046AFBEB318D86C0000000391874095750E82800000059598B4628B90020000003\
    C766854E167408536A0157FFD0EB07FFD0EB036AFE585F5E5BC9C204008B4424045657\
    8B7C24108B008B74380C85F674128B0E85C9740C6A006A0157FFD183C60475EE5FB001\
    5EC3538B5C241855568B7424185703F3EB3C03C350FF5424188BE885ED74408B7E1003\
    FBEB1F79050FB7C1EB058D430203C185C074295055FF54242085C0741F890783C7048B\
    0F85C975DB83C6148B460C85C075BD85F60F95C05F5E5D5BC332C0EBF78B4424045355\
    568B08034C241457EB478B410483F808723F8D68F8D1ED6A005B74320FB7545908668B\
    C266C1E80C6685C074216683F803752B8B44241881E2FF0F00002B44241C03D6035424\
    180102433BDD72CE0349048B3185F675B3B0015F5E5D5BC332C0EBF7565768029FE66A\
    E88B0100008BF05985F67431688DBDC13F56E87C0000008BF8595985FF741E68FF1F7C\
    C956E869000000595985C0740D8B4C240C894104B0018939EB0232C05F5EC353568B74\
    240C83CAFF8A1E84DB743B8A7C24105784FF750A8D43BF3C19770380C3206A085F8BCA\
    0FBEC3D1E933C28BD181F22083B8ED24010F44D1D0FB83EF0175E3468A1E84DB75CB5F\
    F7D25E8BC25BC351515355568B742418B84D5A00005766390675618B463C8B44307885\
    C074568B54301C33FF8B4C30188954241C8B5430208B443024894C241085C974388D1C\
    328D2C308B4C241C0FB745006A018D048103C6894424188B0303C650E84FFFFFFF5959\
    3B44242074164783C50283C3043B7C241072CE33C05F5E5D5B5959C38B4424148B0003\
    C6EBEF56578B7C240C83CAFF33F60FB7076685C0745B5355807C2418000FB7C8894C24\
    1475108D41BF6683F819770783C120894C2414668B5C24146A085D8BC233CAD1E88BD0\
    81F22083B8ED80E1010F44D066D1EB66895C241483ED0174068B4C2414EBD9460FB704\
    776685C075A95D5BF7D25F8BC25EC364A1300000005356578B780C83C7148B37EB288D\
    46F885C074258B581885DB741E8378300074126A00FF7030E85AFFFFFF59593B442410\
    740C8B363BF775D433C05F5E5BC38BC3EBF800000000000000";
    
    // https://github.com/hasherezade/pe_to_shellcode/blob/master/pe2shc/stub2/stub64.bin
    var stub64 = "56488BF44883E4F04883EC20E805000000488BE65EC348895C240857\
    4883EC30488BF9488D4C2420E83702000084C0750883C8FFE9BD000000B84D5A000066\
    39070F85AA00000048635F3C4803DF813B504500000F8597000000488D8BB000000083\
    3900750AB8FDFFFFFFE9860000004C8B4330488BD7E86201000084C07507B8FCFFFFFF\
    EB6F83BB9000000000742A0F28442420488D4C2420488B93900000004C8BC7660F7F44\
    2420E89300000084C07507B8FBFFFFFFEB3C488D8BD00000008339007408488BD7E833\
    0000008B4328B9002000004803C766854B16740E4533C0488BCF418D5001FFD0EB09FF\
    D0EB05B8FEFFFFFF488B5C24404883C4305FC348895C2408574883EC208B01488BFA48\
    8B5C10184885DB741B4C8B0B4D85C974134533C0488BCF418D500141FFD14883C30875\
    E5488B5C2430B0014883C4205FC3488BC4488958084889681048897018488978204156\
    4883EC208BDA498BF04903D84C8BF1EB4A8BC84803CE41FF16488BE84885C074628B7B\
    104803FEEB2679050FB7D1EB07488D56024803D14885D27447488BCD41FF56084885C0\
    743B4889074883C708488B0F4885C975D24883C3148B430C85C075AF4885DB0F95C048\
    8B5C2430488B6C2438488B742440488B7C24484883C420415EC332C0EBE148895C2408\
    48897C2410448B09498BD84C03CA4C8BDA33FF458B114585D27459418379040872F141\
    8B51044C8BC74883EA0848D1EA7435430FB74C41080FB7C166C1E80C6685C074236683\
    F80A752681E1FF0F0000498BC34103CA482BC34903CB49FFC04801014C3BC272CB418B\
    41044C03C8EBA332C0EB02B001488B5C2408488B7C2410C348895C2408488974241057\
    4883EC20488BD9B9029FE66AE8FD010000488BF84885C07432BA8DBDC13F488BC8E893\
    000000488BF04885C0741DBAFF1F7CC9488BCFE87E0000004885C0740B48894308B001\
    488933EB0232C0488B5C2430488B7424384883C4205FC3448A09448ADA4C8BD14183C8\
    FFEB414584DB750C418D41BF3C1977044180C120BA08000000418BC8410FBEC14133C0\
    D1E9448BC14181F02083B8ED2401440F44C141D0F94883EA0175DB49FFC2458A0A4584\
    C975BA41F7D0418BC0C3488BC448895808488968104889701848897820415441564157\
    B84D5A0000448BFA4C8BC16639010F85870000004863413C8B8C088800000085C97478\
    498D040833FF8B68184885ED746A448B4820448B50244D03C8448B601C4D03D0410FB7\
    02458B194D03D883CAFF498D3484EB2741BE080000008BCA0FBEC333C2D1E98BD181F2\
    2083B8ED24010F44D1D0FB4983EE0175E249FFC3418A1B84DB75D2F7D2413BD7742D48\
    FFC74983C2024983C104483BFD72A833C0488B5C2420488B6C2428488B742430488B7C\
    2438415F415E415CC3428B04064903C0EBDC48895C240848897C2410440FB7094183C8\
    FF33FF8ADA448BD74C8BD9EB4684DB750F418D41BF6683F8197705664183C120BA0800\
    0000418BC8410FB7C14133C0D1E9448BC14181F02083B8ED2401440F44C16641D1E948\
    83EA0175DA49FFC2470FB70C53664585C975B4488B5C240841F7D0488B7C2410418BC0\
    C348895C240848896C24104889742418574883EC2065488B0425600000008BE9488B78\
    184883C720488B1F483BDF7430488D43F04885C07427488B70304885F6741E488B4860\
    4885C9740B33D2E834FFFFFF3BC57405488B1BEBD0488BC6EB0233C0488B5C2430488B\
    6C2438488B7424404883C4205FC300000000000000000000000000";
    
    var redir32_64_Start = "4D5A4552E8000000005B4883EB09534881C3";
    var redir32_Start = "4D5A4552E8000000005883E8095005";
    var redir64_Start = "4D5A4552E800000000594883E909488BC14805";
    
    var signatures = [redir32_64_Start, redir32_Start, stub32, redir64_Start, stub64];
    
    for (var i = 0; i < signatures.length; i++) {
        if (Binary.isSignaturePresent(0, Binary.getSize(), signatures[i])) {
            bDetected = 1;
            
            if (i <= 2) {
                sOptions = "x86";
            } else {
                sOptions = "AMD64";
            }
        }
    }
    
    return result(bShowType, bShowVersion, bShowOptions);
}


As you can see, it looks very similar to Javascript. There are several 3 important functions we can see in the example above:

  • init

  • detect

  • result

There are also several important global variables shown:

  • bDetected

  • sOptions

But what do these all do?

Understanding DIE Script through the Qt Script Debugger

Since DIE makes use of the Qt Script environment, it is provided with a built-in debugger that will allow us to view loaded scripts and how these functions relate to the final detection output.

To see that, load up DIE and click on the bottom-left signatures button.


DIE detected example


In the window that appears, select the pe_to_shellcode.1.sg entry on the left under Binary and then click the Debug button in the top-right corner. If the signature isn’t there (because you have a release that hasn’t been updated since 09-24-2022), you can copy the signature code above and save it to a file named pe_to_shellcode.1.sg under the db/Binary directory in your DIE installation.


DIE Debugger


Global Variables

The “Loaded Scripts” pane on the left-hand side of the debugger window can show us all the scripts that have been loaded into the script engine to debug this script. The image above shows us our signature file, but selecting the db/_init script below will show us some important information.


DIE Init Script


We can see 5 global variables (only 2 were set directly in the signature above):

  • sType

  • sName

  • sVersion

  • sOptions

  • bDetected

All can be set through the init function, which is automatically called by the script engine. However, we will typicall only set the first 2 or 3, depending upon if there are different versions of the thing we are trying to detect. In the example above, for example, we are only setting the sType to “shellcode” and sName to “pe_to_shellcode”.

Setting all 4 global variables will result in something like the following display:


DIE Fields


The bDetected variable is simply a boolean (0 == False, 1 == True) that determines if a signature matches a given file.

Auto-magically called Functions

As mentioned earlier, the init function is automatically called by the scripting engine. The same is also true for the detect function.

So these two functions make up the building blocks of our signature files:

  • init

  • detect

For a signature to work, you can include additional functions, but you must have those two functions.

Below is a skeleton signature file you can use for your own scripts:

// DIE's signature file
// Author: <YOUR NAME>

init("<TYPE PLACEHOLDER>","<NAME PLACEHOLDER>", "<VERSION PLACEHOLDER>");


function detect(bShowType,bShowVersion,bShowOptions)
{
    bDetected = 0;

	// Your logic here
    
    return result(bShowType, bShowVersion, bShowOptions);
}

Conclusion

I hope this post might help you become faster acquainted with writing your own signatures for DIE. It’s an amazing tool and the maintainer is fast to respond and polite. Documentation may be lacking, but at its core the DSL is simple.

Important Links:

Detect-It-Easy Existing Signatures

DIE Scripting Engine and Library