windowswinapiwindbgdebug-symbols

Setting a breakpoint on CreateFile() API in WinDbg


I have set up WinDbg for hybrid (user mode to kernel mode) debugging.

In the user mode application, I am calling CreateFile() with certain parameters. I want to break at the CreateFile() call in context of the user mode process, and trace its execution from there.

I tried searching for the CreateFile symbol with the x command. The issue is that x nt!*CreateFile* cannot find the respective symbol. Out of the bunch of symbols it lists, the closest I could find was nt!NtCreateFile, which appears to have a different signature, and presumably an internal API.

Why is the CreateFile() symbol missing in WinDbg?


Solution

  • Function names in Windows

    You can search for Microsoft Windows API functions using the search terms "MSDN <function>". If you do so, this brings you to CreateFileA(). That website tells you that the function is defined in fileapi.h.

    Look inside fileapi.h (find it on your local machine, e.g. using Everything), you'll find these lines:

    #ifdef UNICODE
    #define CreateFile  CreateFileW
    #else
    #define CreateFile  CreateFileA
    #endif // !UNICODE
    

    So, CreateFile() is just an alias for either CreateFileA() or CreateFileW(), depending on whether you compile for ANSI or Unicode.

    You'll find many functions which are redirected like that and have the ...A() and ...W() naming convention. That's outlined by MS on Unicode in the Windows API and they explain the conventions using another example.

    Debugging

    On the same website, there's a section called Requirements, which says

    Header     fileapi.h (include Windows.h)
    Library    Kernel32.lib
    DLL        Kernel32.dll
    

    So the breakpoints can be set with

    0:001> bp kernel32!CreateFileA
    0:001> bp kernel32!CreateFileW
    

    But your x approach also works, if you search in all modules, like

    0:001> x *!CreateFileA
    00007ff8`23dc1320 KERNELBASE!CreateFileA (void)
    00007ff8`25fa4e90 KERNEL32!CreateFileA (CreateFileA)
    

    With both breakpoints (A and W) set, you should find the function:

    0:001> g
    Breakpoint 1 hit
    KERNEL32!CreateFileW:
    00007ff8`25fa4ea0 ff251af40500    jmp     qword ptr [KERNEL32!_imp_CreateFileW (00007ff8`260042c0)] ds:00007ff8`260042c0={KERNELBASE!CreateFileW (00007ff8`23dc1410)}
    

    Module load order

    as followed up in comments

    When should the kernel!* functions be available? Very early, typically at the initial breakpoint already. The first 4 modules loaded are your app, ntdll, kernel32 and kernelbase, if you don't debug a so called native DLL, which tries to get rid of as many dependencies as possible.

    ModLoad: 00007ff7`bf830000 00007ff7`bf866000   YourApp.exe
    ModLoad: 00007ffc`dfdd0000 00007ffc`dffc8000   ntdll.dll
    ModLoad: 00007ffc`df8b0000 00007ffc`df971000   C:\Windows\System32\KERNEL32.DLL
    ModLoad: 00007ffc`dd970000 00007ffc`ddc6d000   C:\Windows\System32\KERNELBASE.dll
    

    and then, depending on the programming language, e.g. C++

    ModLoad: 00007ffc`88280000 00007ffc`8828f000   C:\Windows\SYSTEM32\VCRUNTIME140_1D.dll
    ModLoad: 00007ffc`72710000 00007ffc`7273e000   C:\Windows\SYSTEM32\VCRUNTIME140D.dll
    ModLoad: 00007ffc`6b2e0000 00007ffc`6b3c2000   C:\Windows\SYSTEM32\MSVCP140D.dll
    ModLoad: 00007ffc`00430000 00007ffc`00651000   C:\Windows\SYSTEM32\ucrtbased.dll
    ModLoad: 0000020a`14190000 0000020a`143b1000   C:\Windows\SYSTEM32\ucrtbased.dll
    (4ac.32c): Break instruction exception - code 80000003 (first chance)
    ntdll!LdrpDoDebuggerBreak+0x30:
    00007ffc`dfea0820 cc              int     3
    

    Windows Internals 7 Part 1 says (chapter "Early process initialization")

    In newer versions of Windows, the loader instead builds a dependency map ahead of time, with specific nodes that describe a single DLL and its dependency graph, building out separate nodes that can be loaded in parallel.

    So the load order may even vary between runs, but I haven't seen that for these most basic DLLs.

    If you really don't see kernelbase or kernel32 yet, you have the following options: