Esempio n. 1
0
NTSTATUS
nt_wow64_read_virtual_memory64(HANDLE process, uint64 base, void *buffer,
                               size_t buffer_length, size_t *bytes_read)
{
    /* This syscall was added in 2003 so we can't statically link. */
    typedef NTSTATUS (*NtWow64ReadVirtualMemory64_t)(HANDLE ProcessHandle,
                                                    IN PVOID64 BaseAddress,
                                                    OUT PVOID Buffer,
                                                    IN ULONGLONG BufferSize,
                                                    OUT PULONGLONG NumberOfBytesRead);
    static NtWow64ReadVirtualMemory64_t ntcall;
    NTSTATUS res;
    if (ntcall == NULL) {
# if !defined(NOT_DYNAMORIO_CORE) && !defined(NOT_DYNAMORIO_CORE_PROPER)
        /* The first call may not be during init so we have to unprot */
        if (dynamo_initialized)
            SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
# endif
        ntcall = (NtWow64ReadVirtualMemory64_t)
# ifdef NOT_DYNAMORIO_CORE
            GetProcAddress(GetModuleHandle("ntdll.dll"), "NtWow64ReadVirtualMemory64");
# else
            get_proc_address(get_ntdll_base(), "NtWow64ReadVirtualMemory64");
# endif
# if !defined(NOT_DYNAMORIO_CORE) && !defined(NOT_DYNAMORIO_CORE_PROPER)
        if (dynamo_initialized)
            SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
# endif
    }
    if (ntcall == NULL) {
        /* We do not need to fall back to NtReadVirtualMemory, b/c
         * NtWow64ReadVirtualMemory64 was added in xp64==2003 and so should
         * always exist if we are in a WOW64 process: and we should only be
         * called from a WOW64 process.
         */
        ASSERT_NOT_REACHED();
        res = STATUS_NOT_IMPLEMENTED;
    } else {
        uint64 len;
        res = ntcall(process, (PVOID64) base, buffer,
                     (ULONGLONG) buffer_length, &len);
        if (bytes_read != NULL)
            *bytes_read = (size_t) len;
    }
    return res;
}
Esempio n. 2
0
/* pass non-NULL for thandle if you want this routine to use
 *   Get/SetThreadContext to get the context -- you must still pass
 *   in a pointer to a cxt
 */
BOOL
inject_into_thread(HANDLE phandle, CONTEXT *cxt, HANDLE thandle,
                   char *dynamo_path)
{
    size_t              nbytes;
    BOOL                success = FALSE;
    ptr_uint_t          dynamo_entry_esp;
    ptr_uint_t          dynamo_path_esp;
    LPVOID              load_dynamo_code = NULL; /* = base of code allocation */
    ptr_uint_t          addr;
    reg_t               *bufptr;
    char                buf[MAX_PATH];
    uint                old_prot;

    ASSERT(cxt != NULL);

#ifndef NOT_DYNAMORIO_CORE_PROPER
    /* FIXME - if we were early injected we couldn't call inject_init during
     * startup because kernel32 wasn't loaded yet, so we call it here which
     * isn't safe because it uses app locks. If we want to support a mix
     * of early and late follow children injection we should change load_dynamo
     * to use Nt functions (which we can link) rather then kernel32 functions
     * (which we have to look up).  We could also use module.c code to safely
     * walk the exports of kernel32.dll (we can cache its mod handle when it
     * is loaded). */ 
    if (!inject_initialized) {
        SYSLOG_INTERNAL_WARNING("Using late inject follow children from early injected process, unsafe LdrLock usage");
        SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
        inject_init();
        SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
    }
#else
    ASSERT(inject_initialized);
#endif

    /* soon we'll start using alternative injection with case 102 - leaving block */
    {
        reg_t app_xsp;
        if (thandle != NULL) {
            /* grab the context of the app's main thread */                 
            cxt->ContextFlags = CONTEXT_DR_STATE;
            if (!NT_SUCCESS(nt_get_context(thandle, cxt))) {
                display_error("GetThreadContext failed");
                goto error;
            }
        }
        app_xsp = cxt->CXT_XSP;

        /* copy load_dynamo() into the address space of the new process */
        ASSERT(BUFFER_SIZE_BYTES(buf) > SIZE_OF_LOAD_DYNAMO);
        memcpy(buf, (char*)load_dynamo, SIZE_OF_LOAD_DYNAMO);
        /* R-X protection is adequate for our non-self modifying code,
         * and we'll update that after we're done with
         * nt_write_virtual_memory() calls */

        /* get allocation, this will be freed by os_heap_free, so make sure
         * is compatible allocation method */
        if (!NT_SUCCESS(nt_remote_allocate_virtual_memory(phandle, &load_dynamo_code, 
                                                          SIZE_OF_LOAD_DYNAMO,
                                                          PAGE_EXECUTE_READWRITE,
                                                          MEMORY_COMMIT))) {
            display_error("Failed to allocate memory for injection code");
            goto error;
        }
        if (!nt_write_virtual_memory(phandle, load_dynamo_code, buf,
                                     SIZE_OF_LOAD_DYNAMO, &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* Xref PR 252745 & PR 252008 - we can use the app's stack to hold our data
         * even on WOW64 and 64-bit since we're using set context to set xsp. */
   
        /* copy the DYNAMORIO_ENTRY string to the app's stack */
        _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", DYNAMORIO_ENTRY);
        NULL_TERMINATE_BUFFER(buf);
        nbytes = strlen(buf) + 1; // include the trailing '\0'
        /* keep esp at pointer-sized alignment */
        cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ);
        dynamo_entry_esp = cxt->CXT_XSP;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     buf, nbytes, &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* copy the dynamorio_path string to the app's stack */
        _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", dynamo_path);
        NULL_TERMINATE_BUFFER(buf);
        nbytes = strlen(buf) + 1; // include the trailing '\0'
        /* keep esp at pointer-sized byte alignment */
        cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ);
        dynamo_path_esp = cxt->CXT_XSP;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     buf, nbytes, &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* copy the current context to the app's stack. Only need the
         * control registers, so we use a dr_mcontext_t layout.
         */
        bufptr = (reg_t*) buf;
        *bufptr++ = cxt->CXT_XDI;
        *bufptr++ = cxt->CXT_XSI;
        *bufptr++ = cxt->CXT_XBP;
        *bufptr++ = app_xsp;
        *bufptr++ = cxt->CXT_XBX;
        *bufptr++ = cxt->CXT_XDX;
        *bufptr++ = cxt->CXT_XCX;
        *bufptr++ = cxt->CXT_XAX;
#ifdef X64
        *bufptr++ = cxt->R8;
        *bufptr++ = cxt->R9;
        *bufptr++ = cxt->R10;
        *bufptr++ = cxt->R11;
        *bufptr++ = cxt->R12;
        *bufptr++ = cxt->R13;
        *bufptr++ = cxt->R14;
        *bufptr++ = cxt->R15;
#endif
        /* It would be nice to use preserve_xmm_caller_saved(), but we'd need to
         * link proc.c and deal w/ messy dependencies to get it into arch_exports.h,
         * so we do our own check.  We go ahead and put in the xmm slots even
         * if the underlying processor has no xmm support: no harm done.
         */
        if (IF_X64_ELSE(true, is_wow64_process(NT_CURRENT_PROCESS))) {
            /* PR 264138: preserve xmm0-5.  We fill in all slots even though
             * for 32-bit we don't use them (PR 306394).
             */
            int i, j;
            for (i = 0; i < NUM_XMM_SLOTS; i++) {
                for (j = 0; j < IF_X64_ELSE(2,4); j++) {
                    *bufptr++ = CXT_XMM(cxt, i)->reg[j];
                }
            }
        } else {
            /* skip xmm slots */
            bufptr += XMM_SLOTS_SIZE/sizeof(*bufptr);
        }
        *bufptr++ = cxt->CXT_XFLAGS;
        *bufptr++ = cxt->CXT_XIP;
        ASSERT((char *)bufptr - (char *)buf == sizeof(dr_mcontext_t));
        *bufptr++ = (ptr_uint_t)load_dynamo_code;
        *bufptr++ = SIZE_OF_LOAD_DYNAMO;
        nbytes = sizeof(dr_mcontext_t) + 2*sizeof(reg_t);
        cxt->CXT_XSP -= nbytes;
#ifdef X64
        /* We need xsp to be aligned prior to each call, but we can only pad
         * before the context as all later users assume the info they need is
         * at TOS.
         */
        cxt->CXT_XSP = ALIGN_BACKWARD(cxt->CXT_XSP, XMM_ALIGN);
#endif
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP,
                                     buf, nbytes, &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* push the address of the DYNAMORIO_ENTRY string on the app's stack */
        cxt->CXT_XSP -= XSP_SZ;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     &dynamo_entry_esp, sizeof(dynamo_entry_esp),
                                     &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* push the address of GetProcAddress on the app's stack */
        ASSERT(addr_getprocaddr);
        addr = addr_getprocaddr;
        cxt->CXT_XSP -= XSP_SZ;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     &addr, sizeof(addr), &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* push the address of the dynamorio_path string on the app's stack */
        cxt->CXT_XSP -= XSP_SZ;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     &dynamo_path_esp, sizeof(dynamo_path_esp),
                                     &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

        /* push the address of LoadLibraryA on the app's stack */
        ASSERT(addr_loadlibrarya);
        addr = addr_loadlibrarya;
        cxt->CXT_XSP -= XSP_SZ;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     &addr, sizeof(addr), &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }

#ifdef LOAD_DYNAMO_DEBUGBREAK
        /* push the address of DebugBreak on the app's stack */
        ASSERT(addr_debugbreak);
        addr = addr_debugbreak;
        cxt->CXT_XSP -= XSP_SZ;
        if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, 
                                     &addr, sizeof(addr), &nbytes)) {
            display_error("WriteMemory failed");
            goto error;
        }
#endif

        /* make the code R-X now */
        if (!nt_remote_protect_virtual_memory(phandle, load_dynamo_code, 
                                              SIZE_OF_LOAD_DYNAMO,
                                              PAGE_EXECUTE_READ, &old_prot)) {
            display_error("Failed to make injection code R-X");
            goto error;
        }
        ASSERT(old_prot == PAGE_EXECUTE_READWRITE);

        /* now change Eip to point to the entry point of load_dynamo(), so that
           when we resume, load_dynamo is invoked automatically */
        cxt->CXT_XIP = (ptr_uint_t)load_dynamo_code;
        cxt->CXT_XFLAGS = 0;
        if (thandle != NULL) {
            if (!NT_SUCCESS(nt_set_context(thandle, cxt))) {
                display_error("SetThreadContext failed");
                goto error;
            }
        }

        success = TRUE;
    }
    error:
        /* we do not recover any changes in the child's address space */

    return success;
}