Не так давно мы с коллегой задались вопросом: а по какому принципу формируется 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))
В результате исполнения скрипта мы получим пять нитей:
Как и ожидалось, только одна нить имеет отличный от других описатель безопасности, потому, что даже запущенная из нее нить была создана из целевого процесса. Сами описатели безопасности тоже имеют ожидаемое содержимое:
ΞρεΤΙκ