Thursday, December 8, 2011

The Working of a Semaphore



This can be laughed at but I have always wondered how the limit of semaphore object  is used in windows so I ended up doing a little experiment during my dump analysis routine at work

The signal-state in dispatcher header  is set to the initial count  in this case and this keeps decrementing and once 0 we can’t acquire it any more. This is just a demonstration or a POC for  people who are still learning internals. Initially I thought I will have to set up a live debug scenario to capture this but this was fairly simple using a user mode CDB and livekd.exe. The only glitch in the whole workout is we need to re-launch the livekd every time we need the updated kernel states

This was an exercise I could perform while I was travelling to my office in a cab.  

// Standard code from MSDN demonstrating semaphores
#include <windows.h>
#include <stdio.h>

#define MAX_SEM_COUNT 10
#define THREADCOUNT 12

HANDLE ghSemaphore;

DWORD WINAPI ThreadProc( LPVOID );

void main()
{
    HANDLE aThread[THREADCOUNT];
    DWORD ThreadID;
    int i;

    // Create a semaphore with initial and max counts of MAX_SEM_COUNT

    ghSemaphore = CreateSemaphore(
        NULL,           // default security attributes
        MAX_SEM_COUNT,  // initial count
        MAX_SEM_COUNT,  // maximum count
        NULL);          // unnamed semaphore

    if (ghSemaphore == NULL)
    {
        printf("CreateSemaphore error: %d\n", GetLastError());
        return;
    }

    // Create worker threads

    for( i=0; i < THREADCOUNT; i++ )
    {
        aThread[i] = CreateThread(
                     NULL,       // default security attributes
                     0,          // default stack size
                     (LPTHREAD_START_ROUTINE) ThreadProc,
                     NULL,       // no thread function arguments
                     0,          // default creation flags
                     &ThreadID); // receive thread identifier

        if( aThread[i] == NULL )
        {
            printf("CreateThread error: %d\n", GetLastError());
            return;
        }
    }

    // Wait for all threads to terminate

    WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);

    // Close thread and semaphore handles

    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(aThread[i]);

    CloseHandle(ghSemaphore);
}

DWORD WINAPI ThreadProc( LPVOID lpParam )
{
    DWORD dwWaitResult;
    BOOL bContinue=TRUE;

    while(bContinue)
    {
        // Try to enter the semaphore gate.

        dwWaitResult = WaitForSingleObject(
            ghSemaphore,   // handle to semaphore
            0L);           // zero-second time-out interval

        switch (dwWaitResult)
        {
            // The semaphore object was signaled.
            case WAIT_OBJECT_0:
                // TODO: Perform task
                printf("Thread %d: wait succeeded\n", GetCurrentThreadId());
                bContinue=FALSE;           

                // Simulate thread spending time on task
                Sleep(5*1000*60);

                // Relase the semaphore when task is finished

                if (!ReleaseSemaphore(
                        ghSemaphore,  // handle to semaphore
                        1,            // increase count by one
                        NULL) )       // not interested in previous count
                {
                    printf("ReleaseSemaphore error: %d\n", GetLastError());
                }
                break;

            // The semaphore was nonsignaled, so a time-out occurred.
            case WAIT_TIMEOUT:
                printf("Thread %d: wait timed out\n", GetCurrentThreadId());
                break;
        }
    }
    return TRUE;
}


/*

DEBUGGER OUTPUTS

User mode CDB

D:\addy\src>c:\debug86\cdb semaphore.exe

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: semaphore.exe
Symbol search path is: srv*d:\symbols\public*http://msdl.microsoft.com/download/symbols;srv*d:\symbols\citrix*http://
Executable search path is:
ModLoad: 00310000 0031f000   image00310000
ModLoad: 77090000 771cd000   ntdll.dll
ModLoad: 75930000 75a04000   C:\Windows\system32\kernel32.dll
ModLoad: 753c0000 7540a000   C:\Windows\system32\KERNELBASE.dll
(21e8.4f4): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0041f3a0 edx=770d6344 esi=fffffffe edi=00000000
eip=7712ebbe esp=0041f3bc ebp=0041f3e8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
7712ebbe cc              int     3
0:000> bp kernel32!CreateSemaphoreA
0:000> bp kernel32!CreateSemaphoreW
0:000> g
Breakpoint 0 hit
eax=005c2150 ebx=7ffdf000 ecx=00000001 edx=770d6344 esi=00000000 edi=00000000
eip=7596bdd7 esp=0041f788 ebp=0041f7d4 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
kernel32!CreateSemaphoreA:
7596bdd7 8bff            mov     edi,edi
0:000> kvn
*** WARNING: Unable to verify checksum for image00310000
*** ERROR: Module load completed but symbols could not be loaded for image00310000
# ChildEBP RetAddr  Args to Child
00 0041f784 00311014 00000000 0000000a 0000000a kernel32!CreateSemaphoreA (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
01 0041f7d4 00311385 00000001 005c2120 005c2150 image00310000+0x1014
02 0041f81c 75981114 7ffdf000 0041f868 770eb429 image00310000+0x1385
03 0041f828 770eb429 7ffdf000 731c916d 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
04 0041f868 770eb3fc 003113db 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
05 0041f880 00000000 003113db 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])
0:000> gu
eax=00000020 ebx=7ffdf000 ecx=7ffde000 edx=00000000 esi=00000000 edi=00000000
eip=00311014 esp=0041f79c ebp=0041f7d4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
image00310000+0x1014:
00311014 a3dcca3100      mov     dword ptr [image00310000+0xcadc (0031cadc)],eax ds:0023:0031cadc=00000000
0:000> g
 0:000>** Eax has the return value from the CreateSemaphore which is the handle this value can be used to
 0:000>**  get the object once we change the process context in the livekd launched debugger in this case kd

Live KD
Creation of Semaphore object
0: kd> .process -r -p 89594030
Implicit process is now 89594030
Loading User Symbols
....
0: kd> !handle 20

PROCESS 89594030  SessionId: 1  Cid: 21e8    Peb: 7ffdf000  ParentCid
    DirBase: d98bbfc0  ObjectTable: ba811088  HandleCount:   8.
    Image: semaphore.exe

Handle table at ec021000 with 8 entries in use

0020: Object: 89aba490  GrantedAccess: 001f0003 Entry: ec021040
Object: 89aba490  Type: (85ed1518) Semaphore
    ObjectHeader: 89aba478 (new version)
        HandleCount: 1  PointerCount: 1


0: kd> dt nt!_KSEMAPHORE 89aba490 ç init via KeInitializeSemaphore
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n10 <===
0: kd> dt nt!_KSEMAPHORE 89aba490 Header.
   +0x000 Header  :
      +0x000 Type    : 0x5 ''
      +0x001 TimerControlFlags : 0 ''
      +0x001 Absolute : 0y0
      +0x001 Coalescable : 0y0
      +0x001 KeepShifting : 0y0
      +0x001 EncodedTolerableDelay : 0y00000 (0)
      +0x001 Abandoned : 0 ''
      +0x001 Signalling : 0 ''
      +0x002 ThreadControlFlags : 0x5 ''
      +0x002 CpuThrottled : 0y1
      +0x002 CycleProfiling : 0y0
      +0x002 CounterProfiling : 0y1
      +0x002 Reserved : 0y00000 (0)
      +0x002 Hand    : 0x5 ''
      +0x002 Size    : 0x5 ''
      +0x003 TimerMiscFlags : 0 ''
      +0x003 Index   : 0y0
      +0x003 Processor : 0y00000 (0)
      +0x003 Inserted : 0y0
      +0x003 Expired : 0y0
      +0x003 DebugActive : 0 ''
      +0x003 ActiveDR7 : 0y0
      +0x003 Instrumented : 0y0
      +0x003 Reserved2 : 0y0000
      +0x003 UmsScheduled : 0y0
      +0x003 UmsPrimary : 0y0
      +0x003 DpcActive : 0 ''
      +0x000 Lock    : 0n327685
      +0x004 SignalState : 0n10 <<====
      +0x008 WaitListHead : _LIST_ENTRY [ 0x89aba498 - 0x89aba498 ]


After acquisitions
----------------------
0: kd> .process -r -p 89594030
Implicit process is now 89594030
Loading User Symbols
....
0: kd> !handle 20

PROCESS 89594030  SessionId: 1  Cid: 21e8    Peb: 7ffdf000  ParentCid: 0f3c
    DirBase: d98bbfc0  ObjectTable: ba811088  HandleCount:  22.
    Image: semaphore.exe

Handle table at ec021000 with 22 entries in use

0020: Object: 89aba490  GrantedAccess: 001f0003 Entry: ec021040
Object: 89aba490  Type: (85ed1518) Semaphore
    ObjectHeader: 89aba478 (new version)
        HandleCount: 1  PointerCount: 2


0: kd> dt nt!_KSEMAPHORE 89aba490
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n10
0: kd> dt nt!_KSEMAPHORE 89aba490 Header.
   +0x000 Header  :
      +0x000 Type    : 0x5 ''
      +0x001 TimerControlFlags : 0 ''
      +0x001 Absolute : 0y0
      +0x001 Coalescable : 0y0
      +0x001 KeepShifting : 0y0
      +0x001 EncodedTolerableDelay : 0y00000 (0)
      +0x001 Abandoned : 0 ''
      +0x001 Signalling : 0 ''
      +0x002 ThreadControlFlags : 0x5 ''
      +0x002 CpuThrottled : 0y1
      +0x002 CycleProfiling : 0y0
      +0x002 CounterProfiling : 0y1
      +0x002 Reserved : 0y00000 (0)
      +0x002 Hand    : 0x5 ''
      +0x002 Size    : 0x5 ''
      +0x003 TimerMiscFlags : 0 ''
      +0x003 Index   : 0y0
      +0x003 Processor : 0y00000 (0)
      +0x003 Inserted : 0y0
      +0x003 Expired : 0y0
      +0x003 DebugActive : 0 ''
      +0x003 ActiveDR7 : 0y0
      +0x003 Instrumented : 0y0
      +0x003 Reserved2 : 0y0000
      +0x003 UmsScheduled : 0y0
      +0x003 UmsPrimary : 0y0
      +0x003 DpcActive : 0 ''
      +0x000 Lock    : 0n327685
      +0x004 SignalState : 0n0 ç Queue Full can’t acquire anymore
      +0x008 WaitListHead : _LIST_ENTRY [ 0x89aba498 - 0x89aba498 ]


Tracking each acquisition

eax=00000020 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=7597ef83 esp=0076ff04 ebp=0076ff04 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
kernel32!WaitForSingleObjectExImplementation+0x48:
7597ef83 8bc8            mov     ecx,eax
0:001> gu
eax=00000000 ebx=00000000 ecx=753c17c4 edx=770d6344 esi=00000000 edi=00000000
eip=7597ef52 esp=0076ff18 ebp=0076ff18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
kernel32!WaitForSingleObject+0x12:
7597ef52 5d              pop     ebp
0:001> t
Breakpoint 0 hit
eax=75981102 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=00151100 esp=00c0f988 ebp=00c0f990 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc:
00151100 55              push    ebp
0:002>
eax=75981102 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=00151101 esp=00c0f984 ebp=00c0f990 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc+0x1:
00151101 8bec            mov     ebp,esp
0:002>
eax=75981102 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=00151103 esp=00c0f984 ebp=00c0f984 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc+0x3:
00151103 83ec0c          sub     esp,0Ch
0:002> kvn
# ChildEBP RetAddr  Args to Child
00 00c0f984 75981114 00000000 00c0f9d0 770eb429 semaphore!ThreadProc+0x3 (FPO: [Non-Fpo]) (CONV: std
01 00c0f990 770eb429 00000000 738241d2 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
02 00c0f9d0 770eb3fc 00151005 00000000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
03 00c0f9e8 00000000 00151005 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])


0: kd> dt nt!_KSEMAPHORE 86acba70

*** ERROR: Module load completed but symbols could not be loaded for LiveKdD.SYS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n10
0: kd>    +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n10
0: kd> dt nt!_KSEMAPHORE 86acba70 Header.
   +0x000 Header  :
      +0x000 Type    : 0x5 ''
      +0x001 TimerControlFlags : 0 ''
      +0x001 Absolute : 0y0
      +0x001 Coalescable : 0y0
      +0x001 KeepShifting : 0y0
      +0x001 EncodedTolerableDelay : 0y00000 (0)
      +0x001 Abandoned : 0 ''
      +0x001 Signalling : 0 ''
      +0x002 ThreadControlFlags : 0x5 ''
      +0x002 CpuThrottled : 0y1
      +0x002 CycleProfiling : 0y0
      +0x002 CounterProfiling : 0y1
      +0x002 Reserved : 0y00000 (0)
      +0x002 Hand    : 0x5 ''
      +0x002 Size    : 0x5 ''
      +0x003 TimerMiscFlags : 0 ''
      +0x003 Index   : 0y0
      +0x003 Processor : 0y00000 (0)
      +0x003 Inserted : 0y0
      +0x003 Expired : 0y0
      +0x003 DebugActive : 0 ''
      +0x003 ActiveDR7 : 0y0
      +0x003 Instrumented : 0y0
      +0x003 Reserved2 : 0y0000
      +0x003 UmsScheduled : 0y0
      +0x003 UmsPrimary : 0y0
      +0x003 DpcActive : 0 ''
      +0x000 Lock    : 0n327685
      +0x004 SignalState : 0n9
      +0x008 WaitListHead : _LIST_ENTRY [ 0x86acba78 - 0x86acba78 ]

2 more acquires

0:003>
eax=00000020 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=7597ef40 esp=0060f8c8 ebp=0060f8e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
kernel32!WaitForSingleObject:
7597ef40 8bff            mov     edi,edi
0:003>
eax=00000020 ebx=00000000 ecx=00000000 edx=00151005 esi=00000000 edi=00000000
eip=7597ef42 esp=0060f8c8 ebp=0060f8e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
kernel32!WaitForSingleObject+0x2:
7597ef42 55              push    ebp
0:003> gu
eax=00000000 ebx=00000000 ecx=753c17c4 edx=770d6344 esi=00000000 edi=00000000
eip=00151125 esp=0060f8d4 ebp=0060f8e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc+0x25:
00151125 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0060f8dc=00000000
0:003> t
eax=00000000 ebx=00000000 ecx=753c17c4 edx=770d6344 esi=00000000 edi=00000000
eip=00151128 esp=0060f8d4 ebp=0060f8e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc+0x28:
00151128 8b4dfc          mov     ecx,dword ptr [ebp-4] ss:0023:0060f8dc=00000000
0:003>
eax=00000000 ebx=00000000 ecx=00000000 edx=770d6344 esi=00000000 edi=00000000
eip=0015112b esp=0060f8d4 ebp=0060f8e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
semaphore!ThreadProc+0x2b:


0: kd> dt nt!_KSEMAPHORE 86acba70
*** ERROR: Module load completed but symbols could not be loaded for LiveKdD.SYS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 Limit            : 0n10
0: kd> dt nt!_KSEMAPHORE 86acba70 Header.
   +0x000 Header  :
      +0x000 Type    : 0x5 ''
      +0x001 TimerControlFlags : 0 ''
      +0x001 Absolute : 0y0
      +0x001 Coalescable : 0y0
      +0x001 KeepShifting : 0y0
      +0x001 EncodedTolerableDelay : 0y00000 (0)
      +0x001 Abandoned : 0 ''
      +0x001 Signalling : 0 ''
      +0x002 ThreadControlFlags : 0x5 ''
      +0x002 CpuThrottled : 0y1
      +0x002 CycleProfiling : 0y0
      +0x002 CounterProfiling : 0y1
      +0x002 Reserved : 0y00000 (0)
      +0x002 Hand    : 0x5 ''
      +0x002 Size    : 0x5 ''
      +0x003 TimerMiscFlags : 0 ''
      +0x003 Index   : 0y0
      +0x003 Processor : 0y00000 (0)
      +0x003 Inserted : 0y0
      +0x003 Expired : 0y0
      +0x003 DebugActive : 0 ''
      +0x003 ActiveDR7 : 0y0
      +0x003 Instrumented : 0y0
      +0x003 Reserved2 : 0y0000
      +0x003 UmsScheduled : 0y0
      +0x003 UmsPrimary : 0y0
      +0x003 DpcActive : 0 ''
      +0x000 Lock    : 0n327685
      +0x004 SignalState : 0n7
      +0x008 WaitListHead : _LIST_ENTRY [ 0x86acba78 - 0x86acba78 ]

      .      .       .      .    .. .     
and so on till signal state becomes 0



*/

No comments:

Post a Comment