Next week, the 16th Working Conference on Reverse Engineering (WCRE) will be held in Lille, France. I will be there presenting NTrace: Function Boundary Tracing for Windows on IA-32. NTrace is a dynamic function boundary tracing toolkit for IA-32/x86 that can be used to trace both kernel and user mode Windows components – examples for components that can be traced include the kernel itself (ntoskrnl), drivers like NTFS as well as user mode components such as kernel32, shell32 or even explorer.
The Windows kernel maintains two types of threads – Non-GUI threads, and GUI threads. Non-GUI threads threads use the default stack size of 12KB (on i386, which this this discussion applies to) and the default System Service Descriptor table (SSDT), KeServiceDescriptorTable. GUI threads, in contrast, are expected to have much larger stack requirements and thus use an extended stack size of 60 KB (Note: these are the numbers for Svr03 and may vary among releases).
Thread IDs uniquely identify a thread – this certainly holds for user mode threads and should also hold for kernel mode threads. But there is one kind of thread where the ID does not uniquely identify a KTHREAD – the Idle thread. On a uniprocessor system, there is only one Idle thread and this idle thread will have the thread ID 0 (in process 0). On a multiprocessor system, however, Windows creates one Idle thread per CPU.
cfix 1.1 introduces a number of new features. The most important among these is the additional ability to write kernel mode unit tests, i.e. unit tests that are run in kernel mode. Needless to say, cfix 1.1 still supports user mode unit tests. All contemporary unit testing frameworks focus on unit testing in user mode. Certainly, the vast majority of testing code can be assumed to be targeting user mode, so this does not come at a surprise.
The stack pointer, esp on i386, denotes the top of the stack. All memory below the stack pointer (i.e. higher addresses) is occupied by parameters, variables and return addresses; memory above the stack pointer must be assumed to contain garbage. When programming in assembly, it is equally easy to use memory below and above the stack pointer. Reading from or writing to addresses beyond the top of the stack is unusual and under normal circumstances, there is little reason to do so.