Инжект кода в процессы: проблема

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by VERte][, 20 Apr 2012.

  1. VERte][

    VERte][ Elder - Старейшина

    Joined:
    17 May 2007
    Messages:
    240
    Likes Received:
    163
    Reputations:
    32
    Всем приветы.
    Столкнулся тут с одной траблой - внедряюсь в процесс через схему 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;
    }
    
     
  2. Kaimi

    Kaimi Well-Known Member

    Joined:
    23 Aug 2007
    Messages:
    1,732
    Likes Received:
    811
    Reputations:
    231
    Ты бы хоть в отладчике посмотрел свой код. У StringCchCopyA аргумент неправильный - путь к длл обрезается, jmp far непойми куда совершается...
     
    _________________________
  3. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    кстати для этого есть 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 );
    }
    
    
     
    #3 greki_hoy, 21 Apr 2012
    Last edited: 21 Apr 2012
  4. VERte][

    VERte][ Elder - Старейшина

    Joined:
    17 May 2007
    Messages:
    240
    Likes Received:
    163
    Reputations:
    32
    До того как проверять что за код в отладчике получился, надо сначала понять выполнятся ли он вообще или нет, а он не выполняется, я ставил в ольке бряк на выполнение, код вообще управление не получает
     
  5. Kaimi

    Kaimi Well-Known Member

    Joined:
    23 Aug 2007
    Messages:
    1,732
    Likes Received:
    811
    Reputations:
    231
    Допустим получает. По крайней мере на xp x86
     
    _________________________
  6. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,311
    Reputations:
    327
    ставьте бряки виндебагом, олька это некошерно и не трушно