Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

Windows Process Monitoring

johnhouldingjohnhoulding Member Posts: 17
Hello Everyone. Firstly Sorry for my bad English. But i will try explain my problem .

I need process monitor wdm driver which driver get newly created process and send process exe path to usermode app after user mode app scan file it send true or false to driver to block or allow operation. yesterday I write my test driver but without blocking process creation.

Firstly in DriverEntry function I call IoCreateDevice and IoCreateSymbolicLink for user mode communication. After this I call IoCreateNotificationEvent(\\BaseNamedObjects\\testevent) for signal user mode app.Then I register PsSetCreateProcessNotifyRoutineEx. Inside callback function I get newprocess exe path and copy it global wchar variable(wchar pathname[1024] and then with KeSetEvent and signal usermode app .On the usermode side UserOpen event obj with OpenEvent (testevent) and in while block user mode call WaitForSingleObject(testevent) when new process create driver copy name to global var signal user mode process and usermode send IOCTL_GET_PATH to driver .driver copy global var to user buffer and return.After this user mode app prints pathnames to console output. I tested this lots of computer and lots of process and it works fine but it suprised me)) because i didnt use any sync(mutex spin etc) operation but userapp without any problems writes all new processes path to console? So my first question is can I use this method( when i dont want to block process,I only want write newly created process path to console) Can it makes any problem for me ?
My second question about prevent newly created processes.

I didnt test it its only my opinion .Before all , user mode app Create new handle and with DeviceIoControl send handle to driver.kernel layer driver copy this handle to global variable.( I use ObReferenceObjectByPointer) and inside callback function driver first copy new process pathname to global var then signal usermode and after this driver get wait state with WaitForSingleObject(wait for handle which user sended) and in user layer after usermode process scan filename then send answer (1 or 0) to driver with ioctl ,driver copy answer global variable and then userapp signal event which driver wait. Driver WakeUp and read answer which contains true or false and do next job.

Again sorry for my bad English .I hope someone can help me

Comments

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 7,505

    Firstly Sorry for my bad English

    No need to apologize. We'll do whatever we can to help, and ask questions when we're not sure we understand!

    On the usermode side UserOpen event obj with OpenEvent (testevent) and in while block user mode call WaitForSingleObject(testevent) when new process create driver copy name to global var signal user mode process and usermode send IOCTL_GET_PATH to driver .driver copy global var to user buffer and return.

    ...

    So my first question is can I use this method... Can it makes any problem for me ?

    You are correct to be surprised. You definitely need some sort of synchronization with this approach.

    BUT... I think there's a better way to do what you want to do. Why not have your user-mode app open the device created by the driver, and set the driver some IOCTLs asynchronously (open with FILE_FLAG_OVERLAPPED and send the IOCTLs supplying OVERLAPPED structure) supplying a different OutBuffer in each of the IOCTLs that it sends. The app can then wait for one of the IOCTLs to complete (it could use a completion port or call GetOverlappedResult or whatever you prefer... this is strictly Windows user-mode programming).

    When the driver wants to return a process name to the application, it copies the process name into the IOCTL OutBuffer and completes the IRP.

    The app see the IOCTL complete, and then it has the name of the process and can print it. When it's done, it sends the IOCTL back to the driver.

    You can use this same approach for your second requirement as well.

    Did I explain my idea clearly enough??

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • johnhouldingjohnhoulding Member Posts: 17
    Oh Mr Peter thank you so much for your reply . After wrote this post I open your book and reading it)) Yes I understand your method and try it. But i have new questions? I know sometimes i have stupid question but Im newbie and really want to do this. Just imagine usermode app call ioctl with Overlapped structure .That is ok. But inside Notify callback routine how i copy new process image path to outbuffer? Because there isnt Irp? and my second question is when usermode get imagepath over overlapped and send its to scan operation what happening when new process is created? Can I get new pathname after scan operation?
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,158

    Because there isn't Irp?

    Of course there is. The driver received an IRP when the app called DeviceIoControl. You save that IRP somewhere (a WDFQUEUE, if you're writing in KMDF). When your notify callback happens, you pop the top request from the queue and complete it.

    when usermode get imagepath over overlapped and send its to scan operation what happening when new process is created?

    I'm not sure what you're asking. The OVERLAPPED structure is only used to enable asynchronous I/O, so you can have multiple requests going at one time. The data is still transmitted in the ioctl's buffers.

    Can I get new pathname after scan operation?

    The application submits, for example, 4 requests right in a row. They all sit in the driver, waiting for something to happen. When you get a callback, you pop the first request, fill it in, and complete it. Now there are 3 waiting requests. The application does whatever it needs to do, then resubmits the request it just finished. Now there are 4 again. That continues, forever.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • johnhouldingjohnhoulding Member Posts: 17
    edited November 27

    Thank you Mr Robertson for answer. Today I re created my project So for implement what I want I created 2 event object one for user mode app and one for kernel mode. 1. UserMode app CreateEvent and Send it driver via IOCTL and driver use ObReferenceObjectByPointer copy handle value to global variable kernel. And inside DriverEntry I created IoNotificationEvent for user mode app. When new process Created in callback routine first kernelmode signal usermodeapp(with NotificationEvent) and after this kernel mode goes waitmode with (KeWaitForSingleObject) on the user side usermode sleeping 5 secons and SetEvent for wake up kernel . So I tested it and it works normally but some miss there. No all created process printed?If you have free time can yo check my code I will be happy. .Can it makes problem? I attached visual studio project. Thank you very much.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 7,505

    @johnhoulding You're still missing the point, Mr. Houlding.

    You don't want to share ANY events between your driver and your user-mode app. You don't want to use any global memory.

    Do it this way:

    1) User allocates a buffer that is, for example, 4K (just call malloc)
    2) User sends IOCTL (as I said before, you'll want to send the IOCTL asynchronously) with the pointer to the buffer that was just allocated above as the OutBuffer in the IOCTL. Make sure the driver defines the IOCTL using the CTL_CODE macro, specifying METHOD_BUFFERED. In practice, you probably want the app to have multiple of these IOCTLs in progress at the same time so that the driver always has one on its queue when it needs it (see the steps below).
    3) Driver gets this IOCTL, calls IoMarkIrpPending, and puts it on a list. Create a listhead for this list in your Device Extension (the data structure to use is LIST_ENTRY). You can use InsertTailList and queue Irp->Tail.Overlay.ListEntry for the list.
    4) NEXT... your driver gets called back in its Create Process Notify callback. From within that callback, the driver removes an IRP from the list (that was previously put on the list in step 3, above... use RemoteHeadList). Copy the name/path or whatever you want to the OutBuffer of the IOCTL IRP that you just removed from the list. You get the pointer to the OutBuffer from the IRP, in the Irp->AssociatedIrp.SystemBuffer field. Just memcpy the data. Remember how many bytes you copied!
    5) Complete the IOCTL IRP: Fill in Irp->IoStatus.Status with STATUS_SUCCESS and Irp->IoStatus.Information with the number of data bytes you copied to the IOCTL OutBuffer... call IoCompleteRequest. Again... all of the things in Steps 4 and 5 are done in your notification callback.
    6) Back in your user-mode app, the app now sees the IOCTL is complete (perhaps the app has called GetOverlappedResult or perhaps you're using a completion port) and now the data you copied to the OutBuffer in your driver's notification callback is available to the app in his OutBuffer.
    7) When the user-mode app finishes processing the returned data, it sends the IOCTL back to the driver to use again.

    Don't use shared events. Don't use shared/global memory.

    I hope that helps,

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • johnhouldingjohnhoulding Member Posts: 17

    Thank you Mr Peter I understand and as soon as possible i will try it.

  • johnhouldingjohnhoulding Member Posts: 17
    edited December 2

    Hello Mr Peter again... I tested your method today. So in usermode app I send io control code with overlapped structure to driver. In driver layer I first call IoMarkIrpPending and after this I add irp to to my list. And user mode app call GetOverlappedMethod to get wait mode. When I run notepad.exe in driver layer I call RemoveHeadList and Complete Irp request . it works but there is some of problem. my userapp not multi threaded app i have only one thread and one io request. On the testing app ,after driver call IoCompleteRequest my event signaled then I call Sleep(5000) and when new notepad.exe is executed i get BSOD. Because of there is only one IRP in queue.How can I solve this problem? I think i must have sync method for doing that like when new process created i add new process path to list and signal usermode app. Or can i use this method? For example i send 20 device io control and call WaitForMultipleObject and one of events signaled i get path do my work on process path and resend it device driver.

    while (1)
        {
    
            bControl = DeviceIoControl(hDriver, IOCTL_PEND_ME, NULL, 0, NULL, 0, &nRead, &ov);
    
    
            bRet = GetOverlappedResult(hDriver, &ov, &nRead, TRUE);
    
            Sleep(5000);
    
            printf("%d:%d\n", bRet, GetLastError());
    
            ResetEvent(ov.hEvent);
        }
    
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 7,505

    my userapp not multi threaded app i have only one thread and one io request

    Well, that’s a major problem if not THE problem. You need a multi-threaded app that sends many IRPs to your driver, so you always have one available.

    There are many ways to do this in Windows... it’s all simple user-mode programming and out of the scope of forum. Search for using Completion Ports.

    There IS an interesting side-issue, and that is in your driver you will need to handle cancel processing. Consider what happens when your app sends (for example) ten IOCTLs to the driver, you have those ten IRPs on your queue, and the app exits. You need to remove those IRPs from the queue and complete them (usually with STATUS_CANCELED) before your app will be allowed to exit.

    Good luck,

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,158

    Because of there is only one IRP in queue.

    Are you seriously suggesting that you used RemoveHeadList without checking whether the list was empty first? And when you got a BSOD you seriously could not figure out how to fix that? YOU have to decide how you will handle this case. Presumably, if there is no one to notify, you will just return without doing anything, but it gives me grave concerns about your programming ability that you couldn't figure this out. This is just basic defensive programming.

    You don't need to be multithreaded to do this properly. All you need is 3 or 4 OVERLAPPED structures, presumably in an array. Each one will need its own event. You can then store the event handles in another array. Then, in your while loop, you use "WaitForMultipleObjects" and pass it the array. The return value will tell you which request completed. Approximately like this:

    OVERLAPPED olap[4];
    HANDLE events[4];
    for( int i = 0; i < 4; i++ ) {
        events[i] = CreateEvent( ... );
        ZeroMemory( &olap[i], sizeof(OVERLAPPED) );
        olap[i].event = events[i];
        DeviceIoControl( ... , &olap[i] );
    }
    while( 1 ) {
        result = WaitForMultipleObjects( 4, events, ... );
        if( result is not an error ) {
            int which = result - WAIT_OBJECT_0;
            GetOverlappedResult( hDriver, &olap[which], &nRead, TRUE );
            ZeroMemory( &olap[which], sizeof(OVERLAPPED));
            olap[which].event = events[which];
            DeviceIoControl( ..., &olap[which] );
        }
    }
    

    And, by the way, you are supposed to clean out the OVERLAPPED structure every time before you use one, by zeroing out everything except the event, as I did above.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • johnhouldingjohnhoulding Member Posts: 17
    edited December 5

    **Are you seriously suggesting that you used RemoveHeadList without checking whether the list was empty first? And when you got a BSOD you seriously could not figure out how to fix that? **

    Yes Mr Roberts I know but this is test case for this reason I didnt use because i knew there is only one irp )). And thank you so much source code ...

    Post edited by johnhoulding on
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 7,505

    You don't need to be multi-threaded to do this properly

    Strictly speaking, absolutely correct. I was a bit quick to conflate what I think is the best way to do this with what you absolutely MUST do.

    They key point here is that you'll need to have multiple IRPs in progress simultaneously, so that your driver will always have one available when it needs to notify user-mode.

    I'd do that with a multi-threaded app using completion ports, but... that's just one way of doing it. Mr. Roberts' pseudo-code will work just fine as well.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • johnhouldingjohnhoulding Member Posts: 17
    > @"Peter_Viscarola_(OSR)" said:
    > (Quote)
    > Strictly speaking, absolutely correct. I was a bit quick to conflate what I think is the best way to do this with what you absolutely MUST do.
    >
    > They key point here is that you'll need to have multiple IRPs in progress simultaneously, so that your driver will always have one available when it needs to notify user-mode.
    >
    > I'd do that with a multi-threaded app using completion ports, but... that's just one way of doing it. Mr. Roberts' pseudo-code will work just fine as well.
    >
    > Peter

    Thank you for much Mr Peter for your reply. as soon as possible I will take osr training . . Yes Mr Peter the project which i work we have scaning engine i make it early.and its single threaded for this reason i look for single threaded model because i dont have time to re edit this project.i have only way to adapt kernel to user mode engine. I will try Mr Roberts method. And about the cancel irp routine . Before app exit app send new ioctl (blabla) to driver and driver first delete process callback notify routine then get all irp from queue step by step and complete this as you say with status canceled?
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 7,505

    And about the cancel irp routine ...

    No, it’s more complex than you think. Suppose the app gets an access violation and aborts?

    Look at IoSetCancelRoutine.

    And... seriously... For this reason alone you’re probably better off at least thinking about writing this driver using WDF instead of WDM. User mode code will be the same, the process mo ignoring code in your driver will be the same, but the IRP handling and such will change and become much easier in WDF. Cancellation will be a non-issue.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
Writing WDF Drivers 21 Oct 2019 OSR Seminar Space & ONLINE
Internals & Software Drivers 18 Nov 2019 Dulles, VA
Kernel Debugging 30 Mar 2020 OSR Seminar Space
Developing Minifilters 27 Apr 2020 OSR Seminar Space & ONLINE