驱动代码的基本逆向分析
示例源码:
//_stdcall #include<ntddk.h> #include<ntstrsafe.h> #pragma code_seg("INT") #define SynLinkName L"\??\freesec_tx" #define DeviceName L"\device\device_tx" UNICODE_STRING uDeviceName; UNICODE_STRING uSynLinkName; DEVICE_OBJECT txDeviceObject; PDEVICE_OBJECT ptxDeviceObject; //格式固定,除了第2个参数和第3个参数. 协商的func_code和I/O模式,这里使用缓冲I/O //用户侧也要定义一模一样的 #define add_func CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) #define sub_func CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) NTSTATUS MyDisPatcher(PDEVICE_OBJECT pDeivceObject,PIRP pIrp) { /* 分发函数参数格式固定:本驱动对象关联的设备对象和I/O管理器传递过来的irp对象地址 本分发函数基于缓冲模式与用户层进行通信. 用户层与驱动层协商好的函数名与数字的映射.以此调用相应函数,实现相应功能 */ NTSTATUS status; ULONG inputBufferSize; ULONG outputBufferSize; ULONG funcCode; //用于获取调用的是哪个函数 int* inputBuffer; int* outputBuffer; //该函数就是是求pIrp->Tail.Overlay.CurrentStackLocation的宏 //获取当前栈单元 PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); if (pDeivceObject != ptxDeviceObject) { status = STATUS_UNSUCCESSFUL; return status; } switch (pIrpStack->MajorFunction) { case IRP_MJ_DEVICE_CONTROL: //用户层与驱动之间传递消息的缓冲区大小 inputBufferSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; //获取用户层发送的ctl_code宏,也就是用户想让驱动干什么, //写代码时已经和用户层协商好用缓冲I/O模式.这里是缓冲I/O funcCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; //这里是用户层想让驱动干什么事的一些参数,如让驱动进行加法运算,则将2个int型数据 //给了这个pIrp->AssociatedIrp.SystemBuffer中,因为知道用户层的代码是传递int,所以 //定义了int*型指针来访问该缓冲区 inputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer; //当驱动处理完用户层交给驱动的任务,需要将结果和一些信息返回给用户层,则 //通过pIrp->AssociatedIrp.SystemBuffer这个参数,因为是进行int加法运算,所以 //将结果保存到这个输出缓冲即可 outputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer; switch (funcCode) { //对用户层发来的功能请求进行相应处理并将执行结果写到输出缓冲中,这样用户层就能收到结果 case add_func: outputBuffer[0] = inputBuffer[0] + inputBuffer[1]; break; case sub_func: outputBuffer[0] = inputBuffer[0] - inputBuffer[1]; break; default: break; } break; default: //其他消息与本设备无关 break; } //处理完irp后,需要进行一些处理,通知I/O管理器处理完成该irp并返回一些信息 pIrp->IoStatus.Information = 4; pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(pIrp, IO_NO_INCREMENT); status = STATUS_SUCCESS; return status; } NTSTATUS MyCreateDevice(PDRIVER_OBJECT pDriverObject) { /* 创建设备函数,先创建设备,失败了打印失败信息并将状态码返回否则提示成功并继续 创建符号链接.失败了就打印失败并将状态码返回否则提示成功并将状态码返回 */ NTSTATUS status; status = IoCreateDevice(pDriverObject, 0, &uDeviceName, FILE_DEVICE_UNKNOWN, 0, 1, &ptxDeviceObject); if (NT_SUCCESS(status)) { DbgPrint("设备创建成功! "); } else { DbgPrint("设备创建失败! "); return status; } status = IoCreateSymbolicLink(&uSynLinkName, &uDeviceName); if (NT_SUCCESS(status)) { DbgPrint("符号链接创建成功! "); } else { DbgPrint("符号链接创建失败! "); return status; } return status; } VOID UnLoadDriver(PDRIVER_OBJECT pDriverObject) { IoDeleteSymbolicLink(&uSynLinkName); IoDeleteDevice(pDriverObject->DeviceObject); DbgPrint("符号链接删除成功! "); DbgPrint("设备删除成功! "); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING us) { NTSTATUS status; ptxDeviceObject = &txDeviceObject; //初始化设备名和符号链接 RtlInitUnicodeString(&uDeviceName, DeviceName); RtlInitUnicodeString(&uSynLinkName, SynLinkName); status = MyCreateDevice(pDriverObject); if (!NT_SUCCESS(status)) { return status; } pDriverObject->DriverUnload = UnLoadDriver; //配置分发函数 pDriverObject->MajorFunction[IRP_MJ_CREATE] = MyDisPatcher; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = MyDisPatcher; pDriverObject->MajorFunction[IRP_MJ_READ] = MyDisPatcher; pDriverObject->MajorFunction[IRP_MJ_WRITE] = MyDisPatcher; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDisPatcher; return status; }
编译后将程序加入到ida.
先看看入口函数
INT:00010648 ; int __stdcall DriverEntry(_DRIVER_OBJECT *pDriverObject, _UNICODE_STRING *us) INT:00010648 _DriverEntry@8 proc near ; CODE XREF: GsDriverEntry(x,x)+42j INT:00010648 INT:00010648 pDriverObject = dword ptr 8 INT:00010648 us = dword ptr 0Ch INT:00010648 INT:00010648 mov edi, edi INT:0001064A push ebp INT:0001064B mov ebp, esp INT:0001064D push esi INT:0001064E mov esi, ds:__imp__RtlInitUnicodeString@8 ; RtlInitUnicodeString(x,x) INT:00010654 push offset SourceString ; "\device\device_tx" 设备名 INT:00010659 push offset _uDeviceName ; DestinationString 设备名字符串对象 INT:0001065E mov _ptxDeviceObject, offset _txDeviceObject INT:00010668 call esi ; RtlInitUnicodeString(x,x) ; RtlInitUnicodeString(x,x) 设置字符串 INT:0001066A push offset a??Freesec_tx ; "\??\freesec_tx" 符号链接名 INT:0001066F push offset _uSynLinkName ; DestinationString 符号链接字符串对象 INT:00010674 call esi ; RtlInitUnicodeString(x,x) ; RtlInitUnicodeString(x,x)设置字符串 INT:00010676 mov esi, [ebp+pDriverObject] ;驱动对象指针保存在esi中, 注意一下 INT:00010679 push esi ; pDriverObject INT:0001067A call _MyCreateDevice@4 ; MyCreateDevice(x) 创建设备对象 INT:0001067F test eax, eax INT:00010681 jl short loc_1069E ;创建失败则退出 INT:00010683 mov ecx, offset _MyDisPatcher@8 ; MyDisPatcher(x,x) 将分发例程地址保存到ecx INT:00010688 mov dword ptr [esi+34h], offset _UnLoadDriver@4 ; UnLoadDriver(x) INT:0001068F mov [esi+38h], ecx ;下面将分发函数赋值给驱动对象的相应位置 INT:00010692 mov [esi+40h], ecx INT:00010695 mov [esi+44h], ecx INT:00010698 mov [esi+48h], ecx INT:0001069B mov [esi+70h], ecx INT:0001069E INT:0001069E loc_1069E: ; CODE XREF: DriverEntry(x,x)+39j INT:0001069E pop esi INT:0001069F pop ebp INT:000106A0 retn 8 INT:000106A0 _DriverEntry@8 endp
分发例程:
INT:00010486 ; int __stdcall MyDisPatcher(_DEVICE_OBJECT *pDeivceObject, _IRP *pIrp) INT:00010486 _MyDisPatcher@8 proc near ; DATA XREF: DriverEntry(x,x)+3Bo INT:00010486 INT:00010486 pDeivceObject = dword ptr 8 INT:00010486 pIrp = dword ptr 0Ch INT:00010486 INT:00010486 mov edi, edi INT:00010488 push ebp INT:00010489 mov ebp, esp INT:0001048B mov eax, [ebp+pDeivceObject] ;保存驱动对象参数于eax
INT:0001048E cmp eax, _ptxDeviceObject ;判断是否是本驱动对象,如果不是就不处理 INT:00010494 mov ecx, [ebp+pIrp] ; Irp ;保存irp参数于ecx INT:00010497 mov edx, [ecx+60h] ;获取irp 的i/o栈 指针 INT:0001049A jz short loc_104A3 INT:0001049C mov eax, 0C0000001h ;eax是status, 即失败的STATUS_UNSUCCESSFUL INT:000104A1 jmp short loc_104DE INT:000104A3 ; --------------------------------------------------------------------------- INT:000104A3 INT:000104A3 loc_104A3: ; CODE XREF: MyDisPatcher(x,x)+14j INT:000104A3 cmp byte ptr [edx], 0Eh ; switch 的处理 INT:000104A6 jnz short loc_104C9 ; 跳到default INT:000104A8 mov edx, [edx+0Ch] INT:000104AB sub edx, 222000h INT:000104B1 mov eax, [ecx+0Ch] ;取得(int*)pIrp->AssociatedIrp.SystemBuffer 用户发来的缓冲区地址 INT:000104B4 jz short loc_104C2 ;add_func 操作 INT:000104B6 sub edx, 4 ; INT:000104B9 jnz short loc_104C9 ;不相等就default处理.否则减 sub_func 操作 INT:000104BB mov edx, [eax] INT:000104BD sub edx, [eax+4] INT:000104C0 jmp short loc_104C7 INT:000104C2 ; --------------------------------------------------------------------------- INT:000104C2 INT:000104C2 loc_104C2: ; CODE XREF: MyDisPatcher(x,x)+2Ej INT:000104C2 mov edx, [eax+4] INT:000104C5 add edx, [eax] INT:000104C7 INT:000104C7 loc_104C7: ; CODE XREF: MyDisPatcher(x,x)+3Aj INT:000104C7 mov [eax], edx ;将结果保存到systembuffer. INT:000104C9 INT:000104C9 loc_104C9: ; CODE XREF: MyDisPatcher(x,x)+20j INT:000104C9 ; MyDisPatcher(x,x)+33j INT:000104C9 and dword ptr [ecx+18h], 0 ;pIrp->IoStatus.Status = STATUS_SUCCESS; INT:000104CD xor dl, dl ; PriorityBoost INT:000104CF mov dword ptr [ecx+1Ch], 4 ;pIrp->IoStatus.Information INT:000104D6 call ds:__imp_@IofCompleteRequest@8 ; IofCompleteRequest(x,x) INT:000104DC xor eax, eax INT:000104DE INT:000104DE loc_104DE: ; CODE XREF: MyDisPatcher(x,x)+1Bj INT:000104DE pop ebp INT:000104DF retn 8
1.exploitme 的漏洞分析
将sys文件命名为a.sys. 通过driver monitor 加载后打开调试器附加上.
kd> g watchdog!WdUpdateRecoveryState: Recovery enabled. DriverEntry: Hello world driver demo!
然后在windbg break 后查看该驱动信息:
lkd> !drvobj a 2 Driver object (864eaca0) is for: DriverA DriverEntry: f7caf93e DriverStartIo: 00000000 DriverUnload: f7caf530 Dispatch routines: [00] IRP_MJ_CREATE f7caf410 +0xf7caf410 [01] IRP_MJ_CREATE_NAMED_PIPE f7caf410 +0xf7caf410 [02] IRP_MJ_CLOSE f7caf410 +0xf7caf410 [03] IRP_MJ_READ f7caf410 +0xf7caf410 [04] IRP_MJ_WRITE f7caf410 +0xf7caf410 [05] IRP_MJ_QUERY_INFORMATION f7caf410 +0xf7caf410 [06] IRP_MJ_SET_INFORMATION f7caf410 +0xf7caf410 [07] IRP_MJ_QUERY_EA f7caf410 +0xf7caf410 [08] IRP_MJ_SET_EA f7caf410 +0xf7caf410 [09] IRP_MJ_FLUSH_BUFFERS f7caf410 +0xf7caf410 [0a] IRP_MJ_QUERY_VOLUME_INFORMATION f7caf410 +0xf7caf410 [0b] IRP_MJ_SET_VOLUME_INFORMATION f7caf410 +0xf7caf410 [0c] IRP_MJ_DIRECTORY_CONTROL f7caf410 +0xf7caf410 [0d] IRP_MJ_FILE_SYSTEM_CONTROL f7caf410 +0xf7caf410 [0e] IRP_MJ_DEVICE_CONTROL f7caf410 +0xf7caf410 [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL f7caf410 +0xf7caf410 [10] IRP_MJ_SHUTDOWN f7caf410 +0xf7caf410 [11] IRP_MJ_LOCK_CONTROL f7caf410 +0xf7caf410 [12] IRP_MJ_CLEANUP f7caf410 +0xf7caf410 [13] IRP_MJ_CREATE_MAILSLOT f7caf410 +0xf7caf410 [14] IRP_MJ_QUERY_SECURITY f7caf410 +0xf7caf410 [15] IRP_MJ_SET_SECURITY f7caf410 +0xf7caf410 [16] IRP_MJ_POWER f7caf410 +0xf7caf410 [17] IRP_MJ_SYSTEM_CONTROL f7caf410 +0xf7caf410 [18] IRP_MJ_DEVICE_CHANGE f7caf410 +0xf7caf410 [19] IRP_MJ_QUERY_QUOTA f7caf410 +0xf7caf410 [1a] IRP_MJ_SET_QUOTA f7caf410 +0xf7caf410 [1b] IRP_MJ_PNP 804f454a nt!IopInvalidDeviceRequest
易知这些不同类型的irp对应相同的分发例程
最右边一侧是irp分发例程. 通过对其反汇编可以得到其中IO控制码
kd> uf 0xf7caf410 .......
f7caf41a e800000000 call f7caf41f f7caf41f 8b450c mov eax,dword ptr [ebp+0Ch] ;eax=irp f7caf422 50 push eax f7caf423 e8a8000000 call f7caf4d0 f7caf428 8945f8 mov dword ptr [ebp-8],eax ;[ebp-8]=irp f7caf42b 8b4df8 mov ecx,dword ptr [ebp-8] f7caf42e 8b5110 mov edx,dword ptr [ecx+10h] f7caf431 8955ec mov dword ptr [ebp-14h],edx f7caf434 8b450c mov eax,dword ptr [ebp+0Ch] f7caf437 8b483c mov ecx,dword ptr [eax+3Ch] f7caf43a 894df4 mov dword ptr [ebp-0Ch],ecx f7caf43d 8b55f8 mov edx,dword ptr [ebp-8] f7caf440 8b4208 mov eax,dword ptr [edx+8] f7caf443 8945fc mov dword ptr [ebp-4],eax f7caf446 8b4df8 mov ecx,dword ptr [ebp-8] f7caf449 8b5104 mov edx,dword ptr [ecx+4] f7caf44c 8955e4 mov dword ptr [ebp-1Ch],edx f7caf44f 8b45f8 mov eax,dword ptr [ebp-8] ;eax=irp f7caf452 8b480c mov ecx,dword ptr [eax+0Ch];ecx指向i/o控制码 f7caf455 894df0 mov dword ptr [ebp-10h],ecx; 给了[ebp-10h] f7caf458 8b550c mov edx,dword ptr [ebp+0Ch] f7caf45b 83c218 add edx,18h f7caf45e 8955e0 mov dword ptr [ebp-20h],edx f7caf461 8b45e0 mov eax,dword ptr [ebp-20h] f7caf464 c70000000000 mov dword ptr [eax],0 f7caf46a 8b4de0 mov ecx,dword ptr [ebp-20h] f7caf46d c7410400000000 mov dword ptr [ecx+4],0 f7caf474 8b55f0 mov edx,dword ptr [ebp-10h];给了edx f7caf477 8955dc mov dword ptr [ebp-24h],edx f7caf47a 817ddc03a08888 cmp dword ptr [ebp-24h],8888A003h ;io控制码 f7caf481 7402 je f7caf485 ;io控制码的处理例程 f7caf483 eb20 jmp f7caf4a5 f7caf485 837dfc04 cmp dword ptr [ebp-4],4 f7caf489 721a jb f7caf4a5 f7caf48b 837de404 cmp dword ptr [ebp-1Ch],4 f7caf48f 7214 jb f7caf4a5 f7caf491 8b45f4 mov eax,dword ptr [ebp-0Ch] f7caf494 8b4dec mov ecx,dword ptr [ebp-14h] f7caf497 8b11 mov edx,dword ptr [ecx] f7caf499 8910 mov dword ptr [eax],edx f7caf49b 8b45e0 mov eax,dword ptr [ebp-20h] f7caf49e c7400404000000 mov dword ptr [eax+4],4 f7caf4a5 8b4de0 mov ecx,dword ptr [ebp-20h] f7caf4a8 8b55e8 mov edx,dword ptr [ebp-18h] f7caf4ab 8911 mov dword ptr [ecx],edx f7caf4ad 32d2 xor dl,dl f7caf4af 8b4d0c mov ecx,dword ptr [ebp+0Ch] f7caf4b2 ff1580f7caf7 call dword ptr ds:[0F7CAF780h] f7caf4b8 8b45e8 mov eax,dword ptr [ebp-18h] f7caf4bb 8be5 mov esp,ebp f7caf4bd 5d pop ebp f7caf4be c20800 ret 8
他的分发函数开始就有很多mov指令, 应该是编译时禁用了优化. 变量赋值操作优化后就少很多代码
从上面一路分析下来,发现io控制码和相应的处理例程
未完待续.........