Windows内核编程实现拦截Xuetr程序
----TTL
寒假的时候,开始学习windows内核编程,想走近windows的内部世界。由于微软对于windows并不开源,所以有些人开始质疑:学习windows内核有什么用?这不就是在瞎耽误工夫吗。当初被这个问题困扰的蹑手蹑脚,不敢前进。但是在一些学长的解答下,我还是勇敢的走进了这趟浑水,按照西电信息安全协会的TimeLine的计划表,丰富自己的学习方向,慢慢的,我对这个问题有了自己的想法。
Windows不开源这并不假。微软公司在一开始并没有打算像现在一样公开一些内核服务,但是随着技术的不断发展,用户的需求不断增加。微软已经看到,要想在windows平台上大放光彩,不得不公开一些内核细节,包括一些安全软件,在系统底层是必须占有一席之地的。由此以来,传统的病毒对抗安全软件也走进了内核,这种对抗更加高级,更加先进,
随之也产生了Rootkit技术。可见windows内核对于学习安全至关重要,对于理解操作系统,更加必不可少。
大家比较熟悉的是win32平台上的编程,也就是SDK编程。MS提供了丰富的接口供自己的用户使用,但是,这仅仅是表面现象,并不是windows的天然代码,(当然内核服务也不是)。用户层的每一个API都对应用户层的一个dll文件,也就是实现部分(常见的三大DLL user32.dll,gdi.dll,kernel.dll)这些文件在用户层又走进了ntdll.dll文件。这所有的函数在内核中被导出。
MS公开的这些系统服务可以在WDK的文档中查到,当然也可以在MSDN中找到,但是微软还有一部分的系统服务并没有公开,但是在调试系统时,这些系统服务的符号表是存在的,也就是说,这些函数微软已经导出,只是没有对这个函数的声明以及文档。所以可以利用一些微软没有公开的系统服务来完成一些我们的需求。
比如ObReferenceObjectByName(),这个系统服务,用来通过名字来获取驱动对象。不过需要自己声明。
NTKERNELAPI
NTSTATUS
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *Object
);
代码很简单,不过能说明如何拦截系统内核和上层应用程序通信的。.
NTSTATUS FilterDriverQuery()
{
NTSTATUS Status;
UNICODE_STRING usObjectName;
RtlInitUnicodeString(&usObjectName,L"\\Driver\\Xuetr-038");
Status = ObReferenceObjectByName(
&usObjectName,
OBJ_CASE_INSENSITIVE,
NULL,
0,
*IoDriverObjectType,
KernelMode,
NULL,
(PVOID*)&g_FilterDriverObject
);
if (!NT_SUCCESS(Status))
{
KdPrint(("failed!"));
return Status;
}
KdPrint(("0x%X",g_FilterDriverObject));
gfn_OrigReadCompleteRoutine=g_FilterDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL];
g_FilterDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=FilterReadCompleteRoutine;
ObDereferenceObject(g_FilterDriverObject);
return STATUS_SUCCESS;
}
自己写了一个函数FilterDriverQuery(),这个函数首先获取了一个名字为"\\Driver\\Xuetr-038"的驱动对象g_FilterDriverObject,然后将该驱动对象的MajorFunction[IRP_MJ_DEVICE_CONTROL]赋值给一个全局变量gfn_OrigReadCompleteRoutine先保存起来,方便我们恢复的时候再用。然后把获得的驱动对象的MajorFunction[IRP_MJ_DEVICE_CONTROL]的函数地址赋值成我自己构造的一个函数FilterReadCompleteRoutine,接着调用ObDereferenceObject(),将我们的FilterDriverObject引用数减一归零,实际上凡是调用ObReference类型的函数,都要调用ObDereferenceObject将其引用数归零,否则在内存中无法释放。
接下来,就是我自己写的过滤函数NTSTATUS FilterReadCompleteRoutine(
__in struct _DEVICE_OBJECT *DeviceObject,
__inout struct _IRP *Irp
)
{
KdPrint(("IRP_MJ_QUERY_SECURITY comming."));
return gfn_OrigReadCompleteRoutine(DeviceObject,Irp);
}
自己写的过滤函数一定要满足驱动对象MajorFunction的类型,包括参数的接口,否则我们无法进行过滤拦截,以上这个函数我什么实际操作也没做,知识打印出一条显示信息,证明我拦截成功。然后便返回这个全局变量(即原有的IRP派遣函数)。
当卸载的时候,我要将驱动对象原来的派遣函数的真是地址恢复回去,原有的地址保存在全局变量中,所以在我的卸载函数里,我只写了一个函数,用来对原有驱动对象进行恢复。
VOID UnFilterDriverRoutine()
{
if (MmIsAddressValid(g_FilterDriverObject))
{
g_FilterDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = gfn_OrigReadCompleteRoutine;
}
首先判断g_FilterDriverObject驱动对象是不是合法,如果合法,便对其恢复。
可见,dicen