Synthetic symbols and modules (WinDbg / DbgEng)

    In this post we will talk about synthetic modules and symbols of the Windows debugging engine (debugger engine). That is, about entities that can be artificially added to the debugger for coloring memory addresses.



    Synthetic modules


    In general, a module within the debugging engine (debugger engine) is a certain continuous area of ​​virtual memory: the base address of the module (the address of the first byte of the projected file) and its size. For each type of debugging (live debugging / dump analysis, user mode / kernel mode, etc.), the debugger has its own mechanism for reading and keeping up to date the list of loaded modules. The easiest way to force the updating of the list of loaded modules is with the .reload command with the / s parameter .


    If we talk, for example, about live debugging of the user-mode process, the list of loaded modules is based on three main loader lists : the module loading order list (InLoadOrderModuleList), the list of modules in memory (InMemoryOrderModuleList) and the initialization order list (InInitializationOrderModuleList). On the one hand, we ( almost ) do not interfere with taking arbitrary data (from a PE file on a disk, for example) and manually marking it in memory for execution. On the other hand, the techniques of removing a regular DLL loaded from the above-mentioned three lists of the loader (hiding the module) have long been known .


    In both cases, when debugging such a situation, it will be useful to mark the area of ​​the hidden module. For this fit synthetic modules. As a practical experiment, you can simply force WinDbg to discard the loaded module from its internal list with the same .reload command , but with the / u parameter .


    Next, in the listing lists, I will use the usual debugging process of the user mode (notepad.exe). First, let's remember the base address of the ntdll module (0x07ffa8a160000) and calculate its size (0x01e1000):


    0:007> lm m ntdll
    Browse full module list
    start             end                 module name
    00007ffa`8a160000 00007ffa`8a341000   ntdll
    0:007> ? 00007ffa`8a341000-00007ffa`8a160000
    Evaluate expression: 1970176 = 00000000`001e1000

    Consider the list of the simple function RtlFreeSid from the ntdll module, which calls the function RtlFreeHeap from this module, simultaneously remembering the addresses of the characters RtlFreeSid and RtlFreeHeap (0x7ffa8a1cbc20 and 0x7ffa8a176df0):


    0:007> uf ntdll!RtlFreeSid
    ntdll!RtlFreeSid:
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    ntdll!RtlFreeHeap (00007ffa`8a176df0)
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    Also, according to the above listing, it is easy to calculate the size of the function ntdll! RtlFreeSid:


    0:007> ? 00007ffa`8a1cbc51 - 0x07ffa8a1cbc20
    Evaluate expression: 49 = 00000000`00000031

    And the size of the function ntdll! RtlFreeHeap is not important for our experiment, so for simplicity we can take it equal to one byte.


    Now simulate hiding the ntdll module:


    0:007> .reload /u ntdll
    Unloaded ntdll

    The field of this work with the address of the function ntdll! RtlFreeSid in the debugger is not so informative (and you can already access the beginning of the function only at the virtual address):


    0:007> uf 00007ffa`8a1cbc20
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    00007ffa`8a176df0
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    In order to add a synthetic module, you need to call the program interface. IDebugSymbols3 :: AddSyntheticModule .There are simply no built-in commands that would allow you to make this call (as far as I know).Mikhail I. Izmestev suggested that there is a similar mechanism - the same .reload , but with the name, address and size parameters: "Module = Address, Size" (and, perhaps, it is implemented on synthetic modules).


    .reload ntdll = 00007ff8`470e1000,001e1000
    0:007> .reload ntdll=00007ff8`470e1000,001e1000
    *** WARNING: Unable to verify timestamp for ntdll
    *** ERROR: Module load completed but symbols could not be loaded for ntdll
    0:007> uf 00007ff8`4714bc20
    ntdll+0x6ac20:
    00007ff8`4714bc20 4053            push    rbx
    00007ff8`4714bc22 4883ec20        sub     rsp,20h
    00007ff8`4714bc26 488bd9          mov     rbx,rcx
    00007ff8`4714bc29 33d2            xor     edx,edx
    00007ff8`4714bc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ff8`4714bc34 4c8bc3          mov     r8,rbx
    00007ff8`4714bc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ff8`4714bc3b e8b0b1faff      call    ntdll+0x15df0 (00007ff8`470f6df0)
    00007ff8`4714bc40 33c9            xor     ecx,ecx
    00007ff8`4714bc42 85c0            test    eax,eax
    00007ff8`4714bc44 480f45d9        cmovne  rbx,rcx
    00007ff8`4714bc48 488bc3          mov     rax,rbx
    00007ff8`4714bc4b 4883c420        add     rsp,20h
    00007ff8`4714bc4f 5b              pop     rbx
    00007ff8`4714bc50 c3              ret

    Well, we'll use the debugger extension, and here comes pykd . In the most recent (at the time of writing) release 0.3.4.3 , functions for managing synthetic modules were added: addSyntheticModule (...) (which is needed in our case) and removeSyntheticModule (...).


    According to the previously memorized base address and module size, we add information about the hidden ntdll module to the debugger (if an error occurs, an exception will be thrown, so silent execution is a sign of success, printing warnings can be ignored):


    0:007> !py
    Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> addSyntheticModule(0x07ffa8a160000, 0x01e1000, 'ntdll')
    *** WARNING: Unable to verify timestamp for ntdll
    >>> exit()

    Now the disassembler listing has become a bit more informative:


    0:007> uf 00007ffa`8a1cbc20
    ntdll+0x6bc20:
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    ntdll+0x16df0 (00007ffa`8a176df0)
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    Or rather, we see that a function inside the ntdll module calls another function inside the same module.


    Synthetic characters


    Listing, after adding a synthetic module, has become more informative, but still not as eloquent as the original one. It lacks characters (in our case, the names of the functions RtlFreeSid and RtlFreeHeap). To fix this again, a call is required, but another software interface - IDebugSymbols3 :: AddSyntheticSymbol . Again, pykd is ready to help us with this:


    0:007> !py
    Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> addSyntheticSymbol(0x07ffa8a1cbc20, 0x31, 'RtlFreeSid')
    <pykd.syntheticSymbol object at 0x0000014D47699518>
    >>> addSyntheticSymbol(0x07ffa8a176df0, 1, 'RtlFreeHeap')
    <pykd.syntheticSymbol object at 0x0000014D476994A8>
    >>> exit()

    The result is very similar to that when using symbols from the pdb file ntdll:


    0:007> uf 00007ffa`8a1cbc20
    ntdll!RtlFreeSid:
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    ntdll!RtlFreeHeap (00007ffa`8a176df0)
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    Features of synthetic entities


    It should be noted that the creation of synthetic symbols is allowed in any existing modules, and not only in synthetic ones. That is, you can add symbols both to modules with accessible pdb files , and to modules for which we do not have debug information files. But the characters outside the modules cannot be created. That is, to mark an arbitrary memory address outside the boundaries of existing modules, you need to create an embedding synthetic module at the beginning, and then (if necessary, the module name may be enough for coloring ) to create a synthetic symbol in it.


    It is also useful to mention that synthetic modules and symbols can be removed with a careless movement of the hand:


    • reloading the debug symbols of the module removes all the synthetic symbols of this module;
    • rebooting all modules removes all synthetic modules.

    ! synexts


    Although the use of pykd is convenient in the overwhelming majority of cases (especially given the presence of a bootstarpper to it ), sometimes you can get into a situation where you need to spend considerable power to use pykd. For example, I once needed to debug with the host machine, which was running Windows XP. As it turned out, for a long time now, pykd does not support XP, and I needed synthetic symbols and modules. It seemed to me that for this task it is easier to assemble a separate small extension that will solve a narrow circle of necessary tasks than to restore full XP support for pykd. As a result, a separate ! Synexts project was created .


    This is a simple extension that has two exports available to the user:


    • ! synexts.addsymbol - creates a synthetic symbol in any existing module;
    • ! synexts.addmodule - creates a synthetic module in the internal list of the debug engine.

    To demonstrate, we again imitate the hiding of the ntdll module:


    0:007> .reload /u ntdll
    Unloaded ntdll
    0:007> uf 00007ffa`8a1cbc20
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    00007ffa`8a176df0
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    Again, we restore knowledge about the module and the characters in the form of synthetic entities, but using the! Synexts extension:


    0:007> !synexts.addmodule ntdll C:\Windows\System32\ntdll.dll 00007ffa`8a160000 0x01e1000
    *** WARNING: Unable to verify timestamp for C:\Windows\System32\ntdll.dll
    Synthetic module successfully added
    0:007> !synexts.addsymbol RtlFreeSid 00007ffa`8a1cbc20 31
    Synthetic symbol successfully added
    0:007> !synexts.addsymbol RtlFreeHeap 00007ffa`8a176df0 1
    Synthetic symbol successfully added

    It is assumed that the compiled synexts.dll library (corresponding to the bit width used by WinDbg) you have already copied to the winext directory of the debugger:


    0:007> .chain
    Extension DLL search Path:
        C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\WINXP;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\arcade;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\pri;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64;<…>
    Extension DLL chain:
        pykd.pyd: image 0.3.4.3, API 1.0.0, built Thu Jan 10 19:56:25 2019
            [path: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\pykd.pyd]
        synexts: API 1.0.0, built Fri Jan 18 17:38:17 2019
            [path: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\synexts.dll]
    <…>

    And again we see the result of adding synthetic symbols to the synthetic module:


    0:007> uf 00007ffa`8a1cbc20
    ntdll!RtlFreeSid:
    00007ffa`8a1cbc20 4053            push    rbx
    00007ffa`8a1cbc22 4883ec20        sub     rsp,20h
    00007ffa`8a1cbc26 488bd9          mov     rbx,rcx
    00007ffa`8a1cbc29 33d2            xor     edx,edx
    00007ffa`8a1cbc2b 65488b0c2560000000 mov   rcx,qword ptr gs:[60h]
    00007ffa`8a1cbc34 4c8bc3          mov     r8,rbx
    00007ffa`8a1cbc37 488b4930        mov     rcx,qword ptr [rcx+30h]
    00007ffa`8a1cbc3b e8b0b1faff      call    ntdll!RtlFreeHeap (00007ffa`8a176df0)
    00007ffa`8a1cbc40 33c9            xor     ecx,ecx
    00007ffa`8a1cbc42 85c0            test    eax,eax
    00007ffa`8a1cbc44 480f45d9        cmovne  rbx,rcx
    00007ffa`8a1cbc48 488bc3          mov     rax,rbx
    00007ffa`8a1cbc4b 4883c420        add     rsp,20h
    00007ffa`8a1cbc4f 5b              pop     rbx
    00007ffa`8a1cbc50 c3              ret

    Also popular now: