rendari

  1. Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL...

    In the last article I wrote about native exe's, I briefly touched upon the issue of IL code being
    left over in generated Native EXE's, and how this could be a possible security hole that hackers could
    exploit when trying to reverse your .NET app. How big of a hole is this, you might ask? Very big. I
    managed to fully rebuild my old managed .NET exe just from the native image provided by Ngen. I aim
    to explain the steps I took in this blog post.

    First things first, we will be using this .NET exe for out experimentations:
    http://www.filesend.net/download.php?f=644ee1dfd1b9246aee11d64b931bd0fb

    It's just a simple Unpackme I wrote and submitted to crackmes.de. If you suspect malicous code, feel
    free to decompile it in Reflector.

    Anyways, extract the exe anywhere, and execute Ngen upon it

    Code:
    H:\temp>ngen unpackme.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50727.42
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly H:\temp\unpackme.exe
    Compiling 1 assembly:
        Compiling assembly H:\temp\unpackme.exe ...
    UnpackMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null <debug>
    
    H:\temp>
    Then browse to your Global Assembly Cache at H:\WINDOWS\assembly\NativeImages_v2.0.50727_32\UnpackMe,
    find UnpackMe.ni.exe, and copy it over to some temporary location, where we can "work" on it.

    Now, the interesting thing about Ngen is that it does not eliminate the IL or the metadata, because
    while the IL code is not needed for execution, the metadata is, because all the strings and other
    relevant data that the program needs are contained within the metadata. So, Ngen copies all the
    metadata to the .IL section of the native exe, and copies the IL code as an afterthought (I assume
    for debugging purposes). Anyways, the point is that native exe's generated with Ngen have the
    IL code and metadata still in them, so a wily hacker should be able to use them to rebuild a managed
    exe similar to the original exe before Ngen had its way with it. The hacker can then load this
    managed exe into Reflector to decompile it, and steal your code or crack it with ease.

    NOTE: managed code = bytecode (decompilable)
    unmanaged code = native code (I wish we could decompile that =( )

    This is a major issue because all protectors that boast converting IL code to native code to protect
    your programs all use Ngen, so if the protectors forget to remove the IL you (the reverser) can just
    reconstruct the original exe and then have fun with it =)

    Anyways, so you have a copy of UnpackMe.ni.exe. This is what you would get after a "native" protector
    converted your IL code into native code. So, UnpackMe.ni.exe is all a reverser trying to reverse this
    target would have availible to him.

    Now, let us proceed to convert UnpackMe.ni.exe into a decompilable exe =).

    Load UnpackMe.ni.exe into CFF explorer, and browse around a bit. You will see that the exe is not
    a valid win32 exe (no entrypoint) and that there appears to be next to no metadata (No Methods metadata
    table or any of that other good stuff...). Seems like we have our work cut out for us.

    In hindsight: no, not really.

    First things first: we want to have valid metadata. Thankfuly, with CFF explorer this is easy to
    achieve since Daniel Pistelli (the author) documented the native header of .NET exe's enough for us
    so that we don't have to use a hex editor to find the IL code/metadata anymore (btw, thanks Daniel
    for this neat lil tool =) )

    In CFF Explorer, go to a little tab on the left market "Native header". You will see that the "Native
    Header" is mostly populated with unknown RVA's and sizes. However, there are 4 DWORD's that are of interest
    to us. They are:
    Original metadata RVA - 0x00012000
    Original metadata size - 0x00002BC0
    Original MSIL code RVA - 0x00014D30
    Original MSIL Code Size - 0x00000DBF

    Ok, write down these values somewhere. Now, go to the tab in the left of CFF Explorer marked ".NET directory",
    and replace the values "Metadata RVA" and "Metadata Size" with the "Original metadata RVA" and
    "Original metadata size" values that we got from the native header. Also change the Flags dword from
    00000004 (IL Library) to 00000004 (IL only). Cool, now click on the "Tables" tab, so that we can browse the
    metadata tables that now became availible to us.

    The "Method" tables are of particular interest to us, because these are the ones that tell us where the IL code
    is. So, click on the "Method" tables to open the list. There are 56 Methods btw. Note that, since we will need
    that later on. Now, click on the first Method you see. In this case, it is cctor. CFF Explorer will then display
    all the interesting things about the "cctor" method. If you look at the method RVA, you will see that it is
    00000004. That's way too low. So, what is wrong here? Well, that RVA is actually the displacement from the
    "Original MSIL code RVA" value in the Native header. So, to get the "real" RVA is actually:

    cctor RVA + Original MSIL code RVA = Real RVA.

    Our next step, therefore is to replace all the RVA's in these tables with their "Real RVA's", so that reflector
    and ILDasm may load them. I wrote a primitive little CFF explorer script for this. Here it is:

    Code:
    -- this functions checks if a flag is set
    function IsFlag(value, flag)
        if (value & flag) == flag then
            return true
        end
        return false
    end
    
    -- --------------------------------------------------
    -- the main code starts here
    -- --------------------------------------------------
    
    filename = GetOpenFile("Open...",  "All\n*.*\nexe\n*.exe\ndll\n*.dll\n")
    
    
    if filename == null then
        return
    end
    
    pehandle = OpenFile(filename)
    
    if pehandle == null then
        return
    end
    
    -- get dotNet Directory offset if any
    
    dotnetoffset = GetOffset(pehandle, PE_DotNETDirectory)
    
    
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    
    MethodRVA = 0x0000D406
    MethodCounter = 0
    OrigMSILRVA = 0x00014D30
    NumberOfMethod = 56
    junkDWORD = 0
    
    
    
    while MethodCounter < NumberOfMethod do
    
        junkDWORD = ReadDword(pehandle, MethodRVA)
        junkDWORD = junkDWORD + OrigMSILRVA
        WriteDword(pehandle, MethodRVA, junkDWORD)
        MethodRVA = MethodRVA + 0x0E
        MethodCounter = MethodCounter + 1      
    
    
    end
    
    
    
    
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------------------------
    
        filename = GetSaveFile("Save As...",  "All\n*.*\nexe\n*.exe\ndll\n*.dll\n")
    
        if filename == null then
            MsgBox("Couldn't save file", "fast dotnet fix", MB_ICONEXCLAMATION)
        else
            if SaveFileAs(pehandle, filename) == true then
                MsgBox("File successfully saved.", "fast dotnet fix", MB_ICONINFORMATION)
            else
                MsgBox("Couldn't save file", "fast dotnet fix", MB_ICONEXCLAMATION)
            end
        end
    
    return
    NOTE: If you are working on an executable other than the one I provided, fill in
    MethodRVA
    OrigMSILRVA
    NumberOfMethod
    with the appropriate values from the exe you are working on.

    NOTE: MethodRVA = the RVA of the first Method table entry in the metadata. In this case, the offset of
    where the Method table for the "cctor" method starts.

    Take the above script, and copy paste it into a new file called *anything*.cff. Then double click on the
    script to get CFF explorer to execute it, select "Unpackme.ni.exe" to be processed, and save the processed
    file as "fixing1.exe". Remember to save the changes we have done to Unpackme.ni.exe before executing this
    script on it.

    Now, close Unpackme.ni.exe and open fixing1.exe ...
    Categories
    Uncategorized