Win10遍历句柄表+修改权限过Callback保护

本帖转载于http://www.m5home.com/bbs/thread-8847-1-1.html
本想发到看雪,但自己太菜,看雪“牛人”又太多,想想还是发到紫水晶吧。
感谢 TA 的 WIN64 教程带我走上驱动之路,想想除了个 VIP 账号就没教过学费,以后多在论坛发帖来回报一下吧。

正篇:
XP 和 Win7 上关于句柄表的文章不少,一点不懂的朋友请自行百度谷歌搜索,方法没变,依旧使用 ExpLookupHandleTableEntry 。

主要说说 Win10 的变化,相较于 Win7 这两个结构体有所改变: HANDLE_TABLE 和 HANDLE_TABLE_ENTRY ,另外多了一个 HANDLE_TABLE_FREE_LIST 。

根据 WinDBG 逆向出的结构定义如下:
1: kd> dt nt!_HANDLE_TABLE

  • 0x000 NextHandleNeedingPool : Uint4B
  • 0x004 ExtraInfoPages : Int4B
  • 0x008 TableCode : Uint8B
  • 0x010 QuotaProcess : Ptr64 _EPROCESS
  • 0x018 HandleTableList : _LIST_ENTRY
  • 0x028 UniqueProcessId : Uint4B
  • 0x02c Flags : Uint4B
  • 0x02c StrictFIFO : Pos 0, 1 Bit
  • 0x02c EnableHandleExceptions : Pos 1, 1 Bit
  • 0x02c Rundown : Pos 2, 1 Bit
  • 0x02c Duplicated : Pos 3, 1 Bit
  • 0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit
  • 0x030 HandleContentionEvent : _EX_PUSH_LOCK
  • 0x038 HandleTableLock : _EX_PUSH_LOCK
  • 0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
  • 0x040 ActualEntry : [32] UChar
  • 0x060 DebugInfo : Ptr64 _HANDLE_TRACE_DEBUG_INFO

typedef struct _HANDLE_TABLE
{
ULONG32 NextHandleNeedingPool;
LONG32 ExtraInfoPages;
ULONG_PTR TableCode;
PEPROCESS QuotaProcess;
LIST_ENTRY HandleTableList;
ULONG32 UniqueProcessId;
union
{
ULONG32 Flags;
struct
{
BOOLEAN StrictFIFO : 1;
BOOLEAN EnableHandleExceptions : 1;
BOOLEAN Rundown : 1;
BOOLEAN Duplicated : 1;
BOOLEAN RaiseUMExceptionOnInvalidHandleClose : 1;
};
};
ULONG_PTR HandleContentionEvent;
ULONG_PTR HandleTableLock;
union
{
HANDLE_TABLE_FREE_LIST FreeLists[1];
BOOLEAN ActualEntry[32];
};
PVOID DebugInfo;
} HANDLE_TABLE, *PHANDLE_TABLE;
复制代码
1: kd> dt nt!_HANDLE_TABLE_ENTRY

  • 0x000 VolatileLowValue : Int8B
  • 0x000 LowValue : Int8B
  • 0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO
  • 0x008 HighValue : Int8B
  • 0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
  • 0x008 LeafHandleValue : _EXHANDLE
  • 0x000 RefCountField : Int8B
  • 0x000 Unlocked : Pos 0, 1 Bit
  • 0x000 RefCnt : Pos 1, 16 Bits
  • 0x000 Attributes : Pos 17, 3 Bits
  • 0x000 ObjectPointerBits : Pos 20, 44 Bits
  • 0x008 GrantedAccessBits : Pos 0, 25 Bits
  • 0x008 NoRightsUpgrade : Pos 25, 1 Bit
  • 0x008 Spare1 : Pos 26, 6 Bits
  • 0x00c Spare2 : Uint4B

typedef struct _HANDLE_TABLE_ENTRY
{
union
{
LONG_PTR VolatileLowValue;
LONG_PTR LowValue;
PVOID InfoTable;
LONG_PTR RefCountField;
struct
{
ULONG_PTR Unlocked : 1;
ULONG_PTR RefCnt : 16;
ULONG_PTR Attributes : 3;
ULONG_PTR ObjectPointerBits : 44;
};
};
union
{
LONG_PTR HighValue;
struct _HANDLE_TABLE_ENTRY *NextFreeHandleEntry;
EXHANDLE LeafHandleValue;
struct
{
ULONG32 GrantedAccessBits : 25;
ULONG32 NoRightsUpgrade : 1;
ULONG32 Spare1 : 6;
};
ULONG32 Spare2;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
复制代码
1: kd> dt nt!_HANDLE_TABLE_FREE_LIST

  • 0x000 FreeListLock : _EX_PUSH_LOCK
  • 0x008 FirstFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
  • 0x010 LastFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
  • 0x018 HandleCount : Int4B
  • 0x01c HighWaterMark : Uint4B
  • 0x020 Reserved : [8] Uint4B

typedef struct _HANDLE_TABLE_FREE_LIST
{
ULONG_PTR FreeListLock;
PHANDLE_TABLE_ENTRY FirstFreeHandleEntry;
PHANDLE_TABLE_ENTRY lastFreeHandleEntry;
LONG32 HandleCount;
ULONG32 HighWaterMark;
ULONG32 Reserved[8];
} HANDLE_TABLE_FREE_LIST, *PHANDLE_TABLE_FREE_LIST;
复制代码

只是一部分成员的偏移变化了,要注意的是表示对象地址的成员移到了 ObjectPointerBits 上,长度为 44 位的数据。
Win10上内核对象的地址算法:
Object = Entry->ObjectPointerBits;
Object <<= 4;
Object |= 0xFFFF000000000000;
Object += 0x30;
复制代码

另外对象权限也变到了 GrantedAccessBits 成员中,长度为 25 位的数据。

关于 ExpLookupHandleTableEntry 的实现上,相较 Win7 的版本仅仅优化了下代码逻辑,可以说没有变化。
直接抄 WRK 的代码,建议多看几遍代码,配合网上的其他帖子理解一下句柄表结构,我就不废话了(懒)。

本来想写一大堆,但感觉都是废话删掉了,结果就变成这么一篇偷懒贴,干脆直接上核心代码吧:
NTSTATUS ViewHandle(ULONG32 ProcessId, POBJECT_INFO Buffer)
{
PEPROCESS EProcess = NULL;
ULONG_PTR Handle = 0;
PHANDLE_TABLE_ENTRY Entry = NULL;
PVOID Object = NULL;
POBJECT_TYPE ObjectType = NULL;

    if (!NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)ProcessId, &EProcess)))
    {
            return STATUS_UNSUCCESSFUL;
    }

    for (Handle = 0;; Handle += HANDLE_VALUE_INC)
    {
            Entry = ExpLookupHandleTableEntry(*(PHANDLE_TABLE*)((PUCHAR)EProcess + g_HandleTableOffset), *(PEXHANDLE)&Handle);
            if (Entry == NULL)
            {
                    break;
            }

            *(ULONG_PTR*)&Object = Entry->ObjectPointerBits;
            *(ULONG_PTR*)&Object <<= 4;
            if (Object == NULL)
            {
                    continue;
            }

            *(ULONG_PTR*)&Object |= 0xFFFF000000000000;
            *(ULONG_PTR*)&Object += 0x30;
            ObjectType = ObGetObjectType(Object);
            if (ObjectType == NULL)
            {
                    continue;
            }

            wcscpy(Buffer->szTypeName, *(PCWSTR*)((PUCHAR)ObjectType + 0x18));
            Buffer->Handle = (HANDLE)Handle;
            Buffer->AccessMask = Entry->GrantedAccessBits;
            Buffer->Address = Object;

            Buffer++;
    }

    ObDereferenceObject(EProcess);

    return STATUS_SUCCESS;

}
复制代码

效果是这样的,对照WIN64AST:

这点破东西貌似不够看,再加个然并卵的玩意。

修改进程句柄权限过 Callback 保护:
if (wcscmp((PCWSTR)((PUCHAR)ObjectType + 0x18), L"Process") == 0)
{
if (*(PULONG32)((PUCHAR)Object + 0x2E8) == PassiveId)
{
Entry->GrantedAccessBits = 0x1FFFFF;
Status = STATUS_SUCCESS;
}
}
复制代码

拿 TP 举例,通常情况下,在 Ring3 调用 OpenProcess 传递 PROCESS_ALL_ACCESS 参数,该句柄对应的进程对象权限是 0x1FFFFF ,但对 TP 保护的游戏进行同样做法后会发现权限变成了 0x1FFD85,它的 Callback 中抹掉了进程对象的这些权限:
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_DUP_HANDLE (0x0040)
#define PROCESS_SET_INFORMATION (0x0200)
复制代码

直接在 TP 的 Callback 中写 ret 游戏会封号,而且 TP 卸载时会蓝屏,内部通信肯定是有的,尝试过逆向,一堆 jmp 把我弄晕了,干脆就换种办法。

如上代码,我的做法是,遍历句柄表,判断对象类型为 “PROCESS” ,然后根据 EPROCESS 对象获得对应进程进程的 PID ,如果是被保护的进程就把权限写成 0x1FFFFF ,测试过 DXF 有效。

效果是这样的,恢复权限之前和之后分别为:

听说这是一种比较二逼的解决方法,没办法谁让我菜呢。如果有更好的办法还望不吝赐教。

您可以使用FindWindowEx函数来遍历窗口句柄。FindWindowEx函数用于在父窗口下查找具有指定类名和窗口名的子窗口。 下面是一个示例代码,演示如何使用FindWindowEx来遍历窗口句柄: ```cpp #include <windows.h> void EnumerateChildWindows(HWND hWnd) { HWND hChildWnd = NULL; hChildWnd = FindWindowEx(hWnd, hChildWnd, NULL, NULL); while (hChildWnd != NULL) { // 处理子窗口 // 获取子窗口的类名 char className[256]; GetClassName(hChildWnd, className, sizeof(className)); // 获取子窗口的标题 char windowTitle[256]; GetWindowText(hChildWnd, windowTitle, sizeof(windowTitle)); // 打印子窗口信息 printf("Class Name: %s\n", className); printf("Window Title: %s\n", windowTitle); // 递归处理子窗口的子窗口 EnumerateChildWindows(hChildWnd); // 查找下一个子窗口 hChildWnd = FindWindowEx(hWnd, hChildWnd, NULL, NULL); } } int main() { HWND hParentWnd = GetDesktopWindow(); EnumerateChildWindows(hParentWnd); return 0; } ``` 在上述示例中,我们首先获取了桌面的窗口句柄作为父窗口。然后,我们定义了一个递归函数EnumerateChildWindows,该函数使用FindWindowEx函数来查找父窗口下的子窗口,并打印子窗口的类名和标题。然后,我们递归调用该函数来处理每个子窗口的子窗口,以遍历所有层级的窗口。 请注意,此示例代码是使用C++编写的,可以在Windows平台上进行编译和运行。您可能需要包含Windows.h头文件并链接用户32.lib库来使用相关函数和类型。 希望对您有帮助!如果您有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值