Знаю, что темы уже поднимались, и неоднократно обсуждались на форуме. Но перечитав все, я так и не смог добиться стабильного простого аналога CreateProcess в ring0 поэтому, если не трудно, если у кого-то есть __РАБОЧИЙ__ (например на ВинХР) пример такого кода, я был бы очень благодарен. Задача простая: написать простейший драйвер, который бы в DriverEntry тока и делал бы, что запускал новый процесс. На входе - путь к исполняемому файлу на диске, и EPROCESS/HANDLE на родительский процесс (от имени которого создается процесс). есть 2 варианта: 1) APC - если не трудно, у кого есть - рабочий пример 2) вызов ZwCreateProcess/CreateThread из ntdll (через поиск их адресов в таблицу SST) - если не трудно, рабочий пример. 3) - ещё варианты? пробовал второй вариант, брал код с Нэббета - не удается его запустить... спасибо заранее..
1) по запуску APC на hellknights.void.ru был рабочий пример от Cr4sh'a/Great 2) через вызов ZwCreateProcess и т.д. мороки столько, что мама не горюй... 3) еще вариант - инжект кода в процесс в юзермоде, который запустит нужный процесс, здесь есть статья на эту тему Код Нэббета предназначен для запуска процесса из r3, посмотри внимательнее, хоть и описывает все стадии запуска процесса.
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): CLIENT_ID ClientId; USER_STACK UserStack; CONTEXT Context; UserStack.ExpandableStackBottom = UserStack.ExpandableStackLimit = UsermodeStack; UserStack.ExpandableStackBase = (PVOID)( (ULONG_PTR)UsermodeStack + 0x10000 ); RtlZeroMemory( &Context, sizeof(CONTEXT) ); Context.ContextFlags = CONTEXT_FULL; Context.SegGs = 0; Context.SegFs = 0x3B; Context.SegEs = 0x23; Context.SegDs = 0x23; Context.SegSs = 0x23; Context.SegCs = 0x1B; Context.EFlags = 0x3000; Context.Esp = (ULONG)UserStack.ExpandableStackBase - 4; Context.Eip = (ULONG_PTR) UsermodeAddress; PMDL CtxMdl; PVOID CtxMapped; CtxMdl = IoAllocateMdl( &Context, sizeof(CONTEXT), 0, 0, 0 ); if( !CtxMdl ) ... //handle error; MmBuildMdlForNonPagedPool( CtxMdl ); CtxMapped = MmMapLockedPagesSpecifyCache( CtxMdl, UserMode, MmCached, NULL, 0, NormalPagePriority ); if( !CtxMapped ) ... // handle error Status = NtCreateThread( &hThread, THREAD_ALL_ACCESS, NULL, hProcess, &ClientId, (PCONTEXT) CtxMapped, &UserStack, FALSE ); Код без труда можно написать самому по тому плану, который я написал.
Пару замечаний к предыдущему посту Great: - в юзермоде по-моему, должны находиться еще и PROCESS_BASIC_INFORMATION, PROCESS_PARAMETERS (RTL_USER_PROCESS_PARAMETERS), которые необходимы для запуска процесса при создании его параметров ... Геморрой, вообщем...
никакой не геморрой - раз. два - какие процесс бейсик инфо, ты о чем? Я говорю просто про создание нового потока в СУЩЕСТВУЮЩЕМ процессе через NtCreateThread. А уж юзермодный поток создаст новый процесс без проблем - CreateProcess чето ты меня не понял походу
Наверное. Мы просто имеем ввиду разные стадии запуска процесса. Я имею ввиду именно создание НОВОГО ПРОЦЕССА (ZwCreateSection/ZwQuerySection/ZwCreateProcess и т.д.) из ядра, когда РУЧКАМИ приходится создавать и заполнять необходимые структуры, необходимые для его "жизнеспособности". Написанный выше код справедлив, с этим никто не спорит, просто я имею ввиду, что при создании процесса из ядра необходимо соблюсти кучу условий. К примеру, перед созданием потока (NtCreateThread/NtResumeThread), если окружение процесса (скажем, без вызова NtQueryInformationProcess...) не будет инициализировано, процесс не запустится. У меня код начинает бсодить.
Это очень геморройно, гораздо геморройнее создания потока. Проще создать поток, а из потока новый процесс. я не имею в виду запуск процесса из ядра вообще. перечитай мой пост
всем спасибо. APC код работает. На самом деле, я уже применял метод запуска из r0, используя процесс 'explorer.exe' - модифицировал указатель на DispatchMessageW в IAT эксплорера, и ждал когда он обработает следующее сообщение, при этом мой код записанный в адресное пространство эксплорера получал управление. У этого кода был явный недостаток: необходим был эксплорер процесс, и прокачка сообщений в нем. Но был и плюс: код успешно работал на всех х32 ОС (Win2k - Vista) без каких либо спец. подстроек под конкретную ось, т.е. не было никаких таблиц со смещениями характерными для той или иной оси. А также был успешно портирован на х64 оси (ХП и Виста). С APC же как я понял прийдется затачивать код под каждую ось отдельно (смещения allertable в ETHREAD) и неизвестно как он себя поведет на х64. вобщем - будем посмотреть, что можно сделать с АПС - главное теперь у меня есть рабочий под ХП код. Возможно даже правильнее будет всетаки пользовать NtCreateThread вариант (описание Great)..
NtCreateThread так и не выходит запустить.. Код, стэк и CONTEXT промаплены в АП эксплорера, но ZwCreateThread возвращает 0xC0000005 - Access vialoation.
мой код: Код (Text): #define STACK_SIZE 0x10000 #define CODE_SIZE 0x100 #define ALL_MEM (STACK_SIZE + CODE_SIZE + sizeof(CONTEXT)) CLIENT_ID ClientId; PVOID pStack; PVOID pCode; USER_STACK UserStack; CONTEXT* pContext; PVOID pMem; PMDL pMdl; PVOID pUserMem; NTSTATUS stat; HANDLE hThread; pMem = ExAllocatePool(NonPagedPool, ALL_MEM); if (NULL == pMem) { DBGPRINT("Can't ExAllocatePool!!!"); return; } pMdl = IoAllocateMdl(pMem, ALL_MEM, FALSE, FALSE, NULL); if (NULL == pMdl) { ExFreePool(pMem); DBGPRINT("Can't IoAllocateMdl"); return; } MmProbeAndLockPages(pMdl, KernelMode, IoWriteAccess); pUserMem = MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); if (NULL != pUserMem) { DBGPRINT("MmMapLockedPagesSpecifyCache: 0x%08X", pUserMem); pStack = pUserMem; pContext = (CONTEXT*)((ULONG_PTR)pUserMem + STACK_SIZE); pCode = (PVOID)((ULONG_PTR)pUserMem + STACK_SIZE + sizeof(CONTEXT)); UserStack.ExpandableStackBottom = UserStack.ExpandableStackLimit = pStack; UserStack.ExpandableStackBase = (PVOID)( (ULONG_PTR)pStack + STACK_SIZE); RtlZeroMemory(pContext, sizeof(CONTEXT)); pContext->ContextFlags = CONTEXT_FULL; pContext->SegGs = 0; pContext->SegFs = 0x3B; pContext->SegEs = 0x23; pContext->SegDs = 0x23; pContext->SegSs = 0x23; pContext->SegCs = 0x1B; pContext->EFlags = 0x3000; pContext->Esp = (ULONG)UserStack.ExpandableStackBase - 4; pContext->Eip = (ULONG_PTR) pCode; stat = ((t_ZwCreateThread)(sst_func[N_ZwCreateThread].pFunc))( &hThread, THREAD_ALL_ACCESS, NULL, hProcess, &ClientId, pContext, &UserStack, FALSE); DBGPRINT("ZwCreateThread stat: 0x%08X", stat); MySleep(5000); DBGPRINT("EndWaitForThread"); } else { DBGPRINT("Can't MmMapLockedPagesSpecifyCache"); } MmUnmapLockedPages(pUserMem, pMdl); MmUnlockPages(pMdl); IoFreeMdl(pMdl); ExFreePool(pMem); Перед этим я нахожу процесс explorer.exe, делаю атач (KeStackAttachProcess), и беру хэндл процесса (ZwOpenProcess). В код секцию пока ничего не пишу.. по идее - процесс должен крэшануться просто при успешном запуске треда..
контекст не обязательно вообще мапить именно в тот процесс, где создается поток. достаточно в юзермодное АП System к примеру. делать MmProbeAndLockPages для NonPagedPool тоже излишне. и вообще зачем растрачивать системные PTE, лучше выделить сразу физстраницы через MmAllocatePagesForMdl и промапить это в юзермод. да кстати я бы из соображений стабильности не удерживал лишний раз чужой контекст. как только код и стек промаплены в нужный процесс, можно смело делать detach. возможно, createthread'у это не нравится. кстати, лучше так не делать - она может хукаться какойнибудь гадостью, которая может не дать открыть процесс. лучше самому создать хендл через ObOpenObjectByPointer, тем более раз ты уже сам нашел EPROCESS нужный.