Здравствуйте! На некоторых компьютерах клиентов получаю status == 0xC0000017(STATUS_NO_MEMORY) при попытке получить handl-ы процессов с помощью ZwQuerySystemInformation из юзермода. Свободной памяти в системе много. Иногда помогает перезагрузка. Что я делаю не так? Код: Код (Text): NTSTATUS status; ULONG handleInfoSize = 0x10000; SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; PSYSTEM_HANDLE_INFORMATION_EX handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)malloc(handleInfoSize); while ((status = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) { handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)realloc(handleInfo, handleInfoSize *= 2); } if (!NT_SUCCESS(status)) { // тут получаю статус 0xC0000017 (STATUS_NO_MEMORY) }
А зачем так много? Типа щедрый? --- Сообщение объединено, 31 май 2020 --- А вот эта строка особенно понятна.
Попробуй в последнем параметре передать указатель на ULONG на стеке, некоторые функции натив апи ничего не знают про опциональные параметры.
Например, у меня сейчас на компе функция возвращает инфу про 111597 handl-а. 56 x 111597 = 6249432 = 0x5F5BD8 Я изначально выделяю 0x10000 байт. Так что это совершенно немного и не является щедростью. Спасибо. Попробую. Только странно, что иногда работает, а иногда - нет.
Это си, бро, она имеет место быть в нкоторых говнокодах, даже в ядре линукс любят так делать. --- Сообщение объединено, 31 май 2020 --- Но если учесть, что функция возвращает требуемый размер последним параметром, то умножать размер на 2 наверное весьма избыточно.
Код (Text): unsigned long return_lenght = 0; NTSTATUS status = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, NULL, 0, &return_lenght); В результате: status = 0xc0000004 (STATUS_INFO_LENGTH_MISMATCH) return_lenght = 0x24 Так что не возвращает требуемый размер последним параметром.
А как мне ее вызвать, чтобы получить количество необходимой памяти для получения инфы про все handl-ы?
Я даже не подозревал что это С.... Думал это цитата из Псалтыря, хотел сказать: потому и не работает, что боженька не хотит...
Она тебе вернула 0x24, тебе надо выделить 0x24, не? --- Сообщение объединено, 1 июн 2020 --- Тебе же уже написали цитату из доков, удивительно, почему же она вернула именно 0х24: --- Сообщение объединено, 1 июн 2020 --- Пути господни неисповедимы, тебе бы Минздрав объяснил, если бы не выпилился отседова. Или это админы его выпилили? --- Сообщение объединено, 1 июн 2020 --- Это видимо "таблица" c одним элементом. --- Сообщение объединено, 1 июн 2020 --- Нет, это просто дурацкое, но документированное поведение для этой апи: --- Сообщение объединено, 1 июн 2020 --- Вполне вероятно, что в системе слишком много дескрипторов и общее их число не влезает в какой-то внутренний буффер функции, ну или функция не может выделить внутренний буффер такого размера. Это объясняет, почему функция иногда отрабатывает. Можно порекомендовать собрать кодец под х64 и протестить (ты же под х86 собераешь, судя по тому, что тебе 0х24 вернулось).
Ты ошибаешься. Попробуй вызвать ZwQuerySystemInformation с размером буффера 0x24 байта. Сильно удивишься. Пока размер буффера не будет больше или равно размеру инфы про все handl-ы - будешь получать статус STATUS_INFO_LENGTH_MISMATCH Например, при количестве handl-ов 114991: размер буфера 0x312128 байт или меньше - получаем STATUS_INFO_LENGTH_MISMATCH размер буфера 0x31212C байт или больше - получаем STATUS_SUCCESS Код (Text): typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { PVOID Object; ULONG ProcessId; HANDLE Handle; ULONG GrantedAccess; USHORT CreatorBackTraceIndex; USHORT ObjectTypeNumber; ULONG HandleAttributes; ULONG Reserved; } SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; typedef struct _SYSTEM_HANDLE_INFORMATION_EX { ULONG_PTR HandleCount; ULONG_PTR Reserved; SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; } SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; Обоснование 0x31212C байт: 4 байта занимает HandleCount 4 байта занимает Reserved 0x312124 байта занимает массив Handles (114991 * sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX)) Тут как бы даже описание структуры([1]) намекает, что после поля "Reserved" идет массив структур SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX P.S. Я долго писАл ответ. Ты уже и без моего сообщения понял суть. К сожалению, нужно использовать это в 32 битном процессе. =((( ---- Кстати, получается, что это утверждение ошибочное. The SYSTEM_HANDLE_INFORMATION_EX is 0x24 or 0x38 bytes in 32-bit and 64-bit Windows, respectively. Ибо у меня винда 64 битная и размер структуры с одним елементом == 0x24.
Если выделить сразу слишком много памяти, без реаллок? Будет ли тогда ошибка? Так то могу посоветовать разве что посмотреть сорцы Process Haker'a, там все корректно работает с миллионами хендлов. Но врядли в этом куске кода можно ошибиться.
M0rg0t, в исходниках Process Hacker-а почти такой же код как у меня. Код (Text): static ULONG initialBufferSize = 0x10000; NTSTATUS status; PVOID buffer; ULONG bufferSize; bufferSize = initialBufferSize; buffer = PhAllocate(bufferSize); while ((status = NtQuerySystemInformation( SystemExtendedHandleInformation, buffer, bufferSize, NULL )) == STATUS_INFO_LENGTH_MISMATCH) { PhFree(buffer); bufferSize *= 2; // Fail if we're resizing the buffer to something very large. if (bufferSize > PH_LARGE_BUFFER_SIZE) return STATUS_INSUFFICIENT_RESOURCES; buffer = PhAllocate(bufferSize); } if (!NT_SUCCESS(status)) { PhFree(buffer); return status; } Спасибо. Попробую.
Ну попробуй прибить процессы, которые сжирают наибольшее количество хендлов, и проверь. Если моя теория верна, то никакие выделения, ни на каких буфферах тебе не помогут. Если заведомо известно, что программа будет работать на 64-битных операционных системах, то вполне можно через хевенс гейт выпрыгнуть в 64-битный режим. Но это уже малварная техника. 64-битная ntdll уже есть в WOW64 процессах, то есть можно и память выделить и функцию вызвать. --- Сообщение объединено, 1 июн 2020 --- От разрядности системы это не зависит, размер структуры зависит от разрядности процесса, в 64-битном процессе у тебя будет 0x38.
https://habr.com/ru/company/infopulse/blog/433906/ https://github.com/giampaolo/psutil/blob/master/psutil/arch/windows/process_handles.c
ormoulu, спасибо. Я видел эту инфу. У меня в этом плане все ОК. Используется новый класс для получения дескрипторов - SystemExtendedHandleInformation.
А сколько проходов цикла делается и какой размер буфера выделен, когда возникает ошибка? М.б. не хватает системной/ядерной памяти для получения всех хэндлов.
Посмотрел я через Teamviewer на ситуацию с проблемным компом. Процесс зверька "C:\Windows\System\svchost.exe" открыл 11 миллионов дескрипторов и продолжал открывать новые с большой скоростью. Оригинальный "svchost.exe" находится в "C:\Windows\System32". Даже если я и получу инфу про эти все дескрипторы - обработка скажется на производительности. 0x20000000 байт
Aiks, Смотри, этому коду лет 5, снималась статистика(XP/7 наверно я не помню точно): Код (Text): .data PUBLIC g_Objects PUBLIC g_ObjSize PUBLIC g_ObjN g_Objects PSYSTEM_HANDLE_INFORMATION ? ; Буфер для SystemHandleInformation g_ObjSize ULONG ? g_ObjN ULONG ? .code ; + ; BUGBUG: ИНОГДА ВОЗВРАЩАЕТСЯ ИНВАЛИДНОЕ ЧИСЛО ОПИСАТЕЛЕЙ(NumberOfHandles). ; ; eg: ; SystemInformationLength: 0x29000 ; NumberOfHandles: 0x3D06 -> length may be 0x3D064 ; ReturnLength: 0x276D4 -> real length, N = 0x276D ; ; st: 0x15A77/0x13816, ~90% failed. ; GetSnapObjects proc uses ebx Local Buffer[sizeof(SYSTEM_HANDLE_INFORMATION)]:BYTE Local S1ze:ULONG xor ebx,ebx cmp g_Objects,ebx jne Query ; Буфер был выделен. Alloc: mov g_Objects,ebx invoke ZwQuerySystemInformation, SystemHandleInformation, addr Buffer, sizeof(SYSTEM_HANDLE_INFORMATION), addr g_ObjSize cmp eax,STATUS_INFO_LENGTH_MISMATCH jne Exit add g_ObjSize,X86_PAGE_SIZE invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr g_Objects, Ebx, addr g_ObjSize, MEM_COMMIT, PAGE_READWRITE test eax,eax jnz Exit Query: invoke ZwQuerySystemInformation, SystemHandleInformation, g_Objects, g_ObjSize, addr g_ObjN .if Eax == STATUS_INFO_LENGTH_MISMATCH invoke ZwFreeVirtualMemory, NtCurrentProcess, addr g_Objects, addr g_ObjSize, MEM_RELEASE jmp Alloc .endif mov ecx,g_Objects .if !Eax ; Рефрешим буфер, так как в случае ошибки слепок может быть инкорректен. ; В целях оптимизации возможно использование ReturnLength без рефреша. mov ecx,SYSTEM_HANDLE_INFORMATION.NumberOfHandles[ecx] shl ecx,4 add ecx,SYSTEM_HANDLE_INFORMATION.Handles .if g_ObjN != Ecx ifdef OPT_FAST_SNAPSHOT mov edx,g_ObjN mov ecx,g_Objects shr edx,4 mov SYSTEM_HANDLE_INFORMATION.NumberOfHandles[ecx],edx xor eax,eax else jmp Query endif .endif .endif Exit: setz al movzx eax,al ret ; * В случае ошибки буфер не освобождаем. GetSnapObjects endp - система вела себя анстаб с этим запросом, наверно это и осталось. После обнаружения так давно этой не стабильности всегда говорил что эти сервисы юзать не следует. Память там точно не причём, это анстаб ядра какой был на младших версиях, интересно какая у вас версия системы. Если это старшие версии, то есть предположение что анстаб введён намеренно, так как исключительно малварь запрашивает описатели.
Indy_, у вас в коде используется старый класс для получения дескрипторов. Вместо "SystemHandleInformation" нужно использовать "SystemExtendedHandleInformation". Проблемы которые возникают при использовании старого класса описаны по ссылке от ormoulu: https://habr.com/ru/company/infopulse/blog/433906/ Система у клиента Windows 10 x64. Кстати, когда было выделено 0x20000000 байт - функция вернула статус 0xC0000017. А когда выделил 0x1FFE0000 - функция вернула успех с такими результатами: В ReturnLength - 0x1197E1A8 В handleInfo->HandleCount - 0xA0DA58 Можно сделать вывод, что нужно наращивать размер буфера постепенно, а не удваивать при STATUS_INFO_LENGTH_MISMATCH. Но в любом случае, какой нибудь зверек может открыть намного больше дескрипторов и даже это не поможет. Так что тут либо отказываться от данного способа, либо игнорить его при детекте такой ситуации.