NtQuerySystemInformation - 0xC0000017

Тема в разделе "WASM.WIN32", создана пользователем Aiks, 31 май 2020.

  1. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    Здравствуйте! На некоторых компьютерах клиентов получаю status == 0xC0000017(STATUS_NO_MEMORY) при попытке получить handl-ы процессов с помощью ZwQuerySystemInformation из юзермода. Свободной памяти в системе много. Иногда помогает перезагрузка. Что я делаю не так? Код:

    Код (Text):
    1.  
    2. NTSTATUS status;
    3. ULONG handleInfoSize = 0x10000;
    4. SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64;
    5. PSYSTEM_HANDLE_INFORMATION_EX handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)malloc(handleInfoSize);
    6.  
    7. while ((status = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
    8. {
    9.     handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)realloc(handleInfo, handleInfoSize *= 2);
    10. }
    11.  
    12. if (!NT_SUCCESS(status))
    13. {
    14.     // тут получаю статус 0xC0000017 (STATUS_NO_MEMORY)
    15. }
    16.  
     
  2. Bedolaga

    Bedolaga Member

    Публикаций:
    0
    Регистрация:
    10 июл 2019
    Сообщения:
    131
    А зачем так много? Типа щедрый?
    --- Сообщение объединено, 31 май 2020 ---
    А вот эта строка особенно понятна.
     
  3. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Попробуй в последнем параметре передать указатель на ULONG на стеке, некоторые функции натив апи ничего не знают про опциональные параметры.
     
    Aiks нравится это.
  4. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    Например, у меня сейчас на компе функция возвращает инфу про 111597 handl-а.

    56 x 111597 = 6249432 = 0x5F5BD8

    Я изначально выделяю 0x10000 байт. Так что это совершенно немного и не является щедростью.

    Спасибо. Попробую. Только странно, что иногда работает, а иногда - нет.
     
  5. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Это си, бро, она имеет место быть в нкоторых говнокодах, даже в ядре линукс любят так делать.
    --- Сообщение объединено, 31 май 2020 ---
    Но если учесть, что функция возвращает требуемый размер последним параметром, то умножать размер на 2 наверное весьма избыточно.
     
    Aiks нравится это.
  6. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    Код (Text):
    1. unsigned long return_lenght = 0;
    2.  
    3. NTSTATUS status = ZwQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, NULL, 0, &return_lenght);
    В результате:

    status = 0xc0000004 (STATUS_INFO_LENGTH_MISMATCH)
    return_lenght = 0x24

    Так что не возвращает требуемый размер последним параметром.
     
  7. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Эмм, как бы она и должна тебе была это вернуть при таком вызове.
     
    Aiks нравится это.
  8. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    А как мне ее вызвать, чтобы получить количество необходимой памяти для получения инфы про все handl-ы?
     
  9. Bedolaga

    Bedolaga Member

    Публикаций:
    0
    Регистрация:
    10 июл 2019
    Сообщения:
    131
    Я даже не подозревал что это С....
    Думал это цитата из Псалтыря, хотел сказать: потому и не работает, что боженька не хотит...
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Она тебе вернула 0x24, тебе надо выделить 0x24, не?
    --- Сообщение объединено, 1 июн 2020 ---
    Тебе же уже написали цитату из доков, удивительно, почему же она вернула именно 0х24:
    --- Сообщение объединено, 1 июн 2020 ---
    Пути господни неисповедимы, тебе бы Минздрав объяснил, если бы не выпилился отседова. Или это админы его выпилили?
    --- Сообщение объединено, 1 июн 2020 ---
    Это видимо "таблица" c одним элементом.
    --- Сообщение объединено, 1 июн 2020 ---
    Нет, это просто дурацкое, но документированное поведение для этой апи:
    --- Сообщение объединено, 1 июн 2020 ---
    Вполне вероятно, что в системе слишком много дескрипторов и общее их число не влезает в какой-то внутренний буффер функции, ну или функция не может выделить внутренний буффер такого размера. Это объясняет, почему функция иногда отрабатывает. Можно порекомендовать собрать кодец под х64 и протестить (ты же под х86 собераешь, судя по тому, что тебе 0х24 вернулось).
     
    Aiks и M0rg0t нравится это.
  11. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    Ты ошибаешься. Попробуй вызвать ZwQuerySystemInformation с размером буффера 0x24 байта. Сильно удивишься. :grin:

    Пока размер буффера не будет больше или равно размеру инфы про все handl-ы - будешь получать статус STATUS_INFO_LENGTH_MISMATCH

    Например, при количестве handl-ов 114991:

    размер буфера 0x312128 байт или меньше - получаем STATUS_INFO_LENGTH_MISMATCH
    размер буфера 0x31212C байт или больше - получаем STATUS_SUCCESS

    Код (Text):
    1. typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
    2.     PVOID Object;
    3.     ULONG ProcessId;
    4.     HANDLE Handle;
    5.     ULONG GrantedAccess;
    6.     USHORT CreatorBackTraceIndex;
    7.     USHORT ObjectTypeNumber;
    8.     ULONG HandleAttributes;
    9.     ULONG Reserved;
    10. } SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
    11.  
    12. typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
    13.     ULONG_PTR HandleCount;
    14.     ULONG_PTR Reserved;
    15.     SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
    16. } 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.
     
    Последнее редактирование: 1 июн 2020
  12. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Если выделить сразу слишком много памяти, без реаллок? Будет ли тогда ошибка?
    Так то могу посоветовать разве что посмотреть сорцы Process Haker'a, там все корректно работает с миллионами хендлов. Но врядли в этом куске кода можно ошибиться.
     
    Aiks нравится это.
  13. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    M0rg0t, в исходниках Process Hacker-а почти такой же код как у меня. :)

    Код (Text):
    1.     static ULONG initialBufferSize = 0x10000;
    2.     NTSTATUS status;
    3.     PVOID buffer;
    4.     ULONG bufferSize;
    5.     bufferSize = initialBufferSize;
    6.     buffer = PhAllocate(bufferSize);
    7.  
    8.     while ((status = NtQuerySystemInformation(
    9.         SystemExtendedHandleInformation,
    10.         buffer,
    11.         bufferSize,
    12.         NULL
    13.         )) == STATUS_INFO_LENGTH_MISMATCH)
    14.     {
    15.         PhFree(buffer);
    16.         bufferSize *= 2;
    17.         // Fail if we're resizing the buffer to something very large.
    18.         if (bufferSize > PH_LARGE_BUFFER_SIZE)
    19.             return STATUS_INSUFFICIENT_RESOURCES;
    20.         buffer = PhAllocate(bufferSize);
    21.     }
    22.  
    23.     if (!NT_SUCCESS(status))
    24.     {
    25.         PhFree(buffer);
    26.         return status;
    27.     }
    Спасибо. Попробую.
     
  14. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Ну попробуй прибить процессы, которые сжирают наибольшее количество хендлов, и проверь. Если моя теория верна, то никакие выделения, ни на каких буфферах тебе не помогут. Если заведомо известно, что программа будет работать на 64-битных операционных системах, то вполне можно через хевенс гейт выпрыгнуть в 64-битный режим. Но это уже малварная техника. 64-битная ntdll уже есть в WOW64 процессах, то есть можно и память выделить и функцию вызвать.
    --- Сообщение объединено, 1 июн 2020 ---
    От разрядности системы это не зависит, размер структуры зависит от разрядности процесса, в 64-битном процессе у тебя будет 0x38.
     
    Aiks нравится это.
  15. ormoulu

    ormoulu Well-Known Member

    Публикаций:
    0
    Регистрация:
    24 янв 2011
    Сообщения:
    1.208
  16. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    ormoulu, спасибо. Я видел эту инфу. У меня в этом плане все ОК. Используется новый класс для получения дескрипторов - SystemExtendedHandleInformation.
     
  17. ormoulu

    ormoulu Well-Known Member

    Публикаций:
    0
    Регистрация:
    24 янв 2011
    Сообщения:
    1.208
    А сколько проходов цикла делается и какой размер буфера выделен, когда возникает ошибка? М.б. не хватает системной/ядерной памяти для получения всех хэндлов.
     
  18. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    Посмотрел я через Teamviewer на ситуацию с проблемным компом.

    Процесс зверька "C:\Windows\System\svchost.exe" открыл 11 миллионов дескрипторов и продолжал открывать новые с большой скоростью. Оригинальный "svchost.exe" находится в "C:\Windows\System32".

    Даже если я и получу инфу про эти все дескрипторы - обработка скажется на производительности. :rofl:

    0x20000000 байт
     
    Последнее редактирование: 1 июн 2020
  19. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    Aiks,

    Смотри, этому коду лет 5, снималась статистика(XP/7 наверно я не помню точно):

    Код (Text):
    1. .data
    2. PUBLIC g_Objects
    3. PUBLIC g_ObjSize
    4. PUBLIC g_ObjN
    5.  
    6. g_Objects    PSYSTEM_HANDLE_INFORMATION ?    ; Буфер для SystemHandleInformation
    7. g_ObjSize    ULONG ?
    8. g_ObjN    ULONG ?
    9.  
    10. .code
    11. ; +
    12. ; BUGBUG: ИНОГДА ВОЗВРАЩАЕТСЯ ИНВАЛИДНОЕ ЧИСЛО ОПИСАТЕЛЕЙ(NumberOfHandles).
    13. ;
    14. ; eg:
    15. ;    SystemInformationLength: 0x29000
    16. ;    NumberOfHandles: 0x3D06 -> length may be 0x3D064
    17. ;    ReturnLength: 0x276D4 -> real length, N = 0x276D
    18. ;
    19. ;    st: 0x15A77/0x13816, ~90% failed.
    20. ;
    21. GetSnapObjects proc uses ebx
    22. Local Buffer[sizeof(SYSTEM_HANDLE_INFORMATION)]:BYTE
    23. Local S1ze:ULONG
    24.     xor ebx,ebx
    25.     cmp g_Objects,ebx
    26.     jne Query    ; Буфер был выделен.
    27. Alloc:
    28.     mov g_Objects,ebx
    29.     invoke ZwQuerySystemInformation, SystemHandleInformation, addr Buffer, sizeof(SYSTEM_HANDLE_INFORMATION), addr g_ObjSize
    30.     cmp eax,STATUS_INFO_LENGTH_MISMATCH
    31.     jne Exit
    32.     add g_ObjSize,X86_PAGE_SIZE
    33.     invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr g_Objects, Ebx, addr g_ObjSize, MEM_COMMIT, PAGE_READWRITE
    34.     test eax,eax
    35.     jnz Exit
    36. Query:
    37.     invoke ZwQuerySystemInformation, SystemHandleInformation, g_Objects, g_ObjSize, addr g_ObjN
    38.     .if Eax == STATUS_INFO_LENGTH_MISMATCH
    39.         invoke ZwFreeVirtualMemory, NtCurrentProcess, addr g_Objects, addr g_ObjSize, MEM_RELEASE
    40.         jmp Alloc
    41.     .endif
    42.     mov ecx,g_Objects
    43.     .if !Eax
    44. ;        Рефрешим буфер, так как в случае ошибки слепок может быть инкорректен.
    45. ;        В целях оптимизации возможно использование ReturnLength без рефреша.
    46.         mov ecx,SYSTEM_HANDLE_INFORMATION.NumberOfHandles[ecx]
    47.         shl ecx,4
    48.         add ecx,SYSTEM_HANDLE_INFORMATION.Handles
    49.         .if g_ObjN != Ecx
    50.             ifdef OPT_FAST_SNAPSHOT
    51.                 mov edx,g_ObjN
    52.                 mov ecx,g_Objects
    53.                 shr edx,4
    54.                 mov SYSTEM_HANDLE_INFORMATION.NumberOfHandles[ecx],edx
    55.                 xor eax,eax
    56.             else
    57.                 jmp Query
    58.             endif
    59.         .endif
    60.     .endif
    61. Exit:
    62.     setz al
    63.     movzx eax,al
    64.     ret    ; * В случае ошибки буфер не освобождаем.
    65. GetSnapObjects endp
    - система вела себя анстаб с этим запросом, наверно это и осталось. После обнаружения так давно этой не стабильности всегда говорил что эти сервисы юзать не следует. Память там точно не причём, это анстаб ядра какой был на младших версиях, интересно какая у вас версия системы. Если это старшие версии, то есть предположение что анстаб введён намеренно, так как исключительно малварь запрашивает описатели.
     
    Aiks нравится это.
  20. Aiks

    Aiks Member

    Публикаций:
    0
    Регистрация:
    16 апр 2017
    Сообщения:
    109
    Адрес:
    Украина
    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.

    Но в любом случае, какой нибудь зверек может открыть намного больше дескрипторов и даже это не поможет.

    Так что тут либо отказываться от данного способа, либо игнорить его при детекте такой ситуации.