Auteur Sujet: [FireEye]Using Precalculated String Hashes when Reverse Engineering Shellcode  (Lu 2869 fois)

0 Membres et 1 Invité sur ce sujet

Hors ligne igor51

  • Admin
  • Mega Power Members
  • *****
  • Messages: 10422
Using Precalculated String Hashes when Reverse Engineering Shellcode


 

In the five years I have been a part of Mandiant's malware analysis
    team (now formally known as M-Labs) there have been times when I've
    had to reverse engineer chunks of shellcode. In this post I will
    give some background on shellcode import resolution techniques and
    how to automate IDA markup to allow faster shellcode reverse
  engineering.


    Reversing Shellcode

The easiest way to determine what
    a piece of shellcode does is to allow it to run within a monitored
    environment. This may not work if the shellcode is launched by an
    exploit and you don't have the correct version of the vulnerable
    program. In our experience working with shellcode we've seen many
    malware samples that contain an embedded shellcode buffer that is
    injected into other processes to execute. However, getting this
    embedded piece of shellcode to run correctly may not always to be
    feasible. Static analysis of the shellcode may be required in these
    types of situations to determine its functionality.

Shellcode
    binaries are typically not very large, so they don't present a huge
    challenge to reverse engineer, but common techniques shellcode
    authors employ often have a side-effect of hampering reverse
    engineering. One such technique is using hashes of API function when
    manually resolving import functions.


    Shellcode Import Resolution

A developer writing a
    normal program can use kernel32.dll's LoadLibraryA and
    GetProcAddress to load arbitrary DLLs and retrieve its
    exported function pointers. Shellcode authors usually face size
    constraints, so including the full string of each API function it
    wishes to use could be prohibitive. Rather than using the full
    string, a more size efficient method is to pre-compute numeric
    hashes of function names and include these values in the shellcode.
    This changes the import resolution process as shellcode can no
    longer rely on GetProcAddress to retrieve the function
    pointers. Instead it must parse the DLL PE files in memory to find
    the export directory and traverse its array of export functions. For
    each export function name, the shellcode calculates the hash and
    compares it against the value embedded in the shellcode. The correct
    API function has been found when the values match. Background
    information on this technique can be found in the paper that was
    released by the Last Stage of Delirium as part of their winasm
   
project (http://lsd-pl.net/projects/winasm.zip).

This
    may sound difficult, but luckily most shellcode authors reuse known
    hash algorithms and values, making the life of the reverse engineer
    much better. The most common hash function that I've seen in
    recovered shellcode samples is included with Metasploit. The
    algorithm is shown in pseudocode in Figure 1.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-1_112912.png">      width="651" height="103" class="size-full wp-image-3882"
      title="Figure 1: ROR-13 Pseudocode"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-1_112912.png" />Figure
  1: ROR-13 Pseudocode

The hash is in no way a strong cryptographic
    hash, but it accomplishes the goal of calculating an integer value
    based on an arbitrary length input string. The only real constraint
    on the hash algorithm is that every API function the developer
    wishes to use within a DLL must have a unique hash value, and this
    simple ROR-13 calculation is very effective. Unusual hashes that
    I've come across are usually slight variant of this idea: they may
    rotate by a different amount, rotate left rather than right, or use
    some other operators to mix all of the characters in the input
    string to form an integer result.


    Automating Shellcode Import Markup

When first starting
    to reverse engineer shellcode you'll most likely search online for
    these magic values, or calculate these values yourself and store
    them in a text file for future reference. Over time, as I reviewed
    more samples, I realized this was an annoying and repetitive task
    that is ripe for automation through IDA scripting.

Due to
    frequent code reuse among shellcode authors, I felt releasing my set
    of IDA scripts would be helpful to malware analysts. Pre-computing
    common API function names using known hash algorithms is feasible,
    and if a new hash algorithm is encountered it likely will not be too
    difficult to implement to generate its hashes. There has only been
    one instance, involving string hash from Poison Ivy RAT, in which
    this wasn't the case.

You may find the scripts at the
    Mandiant GitHub repository:       href="https://github.com/mandiant/Reversing"
  target="_blank">https://github.com/mandiant/Reversing.


   

There are two parts:


  1.       make_sc_hash_db.py is responsible for the
          pre-calculation of function names hashes. It is a command-line
          Python script that contains an implementation of the hash
          algorithms I've encountered in the past. It processes a directory
          of DLLs, calculates the hash of each export function and stores
          all of these into an SQLite database.

  2.       shellcode_hash_search.py is an IDAPython script that opens
            the SQLite database containing the precalculated hashes and
            searches th
    rough the current binary for known
        hashes.


    make_sc_hash_db.py can be run as shown in Figure 2,
    where the first argument is the SQLite database name to create and
    the second argument is the directory containing interesting DLLs. If
    you prefer to skip this step, a sample database is included within
    the distribution.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-2_112912.png">      width="654" height="40" class="size-full wp-image-3883"
      title="Figure 2: Example database creation"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-2_112912.png" />Figure
  2: Example database creation

When the
    shellcode_hash_search.py IDAPython script runs it
    prompts the user for the database to use and then queries the user
    for additional search parameters. It shows all of the hash
    algorithms stored in the database and provides some simple
    pseudocode as shown in Figure 3. The script tries to use PySide
    Python bindings for QT that HexRays distributes (available as a
    separate download for IDA here     href="http://www.hex-rays.com/products/ida/support/download.shtml">http://www.hex-rays.com/products/ida/support/download.shtml).
    If the HexRays distribution of PySide is not included, it falls back
    to presenting a series of simple dialogs to get the same information
    from the user.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-3-QT-GUI-view.png">      width="300" height="185" class="size-medium wp-image-3884"
      title="Figure 3: QT GUI view"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-3-QT-GUI-view-300x185.png" />Figure
  3: QT GUI view

If nothing has been highlighted, the script searches
    through the highlighted region or the current segment. The searching
    process queries each DWORD (if the DWORD Array option
    is checked) and each instruction operand (if the Instr
    Operands
option is checked) to determine if that is a known
    hash for the selected hash algorithms. When a hash value is found a
    line comment is added in IDA as shown in Figure 4.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-4-Comments-are-added-for-identified-hashes.png">      width="300" height="150" class="size-medium wp-image-3885"
      title="Figure 4: Comments are added for identified hashes"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-4-Comments-are-added-for-identified-hashes-300x150.png" />Figure
  4: Comments are added for identified hashes

It's also common for
    shellcode authors to use DWORD arrays of hashes rather than pushing
    each one as a function argument, and this is shown in Figure 5.


          href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-5-DWORD-array-of-hashes.png">      width="300" height="149" class="size-medium wp-image-3886"
      title="Figure 5: DWORD array of hashes"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-5-DWORD-array-of-hashes-300x149.png" />Figure
  5: DWORD array of hashes

If the Create Struct option is
    checked and hash values are found at consecutive addresses the
    script creates an IDA structure as shown in Figure 6.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-6-Generated-structure-of-function-pointers.png">      width="300" height="221" class="size-medium wp-image-3887"
      title="Figure 6: Generated structure of function pointers"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-6-Generated-structure-of-function-pointers-300x221.png" />Figure
  6: Generated structure of function pointers

This structure may be
    useful if the shellcode authors treat this as an array of function
    pointers, as it allows the analyst to convert
    [base+index] references into structure references. This
    is shown in Figure 7 where the call instructions are easier to
    understand by using known structure offsets.

      href="https://www.fireeye.com/content/dam/legacy/ammo/Figure-7-Sample-function-pointer-array-usage.png">      width="300" height="199" class="size-medium wp-image-3888"
      title="Figure 7: Sample function pointer array usage"
  src="https://www.fireeye.com/content/dam/legacy/ammo/Figure-7-Sample-function-pointer-array-usage-300x199.png" />Figure
  7: Sample function pointer array usage

The
    shellcode_hashes_search_plugin.py IDAPython plugin is
    provided if you would like this functionality available from IDA's
    plugins menu. Simply copy this file to your
    %PROGRAMFILES%IDAplugins directory and make sure the
    other python files are accessible by setting your PYTHONPATH
   
environment variable correctly.


    Conclusion

Frequently, reversing shellcode is much
    more tedious than reversing a typical binary. The lack of imports
    resolved by IDA is a big reason for the slowed reversing timeline.
    By using the IDAPython scripts we've described above, and released
    on our GitHub page, we hope your shellcode reversing will
  improve.


Source: Using Precalculated String Hashes when Reverse Engineering Shellcode

Tags: