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