2014年9月11日星期四

内核中的APC

/*
文件名:KeInsertQueueApc.c

应用层有相应的公开的APC函数,如:QueueUserAPC。
但是内核中却没有公开的函数,在微软的网站上搜索下,只搜索到一篇文章:
http://www.microsoft.com/msj/0799/nerd/nerd0799.aspx
很早的了,上个世纪的。而且还有隐含的声明。
APC作为一个操作系统的基本的机制,岂能没有?除非你把应用层的也删除了等原因。
其实这些APC函数在内核中已经导出了,且在很早的版本都有了。
所以可以放心使用这些函数。
内核驱动开发的功能和性能在于开发人员的认识和胆量。
胆量从何而来,在于认识的熟练程度。

顺便说一下:
内核的DPC函数倒是公开了,如:
KeInitializeDpc
KeInsertQueueDpc
KeRemoveQueueDpc

本文的一些数据来自WRK和上面的链接。

本文只是一个简单的示例:
在内核中调用应用程序事先准备好的函数。
更多的功能请看网上:如杀进程,注入DLL等。

made by correy
made at 2014.09.11
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/

#include "ntddk.h"

#define FILE_DEVICE_UNKNOWN             0x00000022
#define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

typedef
VOID
(*PKNORMAL_ROUTINE) (
    IN PVOID NormalContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

typedef
VOID
(*PKKERNEL_ROUTINE) (
    IN struct _KAPC *Apc,
    IN OUT PKNORMAL_ROUTINE *NormalRoutine,
    IN OUT PVOID *NormalContext,
    IN OUT PVOID *SystemArgument1,
    IN OUT PVOID *SystemArgument2
    );

typedef
VOID
(*PKRUNDOWN_ROUTINE) (
    IN struct _KAPC *Apc
    );

typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
} KAPC_ENVIRONMENT;

NTKERNELAPI
VOID
KeInitializeApc (
    __out PRKAPC Apc,
    __in PRKTHREAD Thread,
    __in KAPC_ENVIRONMENT Environment,
    __in PKKERNEL_ROUTINE KernelRoutine,
    __in_opt PKRUNDOWN_ROUTINE RundownRoutine,
    __in_opt PKNORMAL_ROUTINE NormalRoutine,
    __in_opt KPROCESSOR_MODE ProcessorMode,
    __in_opt PVOID NormalContext
    );

PLIST_ENTRY
KeFlushQueueApc (
    __inout PKTHREAD Thread,
    __in KPROCESSOR_MODE ProcessorMode
    );

NTKERNELAPI
BOOLEAN
KeInsertQueueApc (
    __inout PRKAPC Apc,
    __in_opt PVOID SystemArgument1,
    __in_opt PVOID SystemArgument2,
    __in KPRIORITY Increment
    );

BOOLEAN
KeRemoveQueueApc (
    __inout PKAPC Apc
    );

void     KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS        ntStatus;
    UNICODE_STRING  uszDriverString;
    UNICODE_STRING  uszDeviceString;
    UNICODE_STRING  uszNotifyEventString;

    PDEVICE_OBJECT  pDeviceObject;
         
    __debugbreak();
   
    RtlInitUnicodeString(&uszDriverString, L"\\Device\\test");// Point uszDriverString at the driver name  
    ntStatus = IoCreateDevice(DriverObject, 0, &uszDriverString, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);// Create and initialize device object
    if(ntStatus != STATUS_SUCCESS)
        return ntStatus;  
 
    RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\test");// Point uszDeviceString at the device name  
    ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);// Create symbolic link to the user-visible name
    if(ntStatus != STATUS_SUCCESS)
    {      
        IoDeleteDevice(pDeviceObject);// Delete device object if not successful
        return ntStatus;
    }

    // Load structure to point to IRP handlers...
    DriverObject->DriverUnload                         = KnotifyUnloadDriver;
    DriverObject->MajorFunction[IRP_MJ_CREATE]         = KnotifyDispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = KnotifyDispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KnotifyDispatchIoctl;
     
    return ntStatus;// Return success
}


NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information=0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return(STATUS_SUCCESS);
}


void KMApcCallback(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2)
    /*
    0: kd> k
    ChildEBP RetAddr
    b1a6fcfc 8050099b KNotify!KMApcCallback [d:\temp\sources\msj\column\0799\kernel\knotify.c @ 164]
    b1a6fd4c 80542853 nt!KiDeliverApc+0x1af
    b1a6fd4c 7c92e514 nt!KiServiceExit+0x59
    0012fef8 7c92d21a ntdll!KiFastSystemCallRet
    0012fefc 7c8023f1 ntdll!ZwDelayExecution+0xc
    0012ff54 0040110b kernel32!SleepEx+0x61
    0012ff78 00401228 ConsoleApplication1!main+0xab [e:\code\consoleapplication1\consoleapplication1.cpp @ 51]
    0012ffc0 7c816037 ConsoleApplication1!__tmainCRTStartup+0xfe [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 241]
    0012fff0 00000000 kernel32!BaseProcessStart+0x23
    */
{
    ExFreePool(Apc);
    return;
}


NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    NTSTATUS           ntStatus;
    PIO_STACK_LOCATION irpStack  = IoGetCurrentIrpStackLocation(Irp);

    switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
    {
        case IOCTL_NOTIFY_REGISTER_APC:
        {
            PKAPC Apc;
            ULONG *UserRoutine = (ULONG *)Irp->AssociatedIrp.SystemBuffer;

            Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
            KeInitializeApc(Apc,
                            KeGetCurrentThread(),
                            OriginalApcEnvironment,
                            (PKKERNEL_ROUTINE)&KMApcCallback, // kernel-mode routine
                            0,                                // rundown routine
                            (PKNORMAL_ROUTINE)*UserRoutine,   // user-mode routine
                            UserMode, (PVOID)(ULONG)1);
            KeInsertQueueApc(Apc, (PVOID)(ULONG)2, (PVOID)(ULONG)3, 0);
            ntStatus = STATUS_SUCCESS;
            break;
        }    
        default:
            break;
    }

    Irp->IoStatus.Status = ntStatus;
   
    // Set # of bytes to copy back to user-mode...
    if(ntStatus == STATUS_SUCCESS)
        Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    else
        Irp->IoStatus.Information = 0;

    if(ntStatus != STATUS_PENDING)
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return ntStatus;
}


void KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING  uszDeviceString;

    IoDeleteDevice(DriverObject->DeviceObject);

    RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\test");
    IoDeleteSymbolicLink(&uszDeviceString);
}
----------------------------------------------------------------------------------------------------------------------------------------------
#include "stdafx.h"

#include <Windows.h>

#define FILE_DEVICE_UNKNOWN             0x00000022
#define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)


void ApcCallback(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2)
    /*
    0: kd> k
    ChildEBP RetAddr
    0012fc1c 7c92e457 ConsoleApplication1!ApcCallback [e:\code\consoleapplication1\consoleapplication1.cpp @ 20]
    0012ff54 0040110b ntdll!KiUserApcDispatcher+0x7
    0012ff78 00401228 ConsoleApplication1!main+0xab [e:\code\consoleapplication1\consoleapplication1.cpp @ 51]
    0012ffc0 7c816037 ConsoleApplication1!__tmainCRTStartup+0xfe [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 241]
    0012fff0 00000000 kernel32!BaseProcessStart+0x23
    */
{
    wchar_t Message[80];
    wsprintf(Message,L"APC Callback!\n P1 == %lx\n P2 == %lx\n P3 == %lx", NormalContext,  SystemArgument1, SystemArgument2);
    MessageBox(NULL, Message, L"Unotify", MB_OK);
}


 int __cdecl main ()
{
    __debugbreak();

    HANDLE hDevice = CreateFile(L"\\\\.\\test", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if(hDevice == INVALID_HANDLE_VALUE)
    {
        MessageBox(0,L"请加载驱动!",0,0);
        return 0;
    }

    DWORD dwBytesReturned;
    DWORD pfnApc = (DWORD)&ApcCallback;    
    BOOL bReturnCode = DeviceIoControl(hDevice, IOCTL_NOTIFY_REGISTER_APC, &pfnApc, sizeof(DWORD), 0, 0, &dwBytesReturned, 0);
    if(bReturnCode == FALSE)
    {
        MessageBox(0,L"DeviceIoControl失败!",0,0);
        return 0;
    }
   
    MessageBox(NULL,L"APC Dispatched!",L"Unotify",MB_OK);//先显示这个,再显示ApcCallback的消息框。不过这也可以想办法改变的。
       
    SleepEx(INFINITE, TRUE);//这一行是所有实现的关键。

    return 0;
}

2014年9月4日星期四

驱动创建和访问用户堆

#include <ntifs.h>
#include <windef.h>


/*
文件名:RtlCreateHeap.c 
其实最主要的还是RtlCreateHeap的说明,注意这个可以有回调函数。
RtlAllocateHeap这个函数也不能在指定的地址上申请内存。

made by correy
made at 2014.09.04
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;   
    PVOID HeapHandle = 0;
    PVOID p = 0;
    BOOLEAN B = FALSE ;
    SIZE_T  Size = 9;
    ULONG  Alignment = 1;

    KdBreakPoint();

    HeapHandle = RtlCreateHeap(0, 0, 0, 0, 0, 0);
    if (HeapHandle == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }    

    KdPrint(("HeapHandle == %p!\n", HeapHandle));

    p = RtlAllocateHeap(HeapHandle, HEAP_ZERO_MEMORY, Size);
    if (p == NULL) {
        HeapHandle = RtlDestroyHeap(HeapHandle);
        if (HeapHandle != NULL) {
            KdPrint(("RtlDestroyHeap fails!\n"));
        }
        return STATUS_INSUFFICIENT_RESOURCES;
    }  

    KdPrint(("p == %p!\n", p));

    __try {
        ProbeForWrite(p, Size, Alignment);
        *(char *)p = 9;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        KdPrint(("ExceptionCode == 0x%x!\n", GetExceptionCode()));
    }

    B = RtlFreeHeap(HeapHandle, HEAP_NO_SERIALIZE, p);
    if (B == FALSE )
    {
        KdPrint(("RtlFreeHeap fails!\n"));
    }

    HeapHandle = RtlDestroyHeap(HeapHandle);
    if (HeapHandle != NULL) {
        KdPrint(("RtlDestroyHeap fails!\n"));
    }

    return status;    
} 

/*
运行结果如下:
HeapHandle == 00180000!
p == 00180688!

这些内存在哪个进程呢?
函数没有指明,但是函数的说明指明了。
*/

InstallHinfSection安装驱动

RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 C:\Documents and Settings\Administrator\桌面\test-minifilter\test.inf

rem 带空格的路径也不要加分号;路径最好用全路径,不要用相对路径或仅仅一个文件名。
rem minifilter的INF文件建议使用WDK7600的,不建议使用VS2012生成的。因为VS2012生成的能加载成功,但是有的回调不会被调用。
rem 这是文件过滤驱动的标准的加载办法,具体的可见WDK的Using an INF File to Install a File System Filter Driver。
rem 但是minifilter里面没有说,只说了INF文件及加载的顺序。
rem 更多信息请查看InstallHinfSection函数的用法:http://msdn.microsoft.com/en-us/library/aa376957(v=vs.85).aspx。
rem 最好配有CAT文件。
rem 卸载的就不用说了。
rem 安装的办法有很多种,正确的还是微软建议的FilterLoad/FltLoadFilter函数,命令行的还有fltmc load /sc start /net start. 
rem 函数是没有返回值的,编写代码和调用命令是差不多的,就怕RUNDLL32.EXE 不存在的情况。

/*
#include <Windows.h>

#include <Setupapi.h>
#pragma comment (lib,"Setupapi.lib")

 int __cdecl main ()
{
    InstallHinfSection(NULL,NULL,TEXT("DefaultInstall 132 C:\\test-minifilter\\test.inf"),0); //只是安装,没有启动。
    return 0;
}
*/

made by correy
made at 2014.09.04
email:kouleguan at hotmail dot com




2014年9月2日星期二

内核中GUID/UUID/LUID

#include <ntifs.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。


/*
文件名:ExUuidCreate.C 
功能和目的:示例内核中GUID/UUID/LUID的用法。
起因:Windows系统的有些上述的ID是固定不变的。最好能记住。COM中也可能如此。

ExUuidCreate 

RtlGUIDFromString
RtlStringFromGUID

RtlConvertLongToLuid 有实现代码的函数
RtlConvertUlongToLuid 有实现代码的函数
RtlEqualLuid 这是个宏
RtlIsZeroLuid 这是个宏

ConvertInterfaceGuidToLuid 
ConvertInterfaceLuidToGuid
不过这两个都是:Available in Windows Vista and later versions of the Windows operating systems.

ZwAllocateLocallyUniqueId
这个函数在WDK中没有说:Available in Windows Vista and later versions of Windows.
在下面的连接中都说了:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff566415(v=vs.85).aspx
http://technet.microsoft.com/en-ca/ff566415(v=vs.90).aspx
http://technet.microsoft.com/it-it/subscriptions/ff566415.aspx
经查看,这个函数XP-32的内核下没有导出。
而且WDK只有函数原型,没有实现的代码,LIB里面啥样没有看。

The locally unique identifier (LUID) is a 64-bit value guaranteed to be unique only on the system on which it was generated.
The LUID structure is an opaque structure that specifies an identifier that is guaranteed to be unique on the local machine. 
WDK里面的这两句话矛盾不?

made by correy
made at 2014.08.15
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define tag  'tset' //test


/*
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426(v=vs.85).aspx
Disk Drives
Class = DiskDrive
ClassGuid = {4d36e967-e325-11ce-bfc1-08002be10318}
This class includes hard disk drives. See also the HDC and SCSIAdapter classes.
在整个WinDDK\7600.16385.1目录中只有两个INF文件包含4d36e967。WRK中也没有搜索到。
所以这个还得自己定义,系统没有定义。

更多的信息:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff547786(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff542998(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428(v=vs.85).aspx
*/
DEFINE_GUID (DiskClassGuid, 0x4d36e967, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);


BOOL RtlEqualUuid(IN UUID * pu1, IN UUID * pu2)
{
    return RtlEqualMemory(pu1, pu2, sizeof(UUID));  //RtlCompareMemory 
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;   
    UUID  Uuid = {0};//typedef GUID UUID;
    UUID  Uuid2 = DiskClassGuid;
    UNICODE_STRING  GuidString = {0};
    BOOL B = FALSE ;
    LUID Luid = {0};//查看结构用的.

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    status = ExUuidCreate(&Uuid);//注意这个是没有前后的大括号的,但是调试器显示的时候可能会显示上.
    if (!NT_SUCCESS(status)) {
        return status;
    }

    GuidString.MaximumLength = MAX_PATH;
    GuidString.Buffer = ExAllocatePoolWithTag( NonPagedPool, GuidString.MaximumLength, tag );
    if (GuidString.Buffer == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }    
    RtlZeroMemory(GuidString.Buffer, GuidString.MaximumLength);

    status = RtlStringFromGUID(&Uuid, &GuidString);//#define REFGUID const GUID &
    if (!NT_SUCCESS(status)) {
        ExFreePool(GuidString.Buffer);
        return status;
    }

    KdPrint(("GuidString %wZ\n", &GuidString));  //这个是有前后的大括号的.

    status = RtlGUIDFromString(&GuidString, &Uuid);//各个变量/参数保持不变.
    if (!NT_SUCCESS(status)) {
        ExFreePool(GuidString.Buffer);
        return status;
    }

    //B = RtlEqualLuid((LUID *)&Uuid, (LUID *)&DiskClassGuid);
    B = RtlEqualUuid(&Uuid, &Uuid2);//肯定不相等.B == 0.

    ExFreePool(GuidString.Buffer);

    return status;    
}