Reverse the keyboard filter driver written by yourself

  If you have nothing to do, reverse the drive you wrote to deepen your understanding.
  Here the reverse drive is divided into Debug版, Release版and 加载PDB版. The characteristics of the three versions are: The
  Debug version will not be optimized by the compiler and is more suitable for learning reverse engineering.
  The Release version is released to the outside world. Many structures and disassembly are optimized and deformed during reverse engineering. It can only be said that it has the characteristics of the original code.
  Loading the PDB version is almost equivalent to reading the source code, there is no challenge.

  Therefore, the focus of the study here is the own driver compiled by the reverse Debug version.

Debug版

Insert picture description here
  First drag in the DriverEntry recognized by IDA is not our actual DriverEntry function. It is sub_401250.
  Note that the assignment of the DestinationString structure at the entrance and the WdfVersionBind function are all added by the system after compilation.
  What are the specific initialization functions that can load the PDB and look at it. This is of little significance.

Insert picture description here
  The main function registration callback function sub_4012F0 is DriverUnload, and the registration callback function sub_401210 is MajorFunction.

Insert picture description here
  It can be seen that this is traversing the doubly linked list, but you may not be able to guess that this is the LDR_DATA_TABLE_ENTRY structure. Because DriverObject+0x14 is DriverSection, here is the linked list of driver modules.
Insert picture description here
  When a function is dispatched in the reverse direction, the type of IRP can be confirmed according to the array index of MajorFunction, and the dispatch function that needs to be carefully studied is located. The first parameter of these dispatch functions is mostly DriverObject or DeviceObject.
  After loading the PDB, it is found that sub401bc0 is __CheckForDebuggerJustMyCode.
Insert picture description here
The DriverObject structure is very important, so look forward and backward here.
The number of elements of MajorFunction in the source code is 0x1B, 0x1B+1=0x1Ch, which is the decimal 28 in the figure. Occupies 28x4=112. That is, the HEX value is 70h, so the size of DriverObject is A8h.
Insert picture description here

typedef struct _IO_STACK_LOCATION {
    
    
    UCHAR MajorFunction;
    UCHAR MinorFunction;
    UCHAR Flags;
UCHAR Control;
Union{
    
    }Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

Insert picture description here
  IDA only has the signature of Windows Driver Kit 7/10 32 bit, and some functions defined in Wdm.h are not recognized, which causes the difficulty of reverse engineering.
Insert picture description here
  The ReadCompleteRoutine function in the source code. Want to crack this function and guess that a2 is the PIRP structure is the key.The key to the reverse is to guess the structure. The first is to have development experience, so that you can better explore the intent of the context. Second, be familiar with some commonly used system structures when writing code.
Insert picture description here
  IRP+0X18h is IoStatus. IPR+0xCh is a pointer AssociatedIrp. The source code is a pointer to SystemBuffer.

union {
    
    
        struct _IRP *MasterIrp;
        __volatile LONG IrpCount;
        PVOID SystemBuffer;
} AssociatedIrp;

  The offset of 0x1Ch is not guessed. Look at the _IO_STATUS_BLOCK structure.

typedef struct _IO_STATUS_BLOCK {
    
    
    union {
    
    
        NTSTATUS Status;
        PVOID Pointer;
    } DUMMYUNIONNAME;

    ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

  The offset 0x1Ch address is 0X18h and then offset 0x4h address, it can be seen that it is the IO_STATUS_BLOCK structure. To put it bluntly, as long as you guess the structure or the meaning of the data in the structure, the intention of the code can be seen correctly.
  Finally, the source code of the sub_401AB0 function, which is the ReadCompleteRoutine function, is attached:

NTSTATUS ReadCompleteRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
{
    
    
	NTSTATUS status = pIrp->IoStatus.Status;
	PKEYBOARD_INPUT_DATA pKeyboardInputData = NULL;
	ULONG ulKeyCount = 0, i = 0;

	if (NT_SUCCESS(status))
	{
    
    
		pKeyboardInputData = (PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;
		ulKeyCount = (ULONG)pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);

		// 获取按键数据
		for (i = 0; i < ulKeyCount; i++)
		{
    
    
			// Key Press
			if (KEY_MAKE == pKeyboardInputData[i].Flags)
			{
    
    
				// 按键扫描码
				DbgPrint("[Down][0x%X]\n", pKeyboardInputData[i].MakeCode);
			}
			// Key Release
			else if (KEY_BREAK == pKeyboardInputData[i].Flags)
			{
    
    
				// 按键扫描码
				DbgPrint("[Up][0x%X]\n", pKeyboardInputData[i].MakeCode);
			}
			//可以添加上这这一句,然后按键全部被改为了 按下 A
//			pKeyboardInputData[i].MakeCode = 0x1e;
		}
	}

	if (pIrp->PendingReturned)
	{
    
    
		IoMarkIrpPending(pIrp);
	}

	// 减少IRP在队列的数量
	((PDEVICE_EXTENSION)pDevObj->DeviceExtension)->ulIrpInQuene--;

	status = pIrp->IoStatus.Status;
	return status;
}

Guess you like

Origin blog.csdn.net/qq_43312649/article/details/109222730