首页
社区
课程
招聘
[原创]R3下取伪句柄表的几种方式
2018-4-7 12:26 7022

[原创]R3下取伪句柄表的几种方式

2018-4-7 12:26
7022

R3下取伪句柄表的几种方式

 

最近碰到一个简单的老游戏,看了一下它的防多开就是通过 CreateFileMapping 函数以只读 方式创建了个文件映射对象 就和互斥体一样,本来劫持个DLL hook下就完事了,但是发现只有几个陌生的DLL可劫持 重点在于这个DLL加载时候对多开的检测已经完成,这就十分尴尬了。。。只好通过远程关闭文件映射句柄来实现多开 然后发现既然DLL可以劫持,又再句柄创建完成后,何不用DLL劫持来干掉句柄,这就滋生出来了几种操作了...

 

好了废话不多谈,开始写了,只适合小白看的基础性文章~~


 

目录

一、句柄(HANDLE)是什么?

句柄(Handle)是Windows表示对象的(不是C++对象)
HWND就是其中一种,表示为窗口句柄。还有其他很多如图标句柄(hIco)、光标句柄(hCursor)、线程句柄(hThread)等等..

https://blog.csdn.net/shuyong1999/article/details/7171683
https://blog.csdn.net/ustbkuang/article/details/77862720
https://blog.csdn.net/maowei117/article/details/55254855
上面的为各大牛们对句柄的一些理解

 

对于句柄来说,我们只要获得了目标的句柄就可以为所欲为了,比如获得有权限的进程句柄就可以读写目标进程数据了(有保护的当我没说...),窗口句柄可以向指定窗口发送消息,线程句柄可以结束(Terminate)或是挂起Suspend指定线程(不是本地进程当我没说...)
当然对于互斥体句柄(Mutant)或是Section类型的句柄还是可以给它关闭为所欲为的。

二、R3下获取句柄的几种方式

我们知道系统层有句柄表,而用户层也能靠3环的几个函数获取:

  • 通过 ZwQuerySystemInformation 函数获取


    此为查询系统信息的函数,当 SystemInformationClass 参数为 SystemHandleInformation(0x10)时查询系统句柄信息,
    SystemInformationClass 如下定义:**
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation,              // 0        Y        N
    SystemProcessorInformation,          // 1        Y        N
    SystemPerformanceInformation,        // 2        Y        N
    SystemTimeOfDayInformation,          // 3        Y        N
    SystemNotImplemented1,               // 4        Y        N
    SystemProcessesAndThreadsInformation, // 5       Y        N
    SystemCallCounts,                    // 6        Y        N
    SystemConfigurationInformation,      // 7        Y        N
    SystemProcessorTimes,                // 8        Y        N
    SystemGlobalFlag,                    // 9        Y        Y
    SystemNotImplemented2,               // 10       Y        N
    SystemModuleInformation,             // 11       Y        N
    SystemLockInformation,               // 12       Y        N
    SystemNotImplemented3,               // 13       Y        N
    SystemNotImplemented4,               // 14       Y        N
    SystemNotImplemented5,               // 15       Y        N
    SystemHandleInformation,             // 16       Y        N
    SystemObjectInformation,             // 17       Y        N
    SystemPagefileInformation,           // 18       Y        N
    SystemInstructionEmulationCounts,    // 19       Y        N
    SystemInvalidInfoClass1,             // 20
    SystemCacheInformation,              // 21       Y        Y
    SystemPoolTagInformation,            // 22       Y        N
    SystemProcessorStatistics,           // 23       Y        N
    SystemDpcInformation,                // 24       Y        Y
    SystemNotImplemented6,               // 25       Y        N
    SystemLoadImage,                     // 26       N        Y
    SystemUnloadImage,                   // 27       N        Y
    SystemTimeAdjustment,                // 28       Y        Y
    SystemNotImplemented7,               // 29       Y        N
    SystemNotImplemented8,               // 30       Y        N
    SystemNotImplemented9,               // 31       Y        N
    SystemCrashDumpInformation,          // 32       Y        N
    SystemExceptionInformation,          // 33       Y        N
    SystemCrashDumpStateInformation,     // 34       Y        Y/N
    SystemKernelDebuggerInformation,     // 35       Y        N
    SystemContextSwitchInformation,      // 36       Y        N
    SystemRegistryQuotaInformation,      // 37       Y        Y
    SystemLoadAndCallImage,              // 38       N        Y
    SystemPrioritySeparation,            // 39       N        Y
    SystemNotImplemented10,              // 40       Y        N
    SystemNotImplemented11,              // 41       Y        N
    SystemInvalidInfoClass2,             // 42
    SystemInvalidInfoClass3,             // 43
    SystemTimeZoneInformation,           // 44       Y        N
    SystemLookasideInformation,          // 45       Y        N
    SystemSetTimeSlipEvent,              // 46       N        Y
    SystemCreateSession,                 // 47       N        Y
    SystemDeleteSession,                 // 48       N        Y
    SystemInvalidInfoClass4,             // 49
    SystemRangeStartInformation,         // 50       Y        N
    SystemVerifierInformation,           // 51       Y        Y
    SystemAddVerifier,                   // 52       N        Y
    SystemSessionProcessesInformation    // 53       Y        N
} SYSTEM_INFORMATION_CLASS;

每一个enum元素对应着查询信息,而 SystemHandleInformation(0x10) 查询的为 SYSTEM_HANDLE_INFORMATIO 结构体指针的信息:

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG            ProcessId;    //进程ID
    UCHAR            ObjectTypeNumber;
    UCHAR            Flags;
    USHORT            Handle;    //句柄
    PVOID            Object;    //句柄对象
    ACCESS_MASK        GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG NumberOfHandles;    //数组数量
    SYSTEM_HANDLE Information[1];    //数组指针
}SYSTEM_HANDLE_INFORMATIO, *PSYSTEM_HANDLE_INFORMATION;

到了这里思路就很清晰了,那么怎么才能区分句柄获得我们想要的句柄呢 ?这里就用到另外一个函数 NtQueryObject :

NtQueryObject 函数用来查询对象句柄信息,当 OBJECT_INFORMATION_CLASS参数为ObjectNameInformation(1)和ObjectTypeInformation(2)时分别查询句柄的名称和句柄类型 它们的定义和查询的结构体如下:

typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectAllInformation,
    ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
//注释
//ObjectBasicInformation 对应结构为:OBJECT_BASIC_INFORMATION
//ObjectNameInformation 对应结构为:OBJECT_NAME_INFORMATION
//ObjectTypeInformation  对应结构为:OBJECT_TYPE_INFORMATION
//ObjectAllInformation  对应结构为:   OBJECT_ALL_INFORMATION
//ObjectDataInformation对应结构为:  OBJECT_DATA_INFORMATION

typedef struct
{
    USHORT Length;    //当前名称长度
    USHORT MaxLen;    //缓冲区最大长度
    USHORT *Buffer;    //Unicode 名称指针
}UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_NAME_INFORMATION {
    UNICODE_STRING          Name;
    WCHAR                   NameBuffer[0];
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING          TypeName;
    ULONG                   TotalNumberOfHandles;
    ULONG                   TotalNumberOfObjects;
    WCHAR                   Unused1[8];
    ULONG                   HighWaterNumberOfHandles;
    ULONG                   HighWaterNumberOfObjects;
    WCHAR                   Unused2[8];
    ACCESS_MASK             InvalidAttributes;
    GENERIC_MAPPING         GenericMapping;
    ACCESS_MASK             ValidAttributes;
    BOOLEAN                 SecurityRequired;
    BOOLEAN                 MaintainHandleCount;
    USHORT                  MaintainTypeList;
    POOL_TYPE               PoolType;
    ULONG                   DefaultPagedPoolCharge;
    ULONG                   DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

到了这里就明了很多了,下面写个简单的Demo实现获取目的进程的句柄:

// 进程提权
bool EnableDebugPrivilege()
{
    HANDLE hToken;
    LUID sedebugnameValue;
    TOKEN_PRIVILEGES tkp;
    if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) )
    {
        return   FALSE;
    }
    if( !LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue) )
    {
        CloseHandle(hToken);
        return false;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = sedebugnameValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if( !AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL) )
    {
        CloseHandle(hToken);
        return false;
    }
    return true;
}
void LogOut(char *Format, ...)
{
    char szBuffer[1024];
    va_list pArgList;
    va_start(pArgList, Format);//参数列表初始化
    vsprintf_s(szBuffer, Format, pArgList);
    va_end(pArgList);
    OutputDebugString(szBuffer);
}

//初始化未文档化函数
BOOL InitUnDocumentProc()
{
    HMODULE hNtdll = GetModuleHandle("Ntdll.dll");
    if( hNtdll == NULL )    return FALSE;

    ZwQuerySystemInformation = \
        (pfnNtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
    ZwQueryObject = \
        (pfnNtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");
    ZwQueryInformationProcess = \
        (pfnNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");

    if( (ZwQuerySystemInformation == NULL) || \
        (ZwQueryObject == NULL) || \
        (ZwQueryInformationProcess == NULL) )
        return FALSE;
    return TRUE;
}

SYSTEM_HANDLE_INFORMATIO_EX *GetSystemProcessHandleInfo()
{
    DWORD buffLen = 0x1000;
    NTSTATUS status;
    BYTE* buff = new BYTE[buffLen];
    do{
        status = ZwQuerySystemInformation(SystemHandleInformation, buff, buffLen, &buffLen);
        if( status == STATUS_INFO_LENGTH_MISMATCH )
        {
            delete[] buff;
            buff = new BYTE[buffLen];
        } else
            break;

    } while( TRUE );
    return (SYSTEM_HANDLE_INFORMATIO_EX*)buff;
}
int _tmain(int argc, _TCHAR* argv[])
{
    EnableDebugPrivilege();
    InitUnDocumentProc();
    NTSTATUS Status;
    SYSTEM_HANDLE* CurHandle;
    OBJECT_NAME_INFORMATION *ObjectName;
    OBJECT_TYPE_INFORMATION *ObjectType;
    char BufferForObjectName[1024];
    char BufferForObjectType[1024];
    SYSTEM_HANDLE_INFORMATIO_EX *pInfo = GetSystemProcessHandleInfo();
    if( pInfo )
    {
        for( DWORD i = 0; i < pInfo->NumberOfHandles; i++ )
        {
            CurHandle = &(pInfo->Information[i]);
            if( CurHandle->ProcessId==GetCurrentProcessId())//自进程
            {
                ZeroMemory(BufferForObjectName, 1024);
                ZeroMemory(BufferForObjectType, 1024);
                //获取句柄类型
                Status = ZwQueryObject((HANDLE)CurHandle->Handle, 
                    ObjectTypeInformation,
                    BufferForObjectType, 
                    sizeof(BufferForObjectType), 
                    NULL);

                ObjectType = (OBJECT_TYPE_INFORMATION*)BufferForObjectType;
                //if( Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status) )
                //    continue;

                //获取句柄名
                Status=ZwQueryObject((HANDLE)CurHandle->Handle, 
                    ObjectNameInformation,
                    BufferForObjectName, 
                    sizeof(BufferForObjectName), 
                    NULL);

                ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;
                //if( Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status) )
                //    continue;
                LogOut("Type:%S Name:%S Handle=%X", ObjectType->TypeName.Buffer,
                    ObjectName->Name.Buffer,
                    CurHandle->Handle);
            }
        }
        delete[] pInfo;
    }
    return 0;
}

上面写个获取自身进程句柄的整个逻辑,下面还有一种获取指定进程伪句柄表的方法

  • 通过 ZwQueryInformationProcess 函数获取


    这个为查询指定进程信息的函数,当 ProcessInformationClass参数为ProcessHandleCount(20) 时候查询进程的句柄引用计数(Count)
typedef enum _PROCESS_INFORMATION_CLASS {
    ProcessBasicInformation,
    ProcessQuotaLimits,
    ProcessIoCounters,
    ProcessVmCounters,
    ProcessTimes,
    ProcessBasePriority,
    ProcessRaisePriority,
    ProcessDebugPort,
    ProcessExceptionPort,
    ProcessAccessToken,
    ProcessLdtInformation,
    ProcessLdtSize,
    ProcessDefaultHardErrorMode,
    ProcessIoPortHandlers,
    ProcessPooledUsageAndLimits,
    ProcessWorkingSetWatch,
    ProcessUserModeIOPL,
    ProcessEnableAlignmentFaultFixup,
    ProcessPriorityClass,
    ProcessWx86Information,
    ProcessHandleCount,
    ProcessAffinityMask,
    ProcessPriorityBoost,
    MaxProcessInfoClass
} PROCESS_INFORMATION_CLASS, *PPROCESS_INFORMATION_CLASS;

查询这个干嘛呢?我们接下来穷举目标进程句柄。没错,是穷举...刚开始是在博客里看到一位大牛写的,发现他思路很清晰,但是代码有点问题,都把逻辑给混淆了。
句柄是以4开始,以4为单位递增...:

从上图不难发现都是句柄值以4递增的,我们穷举的话判断句柄的有效性就可以用函数DuplicateHandle函数实现拷贝有效句柄,失败返回FALSE 从而就可以判断句柄的有效性了。

int _tmain(int argc, _TCHAR* argv[])
{
    EnableDebugPrivilege();
    InitUnDocumentProc();
    NTSTATUS Status;
    HANDLE hSource = NULL;
    HANDLE hDuplicate = NULL;
    DWORD HandleCount;
    OBJECT_NAME_INFORMATION *ObjectName;
    OBJECT_TYPE_INFORMATION *ObjectType;
    char BufferForObjectName[1024];
    char BufferForObjectType[1024];


    hSource = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_DUP_HANDLE | PROCESS_SUSPEND_RESUME, FALSE, GetCurrentProcessId());
    if( hSource != NULL )
    {
        DWORD dwHandle;
        Status = ZwQueryInformationProcess(hSource, ProcessHandleCount, &HandleCount, sizeof(HandleCount), NULL);

        for( DWORD i = 1; i <= HandleCount; i++ )//穷举句柄
        {
            dwHandle = i * 4;
            if( DuplicateHandle(hSource, //复制一个句柄对象 && 判断此句柄是否有效
                (HANDLE)dwHandle,
                GetCurrentProcess(),
                &hDuplicate,
                0, FALSE, DUPLICATE_SAME_ACCESS) )
            {
                ZeroMemory(BufferForObjectName, 1024);
                ZeroMemory(BufferForObjectType, 1024);

                //获取句柄类型
                Status = ZwQueryObject(hDuplicate,
                    ObjectTypeInformation,
                    BufferForObjectType,
                    sizeof(BufferForObjectType),
                    NULL);

                ObjectType = (OBJECT_TYPE_INFORMATION*)BufferForObjectType;
                if( Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status) )
                    continue;

                //获取句柄名
                Status = ZwQueryObject((HANDLE)hDuplicate,
                    ObjectNameInformation,
                    BufferForObjectName,
                    sizeof(BufferForObjectName),
                    NULL);

                //关闭复制的句柄
                CloseHandle(hDuplicate);
                ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;
                if( Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status) )
                    continue;

                printf("Type:%S|Name:%S|Handle:%X\n", ObjectType->TypeName.Buffer,
                    ObjectName->Name.Buffer,hDuplicate);

            }
        }
        CloseHandle(hSource);
    }
    return FALSE;
    return 0;

三、关闭句柄的两种方式

到这里就很简单了,上述已经获得了想到的句柄,但是关闭句柄也有两种不同的情况,本地句柄调用函数CloseHandle即可,而远程的句柄如何关闭呢 ?(就像知道目标的Patch地址而无法在本地执行操作一样),实际调用函数DuplicateHandle即可。

  • 本地进程句柄的关闭

  • 远程进程句柄的关闭


    这里我们关注的是函数DuplicateHandle的最后一个参数,我们查下msdn的定义:

    DUPLICATE_CLOSE_SOURCE(0x00000001) Closes the source handle. This occurs regardless of any error status returned.
    DUPLICATE_SAME_ACCESS(0x00000002) Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.


四、小结:

对于远程和本地两种情况都有两种取的句柄的方法,从而针对不同的情况有4种方法,还有通过DuplicateHandle复制的句柄记得CloseHandle,这样才可以实现跨进程关闭句柄,本地的进程不用DuplicateHandle直接可以关闭,有点啰嗦了...

 

这里就不贴代码了,都打包在附件上。不然显得有点...占空间了...

 

Ps:

  • 这玩意不是网上有很多资料吗 ?为什么你还要发帖?

大牛们对于这种小问题都是一笔带过的,只给了我们这些小白点关键代码和思路,好让我这些 “跑龙套” 方便整理。。。
好了不瞎扯了,上面是一方面,另一方面是还是自己的代码看的习惯.....主要是在学 Markdown 哈哈哈!!

 

上述如有有误,还请各位指证!
如有例外,还请各位大侠补充!


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2018-4-7 13:41 被AperOdry编辑 ,原因:
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (4)
雪    币: 775
活跃值: (2292)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
AperOdry 2018-4-7 12:30
2
0
锚点各位怎么做的呀  ,初学Markdown请指教.
雪    币: 775
活跃值: (2292)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
AperOdry 2018-4-7 13:48
3
0
找到了  [TOC]标签就是了  还可以自动排列
雪    币: 914
活跃值: (2188)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
万剑归宗 1 2018-4-7 14:41
4
0
文章整理的不错,但是打上原创,有点过分
雪    币: 799
活跃值: (452)
能力值: ( LV12,RANK:280 )
在线值:
发帖
回帖
粉丝
Ox9A82 3 2018-9-4 17:19
5
0
总结不错
游客
登录 | 注册 方可回帖
返回