逆向自己写的键盘过滤驱动

  闲来无事,逆向一下自己写的驱动,加深下认识。
  这里把逆向的驱动分为Debug版Release版加载PDB版。三个版本的特点是:
  Debug版不会被编译器优化,比较适合学习逆向。
  Release版是发布对外的,逆向时很多结构体和反汇编都被优化变形了,只能说是有原来代码的特征。
  加载PDB版就几乎等同于阅读源码了,没什么挑战。

  所以这里学习重点讲的是逆向Debug版编译的自己的驱动。

Debug版

在这里插入图片描述
  首先拖入IDA识别的DriverEntry并不是我们实际的DriverEntry函数。而是sub_401250。
  注意入口处DestinationString结构的赋值以及WdfVersionBind函数都是编译后系统添加上去的。
  具体初始化都是些什么函数可以加载PDB后看下,这里意义不大。

在这里插入图片描述
  主函数注册回调函数sub_4012F0为DriverUnload,注册回调函数sub_401210为MajorFunction。

在这里插入图片描述
  这个可以看出来是在遍历双向链表,但是未必能猜得出来这是LDR_DATA_TABLE_ENTRY结构体。因为DriverObject+0x14处是DriverSection,这里是驱动的模块链表。
在这里插入图片描述
  逆向派遣函数时根据MajorFunction的数组索引可以确认IRP的类型,定位需要认真研究的派遣分发函数。这些派遣函数的第一个参数大多是DriverObject或者DeviceObject。
  加载PDB后发现sub401bc0是__CheckForDebuggerJustMyCode。
在这里插入图片描述
DriverObject结构体是非常重要的,这里要正反向看下。
源码中MajorFunction的元素个数是0x1B,0x1B+1=0x1Ch,也就是图中的十进制28。占用28x4=112。即是HEX值70h,所以DriverObject的大小是A8h大小。
在这里插入图片描述

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;

在这里插入图片描述
  IDA中只有Windows Driver Kit 7/10 32 bit的签名,部分Wdm.h中定义的函数没有被识别出来,造成了逆向的困难。
在这里插入图片描述
  源代码中的ReadCompleteRoutine函数,想破解该函数猜出a2是PIRP结构是关键。逆向的关键就是猜对结构体。第一要有开发经验,这样可以更好地摸索上下文的意图。第二,要熟悉写代码时的一些常用的系统结构体。
在这里插入图片描述
  IRP+0X18h是IoStatus。IPR+0xCh是一个指针AssociatedIrp。源代码中是指向SystemBuffer的指针。

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

  偏移0x1Ch的地方并没有猜到,看下_IO_STATUS_BLOCK结构。

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

    ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

  偏移0x1Ch地址处就是0X18h再偏移0x4h地址处,可以看出是IO_STATUS_BLOCK结构。说白了只要猜对结构体,或者猜对了结构体中那一项数据的含义,代码的意图就能正确被看出来。
  最后附属下sub_401AB0函数即ReadCompleteRoutine函数的源代码:

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;
}

猜你喜欢

转载自blog.csdn.net/qq_43312649/article/details/109222730