Всем приветы. Столкнулся тут с одной траблой - внедряюсь в процесс через схему SuspendThread/GetThreadContext/SetThreadContext/ResumeThread. Все функции выполняются нормально, поток суспендится, записываю в память шелл-код, значение eip ставлю на начало этого кода, но вот незадача, после выполнения ResumeThread поток и не думает продолжать выполняться, не смотря на то что функция возвращает 1, т.е. поток должен продолжить выполнение. Тестировал и на Win 7 и XP, результат одинаков - приложение виснет. В качестве приложения выбирал cmd.exe. Могу точно сказать, что трабла где-то в SetThreadContext ибо если его закомментировать, то все окей. В чем может быть проблема? Для наглядности приведу код инжектора (консольная прога, принимает два аргумента: PID и путь к библиотеке): Code: #include <windows.h> #include <stddef.h> #include <strsafe.h> #include <TlHelp32.h> DWORD err; BOOL SetPrivilege( HANDLE hToken, // access token handle LPCTSTR lpszPrivilege, // name of privilege to enable/disable BOOL bEnablePrivilege // to enable or disable privilege ) { TOKEN_PRIVILEGES tp; LUID luid; if ( !LookupPrivilegeValue( NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid ) ) // receives LUID of privilege { MessageBox(NULL,L"LookupPrivilegeValue error", L"Error", MB_OK); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if ( !AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { MessageBox(NULL, L"AdjustTokenPrivileges error", L"Error", MB_OK); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { MessageBox(NULL, L"The token does not have the specified privilege.", L"Error", MB_OK); return FALSE; } return TRUE; } #pragma pack(1) typedef struct _INJECT { BYTE cpushad; BYTE cpushfd; BYTE cpush; DWORD pusharg; WORD ccall; DWORD ccalladr; BYTE cpopfd; BYTE cpopad; BYTE cjmp; DWORD cjmpadr; DWORD AdrLoadLib; CHAR LibName[MAX_PATH + 1]; } INJECT; #pragma pack() int main(int argc, char* argv[]) { DWORD pid, wr, LLadr; HANDLE hProcess, hToken, hThread, hSnapshot; INJECT code; LPVOID startAdr; CONTEXT Context; THREADENTRY32 thInfo; if(argc==3) { //Включение привилегии SeDebugPrivilege OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if(!SetPrivilege(hToken, L"SeDebugPrivilege", TRUE)) MessageBox(NULL, L"SeDebugPrivilege is not enabled", L"Warning!", 0); //if(!SetPrivilege(hToken, L"SeTcbPrivilege", TRUE)) // MessageBox(NULL, L"SeTcbPrivilege is not enabled", L"Warning!", 0); CloseHandle(hToken); //Открытие процесса pid=atoi(argv[1]); hProcess=OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, pid); if(hProcess == NULL) { MessageBox(NULL, L"You have not enough rights to open process", L"Error!", 0); return -1; } //зарезервировать память в процессе startAdr = VirtualAllocEx(hProcess, NULL, sizeof(INJECT), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(startAdr==NULL) { MessageBox(NULL, L"Unable to alloc memory in remote process", L"Error!", 0); return -1; } //получение адреса LoadLibraryA LLadr = (DWORD)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA"); //Поиск потока для внедрения hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); thInfo.dwSize=sizeof(THREADENTRY32); Thread32First(hSnapshot, &thInfo); while(thInfo.th32OwnerProcessID!=pid) Thread32Next(hSnapshot,&thInfo); hThread=OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT| THREAD_SET_CONTEXT,FALSE,thInfo.th32ThreadID); if(hThread == NULL) { MessageBox(NULL, L"You have not enough rights to open thread", L"Error!", 0); return -1; } CloseHandle(hSnapshot); //Остановка потока и получение контекста SuspendThread(hThread); Context.ContextFlags = CONTEXT_FULL; if(!GetThreadContext(hThread,&Context)) { MessageBox(NULL, L"Can't get thread context", L"Error!", 0); return -1; } //Наполнение шелл-кода code.cpushad=0x60; code.cpushfd=0x9c; code.cpush=0x68; code.pusharg=(DWORD)startAdr+offsetof(INJECT,LibName); code.ccall=0x15ff; code.ccalladr=(DWORD)startAdr+offsetof(INJECT,AdrLoadLib); code.cpopfd=0x9d; code.cpopad=0x61; code.cjmp=0xea; code.cjmpadr=Context.Eip; code.AdrLoadLib=LLadr; StringCchCopyA(code.LibName, strlen(argv[2]), argv[2]); //запись шелл-кода в память if(!WriteProcessMemory(hProcess, startAdr, &code, sizeof(INJECT), &wr)) { err=GetLastError(); return -1; } Context.Eip=(DWORD)startAdr; if(!SetThreadContext(hThread,&Context)) { MessageBox(NULL, L"Can't set thread context", L"Error!", 0); return -1; } err=ResumeThread(hThread); CloseHandle(hThread); CloseHandle(hProcess); return 0; } return -1; }
Ты бы хоть в отладчике посмотрел свой код. У StringCchCopyA аргумент неправильный - путь к длл обрезается, jmp far непойми куда совершается...
кстати для этого есть RtlRemoteCall среди прочего она запихивает структуру CONTEXT в стек из шеллкода можно просто вызвать NtContinue или восстановить вручную регистры все из этой же структуры там надо переписать операнд динамически для FF25 jmp dword ptr [] пример можно глянуть в статье Ms-Rem методы внедрения кода часть 2 там как раз такой способ восстановки регистров используется вот исходник RtlRemoteCall Code: /*++ Copyright (c) 1989 Microsoft Corporation Module Name: context.c Abstract: This module implements user-mode callable context manipulation routines. The interfaces exported from this module are portable, but they must be re-implemented for each architecture. Author: Mark Lucovsky (markl) 20-Jun-1989 Revision History: Bryan Willman (bryanwi) 8-Mar-90 Ported to the 80386 --*/ #include "ntrtlp.h" #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME) #pragma alloc_text(PAGE,RtlInitializeContext) #pragma alloc_text(PAGE,RtlRemoteCall) #endif VOID RtlInitializeContext( IN HANDLE Process, OUT PCONTEXT Context, IN PVOID Parameter OPTIONAL, IN PVOID InitialPc OPTIONAL, IN PVOID InitialSp OPTIONAL ) /*++ Routine Description: This function initializes a context structure so that it can be used in a subsequent call to NtCreateThread. Arguments: Context - Supplies a context buffer to be initialized by this routine. InitialPc - Supplies an initial program counter value. InitialSp - Supplies an initial stack pointer value. Return Value: Raises STATUS_BAD_INITIAL_STACK if the value of InitialSp is not properly aligned. Raises STATUS_BAD_INITIAL_PC if the value of InitialPc is not properly aligned. --*/ { RTL_PAGED_CODE(); Context->Eax = 0L; Context->Ebx = 1L; Context->Ecx = 2L; Context->Edx = 3L; Context->Esi = 4L; Context->Edi = 5L; Context->Ebp = 0L; Context->SegGs = 0; Context->SegFs = KGDT_R3_TEB; Context->SegEs = KGDT_R3_DATA; Context->SegDs = KGDT_R3_DATA; Context->SegSs = KGDT_R3_DATA; Context->SegCs = KGDT_R3_CODE; Context->EFlags = 0x200L; // force interrupts on, clear all else. // // Even though these are optional, they are used as is, since NULL // is what these would have been initialized to anyway // Context->Esp = (ULONG) InitialSp; Context->Eip = (ULONG) InitialPc; // // add code to check alignment and raise exception... // Context->ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS; // // Set the initial context of the thread in a machine specific way. // ie, pass the initial parameter to the start address // Context->Esp -= sizeof(Parameter); ZwWriteVirtualMemory(Process, (PVOID)Context->Esp, (PVOID)&Parameter, sizeof(Parameter), NULL); Context->Esp -= sizeof(Parameter); // Reserve room for ret address } NTSTATUS RtlRemoteCall( HANDLE Process, HANDLE Thread, PVOID CallSite, ULONG ArgumentCount, PULONG Arguments, BOOLEAN PassContext, BOOLEAN AlreadySuspended ) /*++ Routine Description: This function calls a procedure in another thread/process, using NtGetContext and NtSetContext. Parameters are passed to the target procedure via its stack. Arguments: Process - Handle of the target process Thread - Handle of the target thread within that process CallSite - Address of the procedure to call in the target process. ArgumentCount - Number of 32 bit parameters to pass to the target procedure. Arguments - Pointer to the array of 32 bit parameters to pass. PassContext - TRUE if an additional parameter is to be passed that points to a context record. AlreadySuspended - TRUE if the target thread is already in a suspended or waiting state. Return Value: Status - Status value --*/ { NTSTATUS Status; CONTEXT Context; ULONG NewSp; ULONG ArgumentsCopy[5]; RTL_PAGED_CODE(); if (ArgumentCount > 4) return STATUS_INVALID_PARAMETER; // // If necessary, suspend the guy before with we mess with his stack. // if (!AlreadySuspended) { Status = NtSuspendThread( Thread, NULL ); if (!NT_SUCCESS( Status )) { return( Status ); } } // // Get the context record for the target thread. // Context.ContextFlags = CONTEXT_FULL; Status = NtGetContextThread( Thread, &Context ); if (!NT_SUCCESS( Status )) { if (!AlreadySuspended) { NtResumeThread( Thread, NULL ); } return( Status ); } // // Pass all parameters on the stack, regardless of whether a // a context record is passed. // // // Put Context Record on stack first, so it is above other args. // NewSp = Context.Esp; if (PassContext) { NewSp -= sizeof( CONTEXT ); Status = NtWriteVirtualMemory( Process, (PVOID)NewSp, &Context, sizeof( CONTEXT ), NULL ); if (!NT_SUCCESS( Status )) { if (!AlreadySuspended) { NtResumeThread( Thread, NULL ); } return( Status ); } ArgumentsCopy[0] = NewSp; // pass pointer to context RtlMoveMemory(&(ArgumentsCopy[1]),Arguments,ArgumentCount*sizeof( ULONG )); ArgumentCount++; } else { RtlMoveMemory(ArgumentsCopy,Arguments,ArgumentCount*sizeof( ULONG )); } // // Copy the arguments onto the target stack // if (ArgumentCount) { NewSp -= ArgumentCount * sizeof( ULONG ); Status = NtWriteVirtualMemory( Process, (PVOID)NewSp, ArgumentsCopy, ArgumentCount * sizeof( ULONG ), NULL ); if (!NT_SUCCESS( Status )) { if (!AlreadySuspended) { NtResumeThread( Thread, NULL ); } return( Status ); } } // // Set the address of the target code into Eip, the new target stack // into Esp, and reload context to make it happen. // Context.Esp = NewSp; Context.Eip = (ULONG)CallSite; Status = NtSetContextThread( Thread, &Context ); if (!AlreadySuspended) { NtResumeThread( Thread, NULL ); } return( Status ); }
До того как проверять что за код в отладчике получился, надо сначала понять выполнятся ли он вообще или нет, а он не выполняется, я ставил в ольке бряк на выполнение, код вообще управление не получает