百度空间 | 百度首页 
 
查看文章
 
[note]360SuperKill学习之--恢复FSD的IRP处理函数
2007年12月18日 星期二 11:44

360SuperKill还是很值得偶等小菜学习的啊。其中的一个便是恢复被cnnic挟持FSD的IRP分发例程:IRP_MJ_CREATE(0x00)、IRP_MJ_SET_INFORMATION(0x06)

思路大致是这样的:
1.> 为ZwQuerySystemInformation传递11号参数.获得SYSTEM_MODULE_INFORMATION基址.然后遍历链表,找到与模块fastfat.sys、ntfs.sys相匹配的,返回其基址ModuleBase[为定位PE在内存中的真实偏移做准备]

2.> 通过指定的路径打开这2个驱动文件     
L"\\SystemRoot\\system32\\drivers\\ntfs.sys"
L"\\SystemRoot\\system32\\drivers\\fastfat.sys"
根据PE结构读取其中一些变量的偏移值-->AddressOfEntryPoint、SizeOfImage、ImageBase
然后算出映射到内存中的文件的实际需要的空间-->NeedSize = SizeOfImage - AddressOfEntryPoint;
为其分配一个非分页内存,将文件的内容都读取到这里

3.> 在这块内存中逐个查找字符串.直到找到原分发例程的函数地址.


        // 对于fastfat.sys和ntfs.sys都是
        // mov dword ptr [esi+IrpIndex], local_XXXX
        // 不晓得MJ这里的0x46或0x43是哪个了。。。
       //
        if (FileContent[i] == 0xC7 &&
                (FileContent[i+1] == 0x46 || FileContent[i+1] == 0x43) &&
                FileContent[i+2] == IrpIndex &&
                FileContent[i+7] == 0xC7)
        {
            // 找到了,这才是原IRP分发例程的原地址
            OldIrpDispatchRoutine = *(PULONG)&FileContent[i+3] ;

i+1到i+7之间的便是原函数地址.此时注意-->
// 因为可能重定位了。所以用实际的基址-程序首选的RVA地址。得到的一个偏移值
        Delta = ModuleBase - ImageBase;

// 加上此偏移值才是真正的内存地址
        OldIrpDispatchRoutine += Delta;
然后除去写保护,恢复之.

真正读懂了还蛮简单的,原来cnnic用的伎俩也没多少嘛。

------------------------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------
// 函数名: RestoreFSDMajorRoutine
//
// 参数:
//      DriverName - 文件系统驱动名
//      FileName - 文件系统驱动文件名
//      DriverPath - 文件系统驱动全路径
//      MajorFunctionIndex - IRP处理函数索引
//
// 返回值:
//       STATUS_SUCCESS - 成功
//       STATUS_NOT_FOUND - 失败,没有找到指定的驱动
//       STATUS_UNSUCCESSFUL - 失败
//
// 说明: 恢复FSD的IRP处理函数
//
NTSTATUS
RestoreFSDMajorRoutine(
    PCWSTR DriverName,
    PUCHAR FileName,
    PCWSTR DriverPath,
    ULONG MajorFunctionIndex
    )
{
    NTSTATUS            ntStatus = STATUS_SUCCESS;
    ULONG               ModuleBase = 0;
    UNICODE_STRING      uniDriverName;
    UNICODE_STRING      uniDriverPath;
    PDRIVER_OBJECT      DriverObject;
    ULONG               CurIrpDispatchRoutine;
    ULONG               OldIrpDispatchRoutine;
    ULONG               IrpIndex;

    OBJECT_ATTRIBUTES   objectAttributes;
    IO_STATUS_BLOCK     ioStatus;
    HANDLE              ntFileHandle;
    LARGE_INTEGER       byteOffset;

    ULONG               NtHeadersOffset;
    ULONG               AddressOfEntryPoint;
    ULONG               SizeOfImage;
    ULONG               ImageBase;
    ULONG               NeedSize;
    ULONG               Delta;
    PUCHAR              FileContent;

    ULONG               i;
    KIRQL               OldIrql;

// 得到驱动文件在内存中的基地址
    ModuleBase = GetModuleBase(FileName);

    if (ModuleBase == 0)
    {
        ntStatus = STATUS_NOT_FOUND;
        goto End;
    }

// 通过驱动文件名获得DriverObject
    RtlInitUnicodeString(&uniDriverName, DriverName);
    ntStatus = ObReferenceObjectByName(&uniDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        0,
        IoDriverObjectType,
        KernelMode,
        NULL,
        &DriverObject);
   
    if (!NT_SUCCESS(ntStatus))
        goto End;

// 当前的IRP分发历程
    CurIrpDispatchRoutine = (ULONG)DriverObject->MajorFunction[MajorFunctionIndex];
    IrpIndex = MajorFunctionIndex;
    IrpIndex = (IrpIndex + 0xE) << 2;
// 索引+14,然后左移2位。[shl,即*4]
   
// 设置属性后,打开指定位置的驱动文件
    RtlInitUnicodeString(&uniDriverPath, DriverPath);
    InitializeObjectAttributes(&objectAttributes, &uniDriverPath,
        OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);

    ntStatus = IoCreateFile(&ntFileHandle,
        FILE_READ_ATTRIBUTES,
        &objectAttributes,
        &ioStatus,
        0,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OPEN,
        0,
        NULL,
        0,
        0,
        NULL,
        IO_NO_PARAMETER_CHECKING);

    if (!NT_SUCCESS(ntStatus))
    {
        ntStatus = STATUS_UNSUCCESSFUL;
        ObDereferenceObject(DriverObject);
        goto End;
    }

// IMAGE_DOS_HEADER
// +0x3c e_lfanew
    byteOffset.LowPart = 0x3C;
    byteOffset.HighPart = 0;

// 打开驱动文件后,读之
    ntStatus = ZwReadFile(ntFileHandle,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        &NtHeadersOffset,
        4,
        &byteOffset,
        NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        goto End;
    }
   
// NtHeadersOffset中保存的是PE头的偏移地址
// IMAGE_OPTIONAL_HEADER
// +0x010 AddressOfEntryPoint
// 指向程序入口RVA地址
    byteOffset.LowPart = NtHeadersOffset + 0x28;
    byteOffset.HighPart = 0;

    ntStatus = ZwReadFile(ntFileHandle,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        &AddressOfEntryPoint,
        4,
        &byteOffset,
        NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        goto End;
    }

// IMAGE_OPTIONAL_HEADER
    // +0x038 SizeOfImage
// 内存中整个PE个映像尺寸
    byteOffset.LowPart = NtHeadersOffset + 0x50;
    byteOffset.HighPart = 0;

    ntStatus = ZwReadFile(ntFileHandle,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        &SizeOfImage,
        4,
        &byteOffset,
        NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        goto End;
    }
   
// IMAGE_OPTIONAL_HEADER
// +0x01c ImageBase
// 载入程序首选的RVA
    byteOffset.LowPart = NtHeadersOffset + 0x34;
    byteOffset.HighPart = 0;

    ntStatus = ZwReadFile(ntFileHandle,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        &ImageBase,
        4,
        &byteOffset,
        NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        goto End;
    }

// 经过一系列的读PE后,得到一些偏移值。计算PE在内存中需要的空间。
// 为其分配一个非分页内存
// 将文件的内容都读取到这里
    NeedSize = SizeOfImage - AddressOfEntryPoint;

    FileContent = ExAllocatePool(NonPagedPool, NeedSize);

    if (FileContent == NULL)
    {
        ntStatus = STATUS_UNSUCCESSFUL;
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        goto End;
    }

    byteOffset.LowPart = AddressOfEntryPoint;
    byteOffset.HighPart = 0;

    ntStatus = ZwReadFile(ntFileHandle,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        FileContent,
        NeedSize,
        &byteOffset,
        NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        ExFreePool(FileContent);
        goto End;
    }

    OldIrpDispatchRoutine = 0;
    if (NeedSize <= 0)
    {
        ntStatus = STATUS_NOT_FOUND;
        ObDereferenceObject(DriverObject);
        ZwClose(ntFileHandle);
        ExFreePool(FileContent);
        goto End;
    }

    // 这样的找法比较容易崩溃。。。汗。。。
    for (i = 0; i < NeedSize; ++i)         
    {
   // 对于fastfat.sys和ntfs.sys都是
   // mov dword ptr [esi+IrpIndex], local_XXXX
   // 不晓得MJ这里的0x46或0x43是哪个了。。。
   //
        if (FileContent[i] == 0xC7 &&
                (FileContent[i+1] == 0x46 || FileContent[i+1] == 0x43) &&
                FileContent[i+2] == IrpIndex &&
                FileContent[i+7] == 0xC7)
        {
            // 找到了,这才是原IRP分发例程的原地址
            OldIrpDispatchRoutine = *(PULONG)&FileContent[i+3] ;

            if (OldIrpDispatchRoutine == 0)
                goto NotFound;

            if (OldIrpDispatchRoutine > SizeOfImage)
                goto NotFound;
           
    // 因为可能重定位了。所以用实际的基址-程序首选的RVA地址。得到的是一个偏移值
            Delta = ModuleBase - ImageBase;

    // 加上此偏移值才是真正的内存地址
            OldIrpDispatchRoutine += Delta;

            if (!MmIsAddressValid((PVOID)OldIrpDispatchRoutine))
                goto NotFound;

            /* ++ RestoreFSDHook --*/
            if (OldIrpDispatchRoutine == CurIrpDispatchRoutine)
                goto NotFound;

            KeAcquireSpinLock(&spinLock, &OldIrql);
            SKillDisableWriteProtect();
            DriverObject->MajorFunction[MajorFunctionIndex] = (PDRIVER_DISPATCH)OldIrpDispatchRoutine;
            SkillEnableWriteProtect();
            KeReleaseSpinLock(&spinLock, OldIrql);

            ExFreePool(FileContent);
            ZwClose(ntFileHandle);
            ObDereferenceObject(DriverObject);

            goto End;

            /* ++ RestoreFSDHook --*/
        }
    }

NotFound:
    ntStatus = STATUS_NOT_FOUND;
    ObDereferenceObject(DriverObject);
    ZwClose(ntFileHandle);
    ExFreePool(FileContent);

End:
    return ntStatus;
}


类别:Note | 浏览() | 评论 (6)
 
最近读者:
 
网友评论:
1
2007年12月18日 星期二 14:27
在这里不能理解为乘呵呵
 
3
2007年12月18日 星期二 14:51
OldIrpDispatchRoutine = FileContent[i+6]; ··· OldIrpDispatchRoutine += FileContent[i+3]; 这一堆其实就等同于OldIrpDispatchRoutine = *(PULONG)&FileContent[i+3] ;
 
4
2007年12月18日 星期二 14:54
严重同意~~~
 
5
2007年12月19日 星期三 15:37
学习!!!!
 
6
2008年02月28日 星期四 03:16
熬夜学习了,楼上说的还是不甚明白呀!
 
7
2008年02月28日 星期四 07:21
精神可嘉啊~~
 
本篇日志被作者设置为禁止发表新评论

     

©2009 Baidu