Rubato and Chord

Reiley's technical blog

August, 2012

  • Rubato and Chord

    Visualize Assembly using DGML

    • 0 Comments

    Starting from Visual Studio 2010 Ultimate there is a cool feature called DGML (Directed Graph Markup Language).

    I wrote a small script to convert the disassembled code from WinDBG into a DGML.

    In order to use it, simply type the following commands under a debug session:

    .shell -o LoadLibraryA.dgml -ci "uf kernel32!LoadLibraryA" cscript.exe /nologo dasm2dgml.js

    A DGML file will be generated with the given name, and here is what it looks like:

    Here is the source code:


    var EBB = [];
    
    var hypertext=function(s){
      var r=[],L=s.length;
      for(var i=0;i<L;i++){
        var c=s.charAt(i);
        switch(c){
          case '"':r.push('&quot;');break;
          case '&':r.push('&amp;');break;
          case '<':r.push('&lt;');break;
          case '>':r.push('&gt;');break;
          default:r.push(c);}}
      return r.join('');
    };
    
    var map=function(f,v){var L=v.length,r=[];for(var i=0;i<L;i++)r.push(f(v[i]));return r;};
    
    (function(){
      var blk;
    
      var CExtendedBasicBlock = function(name, previous, next){
        this.Address = '';
        this.Code = [];
        this.Name = name;
        this.Previous = previous;
        this.Next = next;
      };
    
      while(true)
      {
        if(WScript.StdIn.AtEndOfStream)
          break;
        var strSourceLine = WScript.StdIn.ReadLine().replace(/(^\s+)|(\s+$)/g, '');
        if(!strSourceLine)
          continue;
        if(strSourceLine.match(/.*:$/))
        {
          blk = new CExtendedBasicBlock(strSourceLine.slice(0, -1));
          EBB.push(blk);
        }
        else
        {
          blk.Address = blk.Address || strSourceLine.match(/^[^\s]+/)[0];
          blk.Code.push(strSourceLine.replace(/[^\s]*\s+/, '').replace(/[^\s]*\s+/, ''));
        }
      }
    })();
    
    EBB = EBB.sort(function(x, y){ return x.Address == y.Address ? 0 : x.Address > y.Address ? 1 : -1; });
    for(var i = 1; i < EBB.length; i++)
    {
      EBB[i].Previous = EBB[i - 1];
      EBB[i].Previous.Next = EBB[i];
    }
    
    WScript.Echo('<DirectedGraph Background="#FFFFFF" GraphDirection="TopToBottom" xmlns="http://schemas.microsoft.com/vs/2009/dgml">');
    WScript.Echo('  <Nodes>');
    map(function(blk){
      var content = hypertext(blk.Name + ' (' + blk.Address + ')') + '&#xD;&#xA;';
      map(function(instruction){
        content += '&#xD;&#xA;' + hypertext(instruction);
      }, blk.Code);
      WScript.Echo('    <Node Id="' + hypertext(blk.Name) + '" Label="' + content + '" />');
    }, EBB);
    WScript.Echo('  </Nodes>');
    WScript.Echo('  <Links>');
    map(function(blk){
      map(function(instruction){
        map(function(x){
          var idx = instruction.indexOf(x.Name);
          idx = idx >= 0 ? instruction.charAt(idx + x.Name.length) : -1;
          if(idx == '' || idx == ' ')
            WScript.Echo('    <Link Source="' + hypertext(blk.Name) + '" Target="' + hypertext(x.Name) + '" />');
        }, EBB);
      }, blk.Code);
      if(blk.Next && !(blk.Code[blk.Code.length - 1].match(/^[^\s]+/)[0] in {jmp: 0, ret: 0}))
        WScript.Echo('    <Link Category="FallThrough" Source="' + hypertext(blk.Name) + '" Target="' + hypertext(blk.Next.Name) + '" />');
    }, EBB);
    WScript.Echo('  </Links>');
    WScript.Echo('  <Styles>');
    WScript.Echo('    <Style TargetType="Node">');
    WScript.Echo('      <Setter Property="FontFamily" Value="Consolas" />');
    WScript.Echo('      <Setter Property="FontSize" Value="11" />');
    WScript.Echo('      <Setter Property="Background" Value="White" />');
    WScript.Echo('      <Setter Property="NodeRadius" Value="2" />');
    WScript.Echo('    </Style>');
    WScript.Echo('    <Style TargetType="Link">');
    WScript.Echo('        <Condition Expression="HasCategory(\'FallThrough\')" />');
    WScript.Echo('        <Setter Property="Background" Value="Red" />');
    WScript.Echo('        <Setter Property="Stroke" Value="Red" />');
    WScript.Echo('    </Style>');
    WScript.Echo('  </Styles>');
    WScript.Echo('</DirectedGraph>');
    

    Notes:

    1. This script cannot generate 100% accurate control flow diagram, you will have to do further analysis (e.g. jmp eax).
    2. I haven't got a chance to test under WOA (ARM32), so I leave it as a homework for our readers.

    Enjoy:)

  • Rubato and Chord

    Using Function Evaluation in WinDBG

    • 1 Comments

    People who develop debuggers would know in theory you cannot have a perfect disassembler (especially for x86) and stepper (especially for Step Over). People who develop commercial debuggers would know Function Evaluation (a.k.a. funceval) is a big challenge while implementing an Expression Evaluator. And people who develop the Visual Studio Debugger would face other difficulties - Interop Debugging, Edit & Continue.

    In this article, I'm not going to explain the bloody details of funceval, I just demonstrate how to use funceval in WinDBG and how powerful it is.

    Previously we mentioned the .call command in Microsoft Binary Technologies, at that time we were not able to invoke the function since we don't have private symbols - funceval requires private symbol since debugger needs to understand the calling convention, which is stripped out in public symbol.

    While I cannot use private symbols writting articles for this blog (private symbol is Microsoft privacy, also debugging without private symbol is much more fun), the way I'd take is to create a proxy DLL:


    #include <Windows.h>
    
    VOID WINAPI SetLastError(DWORD dwErrCode){}
    

    Now compile the code into a DLL, with PDB file generated:

    cl.exe funceval.cpp /D UNICODE /Fd /GS- /LD /Od /Zi /link /NOENTRY /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE

    In order to use the proxy DLL, we will use the following approach:

    1. Launch a debug session.
    2. Allocate memory from debugee process.
    3. Inject the proxy DLL into the allocated memory (note that we skipped PE relocation).
    4. Load private symbol of the proxy DLL.
    5. Use WinDBG .call command to kick off funceval from proxy DLL.
    6. Change the IP register to the real address we want to execute.
    7. Start evaluating.

    Here is the automation script, enjoy!

    $$ cdb.exe -xe cpr -c "$$>a< .\funceval.txt" notepad.exe
    
    .echo [Launch Script]
    
    $$ change the following value to the size of funceval.dll
    r $t1 = 0n1536
    
    bp @$exentry; g
    
    .echo [Allocate Memory]
    .foreach ( token { .dvalloc @$t1 } ) {
    	aS alias token
    	.block {
    		.if ($spat("${alias}", "[0-9a-f]+")) {
    			r $t2 = 0x${alias}
    		}
    	}
    	ad /q alias
    }
    
    .printf "[Load Helper DLL(base address = %p, size = %p)]\n", @$t2, @$t1
    .readmem funceval.dll @$t2 (@$t1+@$t2-1)
    
    .block {
    	.sympath .
    }
    
    $$.symopt+ 0x40
    
    .reload /s /f funceval.dll=$t2
    
    $$.symopt- 0x40
    
    .echo [Function Evaluation]
    .call /s funceval!SetLastError kernel32!SetLastError(7777)
    
    g
    
    !gle
    
    .dvfree @$t2 0
    

    And here is the output from my machine:

    [Allocate Memory]
    [Load Helper DLL(base address = 00020000, size = 00000600)]
    Reading 600 bytes.
    [Function Evaluation]
    Thread is set up for call, 'g' will execute.
    WARNING: This can have serious side-effects,
    including deadlocks and corruption of the debuggee.
    LastErrorValue: (Win32) 0x1e61 (7777) - <Unable to get error code text>
    LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0
    Freed 0 bytes starting at 00020000
  • Rubato and Chord

    A Debugging Approach to Application Verifier

    • 1 Comments

    Application Verifier, also known as AppVerifier, is a dynamic instrumentation tool for user mode applications. It is free available from SDK/PSDK, with a set of GUI applications and DLL extensions, plus a good document.

    Let's begin by adding the most famous application - notepad.exe - from the appverif.exe GUI, and launch notepad.exe from WinDBG:

    windbg.exe notepad.exe

    ModLoad: 00620000 00650000   notepad.exe
    ModLoad: 77c00000 77d80000   ntdll.dll
    Page heap: pid 0xE10: page heap enabled with flags 0x3.
    AVRF: notepad.exe: pid 0xE10: flags 0x80643027: application verifier enabled
    ModLoad: 10350000 103b0000   C:\Windows\syswow64\verifier.dll
    Page heap: pid 0xE10: page heap enabled with flags 0x3.
    AVRF: notepad.exe: pid 0xE10: flags 0x80643027: application verifier enabled
    ModLoad: 5cca0000 5cccb000   C:\Windows\SysWOW64\vrfcore.dll
    ModLoad: 0f820000 0f878000   C:\Windows\SysWOW64\vfbasics.dll
    ModLoad: 75330000 75440000   C:\Windows\syswow64\kernel32.dll
    ModLoad: 75c40000 75c86000   C:\Windows\syswow64\KERNELBASE.dll
    ModLoad: 76ee0000 76f80000   C:\Windows\syswow64\ADVAPI32.dll
    ModLoad: 75fd0000 7607c000   C:\Windows\syswow64\msvcrt.dll

    Like we've mentioned in A Debugging Approach to IFEO, the loader code in NTDLL knows how to initialize application verifier.

    windbg.exe -xe cpr notepad.exe

    0:000> sxeld verifier
    0:000> g
    Page heap: pid 0x1DBC: page heap enabled with flags 0x3.
    AVRF: notepad.exe: pid 0x1DBC: flags 0x80643027: application verifier enabled
    ModLoad: 105f0000 10650000   C:\Windows\syswow64\verifier.dll
    eax=00000000 ebx=77d07e00 ecx=00000000 edx=00000000 esi=7efdd000 edi=00000000
    eip=77c1fc42 esp=0018f3a8 ebp=0018f790 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    ntdll!ZwMapViewOfSection+0x12:
    77c1fc42 83c404          add     esp,4
    0:000> k
    ChildEBP RetAddr
    0018f3a8 77ca6fa3 ntdll!ZwMapViewOfSection+0x12
    0018f790 77ca7c29 ntdll!AvrfMiniLoadDll+0x3d1
    0018f7c4 77ca1075 ntdll!AVrfInitializeVerifier+0x252
    0018f7fc 77c80759 ntdll!LdrpInitializeApplicationVerifierPackage+0xab
    0018f878 77c45383 ntdll!LdrpInitializeExecutionOptions+0x222
    0018fa08 77c452d6 ntdll!LdrpInitializeProcess+0x261
    0018fa58 77c39e79 ntdll!_LdrpInitialize+0x78
    0018fa68 00000000 ntdll!LdrInitializeThunk+0x10

    By reading the disassembled code, it's obvious that ntdll!RtlOpenImageFileOptionsKey is used to retrieve the IFEO related information. NTDLL would read from IFEO to see if the application is registered, and whether application verifier is enabled in GlobalFlag (GFLAG). If GFLAG & 0x100 is non-zero, NTDLL would load verifier.dll from %windir%\system32 or %windir%\syswow64, depending on the target bitness.

    0:000> sxeld
    0:000> g
    Page heap: pid 0x1DBC: page heap enabled with flags 0x3.
    AVRF: notepad.exe: pid 0x1DBC: flags 0x80643027: application verifier enabled
    ModLoad: 0f6f0000 0f71b000   C:\Windows\SysWOW64\vrfcore.dll
    eax=00000000 ebx=00000000 ecx=0018f600 edx=0018f601 esi=7efdd000 edi=0018f628
    eip=77c1fc42 esp=0018f4fc ebp=0018f550 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    ntdll!ZwMapViewOfSection+0x12:
    77c1fc42 83c404          add     esp,4
    0:000> k
    ChildEBP RetAddr
    0018f4fc 77c3beec ntdll!ZwMapViewOfSection+0x12
    0018f550 77c3c578 ntdll!LdrpMapViewOfSection+0xc7
    0018f644 77c3c3a9 ntdll!LdrpFindOrMapDll+0x333
    0018f7c4 77c3c4d5 ntdll!LdrpLoadDll+0x2b2
    0018f7fc 77ca746f ntdll!LdrLoadDll+0xaa
    0018f850 77ca7aaa ntdll!AVrfpLoadAndInitializeProvider+0x6f
    0018f874 77c8117c ntdll!AVrfInitializeVerifier+0xd3
    0018fa08 77c452d6 ntdll!LdrpInitializeProcess+0xfba
    0018fa58 77c39e79 ntdll!_LdrpInitialize+0x78
    0018fa68 00000000 ntdll!LdrInitializeThunk+0x10

    NTDLL would further check if IFEO has a REG_SZ value named VerifierDlls. If VerifierDlls is found, it's value will be splitted into DLL names, and these DLLs would be loaded into the target process one by one.

    0:000> dt ntdll!IMAGE_DOS_HEADER 0f6f0000
       +0x000 e_magic          : 0x5a4d
       +0x002 e_cblp           : 0x90
       +0x004 e_cp             : 3
       +0x006 e_crlc           : 0
       +0x008 e_cparhdr        : 4
       +0x00a e_minalloc       : 0
       +0x00c e_maxalloc       : 0xffff
       +0x00e e_ss             : 0
       +0x010 e_sp             : 0xb8
       +0x012 e_csum           : 0
       +0x014 e_ip             : 0
       +0x016 e_cs             : 0
       +0x018 e_lfarlc         : 0x40
       +0x01a e_ovno           : 0
       +0x01c e_res            : [4] 0
       +0x024 e_oemid          : 0
       +0x026 e_oeminfo        : 0
       +0x028 e_res2           : [10] 0
       +0x03c e_lfanew         : 0n240

    0:000> dt ntdll!_IMAGE_NT_HEADERS OptionalHeader.AddressOfEntryPoint 0f6f0000+0n240
       +0x018 OptionalHeader                     :
          +0x010 AddressOfEntryPoint                : 0x2c86

    0:000> ln 0f6f0000+0x2c86
    (0f6f2c86)   vrfcore!DllMain   |  (0f642ca7)   vrfcore!VerifierOpenLayerProperties
    Exact matches:
        vrfcore!DllMain = <no type information>

    The above steps can be automated using script:

    $$ cdb.exe -xe cpr -c "$$>a< .\appverif.txt" notepad.exe
    
    .echo [Launch Script]
    
    sxeld vrfcore; g; sxdld
    
    $$ get vrfcore.dll base address
    r $t1 = vrfcore
    
    .if @$ptrsize == 8 {
    	aS IMAGE_NT_HEADERS _IMAGE_NT_HEADERS64
    } .else {
    	aS IMAGE_NT_HEADERS _IMAGE_NT_HEADERS
    }
    
    $$ get OEP offset
    .block {
    	r $t2 = @@c++(((ntdll!${IMAGE_NT_HEADERS}*)(@$t1 + ((ntdll!_IMAGE_DOS_HEADER*)@$t1)->e_lfanew))->OptionalHeader.AddressOfEntryPoint)
    }
    
    $$ break at OEP
    bp @$t1 + @$t2; g; bc 0
    
    .echo [Hit OEP]
    k
    
    .echo [Arguments]
    dd esp L4
    

    Now we've successfully located the OEP (Original Entry Point, we mentioned that in Data Breakpoints) for vrfcore.dll, set a breakpoint.

    When we hit the breakpoint on vrfcore!DllMain, take a look at the top frame and it showed the second argument passed in is 4:

    DllMain(HINSTANCE hinstDLL = 0f6f0000, DWORD fdwReason= 4, LPVOID lpvReserved)

    It looks like fdwReason = 4 is undocumented on MSDN:

    • DLL_PROCESS_DETACH = 0
    • DLL_PROCESS_ATTACH = 1
    • DLL_THREAD_ATTACH = 2
    • DLL_THREAD_DETACH = 3

    By looking at the disassembled code, the following instructions looks suspecious:

    vsvrfcore!_DllMain:
        mov     edi, edi
        push    ebp
        mov     ebp, esp
        mov     eax, dword ptr [ebp+0Ch]
        push    ebx
        push    esi
        xor     esi, esi
        push    edi
        xor     edi, edi
        inc     esi
        sub     eax, edi
        je      vrfcore!_DllMain+0x426

    vrfcore!_DllMain+0x18:
        dec     eax
        je      vrfcore!_DllMain+0x2aa

    vrfcore!_DllMain+0x1f:
        dec     eax
        je      vrfcore!_DllMain+0x29b

    vrfcore!_DllMain+0x26:
        dec     eax
        je      vrfcore!_DllMain+0x28c

    vrfcore!_DllMain+0x2d:
        dec     eax
        jne     vrfcore!_DllMain+0x283

    vrfcore!_DllMain+0x34:
        mov     ebx, dword ptr [ebp+10h]
        cmp     ebx, edi
        jne     vrfcore!_DllMain+0x79

    vrfcore!_DllMain+0x131:
        mov     edi, offset vrfcore!VfCoreProvider
        mov     dword ptr [ebx], edi
        call    vrfcore!VfCoreProviderInitialize

    From the highlighted code we can see when fdwReason is 4, the following assignment would happen:

    *lpvReserved = (LPVOID)(&vrfcore!VfCoreProvider)

    By looking into the Win2003R2 DDK headers, we can find out the layout defintion for Verifier Provider Descript. So it's time to write a small provider now.


    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    
    // Borrowed from Win2003R2 DDK
    
    #define DLL_PROCESS_VERIFIER 4
    
    typedef VOID (NTAPI * RTL_VERIFIER_DLL_LOAD_CALLBACK) (PWSTR DllName, PVOID DllBase, SIZE_T DllSize, PVOID Reserved);
    typedef VOID (NTAPI * RTL_VERIFIER_DLL_UNLOAD_CALLBACK) (PWSTR DllName, PVOID DllBase, SIZE_T DllSize, PVOID Reserved);
    typedef VOID (NTAPI * RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK) (PVOID AllocationBase, SIZE_T AllocationSize);
    
    typedef struct _RTL_VERIFIER_THUNK_DESCRIPTOR {
      PCHAR ThunkName;
      PVOID ThunkOldAddress;
      PVOID ThunkNewAddress;
    } RTL_VERIFIER_THUNK_DESCRIPTOR, *PRTL_VERIFIER_THUNK_DESCRIPTOR;
    
    typedef struct _RTL_VERIFIER_DLL_DESCRIPTOR {
      PWCHAR DllName;
      DWORD DllFlags;
      PVOID DllAddress;
      PRTL_VERIFIER_THUNK_DESCRIPTOR DllThunks;
    } RTL_VERIFIER_DLL_DESCRIPTOR, *PRTL_VERIFIER_DLL_DESCRIPTOR;
    
    typedef struct _RTL_VERIFIER_PROVIDER_DESCRIPTOR {
      DWORD Length;
      PRTL_VERIFIER_DLL_DESCRIPTOR ProviderDlls;
      RTL_VERIFIER_DLL_LOAD_CALLBACK ProviderDllLoadCallback;
      RTL_VERIFIER_DLL_UNLOAD_CALLBACK ProviderDllUnloadCallback;
      PWSTR VerifierImage;
      DWORD VerifierFlags;
      DWORD VerifierDebug;
      PVOID RtlpGetStackTraceAddress;
      PVOID RtlpDebugPageHeapCreate;
      PVOID RtlpDebugPageHeapDestroy;
      RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK ProviderNtdllHeapFreeCallback;
    } RTL_VERIFIER_PROVIDER_DESCRIPTOR, *PRTL_VERIFIER_PROVIDER_DESCRIPTOR;
    
    // ntdll!DbgPrint
    typedef ULONG (__cdecl* PFN_DbgPrint)(PCH, ...);
    PFN_DbgPrint DbgPrint;
    
    // Here we go
    typedef BOOL (WINAPI* PFN_CloseHandle)(HANDLE);
    BOOL WINAPI ThunkCloseHandle(HANDLE hObject);
    
    static RTL_VERIFIER_THUNK_DESCRIPTOR aThunks[] = {{"CloseHandle", NULL, ThunkCloseHandle}, {}};
    static RTL_VERIFIER_DLL_DESCRIPTOR aDlls[] = {{L"kernel32.dll", 0, NULL, aThunks}, {}};
    static RTL_VERIFIER_PROVIDER_DESCRIPTOR vpd = {sizeof(RTL_VERIFIER_PROVIDER_DESCRIPTOR), aDlls};
    
    BOOL WINAPI ThunkCloseHandle(HANDLE hObject)
    {
      BOOL fRetVal = ((PFN_CloseHandle)(aThunks[0].ThunkOldAddress))(hObject);
      DbgPrint("CloseHandle(%p) = %s\n", hObject, fRetVal ? "TRUE" : "FALSE");
      return fRetVal;
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, PRTL_VERIFIER_PROVIDER_DESCRIPTOR* pVPD)
    {
      switch(fdwReason)
      {
      case DLL_PROCESS_ATTACH:
        ::DisableThreadLibraryCalls(hinstDLL);
        break;
      case DLL_PROCESS_DETACH:
        break;
      case DLL_PROCESS_VERIFIER:
        DbgPrint = (PFN_DbgPrint)::GetProcAddress(::GetModuleHandle(TEXT("NTDLL")), "DbgPrint");
        DbgPrint("CommandLine: %s\n", ::GetCommandLineA());
        *pVPD = &vpd;
        break;
      default:
        ::DebugBreak(); // loader lock, be careful!!!
      }   return TRUE; }

    To compile the source code, use the following command line:

    cl.exe avhook.cpp /D UNICODE /GS- /LD /Od /link /ENTRY:DllMain /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE kernel32.lib

    Copy the generated avhook.dll DLL to %windir%\system32 or %windir%\syswow64 folder, depending on the bitness, and import the IFEO entry into registry:


    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe]
    "VerifierDlls"="avhook.dll"
    "GlobalFlag"="0x100"
    "VerifierFlags"=dword:80000000

    Launch notepad.exe from debugger:

    windbg.exe notepad.exe

    And here is what we got:

    Executable search path is:
    ModLoad: 00160000 00190000   notepad.exe
    ModLoad: 77070000 771f0000   ntdll.dll
    Page heap: pid 0x158C: page heap enabled with flags 0x2.
    AVRF: notepad.exe: pid 0x158C: flags 0x48004: application verifier enabled
    ModLoad: 714a0000 71500000   C:\Windows\syswow64\verifier.dll Page heap: pid 0x158C: page heap enabled with flags 0x2.
    AVRF: notepad.exe: pid 0x158C: flags 0x48004: application verifier enabled
    ModLoad: 715c0000 715c5000   C:\Windows\SysWOW64\hook.dll 
    ...
    
    CloseHandle(00000320) = TRUE
    CloseHandle(0000032C) = TRUE
    CloseHandle(00000330) = TRUE
    CloseHandle(0000034C) = TRUE
    CloseHandle(00000328) = TRUE
    CloseHandle(000001C8) = TRUE

    As you could see, our provider DLL is working as expected :)

Page 1 of 1 (3 items)