Запуск системных вызовов режима ядра из пользовательского режима - PullRequest
0 голосов
/ 22 мая 2018

Я пытался прочитать память некоторых процессов, таких как csrss.exe, используя OpenProcess().Проблема в том, что, как только я использую PROCESS_ALL_ACCESS, эти процессы не могут быть открыты.Поэтому я попытался использовать параметр PROCESS_QUERY_LIMITED_INFORMATION, но это не дает сочных результатов.

Из моего понимания системы эта функция в конечном итоге вызывает ZwOpenProcess().В настоящее время я понимаю, что если к нему обращается приложение из пользовательского режима, этот вызов также будет обрабатываться как вызов из пользовательского режима, а не из режима ядра.

Чтобы обойти эту проверку, я сделалследующее:

  1. Использовал IDA для открытия ntdll.dll, где присутствуют все эти процессы.
  2. Найдите функцию, и вот что я нашел.

Cap_1 Итак, насколько я понимаю, он выполняет тест, а затем, если он оценивается как 0, он выполняет системный вызов с низкой задержкой, который, я считаю, является версией функции в режиме ядра.

Далее я сделал то же самое для ZwReadVirtualMemory(): Cap_2

Итак, вот мои вопросы:

  1. Могу ли я напрямую сделать.asm файл и написать эти процедуры в том же и вызывать их, чтобы получить доступ режима ядра к этим функциям?
  2. Будет ли PROCESS_ALL_ACCESS работать для этих подпрограмм, если я вызову их, используя вышеуказанный метод.
  3. Мне также нужно использовать VirtualQueryEx(), для которого я не смог найти ядроЗамена режима, в таком случае я планирую использовать VirtualQueryEx() вместе с вышеупомянутыми настраиваемыми вызовами.Теперь мой вопрос здесь заключается в том, что, поскольку VirtualQueryEx() не является вызовом режима ядра (не на верхнем уровне, я имею в виду, что ReadProcessMemory() также вызывает ZwReadVirtualMemory, но не является вызовом режима ядра, если инициируется программой пользовательского режима, поэтомув случае с VirtualQueryEx()), это будет проблемой или он вернется обратно в режим пользователя, когда я сделаю следующий пользовательский вызов?

Конечная цель того, чтобы я все это делал, -возможность открывать все процессы с привилегиями уровня ядра, читать их память и выгружать их в файл.Это также включает процессы, которые выполняются на системном уровне, такие как csrss.exe.Если существует какой-либо более простой подход, просим меня прояснить это.

Code :

HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
HANDLE lProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
DWORD error = GetLastError();

if (hProc || lProc)
    {


            //(!hProc && lProc) ? printf("lproc") : printf("hProc"); //Testing Condition
            fProc = (!hProc && lProc) ? lProc : hProc;

        while (1) {
            if ((VirtualQueryEx(fProc, addr1, &meminfo, sizeof(meminfo))) == 0)
            {
                break;
            }


            if ((meminfo.State == MEM_COMMIT))
            {
                static unsigned char display_buffer[1024 * 128];
                SIZE_T bytes_left;
                SIZE_T total_read;
                SIZE_T bytes_to_read;
                SIZE_T bytes_read;
                FILE *f;


                    f = fopen("Dump.txt", "a");


                addr = (unsigned char*)meminfo.BaseAddress;

                //printf("Process Name : %ws\r\n", pName.Buffer);
                fprintf(f, "Process Name : %ws\r\n", pName.Buffer);

                //printf("Base Address : 0x%08x\r\n", addr);
                fprintf(f, "Base Address : 0x%08x\r\n", addr);

                bytes_left = meminfo.RegionSize;

                //printf("Region Size : %d\r\n", bytes_left);
                fprintf(f, "Region Size : %d\r\n", bytes_left);

                total_read = 0;

                //printf("Contents : \r\n");
                fprintf(f, "Contents : \r\n");

                while (bytes_left)
                {
                    bytes_to_read = (bytes_left > sizeof(display_buffer)) ? sizeof(display_buffer) : bytes_left;
                    ReadProcessMemory(hProc, addr + total_read, display_buffer, bytes_to_read, &bytes_read);
                    if (bytes_read != bytes_to_read) break;

                    int j = 0;


                    for (j = 0; j < bytes_to_read; j++)
                    {
                        if ((display_buffer[j] > 31) && (display_buffer[j] < 127)) {
                            //printf("%c ", display_buffer[j]);
                            fprintf(f, "%c", display_buffer[j]);
                        }
                        else {
                            //printf(".");
                            fprintf(f, ".");
                        }
                    }
                    //printf("\r\n");
                    fprintf(f, "\r\n");

                    bytes_left -= bytes_read;
                    total_read += bytes_read;
                }

                fclose(f);

            }

            addr1 = (unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;

        }

    }

        else {

        printf("\nFailed to open process - error - %d\r\n", error);

    }

Итак, здесь я пытаюсь сохранить всю информацию о памяти в файл DUMP.txt для данного процесса.

1 Ответ

0 голосов
/ 25 мая 2018

Windows Vista - Windows 10

Спасибо RbMm за указание на то, что после Windows 10 все изменилось.Фактически, они начали меняться с тех пор, как в Windows Vista.

В Windows Vista появилась концепция защищенного процесса для обеспечения применения DRM к медиа-контенту.
Защищенный процесс не может быть прочитан илиизменен обычным процессом (помимо других ограничений).
Защищенные процессы могут быть загружены только в том случае, если они подписаны, и только в том случае, если они выполняют только ограниченный набор операций.

В Windows 8.1 Microsoft повторно посетила защищенные процессы и представилакласс процесса Protected Process Light (PPL).
PPL может быть запущен только другим PPL;с безопасной загрузкой или с определенным ключом реестра / переменной среды критическими системными процессами являются PPL.
Это означает, что они не могут быть открыты даже учетной записью администратора / системы.

csrss.exe также является PPL начиная с Windows 8.1:

Обратите внимание, что интересно, что Csrss.exe был также наделен уровнем защиты.Он не отвечает за запуск каких-либо специальных защищенных процессов и не имеет интересных данных в памяти, таких как LSASS или системный процесс.Однако в последние годы он приобрел очень плохую репутацию как источник множества эксплойтов Windows

Каждый процесс, помимо уровня защиты, также имеет подпись .
В этих полях указывается, как процесс используется в системе.Значения:

PsProtectedSignerNone = 0n0
PsProtectedSignerAuthenticode = 0n1
PsProtectedSignerCodeGen = 0n2
PsProtectedSignerAntimalware = 0n3
PsProtectedSignerLsa = 0n4
PsProtectedSignerWindows = 0n5
PsProtectedSignerWinTcb = 0n6
PsProtectedSignerMax/PsProtectedSignerWinSystem = 0n7

Наконец, каждый тип подписавшего имеет структуру, определяющую:

  1. С подписчиками других типов разрешено открывать процесс.
  2. Две маски доступа для указания того, какие привилегии не разрешены при попытке открыть процесс.

Каждый подписывающий, но PsProtectedSignerNone ограничивает привилегии

PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SUSPEND_RESUME
PROCESS_TERMINATE (excluding WinTcb, Antimalware and Lsa)
PROCESS_SET_LIMITED_INFORMATION

Для открытияcsrss.exe необходимо запустить процесс запроса как защищенный процесс с соответствующим подписывающим лицом, но для этого требуется, чтобы двоичный файл был подписан Microsoft.В качестве альтернативы, , как указано в RbMm , если вы можете загрузить драйвер ядра, можно изменить структуру EPROCESS, изменив биты уровня защиты.


Windows 7

Эта очень простая программа открывает процесс, запущенный SYSTEM.

#include <windows.h>

int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
)
{
    HANDLE thisProcess = GetCurrentProcess();
    HANDLE thisToken;
    TOKEN_PRIVILEGES newPrivileges;
    HANDLE thatProcess;

    newPrivileges.PrivilegeCount = 1;
    newPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (LookupPrivilegeValue(NULL, "SeDebugPrivilege", &newPrivileges.Privileges[0].Luid) == 0)
    {
        MessageBox(NULL, "LookupPrivilegeValue", "Cannot find privilege", MB_ICONEXCLAMATION);
        ExitProcess(1);     
    }

    if (OpenProcessToken(thisProcess, TOKEN_ADJUST_PRIVILEGES, &thisToken) == 0)
    {
        MessageBox(NULL, "OpenProcessToken", "Cannot open token", MB_ICONEXCLAMATION);
        ExitProcess(2);
    }

    AdjustTokenPrivileges(thisToken, FALSE, &newPrivileges, 0, NULL, NULL);
    if (GetLastError() != ERROR_SUCCESS)
    {
        MessageBox(NULL, "AdjustTokenPrivileges", "Cannot adjust privileges", MB_ICONEXCLAMATION);
        ExitProcess(3);
    }

    thatProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1080);

    if (thatProcess == NULL)
    {
        MessageBox(NULL, "OpenProcess", "Cannot open that process", MB_ICONEXCLAMATION);
        ExitProcess(4);
    }

    MessageBox(NULL, "Done", "Opened", MB_ICONINFORMATION);

}

При запуске под учетной записью, имеющей SeDebugPrivilege , такой как учетная запись администратора, она преуспевает вполучение дескриптора процесса.
Когда он не запускается в указанных выше условиях, он завершается с ошибкой на шаге AdjustTokenPrivileges.
Я протестировал его на своей системе, запустив его как полноправного администратора и обычного пользователя.Только администратор может открыть его.

Он работает, как я объяснил в моем предыдущем комментарии: он включает SeDebugPrivilege в своем маркере доступа, а затем открывает произвольный дескриптор системного процесса.

Примечание Код процесса жестко задан, измените его.

Важно понять, что AdjustTokenPrivileges не может добавлять новых привилегий, он может только включать / отключать привилегии, которые процесс наследует от пользователя.
Насколько я знаю, нет общедоступного API для добавления новой привилегии к токену доступа.
Если программа запускается от имени пользователя без прав администратора, процесс завершится ошибкой, поскольку ни один здравомыслящий администратор не предоставит такую ​​мощную привилегию любому не-admin .

Кроме того, даже если ваша учетная запись является административной, вы, вероятно, работаете как защищенный администратор и, следовательно, токен, назначенный процессу, не будет иметь SeDebugPrivilege.

Вы можете исправить это, запустив с правами администратора или самостоятельно подняв процесс .


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

Это понимание неверно.
КеКогда пользователь недоступен из пользовательского пространства, существует точка входа , которая передает управление из пользовательского пространства в ядро, но пользовательская программа не может читать / записывать память ядра.
Каждая важная информация о системе,включая дескрипторы, хранятся в ядре, вне досягаемости пользовательских программ.

Когда программа использует системный вызов для перехода в ядро, она также передает управление ядру;Проще говоря, вы можете выполнить разработанную функцию ядра, но вы не можете читать / писать ее код или данные.
Это архитектурное ограничение (чтение ЦП), оно очень надежное, даже если обычно обнаруживаются лазейки, чтобы обойти его.
ЕслиВы ищете такую ​​лазейку, вы ищете уязвимость нулевого дня .
Ошибка в ОС.

Проверка прав доступа, очевидно, выполняется кодом ядра, поэтому вы не можете увидеть, потрогать или обойти его, если не сделаете программу частью ядра.
Это драйвер , вы можете разработать его с помощью WDK (в отличие от SDK).
Для загрузки драйверов по-прежнему требуются права администратора, но администратор или привилегированная установка может заставить драйвер загружаться при запуске (это своего рода сервисы, им не нужен пользователь).
На самом деле сервис должен быть достаточным в этом случае.

Проверка, которую вы видите при разборке - это просто путь совместимости, Windows использует инструкцию syscall, а не устаревшую int 2eh.
Я думал, что устаревший механизмпропал.
Оба пути в конечном итоге объединятся в ядре.

Итак, насколько я понимаю, он выполняет тест, затем, если он оценивается как 0, он выполняет системный вызов с низкой задержкой, который, как я считаю, является версией функции режима ядра

Это неверно, как объяснено выше.
Вы можете почувствовать, что это не может быть правдой, потому что в обоих путях нет кода (где находится версия функции не в режиме ядра?).


Могу ли я напрямую создать файл .asm, написать эти процедуры в одном и том же режиме и вызвать их для получения доступа к этим функциям в режиме ядра?

Код вызывает системный вызов напрямую, этот интерфейс является частным.
Microsoft оставляет за собой право изменять номера системных вызовов и / или соглашение о вызовах.
ТолькоПубличный API стабилен в разных версиях Windows.
Если вы хотите, вы можете вызвать системный вызов напрямую, выполнив:

mov r10, rcx
mov eax, 26h
syscall

, а затем установив параметр в регистрах так же, как для NtOpenProcess.
Вы никуда не дойдете, это всего лишь встроенная, непереносимая версия исходной функции.

Будет ли PROCESS_ALL_ACCESS работать для этих подпрограмм, если я вызову их с помощью вышеуказанного метода.

Нет, проверка по-прежнему выполняется ядром.

Мне также нужно использовать VirtualQueryEx (), для которого я не смог найти замену режима ядра, вВ этом случае я планирую использовать VirtualQueryEx () вместе с вышеупомянутыми пользовательскими вызовами.Теперь мой вопрос здесь заключается в том, что, поскольку VirtualQueryEx () не является вызовом режима ядра (не в самом верху, я имею в виду, что ReadProcessMemory () также вызывает ZwReadVirtualMemory, но не является вызовом режима ядра, если инициируется программой пользовательского режима, поэтомув случае с VirtualQueryEx ()) это будет проблемой или он вернется в пользовательский режим при следующем пользовательском вызове?

Я не могу полностью понять этот вопрос, извинитедля этого.
Все, что я могу сказать, это то, что: a) WinAPI и частный API - это два разных набора API. б) WinAPI не используют частные API в формате 1: 1, они могут включать болееодин системный вызов (забавный факт: кажется, что Windows использует системные вызовы даже для кода, уже находящегося в кольце 0) c) Все привилегированные действия выполняются ядром, это не по выбору вызывающей программы.

Конечная цель того, чтобы я все это делал, - это иметь возможность открывать все процессы с привилегиями уровня ядра, читать их память и выгружать их в файл.Это также включает процессы, которые выполняются на системном уровне, такие как csrss.exe.Если существует какой-либо более простой подход, просим вас прояснить это.

Ядро не имеет привилегий, пользователи имеют привилегии и, следовательно, имеют процессы.
Вы должны иметь возможность вывести любой пользовательский режимпроцесс с программой, выполняющейся как администратор или служба, работающая как Система.

...