Не так давно мы с коллегой задались вопросом: а по какому принципу формируется DACL дескриптора безопасности объекта нити? В частности стало интересно: а есть ли какое-то наследование между создающей нитью и создаваемой.
Ответ достаточно легко найти к коде WRK. И, начав смотреть ядра Windows, я сразу вспомнил, что уже изучал этот код, когда рассматривал особенность создания процессов. Не секрет, что если мы получим токен привилегированного процесса, имперсонируемся под этим токеном и запустим процесс, то новый процесс не будет привилегированным. Это же относится и к созданию нитей процессов. Функции создания процесса и нити используют расширенную функцию создания nt!_ACCESS_STATE’а: SeCreateAccessStateEx, которая явно парметризуется объектом нити и объектом процесса. Но при создании нити или процесса вызывающая функцию передает в SeCreateAccessStateEx NULL в качестве параметра нити, что обеспечивает игнорирование имперсонированности вызывающей нити, а в расчет берется только основной токен вызывающего процесса. Никаких действий по наследованию описателя безопасности между нитями не предпринимается.
Это достаточно легко проверить на практике: напишем некоторый тест, который будет содержать в себе обработчик нити, запускающий вторую нить. В конце каждого из обработчиков поставим бесконечный Sleep. И запустим нить с адресов входа первого обработчика из первой нити процесса и из внешнего процесса-сервиса. В качестве целевой системы возьмем WinXP SP2, а анализировать результаты будем следующим простеньким скриптом для pykd:
import sys
if len(sys.argv) != 2:
print("Usage : " + sys.argv[0] + " <EPROCESS_ADDR>")
exit()
import pykd
if not pykd.isKernelDebugging():
print("This script is for kernel debugging only")
nt = pykd.module("nt")
# list the thread and collect the unique SDs
print("\t***\n")
lstSecDesc = []
prcObj = nt.typedVar("_EPROCESS", pykd.expr(sys.argv[1]))
for thrdObj in nt.typedVarList(prcObj.ThreadListHead.getAddress(),
"_ETHREAD",
"ThreadListEntry"):
thrdObjHeader = nt.containingRecord(thrdObj.getAddress(),
"_OBJECT_HEADER",
"Body")
sdArrd = (thrdObjHeader.SecurityDescriptor & ~7)
print("\tThread 0x%x, SD: 0x%x" % (thrdObj.getAddress(), sdArrd))
if not(sdArrd in lstSecDesc):
lstSecDesc.append(sdArrd)
# print the unique SDs
print("\n\t***\n")
for sdArrd in lstSecDesc:
print("\tSD: 0x%x" % sdArrd)
print(pykd.dbgCommand("!sd 0x%x" % sdArrd))
В результате исполнения скрипта мы получим пять нитей:
Как и ожидалось, только одна нить имеет отличный от других описатель безопасности, потому, что даже запущенная из нее нить была создана из целевого процесса. Сами описатели безопасности тоже имеют ожидаемое содержимое:
ΞρεΤΙκ