This text tries to describe not only bugs but also various interesting things in MS Windows
operating systems.

Explore, discover, find, describe and contribute, please!

Last update: Aug-21-2001   Can be updated without notice!

Author: EliCZ
Date: Sep-24-2000
OS: All

 BOOL is sometimes BYTE, sometimes DWORD. It means sometimes can be FALSE == n * 0x100.
For example if you return 0xFFFFFF00 from DllMain(,DLL_PROCESS_ATTACH,), the DLL is unmapped
because you've returned FALSE.

 Work with BOOL as with BYTE.

WriteProcessMemory can return NTSTATUS instead of BOOL
Author: EliCZ
Date: Sep-24-2000
OS: NT 4.0, Windows 2000

 When writing to PAGE_EXECUTE or PAGE_EXECUTE_READ region fails,
WriteProcessMemory returns STATUS_ACCESS_VIOLATION instead of FALSE
(maybe VC++ compiler bug?). 

 Result = WriteProcessMemory(...); 
 if((Result == STATUS_ACCESS_VIOLATION) || (Result == FALSE))
   OutputDebugString("WriteProcessMemory failed!");

Remote thread into not-yet-initialized process causes various failures in that process
Author: EliCZ
Date: Sep-24-2000
OS: Windows 2000

 Not-yet-initialized process is process which was created with suspended primary thread
that was not resumed yet (such process contains main module and ntdll.dll only). Process
is initialized (~DllMains are called with DLL_PROCESS_ATTACH, etc..) by the first
nonsuspended thread. Such a thread can be remote only. What it can cause depends on "thread
LPC registration" (via CsrClientConnectToServer) in LPC server (typically csrss.exe process):

 A) Remote thread was not "LPC registered"
    Examples: pure Nt/ZwCreateThread;

    Here is it clear:
     Initialization of KERNEL32.dll (or USER32.dll) fails because thread can't connect
     to LPC server
     {Nt/ZwSecureConnectPort(,"\\Windows\\ApiPort",...) returns STATUS_PORT_CONNECTION_REFUSED
      because thread was not "LPC registered"}
     in its DllMain during DLL_PROCESS_ATTACH. Then DllMain returns FALSE and 
     later (crash) is not important.

 B) Remote thread was "LPC registered"
    Examples: CreateRemoteThread

     DLL initialization is OK (but DLLs are initialized by remote thread), some DLLs
     created new window classes (COMCTL32.dll created SysHeader*, SysList*, SysTab*,..
     classes). Then thread executes the routine which was specified as lpStartAddress
     in call to CreateRemoteThread. Then thread terminates. Alas! It took all newly
     created window classes (USER32.dll and WIN2K.sys classes are preserved) out!
     Later when application wants to create window or dialog containing erased classes
     (e.g. SysListView32), window creation fails.
     Symptoms: Application can't invoke common dialog boxes; TaskMgr can crash;....
     (both common dialogs and dialogs in TaskMgr use/include window classes created by

 After remote thread termination, you will resume suspended primary thread. But this
primary thread brings DLL_THREAD_ATTACH notification to DLLs and TLS callbacks. As you
can see there was total role exchange: primary thread <-> remote thread. Hence, remote
thread into not-yet-initialized process should not be used at all.

 If you really need remote thread into not-yet-initialized process, you should use
CreateRemoteThread function and you should ensure the thread never terminates (at the end
of thread routine put while(TRUE);).

DPMI service "Set Debug Watchpoint" (0B00h) doesn't return handle
Author: EliCZ
Date: Sep-24-2000
OS: Windows 2000

 This DPMI service should return handle to debug watchpoit in BX register if it was
succesful (CF is clear). Unfortunately it doesn't change BX register at all.

 Valid handles are 0..3. Work with hardcoded values:
 CDW(0); CDW(1); CDW(2); CDW(3);
 if(SDW(...)) HandleA = 0; if(SDW(...)) HandleB = 1; if(SDW(...)) HandleC = 2;
 CDW(HandleB); if(SDW(...)) HandleD = 1; if(SDW(...)) HandleE = 3;
 CDW(HandleA); if(SDW(...)) HandleF = 0;

LDT descriptor can be initialized via INT 0x2A
Author: EliCZ
Date: Sep-24-2000
OS: NT 4.0, Windows 2000

 Interrupt 0x2A is normally used as KiGetTickCount but it can be also used in DPMI (DOS or
Win16) applications for fast setting of LDT selectors (NTVDM uses it). Allowed are DPL3 code
or data descriptors (forget call/int gates) with limit <= HighestUserAddress.

 ;Make new (LDT) selector
  MOV   AX,  0
  INT   31H
  JC    DPMIFailed

 ;Now set the descriptor to writeable DATA 0..3FFH
  MOV   EBP, 0F0F0F0F1H ;magic
  MOV   BX,  AX         ;new selector
  MOV   ECX, 0000003FFH ;low  part of descriptor
  MOV   EDX, 00000F200H ;high part of descriptor
  MOV   EAX, EBP        ;magic
  INT   2AH             
  JC    DPMIFailed

API forwarding is API hooking (DLL redirection method) provided by NTDLL/KERNEL32
Author: EliCZ
Date: Sep-24-2000
OS: All

 => primitive API hooking (DLL redirection) detections can give false positive results.

Examples of bad detections:
 VirtualQuery method, comparing highest bytes of API address with module base
and other module walking based detections.

Note for NT:
 Modules containing "backwarded" APIs are loaded dynamically on demand (when thread
needs address of forwarded API).

Limits of API forwarding:
 Module name must have ".dll" extension (it can't be .exe, .drv, ...).

Limits of API forwarding in 9X:
 First, it was nice surprise I've found that 9X supports API forwarding partially.
 But module containing "backwarded" API must be preloaded and ordinals forwarding
(e.g. MyDll.#12) is not supported.

User thread's environment block (TEB) is freed when thread terminates even if there exist
references to this thread
Author: EliCZ
Date: Dec-10-2000
OS: Windows 2000 (NT 4.0 ?)

 That's because RtlQueryProcessDebugInformation (CreateToolhelp23Snapshot(TH32CS_SNAPMODULE,))
sometimes mayn't free new thread's stack. That's because RtlFreeUserStack can't free stack of
terminated thread (stack section is in TEB.E0C) or may crash running thread by freeing its
Solution to free user thread's stack:
 Use ThreadFunc which terminates via RET 4 or via ExitThread (stack is then freed implicitly).
In case thread is terminated via NtTerminateThread:
 a) create thread suspended
 b) get thread TEB and from it thread's stack section
 c) resume thread.



  Windows NT, 4.00.1381, Workstation, Service Pack 6a
  connted via Terminal client, 5.0.2221.1, 128 Bit
  to Windows 2000, 5.00.2195, Advanced Server.

Used API function:

  NtQueryObject in ntdll.dll called from user mode
  with OBJECT_INFORMATION_CLASS of ObjectAllTypesInformation.


  The function returns the right object types
  but only the count of object types wich are
  available on Windows NT 4.0
  Result               Windows NT 4.0
  -------------------  -----------------
  1,  Type             1,  Type
  2,  Directory        2,  Directory
  3,  SymbolicLink     3,  SymbolicLink
  4,  Token            4,  Token
  5,  Process          5,  Process
  6,  Thread           6,  Thread
  7,  Job              7,  Event
  8,  Event            8,  EventPair 
  9,  EventPair        9,  Mutant
  10, Mutant           10, Semaphore
  11, Callback         11, Timer
  12, Semaphore        12, Profile
  13, Timer            13, WindowStation
  14, Profile          14, Desktop
  15, WindowsStation   15, Section
  16, Desktop          16, Key
  17, Section          17, Port
  18, Key              18, Adapter
  19, Port             19, Controller
  20, WaitablePort     20, Device
  21, Adapter          21, Driver
  22, Controller       22, IoCompletion
  23, Device           23, File
 (24, Driver       - not returned)
 (25, IoCompletion - not returned)
 (26, File         - not returned)

Bug info:

  29 Jan 2001, NicoDE (

And EliCZ adds (see also\readme.txt) :
 When querying \Device\NamedPipe\net\NtControlPipeX, the querying
thread may be blocked. Try for example SysInternals HandleEx and
try to view Properties/Security of NtControlPipeX.
BTW: The new HandleEx uses INT 1, when things go wrong in NT.

You can't open Desktop when you don't have DESKTOP_READOBJECTS and DESKTOP_WRITEOBJECTS
access allowed
Author: EliCZ
Date: Aug-21-2001
OS: Windows NT

 That's because Win32K.sys!xxxOpenDesktop adds to access mask, you've passed in a call to
OpenDesktop, 0x81 (DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS). For example, even if you're
trying to open a deskktop for READ_CONTROL only and you don't have D_RO and D_WO granted,
call doesn't succeed.

APC oddities
Author: EliCZ
Date: Aug-21-2001
OS: All

 In Windows 9x aren't APCs queued before thread begins to execute spawned before thread
begins to execute (like in NT). They are spawned when thread enters an alertable state only.

 In Windows NT are APCs queued before thread begins to execute spawned before thread begins
to execute, but in RANDOM order (not in FIFO order).

Bug/trick/interesting thing
Author: <You>
Date: <soon>
OS: ?