_SYSTEM_PROCESS_INFORMATION struct обратная совместимость - PullRequest
0 голосов
/ 25 декабря 2018

Цель

Напишите функцию, запрашивающую состояние потоков процесса.

Решение

Используйте этот полезный пост: Уникальная техника итерации по процессам и сформулируйте начальную функцию:

bool IterateOverThreads() {
    NTSTATUS status;
    PSYSTEM_PROCESS_INFORMATION spi;
    ULONG lBufferSize = 0;

    status = ::NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemProcessInformation, 0, 0,  & lBufferSize);
    if (0xC0000004L != status || 0 == lBufferSize)
        return false;

    unique_ptr<byte[]> pMemory(new byte[lBufferSize]);

    spi = (PSYSTEM_PROCESS_INFORMATION)pMemory.get();

    // get System Information
    if (!NT_SUCCESS(status = ::NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemProcessInformation, spi, lBufferSize,  & lBufferSize)))
        return false;

    // Loop over the list until we reach the last entry
    while (spi->NextEntryDelta) {

        // Calculate the address of the next entry.
        spi = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)spi + spi->NextEntryDelta);

        // iterate over threads
        for (size_t ii = 0; ii < spi->ThreadCount; ++ii) {
            // do whatever with thread attributes
            spi->Threads[ii].State;
            spi->Threads[ii].WaitReason;
        }
    }
    return true;
}

Задача 1

В моем решении / проектах должен использоваться Microsoft SDK версии 7.1.

Структура SYSTEM_PROCESS_INFORMATION изменилась между SDKверсии следующим образом:

Microsoft SDKs\Windows\v7.1A\Include\winternl.h

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    BYTE Reserved1[52];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

Документировано в NtQuerySystemInformation

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    PVOID Reserved2;
    ULONG HandleCount;
    ULONG SessionId;
    PVOID Reserved3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG Reserved4;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    PVOID Reserved5;
    SIZE_T QuotaPagedPoolUsage;
    PVOID Reserved6;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;

Итак, я не могу "наслаждаться" использованиемтакие члены, как NumberOfThreads (или другие).

Исправить проблему 1:

Определить SYSTEM_PROCESS_INFORMATION в моем коде самостоятельно, основываясь на документации

Задача 2

Мое приложение работает на всех Windows, версия которых превышает XP.

Вопрос

Безопасен ли мой код?имеется в виду, безопасен ли доступ к spi->ThreadCount?Могу ли я предположить, что байты там действительны?будет ли рискованно читать байты из моей собственной структуры в старых версиях Windows?

1 Ответ

0 голосов
/ 25 декабря 2018

в настоящее время одно из лучших (я думаю) определение SYSTEM_PROCESS_INFORMATION

, теперь оно действительно для всех текущих версий Windows (включая XP).

доступ к spi-> ThreadCount safe?

да, безопасно.как минимум на всю текущую сборку.(скажем хр уже не изменится).Будет ли это безопасно на будущих сборках (структура не изменена) - это уже другой вопрос.

Мой код в безопасности?

нет, это неправильно.как минимум в 2 балла.сначала после того, как вы получили lBufferSize при первом вызове NtQuerySystemInformation и до того, как использовать его во втором вызове - требуемый размер может быть изменен (увеличен) - так что вам действительно нужно сделать один вызов NtQuerySystemInformation, но в цикле, пока вы не получите STATUS_INFO_LENGTH_MISMATCH,ваш код может работать, но в некоторых случаях происходит сбой.

// Loop over the list until we reach the last entry  
while (spi->NextEntryDelta) {

это всегда ошибка - вы потеряли последнюю запись, у которой NextEntryDelta == 0

возвращает bool, а нелучшая идея для функции, лучше вернуть NTSTATUS.

минимальный правильный код может выглядеть как

NTSTATUS IterateOverThreads()
{
    NTSTATUS status;

    PVOID buf;
    ULONG cb = 0x1000;
    do 
    {
        if (buf = LocalAlloc(0, cb))
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                union {
                    PVOID pv;
                    PBYTE pb;
                    PSYSTEM_PROCESS_INFORMATION spi;
                };

                pv = buf;

                ULONG NextEntryOffset = 0;

                do 
                {
                    pb += NextEntryOffset;

                    DbgPrint("%wZ\n", &spi->ImageName);

                    if (ULONG NumberOfThreads = spi->NumberOfThreads)
                    {
                        PSYSTEM_THREAD_INFORMATION TH = spi->TH;
                        do 
                        {
                            DbgPrint("\t%p %x %x\n", TH->ClientId.UniqueThread, TH->ThreadState, TH->WaitReason);
                        } while (TH++, --NumberOfThreads);
                    }

                } while (NextEntryOffset = spi->NextEntryOffset);
            }
            LocalFree(buf);
        }
        else
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}
...