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

it2026-01-18  5

  闲来无事,逆向一下自己写的驱动,加深下认识。   这里把逆向的驱动分为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; }
最新回复(0)