Запуск usermode процесса из драйвера (ring0)

Тема в разделе "WASM.NT.KERNEL", создана пользователем ratix, 26 янв 2008.

  1. ratix

    ratix New Member

    Публикаций:
    0
    Регистрация:
    13 окт 2005
    Сообщения:
    52
    Адрес:
    Kyrgyzstan
    Знаю, что темы уже поднимались, и неоднократно обсуждались на форуме.

    Но перечитав все, я так и не смог добиться стабильного простого аналога CreateProcess в ring0 :dntknw:
    поэтому, если не трудно, если у кого-то есть __РАБОЧИЙ__ (например на ВинХР) пример такого кода, я был бы очень благодарен.

    Задача простая: написать простейший драйвер, который бы в DriverEntry тока и делал бы, что запускал новый процесс. На входе - путь к исполняемому файлу на диске, и EPROCESS/HANDLE на родительский процесс (от имени которого создается процесс).

    есть 2 варианта:
    1) APC - если не трудно, у кого есть - рабочий пример
    2) вызов ZwCreateProcess/CreateThread из ntdll (через поиск их адресов в таблицу SST) - если не трудно, рабочий пример.
    3) - ещё варианты?

    пробовал второй вариант, брал код с Нэббета - не удается его запустить...


    спасибо заранее..
     
  2. steelfactor

    steelfactor New Member

    Публикаций:
    0
    Регистрация:
    26 апр 2007
    Сообщения:
    501
    1) по запуску APC на hellknights.void.ru был рабочий пример от Cr4sh'a/Great
    2) через вызов ZwCreateProcess и т.д. мороки столько, что мама не горюй...
    3) еще вариант - инжект кода в процесс в юзермоде, который запустит нужный процесс, здесь есть статья на эту тему
    Код Нэббета предназначен для запуска процесса из r3, посмотри внимательнее, хоть и описывает все стадии запуска процесса.
     
  3. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    1. Апц вариант довольно кривой, потому что нужно искать Alertable поток и эта байда выполнится не мгновенно сразу, а через некоторое время. Так же стоит учесть, что NormalRoutine и KernelRoutine совершенно не обязаны выполняться одна за другой, как это неявно предполагается в известном на весь инет коде KernelExec.sys. У нас с Cr4sh были случаи, когда KernelRoutine выполнялась до завершения NormalRoutine, а поскольку в ней (кернел) был вызов MmUnmapLockedPages, то выполнение NormalRoutine завершалось с исключением. В кодесе, про который сказал steelfactor, мы это исправили.

    2. NtCreateThread. Рабочего примера не будет))
    Опишу на словах что нужно сделать:
    1. Спроецировать нужный юзермодный код на юзермодное АП процесса назначения (MmMapLockedPagesSpecifyCache).
    2. Выделить место стек и спроецировать и его тоже на юзермодное АП процесса назначения (я юзал MmAllocatePagesForMdl/MmMapLockedPageSpecifyCahce чтобы выделить физстраницы под стек. конечно, очень расточительно выделять неподкачиваемую память для юзермодного потока, но всетаки искать NtAllocateVirtualMemory я посчитал неудобным. Хотя вообщем то какая разница если все равно идет поиск NtCreateThread).
    3. Создать контекст и описание стека нового потока для последующей передачи в NtCreateThread и вызвать NtCreateThread (конечно же, через int 2e!). Важный момент, до которого я допер не сразу: структура контекста должна быть спроецирована на пользовательское адресное пространство, в противном случае NtCreateThread завершается без ошибки, но создает поток, у которого в контексте откровенный мусор.
    Выглядит это схематически примерно так (предполагается что UsermodeAddress и UsermodeStack - уже спроецированные код и стек нового потока):
    Код (Text):
    1.     CLIENT_ID ClientId;
    2.     USER_STACK UserStack;
    3.     CONTEXT Context;
    4.  
    5.     UserStack.ExpandableStackBottom =
    6.     UserStack.ExpandableStackLimit = UsermodeStack;
    7.     UserStack.ExpandableStackBase = (PVOID)( (ULONG_PTR)UsermodeStack + 0x10000 );
    8.  
    9.     RtlZeroMemory( &Context, sizeof(CONTEXT) );
    10.  
    11.     Context.ContextFlags = CONTEXT_FULL;
    12.  
    13.     Context.SegGs = 0;
    14.     Context.SegFs = 0x3B;
    15.     Context.SegEs = 0x23;
    16.     Context.SegDs = 0x23;
    17.     Context.SegSs = 0x23;
    18.     Context.SegCs = 0x1B;
    19.     Context.EFlags = 0x3000;
    20.     Context.Esp = (ULONG)UserStack.ExpandableStackBase - 4;
    21.     Context.Eip = (ULONG_PTR) UsermodeAddress;
    22.  
    23.     PMDL CtxMdl;
    24.     PVOID CtxMapped;
    25.  
    26.     CtxMdl = IoAllocateMdl( &Context, sizeof(CONTEXT), 0, 0, 0 );
    27.     if( !CtxMdl )  ... //handle error;
    28.  
    29.     MmBuildMdlForNonPagedPool( CtxMdl );
    30.     CtxMapped = MmMapLockedPagesSpecifyCache( CtxMdl,
    31.         UserMode,
    32.         MmCached,
    33.         NULL,
    34.         0,
    35.         NormalPagePriority );
    36.     if( !CtxMapped ) ... // handle error
    37.  
    38.     Status = NtCreateThread(
    39.         &hThread,
    40.         THREAD_ALL_ACCESS,
    41.         NULL,
    42.         hProcess,
    43.         &ClientId,
    44.         (PCONTEXT) CtxMapped,
    45.         &UserStack,
    46.         FALSE );
    Код без труда можно написать самому по тому плану, который я написал.
     
  4. steelfactor

    steelfactor New Member

    Публикаций:
    0
    Регистрация:
    26 апр 2007
    Сообщения:
    501
    Пару замечаний к предыдущему посту Great:
    - в юзермоде по-моему, должны находиться еще и PROCESS_BASIC_INFORMATION, PROCESS_PARAMETERS (RTL_USER_PROCESS_PARAMETERS), которые необходимы для запуска процесса при создании его параметров ... Геморрой, вообщем...
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    никакой не геморрой - раз. два - какие процесс бейсик инфо, ты о чем?
    Я говорю просто про создание нового потока в СУЩЕСТВУЮЩЕМ процессе через NtCreateThread. А уж юзермодный поток создаст новый процесс без проблем - CreateProcess

    чето ты меня не понял походу
     
  6. steelfactor

    steelfactor New Member

    Публикаций:
    0
    Регистрация:
    26 апр 2007
    Сообщения:
    501
    Наверное. Мы просто имеем ввиду разные стадии запуска процесса.
    Я имею ввиду именно создание НОВОГО ПРОЦЕССА (ZwCreateSection/ZwQuerySection/ZwCreateProcess и т.д.) из ядра, когда РУЧКАМИ приходится создавать и заполнять необходимые структуры, необходимые для его "жизнеспособности".
    Написанный выше код справедлив, с этим никто не спорит, просто я имею ввиду, что при создании процесса из ядра необходимо соблюсти кучу условий. К примеру, перед созданием потока (NtCreateThread/NtResumeThread), если окружение процесса (скажем, без вызова NtQueryInformationProcess...) не будет инициализировано, процесс не запустится. У меня код начинает бсодить.
     
  7. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Это очень геморройно, гораздо геморройнее создания потока. Проще создать поток, а из потока новый процесс.

    я не имею в виду запуск процесса из ядра вообще. перечитай мой пост
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Не пойму, в чём проблема ?
     
  9. steelfactor

    steelfactor New Member

    Публикаций:
    0
    Регистрация:
    26 апр 2007
    Сообщения:
    501
    Clerk
    да, собственно, ни в чем... друг друга не поняли...
     
  10. ratix

    ratix New Member

    Публикаций:
    0
    Регистрация:
    13 окт 2005
    Сообщения:
    52
    Адрес:
    Kyrgyzstan
    всем спасибо.

    APC код работает.

    На самом деле, я уже применял метод запуска из r0, используя процесс 'explorer.exe' - модифицировал указатель на DispatchMessageW в IAT эксплорера, и ждал когда он обработает следующее сообщение, при этом мой код записанный в адресное пространство эксплорера получал управление.
    У этого кода был явный недостаток: необходим был эксплорер процесс, и прокачка сообщений в нем.
    Но был и плюс: код успешно работал на всех х32 ОС (Win2k - Vista) без каких либо спец. подстроек под конкретную ось, т.е. не было никаких таблиц со смещениями характерными для той или иной оси. А также был успешно портирован на х64 оси (ХП и Виста).

    С APC же как я понял прийдется затачивать код под каждую ось отдельно :dntknw: (смещения allertable в ETHREAD)
    и неизвестно как он себя поведет на х64.
    вобщем - будем посмотреть, что можно сделать с АПС - главное теперь у меня есть рабочий под ХП код.

    Возможно даже правильнее будет всетаки пользовать NtCreateThread вариант (описание Great)..
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    если создовать NtCreateThread то не надо ниче такого кажется..
     
  12. ratix

    ratix New Member

    Публикаций:
    0
    Регистрация:
    13 окт 2005
    Сообщения:
    52
    Адрес:
    Kyrgyzstan
    NtCreateThread так и не выходит запустить..

    Код, стэк и CONTEXT промаплены в АП эксплорера, но ZwCreateThread возвращает 0xC0000005 - Access vialoation.
     
  13. ratix

    ratix New Member

    Публикаций:
    0
    Регистрация:
    13 окт 2005
    Сообщения:
    52
    Адрес:
    Kyrgyzstan
    мой код:
    Код (Text):
    1. #define STACK_SIZE 0x10000
    2. #define CODE_SIZE 0x100
    3.  
    4. #define ALL_MEM (STACK_SIZE + CODE_SIZE + sizeof(CONTEXT))
    5.  
    6.  
    7.     CLIENT_ID ClientId;
    8.     PVOID pStack;
    9.     PVOID pCode;
    10.     USER_STACK UserStack;
    11.     CONTEXT* pContext;
    12.     PVOID pMem;
    13.     PMDL pMdl;
    14.     PVOID pUserMem;
    15.     NTSTATUS stat;
    16.     HANDLE hThread;
    17.  
    18.     pMem = ExAllocatePool(NonPagedPool, ALL_MEM);
    19.     if (NULL == pMem)
    20.     {
    21.         DBGPRINT("Can't ExAllocatePool!!!");
    22.         return;
    23.     }
    24.  
    25.     pMdl = IoAllocateMdl(pMem, ALL_MEM, FALSE, FALSE, NULL);
    26.     if (NULL == pMdl)
    27.     {
    28.         ExFreePool(pMem);
    29.         DBGPRINT("Can't IoAllocateMdl");
    30.         return;
    31.     }
    32.  
    33.     MmProbeAndLockPages(pMdl, KernelMode, IoWriteAccess);
    34.  
    35.     pUserMem = MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
    36.     if (NULL != pUserMem)
    37.     {
    38.         DBGPRINT("MmMapLockedPagesSpecifyCache: 0x%08X", pUserMem);
    39.  
    40.         pStack = pUserMem;
    41.         pContext = (CONTEXT*)((ULONG_PTR)pUserMem + STACK_SIZE);
    42.         pCode = (PVOID)((ULONG_PTR)pUserMem + STACK_SIZE + sizeof(CONTEXT));
    43.  
    44.         UserStack.ExpandableStackBottom = UserStack.ExpandableStackLimit = pStack;
    45.         UserStack.ExpandableStackBase = (PVOID)( (ULONG_PTR)pStack + STACK_SIZE);
    46.  
    47.         RtlZeroMemory(pContext, sizeof(CONTEXT));
    48.         pContext->ContextFlags = CONTEXT_FULL;
    49.  
    50.         pContext->SegGs = 0;
    51.         pContext->SegFs = 0x3B;
    52.         pContext->SegEs = 0x23;
    53.         pContext->SegDs = 0x23;
    54.         pContext->SegSs = 0x23;
    55.         pContext->SegCs = 0x1B;
    56.         pContext->EFlags = 0x3000;
    57.         pContext->Esp = (ULONG)UserStack.ExpandableStackBase - 4;
    58.         pContext->Eip = (ULONG_PTR) pCode;
    59.  
    60.         stat = ((t_ZwCreateThread)(sst_func[N_ZwCreateThread].pFunc))(
    61.             &hThread,
    62.             THREAD_ALL_ACCESS,
    63.             NULL,
    64.             hProcess,
    65.             &ClientId,
    66.             pContext,
    67.             &UserStack,
    68.             FALSE);
    69.         DBGPRINT("ZwCreateThread stat: 0x%08X", stat);
    70.         MySleep(5000);
    71.         DBGPRINT("EndWaitForThread");
    72.     }
    73.     else
    74.     {
    75.         DBGPRINT("Can't MmMapLockedPagesSpecifyCache");
    76.     }
    77.  
    78.     MmUnmapLockedPages(pUserMem, pMdl);
    79.     MmUnlockPages(pMdl);
    80.     IoFreeMdl(pMdl);
    81.     ExFreePool(pMem);
    Перед этим я нахожу процесс explorer.exe, делаю атач (KeStackAttachProcess), и беру хэндл процесса (ZwOpenProcess).

    В код секцию пока ничего не пишу.. по идее - процесс должен крэшануться просто при успешном запуске треда..
     
  14. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    контекст не обязательно вообще мапить именно в тот процесс, где создается поток. достаточно в юзермодное АП System к примеру.

    делать MmProbeAndLockPages для NonPagedPool тоже излишне. и вообще зачем растрачивать системные PTE, лучше выделить сразу физстраницы через MmAllocatePagesForMdl и промапить это в юзермод.

    да кстати я бы из соображений стабильности не удерживал лишний раз чужой контекст. как только код и стек промаплены в нужный процесс, можно смело делать detach. возможно, createthread'у это не нравится.

    кстати,
    лучше так не делать - она может хукаться какойнибудь гадостью, которая может не дать открыть процесс. лучше самому создать хендл через ObOpenObjectByPointer, тем более раз ты уже сам нашел EPROCESS нужный.