基于以下两点,是否可以打开一些内核对象:
-
对象安全描述符
-
调用方令牌(线程令牌(如果存在),否则处理令牌)
通常线程可以打开自身句柄,但也可以是例外,一个是系统创建的线程,用于处理控制台控制信号。
复制的最小代码(
c类++
):
HANDLE g_hEvent;
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
if (CTRL_C_EVENT == dwCtrlType)
{
if (HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION,
FALSE, GetCurrentThreadId()))
{
CloseHandle(hThread);
}
else GetLastError();
SetEvent(g_hEvent);
}
return TRUE;
}
和来自控制台应用程序调用
if (g_hEvent = CreateEvent(0, TRUE, FALSE, 0))
{
if (SetConsoleCtrlHandler(HandlerRoutine, TRUE))
{
// send ctrl+c, for not manually do this
if (GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0))
{
WaitForSingleObject(g_hEvent, INFINITE);
}
SetConsoleCtrlHandler(HandlerRoutine, FALSE);
}
CloseHandle(g_hEvent);
}
可以在测试视图中
OpenThread(THREAD_QUERY_INFORMATION, FALSE, GetCurrentThreadId())
失败,出现错误-
ERROR_ACCESS_DENIED
为什么会这样?需要查找线程安全描述符。简单的代码如下所示:
void DumpObjectSD(HANDLE hObject = GetCurrentThread())
{
ULONG cb = 0, rcb = 0x40;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
PSECURITY_DESCRIPTOR psd = 0;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(psd = alloca(rcb - cb), stack);
}
if (GetKernelObjectSecurity(hObject,
OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION,
psd, cb, &rcb))
{
PWSTR sz;
if (ConvertSecurityDescriptorToStringSecurityDescriptor(psd, SDDL_REVISION_1,
OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION, &sz, &rcb))
{
DbgPrint("%S\n", sz);
LocalFree(sz);
}
break;
}
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
}
并从控制台处理程序线程和普通线程(第一个线程)调用此函数进行比较。
这个
SD公司
通常的进程线程可以如下所示:
对于非提升工艺:
O:S-1-5-21-*
D:(A;;0x1fffff;;;S-1-5-21-*)(A;;0x1fffff;;;SY)(A;;0x121848;;;S-1-5-5-0-LogonSessionId)
S:AI(ML;;NWNR;;;ME)
或用于提升(以管理员身份运行)
O:BA
D:(A;;0x1fffff;;;BA)(A;;0x1fffff;;;SY)(A;;0x121848;;;S-1-5-5-0-LogonSessionId)
S:AI(ML;;NWNR;;;HI)
但当从处理程序线程(由系统自动创建)调用时,我们得到了另一个dacl:
对于非高架:
O:BA
D:(A;;0x1fffff;;;S-1-5-21-*)(A;;0x1fffff;;;SY)(A;;0x121848;;;S-1-5-5-0-LogonSessionId)
S:AI(ML;;NWNR;;;SI)
对于高架:
O:BA
D:(A;;0x1fffff;;;BA)(A;;0x1fffff;;;SY)(A;;0x121848;;;S-1-5-5-0-LogonSessionId)
S:AI(ML;;NWNR;;;SI)
这里不同
SYSTEM_MANDATORY_LABEL
S:AI(ML;;NWNR;;;SI)
这个
"ML"
这里是
SDDL_MANDATORY_LABEL
(
SYSTEM_MANDATORY_LABEL_ACE_TYPE
)
强制性标签权利:
"NW"
-
SDDL_NO_WRITE_UP
(
SYSTEM_MANDATORY_LABEL_NO_WRITE_UP
)
"NR"
-
SDDL_NO_READ_UP
(
SYSTEM_MANDATORY_LABEL_NO_READ_UP
)
和point main-标签
value
(sid):
处理程序线程始终具有
"SI"
-
SDDL_ML_SYSTEM
-系统完整性级别。
而普通线程
"ME"
-
SDDL_MLMEDIUM
-中等完整性级别或
"HI"
-
SDDL_ML_HIGH
-以管理员身份运行时的高完整性级别
因此,由于此线程在令牌中的完整性级别(系统)高于通常的进程完整性级别(如果不是系统进程,则为高完整性级别或低完整性级别),并且没有读写权限,因此我们无法使用读写访问权限打开此线程,只能使用执行访问权限。
我们可以在
HandlerRoutine
-尝试使用打开线程
MAXIMUM_ALLOWED
并使用
NtQueryObject
(使用
ObjectBasicInformation
)
if (HANDLE hThread = OpenThread(MAXIMUM_ALLOWED, FALSE, GetCurrentThreadId()))
{
OBJECT_BASIC_INFORMATION obi;
if (0 <= ZwQueryObject(hThread, ObjectBasicInformation, &obi, sizeof(obi), 0))
{
DbgPrint("[%08x]\n", obi.GrantedAccess);
}
CloseHandle(hThread);
}
我们来到这里:
[00101800]
这意味着:
SYNCHRONIZE | THREAD_RESUME | THREAD_QUERY_LIMITED_INFORMATION
我们还可以查询
ObjectTypeInformation
并获得
GENERIC_MAPPING
对于线程对象。
OBJECT_BASIC_INFORMATION obi;
if (0 <= ZwQueryObject(hThread, ObjectBasicInformation, &obi, sizeof(obi), 0))
{
ULONG rcb, cb = (obi.TypeInfoSize + __alignof(OBJECT_TYPE_INFORMATION) - 1) & ~(__alignof(OBJECT_TYPE_INFORMATION) - 1);
POBJECT_TYPE_INFORMATION poti = (POBJECT_TYPE_INFORMATION)alloca(cb);
if (0 <= ZwQueryObject(hThread, ObjectTypeInformation, poti, cb, &rcb))
{
DbgPrint("a=%08x\nr=%08x\nw=%08x\ne=%08x\n",
poti->GenericMapping.GenericAll,
poti->GenericMapping.GenericRead,
poti->GenericMapping.GenericWrite,
poti->GenericMapping.GenericExecute);
}
}
得到了
a=001fffff
r=00020048
w=00020437
e=00121800
所以我们通常可以用
GenericExecute
访问权限,除非
00020000
(
READ_CONTROL
)因为此访问位于GenericRead和GenericWrite中,并且策略-无读/写。
然而,对于几乎所有需要句柄(线程或泛型)的api,我们可以使用
GetCurrentThread()
-调用线程的伪句柄。当然,这只能用于当前线程。所以我们可以打个比方
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
GetThreadTimes(GetCurrentThread(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
这个
CloseHandle(GetCurrentThread());
也是有效通话-
使用此句柄调用CloseHandle函数无效。
(什么都不会)。这个伪句柄
GENERIC_ALL
已授予访问权限。
所以你的
OpenThread
例程可以检查线程id-如果它等于
GetCurrentThreadId()
-只需返回
GetCurrentThread()
。
我们也可以打电话
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, 0, DUPLICATE_SAME_ACCESS);
这对这个线程也很有用。但通常使用
GetCurrentThread()
够了