2014年6月25日星期三

枚举枚举系统/进程的句柄的信息

#include "stdafx.h"

/*
枚举一个进程的句柄信息。
这是一个基本的功能。

一直想做而没有做。
网上也有好多的代码。
今天算是实现了。

本文修改自微软的CppFileHandle工程。
有一定的可信度。

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

#pragma region Includes
#include <windows.h>
#include <assert.h>
#pragma endregion

#define STATUS_SUCCESS     ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH  ((NTSTATUS)0xc0000004L)

#include <winternl.h>

// Undocumented SYSTEM_INFORMATION_CLASS: SystemHandleInformation
const SYSTEM_INFORMATION_CLASS SystemHandleInformation = (SYSTEM_INFORMATION_CLASS)16;

// The NtQuerySystemInformation function and the structures that it returns are internal to the operating system and subject to change from one  release of Windows to another.
// To maintain the compatibility of your application, it is better not to use the function.
typedef NTSTATUS (WINAPI * PFN_NTQUERYSYSTEMINFORMATION)(
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    OUT PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

typedef NTSTATUS (WINAPI * PFN_ZwQueryObject)(
    __in_opt HANDLE  Handle,
    __in OBJECT_INFORMATION_CLASS  ObjectInformationClass,
    __out_bcount_opt(ObjectInformationLength) PVOID  ObjectInformation,
    __in ULONG  ObjectInformationLength,
    __out_opt PULONG  ReturnLength
    );


// Undocumented structure: SYSTEM_HANDLE_INFORMATION
typedef struct _SYSTEM_HANDLE 
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION 
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;


////摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntobapi.h
//typedef enum _OBJECT_INFORMATION_CLASS {
//    ObjectBasicInformation,
//    ObjectNameInformation,
//    ObjectTypeInformation,
//    ObjectTypesInformation,
//    ObjectHandleFlagInformation,
//    ObjectSessionInformation,
//    MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
//} OBJECT_INFORMATION_CLASS;

//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntobapi.h
typedef struct _OBJECT_NAME_INFORMATION {               // ntddk wdm nthal
    UNICODE_STRING Name;                                // ntddk wdm nthal
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;   // ntddk wdm nthal


BOOL SetPrivilege(
    //HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    ) 
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    HANDLE hToken; // Get a token for this process. 
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
    {
        return( FALSE ); 
    }

    if ( !LookupPrivilegeValue( 
        NULL,            // lookup privilege on local system
        lpszPrivilege,   // privilege to lookup 
        &luid ) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if ( !AdjustTokenPrivileges(hToken,  FALSE,  &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}


/*!
* Enumerate all handles of the specified process using undocumented APIs.
*/
DWORD EnumerateProcessHandles(ULONG pid)
{
    // The functions have no associated import library.
    // You must use the LoadLibrary and GetProcAddress functions to dynamically link to ntdll.dll.
    HINSTANCE hNtDll = LoadLibrary(_T("ntdll.dll"));
    assert(hNtDll != NULL);
    PFN_NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = (PFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, "NtQuerySystemInformation");
    assert(NtQuerySystemInformation != NULL);   

    PFN_ZwQueryObject NtQueryObject = (PFN_ZwQueryObject)GetProcAddress(hNtDll, "NtQueryObject");
    assert(NtQueryObject != NULL);   

    // NtQuerySystemInformation does not return the correct required buffer size if the buffer passed is too small.
    // Instead you must call the function while increasing the buffer size until the function no longer returns STATUS_INFO_LENGTH_MISMATCH.
    DWORD nSize = 4096, nReturn;
    PSYSTEM_HANDLE_INFORMATION pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), 0, nSize); 
    while (NtQuerySystemInformation(SystemHandleInformation, pSysHandleInfo, nSize, &nReturn) == STATUS_INFO_LENGTH_MISMATCH)// Get system handle information.
    {
        HeapFree(GetProcessHeap(), 0, pSysHandleInfo);
        nSize += 4096;
        pSysHandleInfo = (SYSTEM_HANDLE_INFORMATION*)HeapAlloc(GetProcessHeap(), 0, nSize);
    }
    /*
    这个函数已经把整个系统的句柄信息都已经获取了。
    当然还有别的信息,如进程等。
    下面就是根据这些信息进行分类而已,如进程。

    其实这个函数可以显示整个系统的句柄的详细信息。
    */

    DWORD dwhandles = 0;//一个进程的所有的句柄数量。

    // Get the handle of the target process.
    // The handle will be used to duplicate the handles in the process.
    HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (hProcess == NULL)
    {
        _tprintf(_T("OpenProcess failed w/err 0x%08lx\n"), GetLastError());
        return -1;
    }
    /*
    打开这个函数需要权限,经测试系统进程提权也打不开。
    经测试:
    在XP上可以打开系统进程(system)。
    在Windows 7上,提权,以管理员(administrator)权限,甚至以服务的权限(NT AUTHORITY\SYSTEM)都不行,估计的会话隔离导致(可服务应该也在会话0,所以还是驱动的权限大)。
    */

    for (ULONG i = 0; i < pSysHandleInfo->NumberOfHandles; i++)
    {
        PSYSTEM_HANDLE pHandle = &(pSysHandleInfo->Handles[i]);

        //根据进程进行搜索。
        if (pHandle->ProcessId == pid) 
        {
            dwhandles++; // Increase the number of handles

            /*
            经测试发现:EtwRegistration(39)类型的句柄会失败。
            0x00000032 不支持该请求。 
            */
            HANDLE hCopy;// Duplicate the handle in the current process
            if (!DuplicateHandle(hProcess, (HANDLE)pHandle->Handle, GetCurrentProcess(), &hCopy, MAXIMUM_ALLOWED, FALSE, 0))
            {
                wprintf(L"DuplicateHandle fail with 0x%x,HANDLE:0x%x,ObjectTypeNumber:%d file:%s,fun:%s,line:%d\n", GetLastError(),pHandle->Handle, pHandle->ObjectTypeNumber, __FILE__,__FUNCTION__, __LINE__);
                continue;
            }
            
            /*
            在这里可以利用复制的句柄进行一些操作,如查询值。
            */
            ULONG  ObjectInformationLength = sizeof (OBJECT_NAME_INFORMATION) + 512;
            POBJECT_NAME_INFORMATION poni = (POBJECT_NAME_INFORMATION)HeapAlloc(GetProcessHeap(), 0, ObjectInformationLength);
            assert(poni != NULL);  

            ULONG  ReturnLength;

            /*
            如果句柄的类型是TOKEN,线程,进程等类型的,需要再特殊的处理。
            也就是说这个函数是查询不到的。
            */
            if (NtQueryObject(hCopy, (OBJECT_INFORMATION_CLASS)1, poni, ObjectInformationLength, &ReturnLength) != STATUS_SUCCESS)
            {
                wprintf(L"NtQueryObject fail!\n");
                HeapFree(GetProcessHeap(), 0, poni); 
                continue;
            }
            
            wprintf(L"HANDLE:0x%x, NAME:%wZ\n", pHandle->Handle, &poni->Name);

            HeapFree(GetProcessHeap(), 0, poni); 
            CloseHandle(hCopy);
        }

        //可以显示整个系统的句柄信息。
        //wprintf(L"PID:0x%x\n", pHandle->ProcessId);
        //wprintf(L"\tHANDLE:0x%x\n", pHandle->Handle);
    }

    CloseHandle(hProcess); 
    HeapFree(GetProcessHeap(), 0, pSysHandleInfo);// Clean up. 
    return dwhandles;// Return the number of handles in the process
}


int _tmain(int argc, _TCHAR* argv[])
{
    BOOL B = SetPrivilege(SE_DEBUG_NAME, TRUE);
    DWORD dwFiles = EnumerateProcessHandles(3264);//GetCurrentProcessId()
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

/*
内核版的枚举系统/进程的句柄及其信息的代码。

made by correy
made at 2014.06.26
homepage:http://correy.webs.com
不当之处,请指正。
*/


//这些在WDK 7600.16385.1中没有定义,在WDK8.0中定义了.
//以下代码是解决办法之一.
#ifndef _In_
#define _Inout_
#define _In_
#define _In_opt_
#define _Out_opt_
#endif


//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntexapi.h
// System Information Classes.
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation,
    SystemProcessorInformation,             // obsolete...delete
    SystemPerformanceInformation,
    SystemTimeOfDayInformation,
    SystemPathInformation,
    SystemProcessInformation,
    SystemCallCountInformation,
    SystemDeviceInformation,
    SystemProcessorPerformanceInformation,
    SystemFlagsInformation,
    SystemCallTimeInformation,
    SystemModuleInformation,
    SystemLocksInformation,
    SystemStackTraceInformation,
    SystemPagedPoolInformation,
    SystemNonPagedPoolInformation,
    SystemHandleInformation,//ExpGetHandleInformation处理的。
    SystemObjectInformation,
    SystemPageFileInformation,
    SystemVdmInstemulInformation,
    SystemVdmBopInformation,
    SystemFileCacheInformation,
    SystemPoolTagInformation,
    SystemInterruptInformation,
    SystemDpcBehaviorInformation,
    SystemFullMemoryInformation,
    SystemLoadGdiDriverInformation,
    SystemUnloadGdiDriverInformation,
    SystemTimeAdjustmentInformation,
    SystemSummaryMemoryInformation,
    SystemMirrorMemoryInformation,
    SystemPerformanceTraceInformation,
    SystemObsolete0,
    SystemExceptionInformation,
    SystemCrashDumpStateInformation,
    SystemKernelDebuggerInformation,
    SystemContextSwitchInformation,
    SystemRegistryQuotaInformation,
    SystemExtendServiceTableInformation,
    SystemPrioritySeperation,
    SystemVerifierAddDriverInformation,
    SystemVerifierRemoveDriverInformation,
    SystemProcessorIdleInformation,
    SystemLegacyDriverInformation,
    SystemCurrentTimeZoneInformation,
    SystemLookasideInformation,
    SystemTimeSlipNotification,
    SystemSessionCreate,
    SystemSessionDetach,
    SystemSessionInformation,
    SystemRangeStartInformation,
    SystemVerifierInformation,
    SystemVerifierThunkExtend,
    SystemSessionProcessInformation,
    SystemLoadGdiDriverInSystemSpace,
    SystemNumaProcessorMap,
    SystemPrefetcherInformation,
    SystemExtendedProcessInformation,
    SystemRecommendedSharedDataAlignment,
    SystemComPlusPackage,
    SystemNumaAvailableMemory,
    SystemProcessorPowerInformation,
    SystemEmulationBasicInformation,
    SystemEmulationProcessorInformation,
    SystemExtendedHandleInformation,
    SystemLostDelayedWriteInformation,
    SystemBigPoolInformation,
    SystemSessionPoolTagInformation,
    SystemSessionMappedViewInformation,
    SystemHotpatchInformation,
    SystemObjectSecurityMode,
    SystemWatchdogTimerHandler,
    SystemWatchdogTimerInformation,
    SystemLogicalProcessorInformation,
    SystemWow64SharedInformation,
    SystemRegisterFirmwareTableInformationHandler,
    SystemFirmwareTableInformation,
    SystemModuleInformationEx,
    SystemVerifierTriageInformation,
    SystemSuperfetchInformation,
    SystemMemoryListInformation,
    SystemFileCacheInformationEx,
    MaxSystemInfoClass  // MaxSystemInfoClass should always be the last enum
} SYSTEM_INFORMATION_CLASS;


//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntexapi.h
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
    USHORT UniqueProcessId;
    USHORT CreatorBackTraceIndex;
    UCHAR ObjectTypeIndex;
    UCHAR HandleAttributes;
    USHORT HandleValue;
    PVOID Object;
    ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;


//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntexapi.h
typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntobapi.h
//typedef enum _OBJECT_INFORMATION_CLASS {
//    ObjectBasicInformation,
//    ObjectNameInformation,
//    ObjectTypeInformation,
//    ObjectTypesInformation,
//    ObjectHandleFlagInformation,
//    ObjectSessionInformation,
//    MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
//} OBJECT_INFORMATION_CLASS;

//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntobapi.h
//typedef struct _OBJECT_NAME_INFORMATION {               // ntddk wdm nthal
//    UNICODE_STRING Name;                                // ntddk wdm nthal
//} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;   // ntddk wdm nthal


/*
摘自:http://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx。
有修改。
*/
NTSTATUS /* WINAPI NtQuerySystemInformation */ ZwQuerySystemInformation(_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation,
                                                                        _In_  ULONG SystemInformationLength, _Out_opt_  PULONG ReturnLength);

//摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\internal\base\inc\zwapi.h
NTSTATUS ZwAdjustPrivilegesToken(IN HANDLE TokenHandle, IN BOOLEAN DisableAllPrivileges, IN PTOKEN_PRIVILEGES NewState OPTIONAL, 
                                 IN ULONG BufferLength OPTIONAL, OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL, OUT PULONG ReturnLength);


#define tag 'tset' //test


static NTSTATUS AdjustPrivilege(ULONG Privilege, BOOLEAN Enable)
    /*
    摘自:http://www.osronline.com/article.cfm?article=23
    */
{
    NTSTATUS status;
    TOKEN_PRIVILEGES privSet;
    HANDLE tokenHandle;
    TOKEN_PRIVILEGES tokenPriv;  

    status = ZwOpenProcessTokenEx(NtCurrentProcess(), TOKEN_ALL_ACCESS, OBJ_KERNEL_HANDLE , &tokenHandle);// Open current process token
    if (!NT_SUCCESS(status)) {
        DbgPrint("NtOpenProcessToken failed, status 0x%x\n", status);
        return status;
    }

    // Set up the information about the privilege we are adjusting
    privSet.PrivilegeCount = 1;
    privSet.Privileges[0].Luid = RtlConvertUlongToLuid(Privilege);
    if (Enable) {
        privSet.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    } else {
        privSet.Privileges[0].Attributes = 0;
    }

    status = ZwAdjustPrivilegesToken(tokenHandle,
        FALSE, // don't disable all privileges
        &privSet,
        sizeof(privSet),
        NULL, // old privileges - don't care
        NULL); // returned length
    if (!NT_SUCCESS(status)) {
        DbgPrint("ZwAdjustPrivilegesToken failed, status 0x%x\n", status);
    } 

    (void) ZwClose(tokenHandle);// Close the process token handle

    return status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


NTSTATUS ZwQueryObjectNameByHandle(IN HANDLE Handle, OUT UNICODE_STRING * p_object_name)
{
    unsigned int status = STATUS_UNSUCCESSFUL; 
    PVOID Object;
    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
    ULONG  Length = 1024;//取这个数的原因:参看ObQueryNameString函数的第三个参数的说明:A reasonable size for the buffer to accommodate most object names is 1024 bytes. 
    POBJECT_NAME_INFORMATION pu = 0;

    status = ObReferenceObjectByHandle( Handle, 0, NULL, PreviousMode, &Object, 0 );
    if (!NT_SUCCESS( status )) {
        return( status );
    }

    Length = 0;
    status = ObQueryNameString(Object, pu, 0, &Length);
    if (status != STATUS_INFO_LENGTH_MISMATCH) {
        KdPrint(("ObQueryNameString fail with 0x%X!\n",status));
        ObDereferenceObject(Object);
        return status;
    }

    Length += 512;
    pu = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, Length, tag );
    if (pu == 0) {
        ObDereferenceObject(Object);
        return status;
    }
    RtlZeroMemory(pu, Length);

    status = ObQueryNameString(Object, pu, Length, &Length);
    if (status != STATUS_SUCCESS ) 
    {
        /*
        经查此时的返回值是0xC0000001。
        即连到系统上的设备没有发挥作用。
        此时的句柄的类型是文件。
        而且process explorer也是显示的是没有值的。
        */
        KdPrint(("ObQueryNameString fail with 0x%X!\n",status));
        ExFreePoolWithTag(pu, tag );
        ObDereferenceObject(Object);
        return status;
    }

    ObDereferenceObject(Object);

    //有的对象是没有名字的。
    if (pu->Name.Length == 0)
    {        
        status = STATUS_UNSUCCESSFUL;
    }
    else
    {
        RtlCopyUnicodeString(p_object_name,&pu->Name);   
        //p_object_name->Buffer = pu->Name.Buffer;
        //p_object_name->Length = pu->Name.Length;
        //p_object_name->MaximumLength = pu->Name.MaximumLength;
    }

    ExFreePoolWithTag(pu, tag );

    return status;
}


NTSTATUS EnumerateProcessHandles(IN HANDLE pid, OUT DWORD * process_handle )
{
    unsigned int status = STATUS_UNSUCCESSFUL; 
    DWORD nSize = 4096, nReturn;
    PSYSTEM_HANDLE_INFORMATION pSysHandleInfo;
    CLIENT_ID   ClientId = {0};//不初始化ZwOpenProcess有问题。
    HANDLE  ProcessHandle;
    DWORD dwhandles = 0;//一个进程的所有的句柄数量。
    ULONG i = 0;
    OBJECT_ATTRIBUTES ob;

    pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)ExAllocatePoolWithTag( NonPagedPool, nSize, tag);
    if (pSysHandleInfo == NULL) {
        KdPrint(("ExAllocatePoolWithTag fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(pSysHandleInfo, nSize);
    while (ZwQuerySystemInformation(SystemHandleInformation, pSysHandleInfo, nSize, &nReturn) == STATUS_INFO_LENGTH_MISMATCH)// Get system handle information.
    {
        ExFreePoolWithTag( pSysHandleInfo, tag );
        nSize += 4096;
        pSysHandleInfo = (SYSTEM_HANDLE_INFORMATION*)(PSYSTEM_HANDLE_INFORMATION)ExAllocatePoolWithTag( NonPagedPool, nSize, tag);
        if (pSysHandleInfo == NULL) {
            KdPrint(("ExAllocatePoolWithTag fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        RtlZeroMemory(pSysHandleInfo, nSize);    
    }       

    ClientId.UniqueProcess = pid ;
    InitializeObjectAttributes(&ob, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenProcess(&ProcessHandle, GENERIC_ALL, &ob, &ClientId);
    if (!NT_SUCCESS(status)) {
        KdPrint(("ZwOpenProcess fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
        ExFreePoolWithTag( pSysHandleInfo, tag );
        return status;
    }    

    for (; i < pSysHandleInfo->NumberOfHandles; i++)
    {
        PSYSTEM_HANDLE_TABLE_ENTRY_INFO pHandle = &(pSysHandleInfo->Handles[i]);

        //根据进程进行搜索。
        if (pHandle->UniqueProcessId == (int)pid) 
        {
            HANDLE hCopy;// Duplicate the handle in the current process
            PVOID  ObjectInformation = 0;
            ULONG  ObjectInformationLength = 0;
            ULONG  ReturnLength = 0;
            PPUBLIC_OBJECT_TYPE_INFORMATION ppoti = 0;
            UNICODE_STRING object_name;

            dwhandles++; // Increase the number of handles

            /*
            访问系统进程中句柄类型为进程的IDLE句柄会返回0xc0000022.即拒绝访问。
            process explorer也是如此。估计是显示的PSYSTEM_HANDLE_TABLE_ENTRY_INFO的内容。
            pchunter没有显示句柄为进程和线程的信息。但是process explorer能。
            process explorer默认的情况下是不显示没有名字的句柄的MAPPINGS的。但是可以设置和修改。
            */
            status = ZwDuplicateObject(ProcessHandle, (HANDLE)pHandle->HandleValue, NtCurrentProcess(), &hCopy, PROCESS_ALL_ACCESS , FALSE, DUPLICATE_SAME_ACCESS);
            if (!NT_SUCCESS(status)) {
                KdPrint(("ZwDuplicateObject fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
                continue;
            }

            //status = ZwQueryObject(hCopy, ObjectTypeInformation, ObjectInformation, ObjectInformationLength, &ReturnLength);
            //if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL)
            //{
            //    ObjectInformationLength = ReturnLength;
            //}
            //else
            //{
            //    //continue;
            //    break;//程序发出命令,但命令长度不正确。 C0000004
            //}

            //查询句柄类型,这是字符串。
            ObjectInformationLength = sizeof (PUBLIC_OBJECT_TYPE_INFORMATION) * 2;//这个应该再增加点。加个512应该合适点。             
            ObjectInformation = ExAllocatePoolWithTag( NonPagedPool, ObjectInformationLength, tag);
            if (ObjectInformation == NULL) {
                KdPrint(("ExAllocatePoolWithTag fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
                ZwClose(hCopy);
                return STATUS_INSUFFICIENT_RESOURCES;
            }
            RtlZeroMemory(ObjectInformation, ObjectInformationLength);  
            status = ZwQueryObject(hCopy, ObjectTypeInformation, ObjectInformation, ObjectInformationLength, &ReturnLength);
            if (!NT_SUCCESS(status))
            {
                KdPrint(("ZwQueryObject fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
                ExFreePoolWithTag( ObjectInformation, tag );
                ZwClose(hCopy);
                return status;
            }
            
            object_name.Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, tag );
            if (object_name.Buffer == 0) {
                KdPrint(("ExAllocatePoolWithTag fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
                ExFreePoolWithTag( ObjectInformation, tag );
                ZwClose(hCopy);
                return status;
            }
            RtlZeroMemory(object_name.Buffer, MAX_PATH);
            RtlInitEmptyUnicodeString(&object_name, object_name.Buffer,MAX_PATH);

            ppoti = (PPUBLIC_OBJECT_TYPE_INFORMATION)ObjectInformation;
            

            /*
            如果要查询句柄的值的名字,可以:
            1.用ZwQueryObject的未公开的ObjectNameInformation。其实这是使用ObReferenceObjectByHandle+ObpQueryNameString实现的。
            2.ObReferenceObjectByHandle+ObQueryNameString。
            */
            status = ZwQueryObjectNameByHandle(hCopy, &object_name);
            if (NT_SUCCESS(status))
            {
                KdPrint(("HANDLE:0x%x, TYPE:%wZ, NAME:%wZ\n",pHandle->HandleValue, &ppoti->TypeName, &object_name));
                RtlFreeUnicodeString(&object_name);
                //ExFreePoolWithTag(object_name.Buffer, tag );
            }   
            else 
            {
                KdPrint(("HANDLE:0x%x, TYPE:%wZ\n",pHandle->HandleValue, &ppoti->TypeName));
            }            
            
            status = ZwClose(hCopy);
            //if (!NT_SUCCESS(status))
            //{//有的句柄保护起来,是不准关闭的。
            //    KdPrint(("ZwClose fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
            //    ExFreePoolWithTag( pSysHandleInfo, tag );
            //    ExFreePoolWithTag( ObjectInformation, tag );
            //    return status;
            //}

            ExFreePoolWithTag( ObjectInformation, tag );     
        }
    }

    status = ZwClose(ProcessHandle);
    /*if (!NT_SUCCESS(status)) {//可能有的句柄已经释放了。
        KdPrint(("ZwClose fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
        ExFreePoolWithTag( pSysHandleInfo, tag );
        return status;
    } */   

    ExFreePoolWithTag( pSysHandleInfo, tag );

    * process_handle = dwhandles;

    return status;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    unsigned int status = STATUS_UNSUCCESSFUL;  
    DWORD process_handle = 0;
    HANDLE test_handle = PsGetProcessId(PsInitialSystemProcess);//(HANDLE)1652;//PsGetProcessId(PsInitialSystemProcess);//获取系统进程的id,实用型很强,可以在别处使用,不只是驱动的入口。
    
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    status = AdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE);
    if (!NT_SUCCESS(status)) {
        DbgPrint("AdjustPrivilege SE_RESTORE_PRIVILEGE fail!\n");
        return status;
    }

    status = EnumerateProcessHandles(test_handle, &process_handle);
    if (!NT_SUCCESS(status)) {
        KdPrint(("EnumerateProcessHandles fail with 0x%x, file:%s,fun:%s,line:%d\n",status,  __FILE__,__FUNCTION__, __LINE__));
        return status;
    }        

    return status;//STATUS_SUCCESS
} 

2014年6月16日星期一

枚举当前会话的窗口站和桌面的信息

#include "stdafx.h"

/*
文件名:EnumWindowStations.cpp
功能:枚举当前会话的session->window stations->Desktop->(top-level)Windows->(child)Windows的信息等.

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

#ifndef UNICODE
#define UNICODE
#endif // UNICODE

#include <windows.h>


//BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
//{
//
//    //To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE. 
//    return TRUE;
//}


BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    //fprintf(stderr,"\t\t\tWindows handle:0x%X!\n", hwnd);

    /*
    更多的信息,那就自己动手吧!
    如:名字,处理函数等。
    还可以EnumChildWindows。
    甚至可以EnumThreadWindows(EnumThreadWndProc)。
    */

    //To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE. 
    return TRUE;
}


BOOL CALLBACK EnumDesktopProc(IN LPTSTR lpszDesktop,IN LPARAM lParam)
{
    fprintf(stderr,"\t\tDesktop name:%ws!\n", lpszDesktop);

    HDESK HD = OpenDesktop(lpszDesktop, 0, TRUE, GENERIC_READ);
    if (HD == NULL)
    {
        int x =  GetLastError ();
        return x;
    }

    BOOL B = EnumDesktopWindows(HD,  EnumWindowsProc, 0);
    if (!B)
    {
        int x =  GetLastError ();        
    }

    B = CloseDesktop(HD);
    if (!B)
    {
        int x =  GetLastError ();
        return FALSE ;
    }

    //To continue enumeration, the callback function must return TRUE. To stop enumeration, it must return FALSE. 
    return TRUE;
}


BOOL CALLBACK EnumWindowStationProc(IN LPTSTR lpszWindowStation,IN LPARAM lParam)
{
    fprintf(stderr,"\tWindowStation name:%ws!\n", lpszWindowStation);

    HWINSTA Hws = OpenWindowStation(lpszWindowStation, TRUE, GENERIC_READ);
    if (Hws == NULL)
    {
        int x =  GetLastError ();
        return x;
    }

    BOOL B = EnumDesktops(Hws, EnumDesktopProc, 0);
    if (!B)
    {
        int x =  GetLastError ();        
    }

    B = CloseWindowStation(Hws);
    if (!B)
    {
        int x =  GetLastError ();
        return FALSE ;
    }

    //To continue enumeration, the callback function must return TRUE. To stop enumeration, it must return FALSE.
    return TRUE ;
}


int __cdecl main(int argc, char *argv[])
{
    fprintf(stderr,"当前会话的window stations->Desktop->Windows信息\n");

    BOOL B = EnumWindowStations(EnumWindowStationProc, 0);//Enumerates all window stations in the current session
    if (!B)
    {
        int x =  GetLastError ();
        return x;
    }

    return 0;
}

2014年6月15日星期日

枚举系统的登录会话及其信息

#include "stdafx.h"

/*
文件名:LsaEnumerateLogonSessions.Cpp
功能:枚举系统的登录的会话及其信息。
说明:看名字是枚举会话的,其实还有很多的用户名,会话是会重复的。
      感觉还不如枚举进程,然后收集进程的会话信息方便呢?

会话,好多人都听说过。
如网络上的,网页的链接,TCP/Http的链接等。
这里说的是系统的登录的会话。

这好像用处不大,只是在远程登录/多用户的情况下会考虑到。
但是用到了,就必须会,解决掉。

再说:会话的ID也不一定是连续的。
如登录一个会话,然后再注销,再登录看看:注销的那个会话的ID就不再了,但是后面登录的是还存在的。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa378290(v=vs.85).aspx
这个页面的LSAFreeReturnBuffer也写错了,严格的说是大小写错误。

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


#ifndef UNICODE
#define UNICODE
#endif // UNICODE

#include <windows.h>
//#include <stdio.h>

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

//#include "ntsecapi.h"

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

// If you have the ddk, include ntstatus.h.
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif


void DisplayWinError(LPSTR szAPI, DWORD WinError)
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    // TODO: Get this fprintf out of here!
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, WinError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &MessageBuffer, 0, NULL))
    {
        DWORD dwBytesWritten; // unused
        WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, dwBufferLength, &dwBytesWritten, NULL);// Output message string on stderr.       
        LocalFree(MessageBuffer);// Free the buffer allocated by the system.
    }
}


void DisplayNtStatus(LPSTR szAPI, NTSTATUS Status)
{    
    DisplayWinError(szAPI, LsaNtStatusToWinError(Status));// Convert the NTSTATUS to Winerror. Then call DisplayWinError().
}


int __cdecl main(int argc, char *argv[])
{
    NTSTATUS Status; 
    int iRetVal=RTN_ERROR;
    ULONG LogonSessionCount;
    PLUID LogonSessionList;

    Status = LsaEnumerateLogonSessions(&LogonSessionCount, & LogonSessionList);
    if (Status != STATUS_SUCCESS) {
        DisplayNtStatus("LsaEnumerateLogonSessions", Status);
        return RTN_ERROR;
    }

    /*
    登录的会话还挺多的。
    几个用户同时登录还会重复。
    */

    ULONG i = 0;
    for (PLUID p = LogonSessionList; i < LogonSessionCount; i++ )
    {
        //To retrieve information about a logon session, the caller must be the owner of the session or a local system administrator.
        PSECURITY_LOGON_SESSION_DATA ppLogonSessionData;
        Status = LsaGetLogonSessionData(p, & ppLogonSessionData);
        if (Status != STATUS_SUCCESS) {
            DisplayNtStatus("LsaGetLogonSessionData", Status);
            return RTN_ERROR;//break;
        }

        if (ppLogonSessionData->UserName.Length)
        {
            fprintf(stderr,"UserName:%wZ!\n", &ppLogonSessionData->UserName);
        }
        if (ppLogonSessionData->LogonDomain.Length)
        {
            fprintf(stderr,"LogonDomain:%wZ!\n", &ppLogonSessionData->LogonDomain);
        }
        if (ppLogonSessionData->AuthenticationPackage.Length)
        {
            fprintf(stderr,"AuthenticationPackage:%wZ!\n", &ppLogonSessionData->AuthenticationPackage);
        }

        if (ppLogonSessionData->LogonServer.Length)
        {
            fprintf(stderr,"LogonServer:%wZ!\n", &ppLogonSessionData->LogonServer);
        }
        if (ppLogonSessionData->DnsDomainName.Length)
        {
            fprintf(stderr,"DnsDomainName:%wZ!\n", &ppLogonSessionData->DnsDomainName);
        }
        if (ppLogonSessionData->Upn.Length)
        {
            fprintf(stderr,"Upn:%wZ!\n", &ppLogonSessionData->Upn);
        }

        if (ppLogonSessionData->LogonScript.Length)
        {
            fprintf(stderr,"LogonScript:%wZ!\n", &ppLogonSessionData->LogonScript);
        }
        if (ppLogonSessionData->ProfilePath.Length)
        {
            fprintf(stderr,"ProfilePath:%wZ!\n", &ppLogonSessionData->ProfilePath);
        }
        if (ppLogonSessionData->HomeDirectory.Length)
        {
            fprintf(stderr,"HomeDirectory:%wZ!\n", &ppLogonSessionData->HomeDirectory);
        }
        if (ppLogonSessionData->HomeDirectoryDrive.Length)
        {
            fprintf(stderr,"HomeDirectoryDrive:%wZ!\n", &ppLogonSessionData->HomeDirectoryDrive);
        }

        if (ppLogonSessionData->LogonType == Interactive)
        {
            fprintf(stderr,"LogonType:Interactive!\n");//有几个普通的帐户登录,这里就会显示几次。就是说可以显示多个。
        }
        else if (ppLogonSessionData->LogonType == Network)
        {
            fprintf(stderr,"LogonType:Network!\n");
        }
        else if (ppLogonSessionData->LogonType == Service)
        {
            fprintf(stderr,"LogonType:Service!\n");
        }
        else 
        {
            fprintf(stderr,"LogonType:%d!\n", ppLogonSessionData->LogonType);
        }        

        /*摘自:\Microsoft SDKs\Windows\v7.1A\Include\NTSecAPI.h
        //
        // Values for UserFlags.
        //

        #define LOGON_GUEST                 0x01
        #define LOGON_NOENCRYPTION          0x02
        #define LOGON_CACHED_ACCOUNT        0x04
        #define LOGON_USED_LM_PASSWORD      0x08
        #define LOGON_EXTRA_SIDS            0x20
        #define LOGON_SUBAUTH_SESSION_KEY   0x40
        #define LOGON_SERVER_TRUST_ACCOUNT  0x80
        #define LOGON_NTLMV2_ENABLED        0x100       // says DC understands NTLMv2
        #define LOGON_RESOURCE_GROUPS       0x200
        #define LOGON_PROFILE_PATH_RETURNED 0x400
        // Defined in Windows Server 2008 and above
        #define LOGON_NT_V2                 0x800   // NT response was used for validation
        #define LOGON_LM_V2                 0x1000  // LM response was used for validation
        #define LOGON_NTLM_V2               0x2000  // LM response was used to authenticate but NT response was used to derive the session key

        #if (_WIN32_WINNT >= 0x0600)

        #define LOGON_OPTIMIZED             0x4000  // this is an optimized logon
        #define LOGON_WINLOGON              0x8000  // the logon session was created for winlogon
        #define LOGON_PKINIT               0x10000  // Kerberos PKINIT extension was used to authenticate the user
        #define LOGON_NO_OPTIMIZED         0x20000  // optimized logon has been disabled for this account

        #endif
        */
        if (ppLogonSessionData->UserFlags & LOGON_WINLOGON)
        {
            fprintf(stderr,"UserFlags:LOGON_WINLOGON!\n");//经测试,感觉这个就是普通用户的登录。远程登录的没有试验。
        }
        //else if (ppLogonSessionData->UserFlags & LOGON_NTLMV2_ENABLED)
        //{
        //    fprintf(stderr,"UserFlags:LOGON_NTLMV2_ENABLED!\n");
        //}
        //else if (ppLogonSessionData->UserFlags == LOGON_EXTRA_SIDS)
        //{
        //    fprintf(stderr,"UserFlags:LOGON_EXTRA_SIDS!\n");
        //}
        else if (ppLogonSessionData->UserFlags == 0)
        {
            fprintf(stderr,"UserFlags:%d!\n", ppLogonSessionData->UserFlags);
        }        

        fprintf(stderr,"Session:%d!\n", ppLogonSessionData->Session);

        /*
        还有更多新信息就不打印了,如SID,时间的转换等。
        */

        fprintf(stderr,"\n");
        fprintf(stderr,"\n");

        Status = LsaFreeReturnBuffer(ppLogonSessionData);
        if(Status != STATUS_SUCCESS) {
            DisplayNtStatus(" LSAFreeReturnBuffer", Status);
            return RTN_ERROR;//break;
        }

        p ++;
    }

    Status = LsaFreeReturnBuffer(LogonSessionList);
    if(Status != STATUS_SUCCESS) {
        DisplayNtStatus(" LSAFreeReturnBuffer", Status);
        return RTN_ERROR;
    }   

    iRetVal=RTN_OK;

    return iRetVal;
}

2014年6月14日星期六

枚举用户(组)的特权

#include "stdafx.h"


/*
进程继承用户的权限。
程序员大多知道给进程提权。
但是有的进程的某些权限是不易搞到的。
如:Note that your process must have the SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME privileges for successful execution of CreateProcessAsUser. 
解决办法是用LsaAddAccountRights/LsaRemoveAccountRights给用户添加/删除权限。
然后在用常用的办法提权。
可是这要求注销/重启系统。

记得有个办法是不用重启的,但是忘了方法和链接了。
所以谨记此文。
此文的功能是枚举用户或者用户组的权限的。
此文修改自:Microsoft SDKs\Windows\v6.0\Samples\Security\LSAPolicy\LsaPrivs\LsaPrivs.c。

注意:Unlike privileges, however, account rights are not supported by the LookupPrivilegeValue and LookupPrivilegeName functions.
用户的特权很重要,如:
修改固件环境值。
加载驱动。
启动服务,服务一般不是普通用户运行的。
以操作系统方式执行。
更多信息,请看:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb545671(v=vs.85).aspx  Account Rights Constants
http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx  Privilege Constants
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/08bc7712-548c-4308-a49c-d551a4b5e245.mspx?mfr=true
等等。
 
更多的功能请看:
LsaEnumerateAccountsWithUserRight 
LsaEnumerateTrustedDomains
LsaEnumerateTrustedDomainsEx 
NetEnumerateServiceAccounts (Windows 7/Windows Server 2008 R2)

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


#ifndef UNICODE
#define UNICODE
#endif // UNICODE

#include <windows.h>
#include <stdio.h>

#include "ntsecapi.h" //好几个地方都有这个文件的。

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

// If you have the ddk, include ntstatus.h.
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif


BOOL GetAccountSid(LPTSTR SystemName, LPTSTR AccountName, PSID *Sid)
    /*++
    This function attempts to obtain a SID representing the supplied account on the supplied system.

    If the function succeeds, the return value is TRUE. 
    A buffer is allocated which contains the SID representing the supplied account.
    This buffer should be freed when it is no longer needed by calling HeapFree(GetProcessHeap(), 0, buffer)

    If the function fails, the return value is FALSE. Call GetLastError() to obtain extended error information.
    --*/
{
    LPTSTR ReferencedDomain=NULL;
    LPTSTR TempReferencedDomain = NULL;
    LPTSTR TempSid = NULL;
    DWORD cbSid=128;    // initial allocation attempt
    DWORD cchReferencedDomain=16; // initial allocation size
    SID_NAME_USE peUse;
    BOOL bSuccess=FALSE; // assume this function will fail

    __try
    {
        *Sid = (PSID)HeapAlloc(GetProcessHeap(), 0, cbSid);// initial memory allocations
        if(*Sid == NULL) __leave;

        ReferencedDomain = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, cchReferencedDomain * sizeof(TCHAR));
        if(ReferencedDomain == NULL) __leave;

        // Obtain the SID of the specified account on the specified system.
        while(!LookupAccountName(
            SystemName,         // machine to lookup account on
            AccountName,        // account to lookup
            *Sid,               // SID of interest
            &cbSid,             // size of SID
            ReferencedDomain,   // domain account was found on
            &cchReferencedDomain, &peUse)) 
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 
            {
                TempSid  = (LPTSTR)HeapReAlloc(GetProcessHeap(), 0, *Sid, cbSid);// reallocate memory
                if(TempSid  == NULL) __leave;
                *Sid = TempSid;

                TempReferencedDomain = (LPTSTR)HeapReAlloc(GetProcessHeap(), 0, ReferencedDomain, cchReferencedDomain * sizeof(TCHAR));

                if(TempReferencedDomain == NULL) __leave;
                ReferencedDomain = TempReferencedDomain;              
            }
            else __leave;
        }

        bSuccess=TRUE;// Indicate success.
    } // try
    __finally {// Cleanup and indicate failure, if appropriate.
        HeapFree(GetProcessHeap(), 0, ReferencedDomain);
        if(!bSuccess) {
            if(*Sid != NULL) {
                HeapFree(GetProcessHeap(), 0, *Sid);
                *Sid = NULL;
            }
        }
    } // finally

    return bSuccess;
}


void InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String)
{
    DWORD StringLength;

    if(String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;
        return;
    }

    StringLength = lstrlenW(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);
}


NTSTATUS OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle)
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server;

    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));// Always initialize the object attributes to all zeroes.
    if (ServerName != NULL) {// Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        InitLsaString(&ServerString, ServerName);
        Server = &ServerString;
    } else {
        Server = NULL;
    }

    return LsaOpenPolicy(Server, &ObjectAttributes, DesiredAccess, PolicyHandle);// Attempt to open the policy.
}


void DisplayWinError(LPSTR szAPI, DWORD WinError)
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    fprintf(stderr,"%s error!\n", szAPI);// TODO: Get this fprintf out of here!

    if(dwBufferLength=FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, WinError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &MessageBuffer, 0, NULL))
    {
        DWORD dwBytesWritten; // unused        
        WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, dwBufferLength, &dwBytesWritten, NULL);// Output message string on stderr.
        LocalFree(MessageBuffer);// Free the buffer allocated by the system.
    }
}


void DisplayNtStatus(LPSTR szAPI, NTSTATUS Status)
{
    DisplayWinError(szAPI, LsaNtStatusToWinError(Status));// Convert the NTSTATUS to Winerror. Then call DisplayWinError().
}


int __cdecl main(int argc, char *argv[])
{
    LSA_HANDLE PolicyHandle;
    WCHAR wComputerName[256]=L"";   // static machine name buffer
    TCHAR AccountName[256];         // static account name buffer
    PSID pSid;
    NTSTATUS Status;
    int iRetVal=RTN_ERROR;          // assume error from main

    if(argc == 1) {
        fprintf(stderr,"Usage: %s <Account> [TargetMachine]\n", argv[0]);
        return RTN_USAGE;
    }

    // Pick up account name on argv[1].
    // Assumes source is ANSI. Resultant string is ANSI or Unicode
    _snwprintf_s(AccountName, 256, 255, TEXT("%hS"), argv[1]);//可以是用户也可以是用户组,如:administrator或者administrators.

    // Pick up machine name on argv[2], if appropriate
    // assumes source is ANSI. Resultant string is Unicode.
    if(argc == 3) _snwprintf_s(wComputerName, 256, 255, L"%hS", argv[2]);//这个参数可以无。

    // Open the policy on the target machine. 
    Status = OpenPolicy(
        wComputerName,      // target machine
        POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
        &PolicyHandle       // resultant policy handle
        );
    if(Status != STATUS_SUCCESS) {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    // Obtain the SID of the user/group.
    // Note that we could target a specific machine, but we don't.
    // Specifying NULL for target machine searches for the SID in the following order: well-known, Built-in and local, primary domain,trusted domains.
    if(GetAccountSid(
        NULL,       // default lookup logic
        AccountName,// account to obtain SID
        &pSid       // buffer to allocate to contain resultant SID
        )) 
    {
        //这几行代码是自己的。
        PLSA_UNICODE_STRING UserRights;
        ULONG CountOfRights;
        Status = LsaEnumerateAccountRights(PolicyHandle, pSid, &UserRights, &CountOfRights);
        if(Status != STATUS_SUCCESS) {
            DisplayNtStatus("LsaEnumerateAccountRights", Status);
            return RTN_ERROR;
        }

        ULONG i = 0;
        for (PLSA_UNICODE_STRING plun = UserRights; i < CountOfRights; i++ )
        {
            fprintf(stderr,"%ws!\n", plun->Buffer);
            plun ++;
        }

        Status = LsaFreeMemory(&UserRights);
        if(Status != STATUS_SUCCESS) {
            DisplayNtStatus("LsaFreeMemory", Status);
            return RTN_ERROR;
        }

        iRetVal=RTN_OK;
    }
    else {
        DisplayWinError("GetAccountSid", GetLastError());// Error obtaining SID.
    }

    LsaClose(PolicyHandle);// Close the policy handle.
    if(pSid != NULL) HeapFree(GetProcessHeap(), 0, pSid);// Free memory allocated for SID.
    return iRetVal;
}

内核(对象管理器)中的systemroot

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

#define TAG 'tset' //test

/*
用户层常用%systemroot%,很是方便,驱动呢?
在对象目录的根目录下有个符号链接systemroot,其值是:"\Device\Harddisk0\Partition1\Windows"。
但是在XP的应用层和驱动都没有导出RtlExpandEnvironmentStrings_U。
本文就是演示这些东西的。

注意:
"\Device\Harddisk0\Partition1"
"\Device\HarddiskVolume1" 
"\\.\Harddisk0Partition1"
"\\.\PhysicalDrive0"
的关系。

如果不转换去打开文件的结果是://C0000034 系统找不到指定的文件。
用L"systemroot"或者L"%systemroot%"后面加文件去打开文件,返回的结果是:C000003b 指定的路径无效。

注意:
这个可以在系统启动的很早的时候获取,那时候文件驱动可能还没有加载,但是对象管理器已经初始化了。

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


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{   
    
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{ 
    ULONG ActualLength;
    HANDLE LinkHandle = 0;
    WCHAR NameBuffer[MAX_PATH];
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING LinkString, NameString, file_name, temp;
    NTSTATUS status = STATUS_SUCCESS;
    USHORT x = 0;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    LinkString.Buffer = NameBuffer;
    LinkString.MaximumLength = sizeof(NameBuffer);
    RtlZeroMemory(NameBuffer, sizeof(NameBuffer));

    RtlInitUnicodeString(&NameString, L"\\systemroot");//注意格式。
    InitializeObjectAttributes(&ObjectAttributes, &NameString, OBJ_KERNEL_HANDLE, NULL, NULL);
    status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY | GENERIC_READ , &ObjectAttributes);
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(("ZwOpenSymbolicLinkObject fail with 0x%X\n",status));
        ZwClose(LinkHandle);
        return status;
    }

    status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkString, &ActualLength);
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(("ZwQuerySymbolicLinkObject fail with 0x%X\n",status));
        ZwClose(LinkHandle);
        return status;
    }

    KdPrint(("%wZ \n",&LinkString));//得到的值形如:"\Device\Harddisk0\Partition1\Windows"。
    ZwClose(LinkHandle);


    /*
    还得对这个路径进行解析,对象管理器在下面的情况(ZwOpenFile)是不会解析的,而是返回C0000034 系统找不到指定的文件。
    注意:这是这偷懒不好的解析方式,仅仅演示用的。实际的工程要改进。
    */
    for (x = LinkString.Length; x > 0; x -= 2)
    {
        if (LinkString.Buffer[x/2] == L'\\')
        {
            break;
        }
    }

    temp.Buffer = &LinkString.Buffer[x/2];
    temp.Length = LinkString.Length - x;
    temp.MaximumLength = LinkString.MaximumLength - x;

    LinkString.Length = x; 
    //以上是把UNICODE_STRING以最后的斜杠一分为二。主要是因为转换后的数据也写入这里但是不会造成破坏。这是假定。

    InitializeObjectAttributes(&ObjectAttributes, &LinkString, OBJ_KERNEL_HANDLE, NULL, NULL);
    status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY | GENERIC_READ , &ObjectAttributes);
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(("ZwOpenSymbolicLinkObject fail with 0x%X\n",status));
        ZwClose(LinkHandle);
        return status;
    }

    status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkString, &ActualLength);
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(("ZwQuerySymbolicLinkObject fail with 0x%X\n",status));
        ZwClose(LinkHandle);
        return status;
    }

    KdPrint(("%wZ \n",&LinkString));//得到的值形如:"\Device\Harddisk0\Partition1\Windows"。
    ZwClose(LinkHandle);

    
    /*status = RtlAppendUnicodeToString(&LinkString, &LinkString.Buffer[x]);
    if (!NT_SUCCESS (status)) {
        KdPrint(("RtlAppendUnicodeToString fail with 0x%X!\n", status));
        return status;
    }*/

    status = RtlAppendUnicodeStringToString(&LinkString, &temp );
    if (!NT_SUCCESS (status)) {
        KdPrint(("RtlAppendUnicodeStringToString fail with 0x%X!\n", status));
        return status;
    }


    /*
    测试用例一:
    这个应该是成功的。
    */
    {
        UNICODE_STRING file_name, temp ;
        OBJECT_ATTRIBUTES ob;
        HANDLE FileHandle = 0;
        IO_STATUS_BLOCK  IoStatusBlock = {0};

        file_name.Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG);
        if (file_name.Buffer == NULL) { 
            DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
            return STATUS_UNSUCCESSFUL ;
        }
        RtlZeroMemory(file_name.Buffer, MAX_PATH);
        RtlInitEmptyUnicodeString(&file_name, file_name.Buffer,MAX_PATH);

        RtlCopyUnicodeString(&file_name, &LinkString);
        status = RtlAppendUnicodeToString(&file_name, L"\\regedit.exe");
        if (!NT_SUCCESS (status)) {
            KdPrint(("RtlAppendUnicodeToString fail with 0x%X!\n", status));
            if (file_name.Buffer)
            {
                ExFreePoolWithTag(file_name.Buffer, TAG);
                file_name.Buffer = NULL;
            }
            return status;
        }

        InitializeObjectAttributes(&ob, &file_name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
        status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
            if (file_name.Buffer)
            {
                ExFreePoolWithTag(file_name.Buffer, TAG);
                file_name.Buffer = NULL;
            }
            return status;
        }

        //测试结束的扫尾工作。

        if (file_name.Buffer)
        {
            ExFreePoolWithTag(file_name.Buffer, TAG);
            file_name.Buffer = NULL;
        }

        if (FileHandle)
        {
            status = ZwClose(FileHandle);
            if (!NT_SUCCESS (status)) {
                KdPrint(("ZwClose fail with 0x%x.\n", status));
            } else {
                FileHandle = NULL;
            }
        }
    }

    return status;
} 

2014年6月11日星期三

注册表的符号链接

#include "stdafx.h"

#include <windows.h>

/*
NTFS有硬链接。
注册表也有链接。

这些看似奇迹古怪的东西,有时候还必须会,有时候能起到意想不到的效果。

简单说一下创建注册表链接的要点:
1.用RegCreateKeyEx或者更底层的函数ZwCreateKey等加参数REG_OPTION_CREATE_LINK或者KEY_CREATE_LINK创建一个子键。
  注意:应该不可以嵌套。没有下一步,走到这里在注册表中打开会弹出错误提示的。
2.在这个子键下创建名字为:SymbolicLinkValue,类型为:REG_LINK,值为注册表的内核的路径的項就可以了。
3.删除吗用NtDeleteKey,注意传递的句柄的权限。

参考:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms724844(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff566437(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ee381413.aspx
http://msdn.microsoft.com/en-us/library/hh553920.aspx

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

typedef NTSTATUS (WINAPI * NtDeleteKey_FN) (IN HANDLE  KeyHandle);
NtDeleteKey_FN NtDeleteKey;

void __cdecl _tmain(void)
{
    MessageBox(0, L"请附加调试器", L"调试专用", 0);

    //注意位置及权限。
    HKEY hkResult;
    LONG L = RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"Software\\link", 0, NULL, REG_OPTION_CREATE_LINK | REG_OPTION_VOLATILE, KEY_ALL_ACCESS | KEY_CREATE_LINK, NULL, &hkResult, NULL);
    if (L != ERROR_SUCCESS)
    {
        MessageBox(0,0,0,0);
    }

    //注意名字及值。
    wchar_t * link = L"\\Registry\\MACHINE\\system\\CurrentControlSet";//这个必须是存在的,可以是系统的,也可以是自己创建的。
    L = RegSetValueEx(hkResult, L"SymbolicLinkValue", 0, REG_LINK, (BYTE *)link, (lstrlen(link))* sizeof(wchar_t));//不可 + 1
    if (L != ERROR_SUCCESS)
    {
        MessageBox(0,0,0,0);
    }

    //这个是删除不掉链接的,好像是和句柄及其属性有关。
    L = RegDeleteKey(HKEY_LOCAL_MACHINE, L"Software\\link");
    if (L != ERROR_SUCCESS)
    {
        MessageBox(0,0,0,0);
    }

    //这样也是删除不掉的。
    L = RegDeleteKey(hkResult, 0);
    if (L != ERROR_SUCCESS)
    {
        MessageBox(0,0,0,0);
    }

    /*
    看来只有这样删除了,注意传递给函数的句柄所具有的权限及属性。
    注意:
    1.这只删除的是这个链接,这个链接所代表的值及内容(键和子键等)没有删除。
    2.权限正确(REG_OPTION_OPEN_LINK)还可以删除某些知名的ARK不能删除的不正确的符号链接。
    */
    HMODULE hinstLib = LoadLibrary(TEXT("ntdll.dll")); // Get a handle to the DLL module.     
    if (hinstLib != NULL) // If the handle is valid, try to get the function address. 
    { 
        NtDeleteKey = (NtDeleteKey_FN) GetProcAddress(hinstLib, "NtDeleteKey");  
        if (NULL != NtDeleteKey) // If the function address is valid, call the function.
        {
            int status = NtDeleteKey(hkResult);
            if(status != 0)
            {
                printf("NtDeleteKey fail!");
            }
        }  else {
            printf("find NtDeleteKey fail");
        }

        FreeLibrary(hinstLib); // Free the DLL module.
    } else {
        printf("Load ntdll.dll fail");
    }

    //RegCloseKey(hkResult);
}

2014年6月4日星期三

驱动中枚举/遍历文件

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

#define TAG 'tset' //test

/*
文件名:ZwQueryDirectoryFile.C
功能:在驱动中遍历/枚举文件。

看似一个简单不值一看的功能,花费了我一天的时间,也许我太笨,所以有此记录。

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

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{  
   
}


NTSTATUS ZwEnumerateFile1(IN UNICODE_STRING * directory)// ,FILE_INFORMATION_CLASS fic
    /*
    功能:枚举目录下的文件。没有递归。

    输入参数:可以是路径,句柄,文件对象,这个可以修改,但不能是文件路径。

    路径的格式最好是:L"\\??\\C:\\Windows");或者"\Device\HarddiskVolume1\XXX  \\DosDevices\\C:\\,不然还得转换,结构最好用的UNICODE_STRING,这样安全。

    这个方法的思路是一下读取了一个目录下的所有信息。优缺点是不言而喻的,就是所需的内存的大小,你知道吗?

    其实,ZwEnumerateFile这个函数没有获取所需的内存的大小的功能,一个思路是结构的大小加路径的大小。
    */
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID FileInformation = 0;
    ULONG Length = sizeof (FILE_DIRECTORY_INFORMATION);//这个数设置的太小会导致ZwQueryDirectoryFile蓝屏。
    FILE_DIRECTORY_INFORMATION * fibdi = 0;

    InitializeObjectAttributes(&ob, directory, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
        if ( status == STATUS_OBJECT_NAME_NOT_FOUND || IoStatusBlock.Information == FILE_DOES_NOT_EXIST)  {
            KdPrint(("file does not exist\n"));
        }
        return status;
    }
   
    do
    {
        if (FileInformation)
        {
            ExFreePoolWithTag(FileInformation, TAG);
            FileInformation = NULL;
        }

        FileInformation = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);
        if (FileInformation == NULL) {
            status = STATUS_UNSUCCESSFUL;
            DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
            ZwClose(FileHandle);
            return status;
        }
        RtlZeroMemory(FileInformation, Length);

        status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, FALSE, NULL, TRUE);
        if (!NT_SUCCESS (status))
        {
            KdPrint(("ZwQueryDirectoryFile fail with 0x%x.\n", status));//STATUS_BUFFER_TOO_SMALL == C0000023
           
            //return status;
        }

        Length *= 2;
    } while (!NT_SUCCESS (status));

    for (fibdi = FileInformation ; ; fibdi = (FILE_DIRECTORY_INFORMATION *)((char *)fibdi + fibdi->NextEntryOffset))
    {
        UNICODE_STRING FileName ={0};

        if (FILE_ATTRIBUTE_DIRECTORY == fibdi->FileAttributes)
        {
            //这里可以考虑递归。这里放弃了文件夹的显示。
            continue;
        }

        FileName.Buffer = fibdi->FileName;
        FileName.Length = (USHORT)fibdi->FileNameLength;
        FileName.MaximumLength = FileName.Length + 2;

        KdPrint(("FileName %wZ\n", &FileName));  

        if (fibdi->NextEntryOffset == 0)
        {
            break;
        }
    }

    if (FileInformation)
    {
        ExFreePoolWithTag(FileInformation, TAG);
        FileInformation = NULL;
    }

    ZwClose(FileHandle);

    return status;
}


NTSTATUS ZwEnumerateFile2(IN UNICODE_STRING * directory)// ,FILE_INFORMATION_CLASS fic
    /*
    功能:枚举目录下的文件。没有递归。

    输入参数:可以是路径,句柄,文件对象,这个可以修改,但不能是文件路径。

    路径的格式最好是:L"\\??\\C:\\Windows");或者"\Device\HarddiskVolume1\XXX  \\DosDevices\\C:\\,不然还得转换,结构最好用的UNICODE_STRING,这样安全。

    这个是改进版。
    因为上面的函数,不可能确定目录下有多少目录,如系统目录下有几千个文件。
    这个办法是一个一个来的,参考了KMDKIT的第十一章教程。
    */
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID FileInformation = 0;
    ULONG Length = sizeof (FILE_DIRECTORY_INFORMATION);//这个数设置的太小会导致ZwQueryDirectoryFile蓝屏。  

    InitializeObjectAttributes(&ob, directory, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
        if ( status == STATUS_OBJECT_NAME_NOT_FOUND || IoStatusBlock.Information == FILE_DOES_NOT_EXIST)  {
            KdPrint(("file does not exist\n"));
        }
        return status;
    }

    Length = Length + 520;//为何加这个数字,请看ZwEnumerateFile1的说明。
    FileInformation = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);
    if (FileInformation == NULL) {
        status = STATUS_UNSUCCESSFUL;
        DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
        ZwClose(FileHandle);
        return status;
    }
    RtlZeroMemory(FileInformation, Length);

    status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, TRUE, NULL, TRUE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwQueryDirectoryFile fail with 0x%x.\n", status));//STATUS_BUFFER_TOO_SMALL == C0000023
        ExFreePoolWithTag(FileInformation, TAG);
        ZwClose(FileHandle);
        return status;
    }

    do
    {
        UNICODE_STRING FileName ={0};
        FILE_DIRECTORY_INFORMATION * fibdi = 0;

        status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, TRUE, NULL, FALSE);
        if (status != STATUS_NO_MORE_FILES && status != STATUS_SUCCESS)
        {
            break;//这里好像没有走过。
        }

        fibdi = FileInformation;

        if (FILE_ATTRIBUTE_DIRECTORY == fibdi->FileAttributes)
        {
            //这里可以考虑递归。这里放弃了文件夹的显示。
            continue;
        }

        FileName.Buffer = fibdi->FileName;
        FileName.Length = (USHORT)fibdi->FileNameLength;
        FileName.MaximumLength = FileName.Length + 2;

        KdPrint(("FileName %wZ\n", &FileName));  

    } while (status != STATUS_NO_MORE_FILES);

    if (FileInformation)
    {
        ExFreePoolWithTag(FileInformation, TAG);
        FileInformation = NULL;
    }

    ZwClose(FileHandle);

    return status;
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    //经测试,这三种类型的路径都是可以的。
    UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\??\\C:\\test");
    //UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\DosDevices\\C:\\test");
    //UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume1\\test");
    /*
    注意:
    \\Device\\HarddiskVolume1\\是可以的;
    \\Device\\HarddiskVolume1是不可以的。
    */

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    return ZwEnumerateFile2(&directory);//, FileIdBothDirectoryInformation
}