Click here to Skip to main content
Click here to Skip to main content

The Ultimate Process/Thread spy for Windows 9x.

, 27 Mar 2003
Rate this:
Please Sign up or sign in to vote.
Process/thread creation/destruction detector for Windows 9x .

Introduction

This article teaches you the way to detect Process/Thread creation/destruction on Win9x platforms.

Background Of Project

On Windows, processes and threads are the main area of interest for a system programmer. Many of you might have seen many tools & code for dealing with processes and threads on Windows NT/2000 based systems. Here I am introducing a process/thread creation/destruction detector for Windows 95/98 , really...Smile | :)

Description of Technique

Till now, you might have found ways to detect process creation on NT platforms using Kernel mode component which uses PsSetCreateProcessNotifyRoutine. But like me, you might not have found a way to detect process/threads on Win9X platforms. I found many places on the Web, describing about process creation on Win9X using CREATE_PROCESS message of VMM, but never found it easy to add handler to CREATE_PROCESS in VXD. Because it's undocumented in 98 DDK. MS 98DDK headers/documentation have description about CREATE_THREAD message, but not for CREATE_PROCESS. You will find the definition of CREATE_PROCESS, but wont find how to get the PID of a newly created process. For thread (CREATE_THREAD), the thread ID is passed in EDI register, but for CREATE_PROCESS it's not there. At last, by trial and error I found where it is, the PID value, it is in EDX reg.

Well, let's start talking about the project. There are 2 components in this project: 1st is Win32 RING 3 executable, which creates handle to VXD and passes function address to get called when process/thread creates/destroys. Second is VXD (Not WDM) which uses CREATE_PROCESS, CREATE_THREAD, DESTROY_THREAD, DESTROY_PROCESS VMM messages to detect the process/thread execution/death. VXD uses APC mechanism to call user mode program's function.

Background about VXD component: On Win9x/ME platforms (Consumer Windows), VXDs are the kernel mode components. Simply saying, VXD is ring 0 component on Win9x. VXDs are different from NT drivers in many ways. Like VXDs are not PE binaries, they have VMM's message processing kind of architecture which is very similar to Win32 message processing. VMM (Win98 kernel) sends many messages to VXD. Dispatch Handler defined in VXD gets called for these messages. The parameters like PID/TID are passed in CPU registers. The VXD presented here processes CREATE_PROCESS, DESTROY_PROCESS, CREATE_THREAD, DESTROY_THREAD messages, and calls ring 3 function (from kernel mode to user mode, great !) using APC. Following is the messages map of VXD, to map functions to messages. You can understand it by looking at it, similar to MFC message maps.

BEGIN_DISPATCH_MAP
    ON_SYS_DYNAMIC_DEVICE_INIT(OnSysDynamicDeviceInit)
    ON_SYS_DYNAMIC_DEVICE_EXIT(OnSysDynamicDeviceExit)
    ON_W32_DEVICEIOCONTROL(OnW32DeviceIoControl)
    ON_CREATE_PROCESS(OnProcessCreate)
    ON_CREATE_THREAD(OnCreateThread)
    ON_DESTROY_THREAD(OnDestroyThread)
    ON_DESTROY_PROCESS(OnDestroyProcess)
END_DISPATCH_MAP

Well, following is the DeviceIOControl handler in VXD. This function is called in VXD whenever DeviceIoControl, CreateFile and CloseFile are called in user mode on VXD's handle.

// VXD DevIoControl handler.

DWORD OnW32DeviceIoControl(ULONG dwService/*IOCTL*/, DWORD dwDDB,
    DWORD hDevice, struct DIOCParams* lpDIOCParms
    /*Param's passed from DeviceIoControl*/)
{
    DWORD dwRet = 0;

    switch (dwService)
    {
    case DIOC_OPEN: // CreateFile call
        _Debug_Printf_Service("Created Handle to Chikago driver!\n");
                   break;

    case DIOC_CLOSEHANDLE: 
        _Debug_Printf_Service("Closed Handle to Chikago driver!\n");
        // CloseHandle call
        dwRet =    0; //
        Return    success 
        reak;
        
    case IOCTL_TEST: 
          // _Debug_Printf_Service("Chikago Obtained APC func address.!\n");
        if(lpDIOCParms->lpcbBytesReturned)
              *((PDWORD)(lpDIOCParms->lpcbBytesReturned)) = 0;
           FunctionEventAPC=*((PVOID*)lpDIOCParms->lpvInBuffer ); 
           //Take Function address.
        TheThreadT=Get_Cur_Thread_Handle(); 
           // Store the Thread handle for APC queueing.
           //The APC will be qued in this thread. 
           //TheThreadT is global VOID * variable
        dwRet = 0;

    break; 
    case     IOCTL_RELEASE:
             _Debug_Printf_Service("Release Memory Called%d\n",0);
            _HeapFree(*(PVOID*)lpDIOCParms->lpvInBuffer,0); // Release memory
    default:
        dwRet = 1;
        break;
    }

    return dwRet;
}

Above shown function is the ENGINE part of VXD, it processes IOCTL passed from user mode application. Here you can see 2 major IOCTL codes: IOCTL_TEST and IOCTL_RELEASE. IOCTL_TEST is used to pass the address of callback function using DeviceIoControl. See following code of ring 3 EXE which calls DeviceIOControl to pass the function address. (The function names APCFunc will get called on process/thread creation/death.)

// Win32 App...calling VXD.
int main()
{
    
    BOOL bRet;
    DWORD cbRet;
    char buf[80];
    PVOID inbuf;
    inbuf=APCFunc;

    // Adjustment because VXD is in BIN folder
    SetCurrentDirectory("..\\\\BIN"); 
         // Open handle to VxD
         hDevice = CreateFile("\\\\.\\CHIKAGO.VXD",
                0,
                0, 
                NULL, 
                0,
                FILE_FLAG_DELETE_ON_CLOSE,
                NULL); 

    if (hDevice == INVALID_HANDLE_VALUE)
        printf("Failed to open handle to CHIKAGO.VXD\n");
    else
    {
        // Send the VxD Function address , this function will be called.
        bRet = DeviceIoControl(hDevice,
                    (DWORD)IOCTL_TEST,
                    &inbuf,
                    sizeof(PVOID),
                    buf,
                    sizeof(buf),
                    &cbRet,
                    NULL);


        if (bRet)
            printf("Press CTRL+C to terminate");
        else
            printf("IOCTL_TEST failed\n");

        while(1)
            SleepEx(-1,TRUE); //Keep waiting otherwise APC wont work.        
        CloseHandle(hDevice); //Done
    }
                
    return 0;
}

When VXD gets call for on IOCTL_TEST, it saves the input buffer in global variable named PVOID FunctionEventAPC. The calling thread ID is also stored in TheThreadT named DWORD variable since it's necessary to use APC. Now following is CREATE_PROCESS handler routine (other handlers are same as this.):

void OnProcessCreate(DWORD pid)
{
    APCDataX * apm;
    apm=(APCDataX*)_HeapAllocate(sizeof(APCDataX),0);
    apm->dwCreated =TRUE; //Creation
    apm->dwIdDATA =pid;
    apm->dwThreadOrProcess =TRUE; // Process
    
    _Debug_Printf_Service("%d Process Created\n",pid);
    _VWIN32_QueueUserApc(FunctionEventAPC,(DWORD)apm,TheThreadT);
}

void OnCreateThread(DWORD tid)
{
    APCDataX * apm;
    apm=(APCDataX*)_HeapAllocate(sizeof(APCDataX),0);
    apm->dwCreated =TRUE; // It's Created
    apm->dwIdDATA =tid;
    apm->dwThreadOrProcess =FALSE;// False means it's Thread
    
    _Debug_Printf_Service("%d Thread Created\n",tid);
    _VWIN32_QueueUserApc(FunctionEventAPC,(DWORD)apm,TheThreadT);

}

void OnDestroyProcess(DWORD pid)
{
    APCDataX * apm;
    apm=(APCDataX*)_HeapAllocate(sizeof(APCDataX),0);
    apm->dwCreated =FALSE;// FALSE means it's Destroyed
    apm->dwIdDATA =pid;
    apm->dwThreadOrProcess =TRUE;//It's a Process
    _Debug_Printf_Service("%d Process Destroyed\n",pid);
    _VWIN32_QueueUserApc(FunctionEventAPC,(DWORD)apm,TheThreadT);

}
void OnDestroyThread(DWORD tid)
{

    APCDataX * apm;
    apm=(APCDataX*)_HeapAllocate(sizeof(APCDataX),0);
    apm->dwCreated =FALSE; // FALSE means it's Destroyed
    apm->dwIdDATA =tid;
    apm->dwThreadOrProcess =FALSE; // False means it's Thread
    _Debug_Printf_Service("%d Thread Destroyed\n",tid); 
    _VWIN32_QueueUserApc(FunctionEventAPC,(DWORD)apm,TheThreadT);

}

This handler queues an APC to the thread (which is main function of ring 3 exe in our case.). The VXD passes a structure pointer filled with PID and flags to differentiate between process and thread to user mode function (APCFunc). Here is APCFunc:

void APCFunc(struct APCDataXX * data)
{
    if(data->dwThreadOrProcess==TRUE)
    {
        if(data->dwCreated==TRUE)
        {
            printf("Process Created %u\n",data->dwIdDATA);
        }
        else
        {
            printf("Process Destroyed %u\n",data->dwIdDATA);
        }
    }
    else
    {
        if(data->dwCreated ==TRUE)
        {    
            printf("Thread Created %u\n",data->dwIdDATA);
        }
        else
        {
            printf("Thread Destroyed %u\n",data->dwIdDATA);
        }
            

    }
    DeviceIoControl(hDevice, IOCTL_RELEASE, &data, sizeof(PVOID),0,0,0,0);
    //Call this to release the memory allocated in VXD.
}

Quite simple! It identifies whether process or thread was created/died. And then sends IOCTL_RELESE to free the memory allocated by VXD. VXD frees the memory using _HeapFree service/function of VMM.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Ramkrishna Pawar
Web Developer
India India
No Biography provided

Comments and Discussions

 
GeneralDetect and block process start PinmemberJuliano27-Feb-06 0:30 
GeneralRe: Detect and block process start PinmemberRamkrishna Pawar27-Feb-06 0:36 
GeneralRe: Detect and block process start PinmemberJuliano27-Feb-06 3:27 
GeneralCan't see _Debug_Printf_Service output PinmemberEternia17-Jun-05 1:12 
GeneralRe: Can't see _Debug_Printf_Service output PinmemberEternia128-Jun-05 4:03 
GeneralMore info from Process Pinmemberzandar the great2-Nov-04 9:37 
GeneralRe: More info from Process PinmemberRamkrishna Pawar2-Nov-04 19:04 
GeneralStopping window delete operation PinsussAnonymous14-Aug-04 1:23 
Is there any method through which we could stop or detect the Windows system operation like the deletion of any file on the system, or the paste operation.
 
Thanks

Kml
GeneralRe: Stopping window delete operation PinmemberRamkrishna Pawar14-Aug-04 3:13 
QuestionHow to Hook in registry PinmembermailMonty9-Feb-04 18:27 
AnswerRe: How to Hook in registry PinmemberRamkrishna Pawar9-Feb-04 18:34 
GeneralRe: How to Hook in registry PinmembermailMonty9-Feb-04 18:57 
GeneralRe: How to Hook in registry PinmemberRamkrishna Pawar9-Mar-04 20:51 
Questioninto a DLL? Pinmemberalkolik23-Dec-03 0:50 
AnswerRe: into a DLL? PinmemberRamkrishna Pawar23-Dec-03 1:23 
GeneralRe: into a DLL? Pinmemberalkolik23-Dec-03 2:33 
GeneralRe: into a DLL? PinmemberRamkrishna Pawar23-Dec-03 18:23 
GeneralWINDOWS 2000 version PinmemberNeelWin321-Sep-03 1:51 
GeneralRe: WINDOWS 2000 version PinsussRamkrishna Pawar10-Dec-03 2:48 
GeneralTry TaskInfo2003 PinmemberMarc Clifton28-Mar-03 1:10 
GeneralRe: Try TaskInfo2003 PinmemberRamkrishna Pawar28-Mar-03 1:16 
GeneralRe: Try TaskInfo2003 PinmemberNorm Almond28-Mar-03 2:37 
GeneralRe: Try TaskInfo2003 PinmemberMarc Clifton28-Mar-03 3:32 
GeneralRe: Try TaskInfo2003 PinmemberRamkrishna Pawar30-Mar-03 17:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 28 Mar 2003
Article Copyright 2003 by Ramkrishna Pawar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid