2017年6月2日星期五

驱动中获取哈希

/*
功能:驱动中获取哈希。

注意事项:
1.要链接到cng.lib,而不是应用层的Bcrypt.lib,
  否则驱动启动因为找不到以来的文件,而显示错误码:2,及找不到文件。
  具体的做法是:
  SOURCE文件的TARGETLIBS加上$(DDK_LIB_PATH)\cng.lib
  或者:
  sources.props或类似的文件里的TARGETLIBS加上$(DDK_LIB_PATH)\cng.lib。
  再说下是DDK_LIB_PATH,而不是SDK_LIB_PATH。

made by correy
made at 2017.06.02
http://correy.webs.com
*/


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


#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4100) // 未引用的形参
#pragma warning(disable:4101) // 未引用的局部变量
#pragma warning(disable:4189) // 局部变量已初始化但不引用


#define TAG 'test' //test


BOOL HASH(IN PBYTE rgbMsg, IN ULONG cbInput, LPWSTR algorithm, OUT PBYTE * Hash, DWORD * HashLen)
    /*
    注意:
    1.多字节和单字节。
    2.算法名区分大小写,否者出现异常。
    3.pbHash由调用者释放。

    参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa376217(v=vs.85).aspx
    */
{
    BOOL B = FALSE;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    BCRYPT_ALG_HANDLE hAlg = NULL;
    DWORD cbData = 0;//calculate the size of the buffer to hold the hash object
    DWORD cbHashObject    = 0;
    PBYTE pbHashObject = NULL;
    DWORD cbHash = 0;//calculate the length of the hash
    PBYTE pbHash = NULL;
    BCRYPT_HASH_HANDLE hHash = NULL;

    if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAlg, algorithm, NULL, 0)))//open an algorithm handle
    {
        goto Cleanup;
    }
    
    if(!NT_SUCCESS(status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0)))
    {
        goto Cleanup;
    }
    pbHashObject = (PBYTE)ExAllocatePoolWithTag(NonPagedPoolNx, cbHashObject, TAG);
    if(NULL == pbHashObject)
    {
        goto Cleanup;
    }
    RtlZeroMemory(pbHashObject, cbHashObject);
    
    if(!NT_SUCCESS(status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHash, sizeof(DWORD), &cbData, 0)))
    {
        goto Cleanup;
    }
    pbHash = (PBYTE)ExAllocatePoolWithTag(NonPagedPoolNx, cbHash, TAG);
    if(NULL == pbHash)
    {
        goto Cleanup;
    }
    RtlZeroMemory(pbHash, cbHash);

    if(!NT_SUCCESS(status = BCryptCreateHash(hAlg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0)))//create a hash
    {
        goto Cleanup;
    }
    
    if(!NT_SUCCESS(status = BCryptHashData(hHash, rgbMsg, cbInput, 0)))//hash some data
    {
        goto Cleanup;
    }

    //pbHash是哈希内容,cbHash是哈希的长度。
    
    if(!NT_SUCCESS(status = BCryptFinishHash(hHash, pbHash, cbHash, 0)))//close the hash
    {
        goto Cleanup;
    }

    //wprintf(L"Success!\n");

    * Hash = pbHash;
    * HashLen = cbHash;
    B = TRUE;

Cleanup:

    if(hAlg)
    {
        BCryptCloseAlgorithmProvider(hAlg,0);
    }

    if (hHash)    
    {
        BCryptDestroyHash(hHash);
    }

    if(pbHashObject)
    {
        ExFreePoolWithTag(pbHashObject, TAG);
    }

    //if(pbHash)
    //{
    //    HeapFree(GetProcessHeap(), 0, pbHash);
    //}

    return B;
}


BOOL HASHFILE(LPCTSTR lpFileName, LPWSTR algorithm, LPWSTR lpFileHash)
    /*
    注意:lpFileName支持DOS格式,包括流,
         但是不支持:
         1.NT式的,\Device\XXX 
         2.带环境扩展的,%systemroot%
         3.网络的也不行,如:\Device\Mup\vmware-host\Shared Folders\XXX
         4.还有\SystemRoot\system32\drivers\spsys.sys。
    注意:lpFileHash提供的空间要足够大,足够容纳想要的数据。
    */
{
    unsigned int status = STATUS_SUCCESS;
    BOOL B = FALSE;
    HANDLE hFile = NULL; 
    PBYTE buffer = NULL;
    DWORD NumberOfBytesRead = 0;
    PBYTE Hash;
    DWORD HashLen;
    unsigned int i;
    OBJECT_ATTRIBUTES ob;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    LARGE_INTEGER AllocationSize = {0};
    UNICODE_STRING FileName;
    PFILE_OBJECT FileObject = 0;
    LARGE_INTEGER file_size = {0};
    LARGE_INTEGER ByteOffset = {0};

    RtlInitUnicodeString(&FileName, lpFileName);
    InitializeObjectAttributes(&ob, &FileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwCreateFile(
        &hFile, 
        FILE_ALL_ACCESS | SYNCHRONIZE, 
        &ob,
        &IoStatusBlock,
        &AllocationSize, 
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_VALID_FLAGS, 
        FILE_OPEN, 
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, 
        NULL,
        0
        );
    if (!NT_SUCCESS (status)) 
    {
        return FALSE;
    }

    status = ObReferenceObjectByHandle(hFile, FILE_LIST_DIRECTORY | SYNCHRONIZE, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL );
    ASSERT (NT_SUCCESS( status ));
    status = FsRtlGetFileSize(FileObject, &file_size);
    ASSERT (NT_SUCCESS( status ));
    ASSERT(file_size.QuadPart);
    ASSERT(0 == file_size.HighPart);

    buffer = (PBYTE)ExAllocatePoolWithTag(NonPagedPoolNx, file_size.LowPart, TAG);//文件过大,这里会失败。
    ASSERT(NULL != buffer);
    
    status = ZwReadFile(hFile, NULL, NULL, NULL, &IoStatusBlock, buffer, file_size.LowPart, &ByteOffset, NULL);
    ASSERT (NT_SUCCESS( status ));

    B = HASH(buffer, file_size.LowPart, algorithm, &Hash, &HashLen);
    if (B)
    {
        //确保lpFileHash的大小大于HashLen
        for (i = 0; i < HashLen; i++)
        {
            RtlStringCchPrintfW(&lpFileHash[i * 2], 4, L"%02X", Hash[i]);
        }
    }

    ExFreePoolWithTag(Hash, TAG);
    ExFreePoolWithTag(buffer, TAG);
    ObDereferenceObject(FileObject);
    ZwClose( hFile );
    return B;
}


VOID Unload(_In_ PDRIVER_OBJECT DriverObject)
{  
    UNREFERENCED_PARAMETER(DriverObject);

    PAGED_CODE();

}


NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    wchar_t buffer[MAX_PATH] = {0};
    BOOL B = FALSE;
    
    UNREFERENCED_PARAMETER(RegistryPath);

    PAGED_CODE();

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;
    
    B = HASHFILE(L"\\Device\\HarddiskVolume1\\test.txt", BCRYPT_SHA256_ALGORITHM, buffer);

    return Status;
}

驱动中获取域名的IP地址

/*
功能:获取域名的IP地址。

made by correy
made at 2017.06.01
http://correy.webs.com
*/


#include <ntddk.h>
#include <wsk.h>
#include <ws2def.h>


#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4100) // 未引用的形参
#pragma warning(disable:4101) // 未引用的局部变量
#pragma warning(disable:4189) // 局部变量已初始化但不引用


const WSK_CLIENT_DISPATCH WskAppDispatch = {
  MAKE_WSK_VERSION(1,0), // Use WSK version 1.0
  0,    // Reserved
  NULL  // WskClientEvent callback not required for WSK version 1.0
};


WSK_REGISTRATION WskRegistration;


NTSTATUS
SyncIrpCompletionRoutine(
    __in PDEVICE_OBJECT Reserved,
    __in PIRP Irp,
    __in PVOID Context
    )
{    
    PKEVENT compEvent = (PKEVENT)Context;
    UNREFERENCED_PARAMETER(Reserved);
    UNREFERENCED_PARAMETER(Irp);
    KeSetEvent(compEvent, 2, FALSE);    
    return STATUS_MORE_PROCESSING_REQUIRED;
}


NTSTATUS
KernelNameResolutionSample(
    __in PCWSTR NodeName,
    __in_opt PCWSTR ServiceName,
    __in_opt PADDRINFOEXW Hints,
    __in PWSK_PROVIDER_NPI WskProviderNpi
    )
    //https://docs.microsoft.com/en-us/windows-hardware/drivers/network/resolving-host-names-and-ip-addresses
{
    NTSTATUS status;
    PIRP irp;
    KEVENT completionEvent;
    UNICODE_STRING uniNodeName, uniServiceName, *uniServiceNamePtr;
    PADDRINFOEXW results;
    SOCKADDR_IN * psi = NULL;
    wchar_t buffer[64] = {0};
    PWSTR p = NULL;
    UNICODE_STRING ip = {0};
    const struct in_addr * temp = NULL;

    PAGED_CODE();
    
    RtlInitUnicodeString(&uniNodeName, NodeName);// Initialize UNICODE_STRING structures for NodeName and ServiceName 

    if(ServiceName == NULL) {
        uniServiceNamePtr = NULL;
    }
    else {
        RtlInitUnicodeString(&uniServiceName, ServiceName);
        uniServiceNamePtr = &uniServiceName;
    }
    
    KeInitializeEvent(&completionEvent, SynchronizationEvent, FALSE);// Use an event object to synchronously wait for the WskGetAddressInfo request to be completed. 

    // Allocate an IRP for the WskGetAddressInfo request, and set the IRP completion routine, which will signal the completionEvent when the request is completed.
    irp = IoAllocateIrp(1, FALSE);
    if(irp == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }        

    IoSetCompletionRoutine(irp, SyncIrpCompletionRoutine, &completionEvent, TRUE, TRUE, TRUE);

    // Make the WskGetAddressInfo request.
    WskProviderNpi->Dispatch->WskGetAddressInfo (
        WskProviderNpi->Client,
        &uniNodeName,
        uniServiceNamePtr,
        NS_ALL,
        NULL, // Provider
        Hints,
        &results, 
        NULL, // OwningProcess
        NULL, // OwningThread
        irp);

    // Wait for completion.
    // Note that processing of name resolution results can also be handled directly within the IRP completion routine,
    // but for simplicity, this example shows how to wait synchronously for completion.
    KeWaitForSingleObject(&completionEvent, Executive, KernelMode, FALSE, NULL);
    status = irp->IoStatus.Status;
    IoFreeIrp(irp);
    if(!NT_SUCCESS(status)) {
        return status;
    }

    // Process the name resolution results by iterating through the addresses within the returned ADDRINFOEXW structure.
    //results; // your code here
    psi = (SOCKADDR_IN *)results->ai_addr;//注意:这一行很重要。有时要该为IPV6版本的。
    temp = (const struct in_addr *)&psi->sin_addr;
    p = RtlIpv4AddressToString(temp, buffer);
    ASSERT(p);
    RtlInitUnicodeString(&ip, buffer);
    KdPrint(("ipv4:%wZ.\r\n", &ip));
    
    WskProviderNpi->Dispatch->WskFreeAddressInfo(WskProviderNpi->Client, results);// Release the returned ADDRINFOEXW structure when no longer needed.

    return status;
} 


VOID Unload(_In_ PDRIVER_OBJECT DriverObject)
{  
    UNREFERENCED_PARAMETER(DriverObject);

    PAGED_CODE();

    WskDeregister(&WskRegistration);
}


NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    WSK_CLIENT_NPI wskClientNpi;
    WSK_PROVIDER_NPI wskProviderNpi;
    //UNICODE_STRING test  = RTL_CONSTANT_STRING(L"DESKTOP-SQRJ1QU");
    
    UNREFERENCED_PARAMETER(RegistryPath);

    PAGED_CODE();

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;
    
    wskClientNpi.ClientContext = NULL;
    wskClientNpi.Dispatch = &WskAppDispatch;
    Status = WskRegister(&wskClientNpi, &WskRegistration);
    ASSERT(NT_SUCCESS(Status));

    Status = WskCaptureProviderNPI(&WskRegistration, WSK_INFINITE_WAIT, &wskProviderNpi);
    ASSERT(NT_SUCCESS(Status));

    /*
    做一些事情。
    */
    Status = KernelNameResolutionSample(L"www.baidu.com", NULL, NULL, &wskProviderNpi);
    ASSERT(NT_SUCCESS(Status));

    WskReleaseProviderNPI(&WskRegistration);

    return Status;
}

snwscanf

/*
文件名snwscanf.C

缘故:
系统中格式化字符串的函数不少,如:
2: kd> x nt!*Printf*
fffff800`04dd1fa4 nt!vsnwprintf (<no parameter info>)
fffff800`04dbe0e8 nt!RtlStringCbPrintfA (<no parameter info>)
fffff800`04c96890 nt!RtlStringCbVPrintfA (<no parameter info>)
fffff800`04dd746c nt!snwprintf_s (<no parameter info>)
fffff800`04dd20b0 nt!vsnprintf_l (<no parameter info>)
fffff800`04ec3e04 nt!StringCchPrintfExW (<no parameter info>)
fffff800`04dd36f4 nt!sprintf (<no parameter info>)
fffff800`04dd4164 nt!vsprintf_l (<no parameter info>)
fffff800`04dd7ddc nt!sprintf_s (<no parameter info>)
fffff800`04dd2098 nt!vsnprintf (<no parameter info>)
fffff800`04dd7380 nt!snprintf_s (<no parameter info>)
fffff800`04dd6c70 nt!vswprintf_s (<no parameter info>)
fffff800`04dd1fbc nt!vsnwprintf_l (<no parameter info>)
fffff800`04dd748c nt!vsnwprintf_s (<no parameter info>)
fffff800`04c9cf54 nt!RtlStringCbPrintfW (<no parameter info>)
fffff800`04d83d80 nt!StringCchPrintfW (<no parameter info>)
fffff800`04f867e8 nt!g_AslLogPfnVPrintf = <no type information>
fffff800`04d7e268 nt!RtlStringCbPrintfExW (<no parameter info>)
fffff800`04dd29e0 nt!swprintf (<no parameter info>)
fffff800`04dd29e0 nt!swprintf (<no parameter info>)
fffff800`04dd6750 nt!get_printf_count_output (<no parameter info>)
fffff800`04dd24d0 nt!snprintf (<no parameter info>)
fffff800`05352008 nt!AslLogCallPrintf (<no parameter info>)
fffff800`04dd2578 nt!snwprintf (<no parameter info>)
fffff800`04dd6c50 nt!swprintf_s (<no parameter info>)
fffff800`04dd2aa0 nt!vswprintf_l (<no parameter info>)
fffff800`04dd2a94 nt!vswprintf (<no parameter info>)
fffff800`04dbfdcc nt!RtlUnicodeStringPrintf (<no parameter info>)
fffff800`04dd41dc nt!vsprintf (<no parameter info>)
fffff800`04dd73a0 nt!vsnprintf_s (<no parameter info>)
fffff800`04e5eba0 nt!RtlUnicodeStringPrintfEx (<no parameter info>)
fffff800`04dd7dfc nt!vsprintf_s (<no parameter info>)
fffff800`04d331b0 nt!RtlStringCchPrintfExW (<no parameter info>)
fffff800`04e41a80 nt!RtlStringCbPrintfExA (<no parameter info>)
fffff800`04dbfc5c nt!RtlStringCchPrintfA (<no parameter info>)
fffff800`04d8ca18 nt!RtlStringCchPrintfW (<no parameter info>)
WRK中也有不少,不信,你看代码。

凡是反过来,就没有,如:WRK中没有,XP和2003中没有。

还好vista开始有了,不信,你看:
2: kd> x nt!*scanf*
fffff800`04dd7434 nt!snscanf_s (<no parameter info>)
fffff800`04dd7e44 nt!sscanf_s (<no parameter info>)
fffff800`04dd8298 nt!swscanf_s (<no parameter info>)
fffff800`04dd752c nt!snwscanf_s (<no parameter info>)
有此足矣!

stdio.h中尽管有_snwscanf_s的定义/声明,但是没有实现,不信,你编译下:
error LNK2019: 无法解析的外部符号 _snwscanf_s,该符号在函数 XXX 中被引用。
但是,这不是难事,不信,你看本文的实现办法。

参考:
https://msdn.microsoft.com/zh-cn/library/dktz45bk.aspx

made by correy
made at 2017.03.31
homepage:http://correy.webs.com 
*/

#include <ntifs.h>
//#include <ntddk.h> //这两个次序不能乱(乱会出错的),有上面的,这个可以注释掉。
#include <windef.h>
#include <stdio.h>

#define tag  'tset' //test

#pragma warning(disable:4100) //未引用的形参
#pragma warning(disable:4214) //整形以外的位域类型
#pragma warning(disable:4121) //封装要区分成员对齐方式
#pragma warning(disable:4189) //局部变量已初始化但不引用
#pragma warning(disable:4101) //未引用的局部变量
#pragma warning(disable:4201) //使用了非标准扩展 : 无名称的结构/联合
#pragma warning(disable:4055) //

//typedef OBJECT_TYPE * (*ObGetObjectType)(IN PVOID pObject);
typedef  int (__cdecl * SNWSCANF_S)(//snwscanf_s
   const wchar_t * input,
   size_t length,
   const wchar_t * format,
   ...
);

SNWSCANF_S g_snwscanf_s;


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING test;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    RtlInitUnicodeString( &test, L"_snwscanf_s" );
    g_snwscanf_s = (SNWSCANF_S)MmGetSystemRoutineAddress(&test);//注意:赋值的类型转换。
    if (g_snwscanf_s)
    {
        wchar_t input[] = L"999999999"; 
        int number = 0;
        int i = g_snwscanf_s(input, wcslen(input) * sizeof(wchar_t),  L"%d", &number);
        ASSERT(i);
    }

    return status;
}