Какая принципиальная разница между RtlCreateUserThread и CreateRemoteThread. Вопрос состоит в том что есть машина на ней XP SP3 неком процессе создаю функцией CreateRemoteThread удаленный поток все ок. Потом создаю этот же поток функцией RtlCreateUserThread процесс начинает есть 50% процессорного времени и еще 50% есть csrss ? Может при RtlCreateUserThread надо делать уведомление ? В то время на Viste и Seven обе функции (RtlCreateUserThread и CreateRemoteThread ) отрабатывают нормально
вот исходник с винды правда 2000 Code (Text): HANDLE APIENTRY CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) /*++ Routine Description: A thread object can be created to execute within the address space of the another process using CreateRemoteThread. Creating a thread causes a new thread of execution to begin in the address space of the current process. The thread has access to all objects opened by the process. The thread begins executing at the address specified by the StartAddress parameter. If the thread returns from this procedure, the results are un-specified. The thread remains in the system until it has terminated and all handles to the thread have been closed through a call to CloseHandle. When a thread terminates, it attains a state of signaled satisfying all waits on the object. In addition to the STANDARD_RIGHTS_REQUIRED access flags, the following object type specific access flags are valid for thread objects: - THREAD_QUERY_INFORMATION - This access is required to read certain information from the thread object. - SYNCHRONIZE - This access is required to wait on a thread object. - THREAD_GET_CONTEXT - This access is required to read the context of a thread using GetThreadContext. - THREAD_SET_CONTEXT - This access is required to write the context of a thread using SetThreadContext. - THREAD_SUSPEND_RESUME - This access is required to suspend or resume a thread using SuspendThread or ResumeThread. - THREAD_ALL_ACCESS - This set of access flags specifies all of the possible access flags for a thread object. Arguments: hProcess - Supplies the handle to the process in which the thread is to be create in. lpThreadAttributes - An optional parameter that may be used to specify the attributes of the new thread. If the parameter is not specified, then the thread is created without a security descriptor, and the resulting handle is not inherited on process creation. dwStackSize - Supplies the size in bytes of the stack for the new thread. A value of zero specifies that the thread's stack size should be the same size as the stack size of the first thread in the process. This size is specified in the application's executable file. lpStartAddress - Supplies the starting address of the new thread. The address is logically a procedure that never returns and that accepts a single 32-bit pointer argument. lpParameter - Supplies a single parameter value passed to the thread. dwCreationFlags - Supplies additional flags that control the creation of the thread. dwCreationFlags Flags: CREATE_SUSPENDED - The thread is created in a suspended state. The creator can resume this thread using ResumeThread. Until this is done, the thread will not begin execution. lpThreadId - Returns the thread identifier of the thread. The thread ID is valid until the thread terminates. Return Value: NON-NULL - Returns a handle to the new thread. The handle has full access to the new thread and may be used in any API that requires a handle to a thread object. NULL - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; POBJECT_ATTRIBUTES pObja; HANDLE Handle; CONTEXT ThreadContext; INITIAL_TEB InitialTeb; CLIENT_ID ClientId; ULONG i; #if !defined(BUILD_WOW6432) BASE_API_MSG m; PBASE_CREATETHREAD_MSG a = (PBASE_CREATETHREAD_MSG)&m.u.CreateThread; #endif #if defined(WX86) || defined(_AXP64_) BOOL bWx86 = FALSE; HANDLE Wx86Info; PWX86TIB Wx86Tib; #endif // // Allocate a stack for this thread in the address space of the target // process. // Status = BaseCreateStack( hProcess, dwStackSize, 0L, &InitialTeb ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return NULL; } // // Create an initial context for the new thread. // BaseInitializeContext( &ThreadContext, lpParameter, (PVOID)lpStartAddress, InitialTeb.StackBase, BaseContextTypeThread ); pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL); Status = NtCreateThread( &Handle, THREAD_ALL_ACCESS, pObja, hProcess, &ClientId, &ThreadContext, &InitialTeb, TRUE ); if (!NT_SUCCESS(Status)) { BaseFreeThreadStack(hProcess,NULL, &InitialTeb); BaseSetLastNTError(Status); return NULL; } try { #if defined(WX86) || defined(_AXP64_) // // Check the Target Processes to see if this is a Wx86 process // Status = NtQueryInformationProcess(hProcess, ProcessWx86Information, &Wx86Info, sizeof(Wx86Info), NULL ); if (!NT_SUCCESS(Status)) { leave; } Wx86Tib = (PWX86TIB)NtCurrentTeb()->Vdm; // // if Wx86 process, setup for emulation // if ((ULONG_PTR)Wx86Info == sizeof(WX86TIB)) { // // create a WX86Tib and initialize it's Teb->Vdm. // Status = BaseCreateWx86Tib(hProcess, Handle, (ULONG)((ULONG_PTR)lpStartAddress), dwStackSize, 0L, (Wx86Tib && Wx86Tib->Size == sizeof(WX86TIB) && Wx86Tib->EmulateInitialPc) ); if (!NT_SUCCESS(Status)) { leave; } bWx86 = TRUE; } else if (Wx86Tib && Wx86Tib->EmulateInitialPc) { // // if not Wx86 process, and caller wants to call x86 code in that // process, fail the call. // Status = STATUS_ACCESS_DENIED; leave; } #endif // WX86 // // Call the Windows server to let it know about the // process. // if ( !BaseRunningInServerProcess ) { #if defined(BUILD_WOW6432) Status = CsrBasepCreateThread(Handle, ClientId ); #else a->ThreadHandle = Handle; a->ClientId = ClientId; CsrClientCallServer( (PCSR_API_MSG)&m, NULL, CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX, BasepCreateThread ), sizeof( *a ) ); Status = m.ReturnValue; #endif } else { if (hProcess != NtCurrentProcess()) { CSRREMOTEPROCPROC ProcAddress; ProcAddress = (CSRREMOTEPROCPROC)GetProcAddress( GetModuleHandleA("csrsrv"), "CsrCreateRemoteThread" ); if (ProcAddress) { Status = (ProcAddress)(Handle, &ClientId); } } } if (!NT_SUCCESS(Status)) { Status = (NTSTATUS)STATUS_NO_MEMORY; } else { if ( ARGUMENT_PRESENT(lpThreadId) ) { *lpThreadId = HandleToUlong(ClientId.UniqueThread); } if (!( dwCreationFlags & CREATE_SUSPENDED) ) { NtResumeThread(Handle,&i); } } } finally { if (!NT_SUCCESS(Status)) { BaseFreeThreadStack(hProcess, Handle, &InitialTeb ); NtTerminateThread(Handle, Status); NtClose(Handle); BaseSetLastNTError(Status); Handle = NULL; } } return Handle; } и собственно RtlCreateUserThread Code (Text): NTSTATUS RtlCreateUserThread( IN HANDLE Process, IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, IN BOOLEAN CreateSuspended, IN ULONG ZeroBits OPTIONAL, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN PUSER_THREAD_START_ROUTINE StartAddress, IN PVOID Parameter OPTIONAL, OUT PHANDLE Thread OPTIONAL, OUT PCLIENT_ID ClientId OPTIONAL ) /*++ Routine Description: This function creates a user mode thread in a user process. The caller specifies the attributes of the new thread. A handle to the thread, along with its Client Id are returned to the caller. Arguments: Process - Handle to the target process in which to create the new thread. ThreadSecurityDescriptor - An optional pointer to the Security Descriptor give to the new thread. CreateSuspended - A boolean parameter that specifies whether or not the new thread is to be created suspended or not. If TRUE, the new thread will be created with an initial suspend count of 1. If FALSE then the new thread will be ready to run when this call returns. ZeroBits - This parameter is passed to the virtual memory manager when the stack is allocated. Stacks are always allocated with the MEM_TOP_DOWN allocation attribute. MaximumStackSize - This is the maximum size of the stack. This size will be rounded up to the next highest page boundary. If zero is specified, then the default size will be 64K bytes. CommittedStackSize - This is the initial committed size of the stack. This size is rounded up to the next highest page boundary and then an additional page is added for the guard page. The resulting size will then be commited and the guard page protection initialized for the last committed page in the stack. StartAddress - The initial starting address of the thread. Parameter - An optional pointer to a 32-bit pointer parameter that is passed as a single argument to the procedure at the start address location. Thread - An optional pointer that, if specified, points to a variable that will receive the handle of the new thread. ClientId - An optional pointer that, if specified, points to a variable that will receive the Client Id of the new thread. Return Value: TBS --*/ { NTSTATUS Status; CONTEXT ThreadContext; OBJECT_ATTRIBUTES ObjectAttributes; INITIAL_TEB InitialTeb; HANDLE ThreadHandle; CLIENT_ID ThreadClientId; // // Allocate a stack for this thread in the address space of the target // process. // Status = RtlpCreateStack( Process, MaximumStackSize, CommittedStackSize, ZeroBits, &InitialTeb ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // Create an initial context for the new thread. // RtlInitializeContext( Process, &ThreadContext, Parameter, (PVOID)StartAddress, InitialTeb.StackBase ); // // Now create a thread in the target process. The new thread will // not have a name and its handle will not be inherited by other // processes. // InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, ThreadSecurityDescriptor ); Status = ZwCreateThread( &ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, Process, &ThreadClientId, &ThreadContext, &InitialTeb, CreateSuspended ); if (!NT_SUCCESS( Status )) { #if DBG DbgPrint( "NTRTL: RtlCreateUserThread Failed. NtCreateThread Status == %X\n", Status ); #endif // DBG RtlpFreeStack( Process, &InitialTeb ); } else { if (ARGUMENT_PRESENT( Thread )) { *Thread = ThreadHandle; } if (ARGUMENT_PRESENT( ClientId )) { *ClientId = ThreadClientId; } } // // Return status // return( Status ); } может были какие то изменения начиная с ХР ?
Csrss тратит время на обработку сообщений с порта исключений, которые циклически возникают в созданном потоке.
klzlk так на xp нужно делать уведомление о потоке CsrClientCallServer... если да то почему не нужно его делать на висте и севен я же на всех осях (xp,висте,севен) запускаю в одном сеансе ...
984259h Фолт у вас там рекурсивно возникает(и хэндлится диспетчером исключений в csrss, там сервер порта исключений, который все юзермодные фолты хэндлит). Возможно в других версиях системы содержимое к примеру стека отличается(помню что нэйтивный тред не получает на стеке аргумент, да и без того стопяцот причин может быть). Без отладчика ничего не будет. Бог не может писать на васме.
klzlk И так у меня получается следующая ситуация CreateRemoteThread работает на все осях (хр, висте и севен) RtlCreateUserThread только на ВИСТЕ и СЕВЕН На ХР она грузит проц ? (почему ?) было бы хорошо пройтись отладчиком но как ? то есть я делаю инжект всего РЕ образа (не длл) ? полностью пишу код по смещенной базе потом делаю поток на функцию которая по хешам находит все нужные мне апишки (короче базонезависимый код) Реверсил kernel32 из XP там CreateRemoteThread --> NtCreateThread + CsrClientCallServer. В висте и севен там след ситуация: Kernel32 там CreateRemoteThread-> через (MinWin х3рня с api-ms-win-core-XXX-l1-1-0.dll прогруженая apisetschema.dll) опускается на kernelbase.dll. то есть Kernel32 --> CreateRemoteThread ==> kernelbase.dll --> CreateRemoteThread --> CreateRemoteThreadEx --> NtCreateThreadEx + CsrClientCallServer. аналогично и Kernel32 --> CreateRemoteThreadEx ==> kernelbase.dll --> CreateRemoteThreadEx --> NtCreateThreadEx + CsrClientCallServer. в Ntdll.dll там RtlCreateUserThread --> RtlCreateUserThreadEx --> NtCreateThreadEx. Меня начали смущать сомнения на счет уведомления CsrClientCallServer. в инжектированном потоке используется WaitForSingleObject и WaitForMultipleObjects но в след. топике http://www.wasm.ru/forum/viewtopic.php?id=28243 z0mailbox говорит что без CsrClientCallServer будут косяки ... какая то аномалия
984259h Зачем вам использовать винапи с нэйтивными тредами ? Если используете нэйтив, то и ждать следует должным образом - посредством сервисов(NtWaitForSingleObject etc.). Винапи содержат много лишнего функционала. Суспендите процесс(все потоки, аля F12) и смотрите где висит ваш тред.
Давно была та же хрень, уж не помню как решил, помню только, что уведомление не надо делать. С какими параметрами делаешь BaseCreateStack и BaseInitializeContext?
klzlk Инжект сделан полностью на нетиве !!!!!!!!!!! а тот поток который инжектируется реализован на WINAPI !!!!
qqqqqq Сегодня попробую использовать полностью NtCreateThread то есть полностью выделю память под стек инициализирую контекст ну конечно теб ) пожже отпишу
984259h Ну и зачем это нужно, если есть винапи ? Нэйтив это не круто, это гемор, к которому следует прибегать если не достаточно винапи функционала.
qqqqqq Взял все и исходников винды . все полностью сделал c NtCreateThread создаю поток замороженный потом делаю уведомление CsrClientCallServer (возвращает в ошибку 0xC0000001 STATUS_UNSUCCESSFUL) потом восстанавливаю выполнения потока. Если проигнорить CsrClientCallServer то на семерке и висте работает на XP та самая хрень ...
...а надо взять и переделать чтоб работало 1. BaseCreateStack: - ImageStackSize:=$8000; - делаешь стек Stack:=VirtualAllocEx(hProcess,0,ImageStackSize,{MEM_RESERVE or }MEM_COMMIT,Page_Execute_ReadWrite); - инициализируешь TEB OldStackBase:=Nil; OldStackLimit:=Nil; StackAllocationBase:=Stack; StackBase:=Stack+ImageStackSize; StackLimit:=Stack; 2. BaseInitializeContext Eax:=InitialPc; Ebx:=0; SegGs:=0; SegFs:=56; SegEs:=32; SegDs:=32; SegSs:=32; SegCs:=24; EFlags:=$3000; //IOPL=3 Eip:=InitialPc; ContextFlags:=CONTEXT_FULL; Esp:=InitialSp{-4}; //раскоменть 4, если передаешь потоку параметры
qqqqqq я это все сделал !!! примерно так Code (Text): // Create stack size for remote process SYSTEM_BASIC_INFORMATION SysInfo; NtStatus = NtQuerySystemInformation(SystemBasicInformation,&SysInfo,sizeof(SysInfo),NULL); if (NT_SUCCESS(NtStatus)) { // default stack size of remote process DWORD CommittedStackSize = SysInfo.uPageSize; DWORD MaximumStackSize = SysInfo.uAllocationGranularity; if ( CommittedStackSize >= MaximumStackSize ) MaximumStackSize = ROUND_UP(CommittedStackSize, (1024*1024)); CommittedStackSize = ROUND_UP(CommittedStackSize,SysInfo.uPageSize ); MaximumStackSize = ROUND_UP(MaximumStackSize,SysInfo.uAllocationGranularity); // allocate max memory onto stack PVOID pStack = NULL; NtStatus = NtAllocateVirtualMemory(hProcess,&pStack,0,&MaximumStackSize,MEM_RESERVE,PAGE_READWRITE); if (NT_SUCCESS(NtStatus)) { // fill TEB INITIAL_TEB InitialTeb; InitialTeb.OldInitialTeb.OldStackBase = NULL; InitialTeb.OldInitialTeb.OldStackLimit = NULL; InitialTeb.StackAllocationBase = pStack; InitialTeb.StackBase = (PVOID)((DWORD)pStack + MaximumStackSize); pStack = (PVOID) ((DWORD)pStack + MaximumStackSize - CommittedStackSize); BOOLEAN GuardPage; if (MaximumStackSize > CommittedStackSize) { pStack = (PVOID) ((DWORD)pStack - SysInfo.uPageSize); CommittedStackSize = CommittedStackSize + SysInfo.uPageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } // allocate committed memory onto stack NtStatus = NtAllocateVirtualMemory(hProcess,&pStack,0,&CommittedStackSize,MEM_COMMIT,PAGE_READWRITE); if (NT_SUCCESS(NtStatus)) { InitialTeb.StackLimit = pStack; // if page guarded if (GuardPage) { ULONG OldProtect; DWORD RegionSize = SysInfo.uPageSize; NtStatus = NtProtectVirtualMemory(hProcess,&pStack,&RegionSize,PAGE_GUARD | PAGE_READWRITE,&OldProtect); if (NT_SUCCESS(NtStatus)) { InitialTeb.StackLimit = (PVOID)((DWORD)InitialTeb.StackLimit - RegionSize); // Create an initial context for the new thread. CONTEXT ThreadContext; ThreadContext.Eax = 0L; ThreadContext.Ebx = 1L; ThreadContext.Ecx = 2L; ThreadContext.Edx = 3L; ThreadContext.Esi = 4L; ThreadContext.Edi = 5L; ThreadContext.Ebp = 0L; ThreadContext.SegGs = 0; ThreadContext.SegFs = 0x38; // KGDT_R3_TEB ThreadContext.SegEs = 0x20; // KGDT_R3_DATA; ThreadContext.SegDs = 0x20; // KGDT_R3_DATA; ThreadContext.SegSs = 0x20; // KGDT_R3_DATA; ThreadContext.SegCs = 0x18; // KGDT_R3_CODE; ThreadContext.EFlags = 0x200L; // force interrupts on, clear all else. ThreadContext.Esp = (DWORD)InitialTeb.StackBase; ThreadContext.Eip = (ULONG)pFunction; ThreadContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS; ThreadContext.Esp -= sizeof(pSelfBaseAddress); NtWriteVirtualMemory(hProcess,(PVOID)ThreadContext.Esp,(PVOID)&pSelfBaseAddress,sizeof(pSelfBaseAddress),NULL); ThreadContext.Esp -= sizeof(pSelfBaseAddress); // Reserve room for ret address InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL,NULL); HANDLE hRemoteThread; CLIENT_ID ThreadClientId; NtStatus = NtCreateThread(&hRemoteThread,THREAD_ALL_ACCESS,&ObjectAttributes,hProcess,&ThreadClientId,&ThreadContext,&InitialTeb,TRUE); if (NT_SUCCESS(NtStatus)) { CSRMSG csrmsg = { {0}, {0}, {hProcess,hRemoteThread,(DWORD)ThreadClientId.UniqueProcess,(DWORD)ThreadClientId.UniqueThread}, {0}, 0, {0} }; NtStatus = CsrClientCallServer(&csrmsg,0,0x10001,0x0C); // NtStatus CsrClientCallServer SASTUS_UNSUCCESSFUL (0xC0000001) NtResumeThread(hRemoteThread,NULL); } } } } } } где pFunction стартовый адрес поточной функции а pSelfBaseAddress параметр к ней
984259h Куча лишнего кода, у меня сначала тоже так было - это "классическая инициализация по учебнику". Во-первых NtQuerySystemInformation нафиг нужно, бери MaximumStackSize равным именно 8000h. Во вторых, CommittedStackSize у тебя должно быть равным MaximumStackSize, и весь код ------------------------------------ pStack = (PVOID) ((DWORD)pStack + MaximumStackSize - CommittedStackSize); BOOLEAN GuardPage; if (MaximumStackSize > CommittedStackSize) { pStack = (PVOID) ((DWORD)pStack - SysInfo.uPageSize); CommittedStackSize = CommittedStackSize + SysInfo.uPageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } // allocate committed memory onto stack NtStatus = NtAllocateVirtualMemory(hProcess,&pStack,0,&CommittedStackSize,MEM_COMMIT,PAGE_READWRITE); ------------------------------------ просто не нужен, вызывай AllocateVirtualMemory только 1 раз и сразу выделяй память без резервирования. В-третьих, также выкинь GuardPage и NtProtectVirtualMemory - использование этого ничего не дает. И потом, почему ты после правильной инициализации ThreadContext.Esp = (DWORD)InitialTeb.StackBase делаешь ThreadContext.Esp -= sizeof(pSelfBaseAddress)? Что вообще такое NtWriteVirtualMemory? Это ты пишешь "тело" потока? Так это надо делать в самом начале перед созданием потока. И последнее, у меня все прекрасно работает без уведомления Csr и без создания спящего потока, т.е. не нужно ResumeThread. И самое последнее - по-моему тормоза в полтинник процессорных ресурсов были всязаны именно с неравенством CommittedStackSize и MaximumStackSize. Должно быть CommittedStackSize=MaximumStackSize=8000h
qqqqqq&984259h объясните мне, будьте добры - зачем вот эти все иниты стека, уведомления csrss и т.п. когда этот же код, только "правильней" находится в библиотеке kernel32.dll?
qqqqqq это наблюдается даже если просто юзать RtlCreateUserThread в том то и дело что сначала думал что процессорные тормоза из-за то того что не делаю уведомление ...