Results 1 to 14 of 14

Thread: A catalog of NTDLL kernel mode to user mode callbacks, part 6: LdrInitializeThunk

  1. #1
    Imported blog (Skywing)
    Join Date
    Jan 2008
    Posts
    90

    A catalog of NTDLL kernel mode to user mode callbacks, part 6: LdrInitializeThunk

    Previously, I described the mechanism by which the kernel mode to user mode callback dispatcher (KiUserCallbackDispatcher) operates, and how it is utilized by win32k.sys for various window manager related operations.

    The next special NTDLL kernel mode to user mode “callback” up on the list is LdrInitializeThunk, which is not so much a callback as the entry point at which all user mode threads begin their execution system-wide. Although the Win32 CreateThread API (and even the NtCreateThread system service that is used to implement the Win32 CreateThread) provide the illusion that a thread begins its execution at a specified start routine (or instruction pointer, in the case of NtCreateThread), this is not truly the case.

    CreateThread internally layers in a special kernel32 stub routine (BaseThreadStart or BaseProcessStart) in between the specified thread routine and the actual initial instruction of the thread. The kernel32 stub routine wrappers the call to the user-supplied thread start routine to provide services such a “top-level” SEH frame for the support of UnhandledExceptionFilter.

    However, there exists yet another layer of indirection before a thread begins its execution at the specified thread start routine, even beyond the kernel32 stub routine at the start of all Win32 threads (the way the kernel32 stub routine works changes slightly with Windows Vista, though that is outside the scope of this discussion). The presence of this extra layer of indirection can be inferred by examining the documentation on MSDN for DllMain, which states that all threads call out to the DllMain routine at some point during thread start-up. The kernel32 stub routine is not involved in this process, and obviously the user-supplied thread entry point does not have to explicitly attempt to call DllMain for every loaded DLL with DLL_THREAD_ATTACH. This leaves us with the question of who actually arranges for these DllMain calls to happen when a thread begins.

    The answer to this question is, of course, the feature routine of this article, LdrInitializeThunk. When a user mode thread is readied to begin initial execution after being created, the initial context that is realized is not the context value supplied to NtCreateThread (which would eventually end up in the user-supplied thread entry point). Instead, execution really begins at LdrInitializeThunk within NTDLL, which is supplied a CONTEXT record that describes the initially requested state of the thread (as supplied to NtCreateThread, or as created by NtCreateThreadEx on Windows Vista). This context record is provided as an argument to LdrInitializeThunk, in order to allow for control to (eventually) be transferred to the user-supplied thread entry point.

    When invoked by a new thread, LdrInitializeThunk invokes LdrpInitialize to perform the remainder of the initialization tasks, and then calls upon the NtContinue system service to restore the supplied context record. I have made available a C-like representation of this process for illustration purposes.

    LdrpInitialize makes a determination as to whether the process has already been initialized (for the purposes of NTDLL). This step is necessary as LdrInitializeThunk (and by extension, LdrpInitialize) is not only the actual entry point for a new thread in an already initialized process, but it is also the entry point for the initial thread in a completely new process (making it the first piece of code that is run in user mode in a new process). If the process has not already been initialized, then LdrpInitialize performs process initialization tasks by invoking LdrpInitializeProcess (some process initialization is performed inline by LdrpInitialize in the process initialization case as well). If the process is a Wow64 process, then the Wow64 NTDLL is loaded and invoked to initialize the 32-bit NTDLL’s per-process state.

    Otherwise, if the process is already initialized, then LdrpInitialize invokes LdrpInitializeThread to perform per-thread initialization, which primarily involves invoking DllMain and TLS callbacks for loaded modules while the loader lock is held. (This is the reason why it is not supported to wait on a new thread to run its thread initialization routine from DllMain, because the new thread will immediately become blocked upon the loader lock, waiting for the thread already in DllMain to complete its processing.) If the process is a Wow64 process, then there is again support for making a call to the 32-bit NTDLL for purposes of running 32-bit per-thread initialization code.

    When the required process or thread initialization tasks have been completed, LdrpInitialize returns to LdrInitializeThunk, which then realizes the user-supplied thread start context with a call to the NtContinue system service.

    One consequence of this architecture for process and thread initialization is that it becomes (slightly) more difficult to step through process initialization, because the thread that initializes the process is not the first thread created in the process, but rather the first thread that executes LdrInitializeThunk. This means that one cannot simply create a process as suspended and attach a debugger to the process in order to step through process initialization, as the debugger break-in thread will run the very process initialization code that one wishes to step through before executing the break-in thread breakpoint instruction!

    There is, fortunately, support for debugging this scenario built in to the Windows debugger package. By setting the debugger to break on the ‘create process’ event, it is possible to manually set a breakpoint on a new process (created by the debugger) or a child process (if child process debugging is enabled) before LdrInitializeThunk is run. This support is activated by breaking on the cpr (Create Process) event. For example, using the following ntsd command line, it is possible to examine the target and set breakpoints before any user mode code runs:

    ntsd.exe -xe cpr C:\windows\system32\cmd.exe
    Note that as the loaded module list will not have been initialized at this point, symbols will not be functional, so it is necessary to resolve the offset of LdrInitializeThunk manually in order to set a breakpoint. The process can be continued with the typical “g” command.

    Update: Pavel Labedinsky points out that you can use “-xe ld:ntdll.dll” to achieve the same effect, but with the benefit that symbols for NTDLL are available. This is a better option as it alleviates the necessity to manually resolve the addresses of locations that you wish to set a breakpoint on..

    Next up: Examining RtlUserThreadStart (present on Windows Vista and beyond).



    http://www.nynaeve.net/?p=205

  2. #2
    just a side note when it comes to LdrInitializeThunk, it's a user APC queued by ntos!NtCreateThread, and execution start there always. There is more info about it in ARteam ezine #2 - "PEB Dll hooking" article.

  3. #3
    i would like to say nice articles

  4. #4
    Yes, indeed. A whole bunch of new and interesting articles to add to the reading list.

    Regards,
    JMI

  5. #5
    As of Vista, it's not actually an APC anymore -- it's done through a user-mode exception
    --
    Best regards,
    Alex Ionescu

  6. #6
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Location
    Ring -1
    Posts
    4,204
    Blog Entries
    5
    Kayaker, deroko, lately Alex, and all other member practically knowing more about Windows internals than the Microsoft guys themselves, I must say it's always a true pleasure having you around, posting your ring 0 gems and other well-kept dirty secrets.
    "Give a man a quote from the FAQ, and he'll ignore it. Print the FAQ, shove it up his ass, kick him in the balls, DDoS his ass and kick/ban him, and the point usually gets through eventually."

  7. #7
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,047
    Blog Entries
    5
    Heh, I'm nowhere in Alex's league. I dig with a trowel, he excavates with a backhoe!

  8. #8
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Location
    Ring -1
    Posts
    4,204
    Blog Entries
    5
    Oh, yeah, I did of course forget to thank you all for that charming modesty too. There are people knowing 1% of what you do, while still going around being the worst assholes ever... *cough* LLXX *cough*
    "Give a man a quote from the FAQ, and he'll ignore it. Print the FAQ, shove it up his ass, kick him in the balls, DDoS his ass and kick/ban him, and the point usually gets through eventually."

  9. #9
    Btw, any way to link my user-name with the count of Blog Entries?
    --
    Best regards,
    Alex Ionescu

  10. #10
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,047
    Blog Entries
    5
    There should be a way to tie them together with some php modifications Alex. We'll see what can be done.

  11. #11
    It certainly does seem "fair" to have the Blogs, which are generally very informative, count towards post count. Kayaker has made many modifications to the "stock" vBulletin Blog software to make it work the way we need it to work and, hopefully, this will not be that difficult to accomplish. He has worked some "wonders" already.

    Regards,
    JMI

  12. #12
    Quote Originally Posted by aionescu View Post
    As of Vista, it's not actually an APC anymore -- it's done through a user-mode exception
    interesting, wondering why they have changed this good mechanism in Vista

  13. #13
    Quote Originally Posted by deroko View Post
    interesting, wondering why they have changed this good mechanism in Vista
    It's way faster: exceptions are instant, user APCs are only delivered upon return to user-mode (which was done by setting up a complex stack frame then doing an NtContinue).

    The current model is much cleaner (and faster).
    --
    Best regards,
    Alex Ionescu

  14. #14
    Quote Originally Posted by JMI View Post
    It certainly does seem "fair" to have the Blogs, which are generally very informative, count towards post count. Kayaker has made many modifications to the "stock" vBulletin Blog software to make it work the way we need it to work and, hopefully, this will not be that difficult to accomplish. He has worked some "wonders" already.

    Regards,
    Thanks!

    I didn't mean post-count per-se, but the "Blog Count" I see some people have -- which links to a list of blog entries Would be useful to have those linked -- thanks for trying, Kayaker!
    --
    Best regards,
    Alex Ionescu

Similar Threads

  1. Replies: 0
    Last Post: January 12th, 2008, 00:08
  2. Replies: 0
    Last Post: January 12th, 2008, 00:08
  3. Replies: 0
    Last Post: January 12th, 2008, 00:08
  4. Replies: 0
    Last Post: January 12th, 2008, 00:08
  5. Replies: 0
    Last Post: January 12th, 2008, 00:08

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •