예제 #1
0
static
int
parameters_present(IF_NOT_X64(bool x64_in_wow64))
{
    char path[MAX_PATH];
    int retval;

    /* We should do some sanity checking on our parameters,
       to make sure we can really inject in applications.
       War story: When renaming the product from DynamoRIO to SecureCore
       we'd start injecting and then failing to load a dll for all apps.
    */
#ifndef X64
    if (x64_in_wow64) {
        ASSERT(is_wow64_process(NT_CURRENT_PROCESS));
        retval = get_parameter_64(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH);
    } else
#endif
        retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH);
    if (IS_GET_PARAMETER_SUCCESS(retval)) {
        return 1;
    } else {
        return 0;
    }
}
예제 #2
0
bool
process_attach()
{
    int rununder_mask;
    int should_inject;
    bool takeover = true;
#if VERBOSE
    int len;
    char exename[MAX_PATH];
#endif
    /* FIXME: append to event log to indicate we're in the address space */
    VERBOSE_MESSAGE("inside preinject dll\n");

    ntdll_init();
#ifndef PARAMS_IN_REGISTRY
    /* i#85/PR 212034: use config files */
    config_init();
#endif

#if VERBOSE
    len = GetModuleFileName(NULL, exename, MAX_PATH);
    ASSERT(len > 0);
#endif
#if 0 /* PR 314367: re-enable once it all works */
#    ifndef X64
    /* PR 253431: one method of injecting 64-bit DR into a WOW64 process is
     * via 32-bit AppInit drpreinject.
     * x64 configuration takes precedence over wow64.
     */
    if (is_wow64_process(NT_CURRENT_PROCESS)) {
        should_inject = systemwide_should_preinject_64(NULL, &rununder_mask);
        if (((INJECT_TRUE & should_inject) != 0) &&
            ((INJECT_EXPLICIT & should_inject) == 0) &&
            !is_safe_mode() &&
            parameters_present(true)) {
            VERBOSE_MESSAGE("<"PRODUCT_NAME" is taking over process %d (%s) as x64>\n",
                            GetCurrentProcessId(), exename);
            check_for_run_once(NULL, rununder_mask);
            /* we commit to x64 takeover based on there being a positive
             * rununder setting and an AUTOINJECT entry.  if the AUTOINJECT
             * turns out to be invalid, we'll try the 32-bit.
             */
            takeover = !load_dynamorio_lib(true);
        }
    }
#    endif
#endif /* 0 */
    if (takeover) {
        should_inject = systemwide_should_preinject(NULL, &rununder_mask);
        if (((INJECT_TRUE & should_inject) == 0) ||
            ((INJECT_EXPLICIT & should_inject) != 0) || is_safe_mode() ||
            !parameters_present(IF_NOT_X64(false))) {
            /* not taking over */
            VERBOSE_MESSAGE(PRODUCT_NAME " is NOT taking over process %d (%s)\n",
                            GetCurrentProcessId(), exename);
        } else {
            /* yes, load in dynamo to take over! */
            VERBOSE_MESSAGE("<" PRODUCT_NAME " is taking over process %d (%s)>\n",
                            GetCurrentProcessId(), exename);
            check_for_run_once(NULL, rununder_mask);
            load_dynamorio_lib(IF_NOT_X64(false));
        }
    }
    ntdll_exit();
    /* i#1522: self-unloading messes up the win8+ loader so we return false instead */
    if (running_on_win8_or_later())
        return false;
    else
        return true;
}
예제 #3
0
static bool load_dynamorio_lib(IF_NOT_X64(bool x64_in_wow64))
{
    HMODULE dll = NULL;
    char path[MAX_PATH];
#ifdef DEBUG
    char msg[3 * MAX_PATH];
#endif
    int retval = -1; /* failure */
#ifndef X64
    bool wow64 = is_wow64_process(NT_CURRENT_PROCESS);
    if (x64_in_wow64) {
        ASSERT(wow64);
        retval = get_parameter_64(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH);
    } else
#endif
        retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH);
    if (IS_GET_PARAMETER_SUCCESS(retval)) {
        dr_marker_t mark;
        VERBOSE_MESSAGE("Loading \"%hs\"", path);
        /* The read_and_verify_dr_marker is the canonical check for dr in a
         * process, we double check against GetModuleHandle here just to be
         * extra safe (in case dr failed to initialize before).  Note that
         * GetModuleHandle won't find dr's dll if we implement certian -hide
         * or early_injection proposals. */
        if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) != DR_MARKER_FOUND &&
            GetModuleHandle(DYNAMORIO_LIBRARY_NAME) == NULL
#ifndef X64    /* these ifdefs are rather ugly: just export all routines in x64 builds? */
            && /* check for 64-bit as well */
            (!wow64 ||
             read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) != DR_MARKER_FOUND)
        /* FIXME PR 251677: need 64-bit early injection to fully test
         * read_and_verify_dr_marker_64
         */
#endif
        ) {
            /* OK really going to load dr now, verify that we are injecting
             * early enough (i.e. user32.dll is statically linked).  This
             * presumes preinject is only used with app_init injection which is
             * currently the case. FIXME - should we also check_sole_thread
             * here?  We can't really handle more then one thread when dr is
             * loading, but this can happen with early remote injected threads
             * many of which (CTRL) are relatively harmless.
             */
            LDR_MODULE *mod = get_ldr_module_by_name(L"user32.dll");
            ASSERT(mod != NULL);
            if (ldr_module_statically_linked(mod)) {
#ifndef X64
                if (x64_in_wow64)
                    dll = load_library_64(path);
                else
#endif
                    dll = LoadLibrary(path);
            } else {
                /* FIXME - would be really nice to communicate this back to
                 * the controller. */
#ifdef DEBUG
                _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg),
                          PRODUCT_NAME
                          " Error: improper injection - " PRODUCT_NAME
                          " (%s) can't inject into process %s (%s) (user32.dll "
                          "not statically linked)\n",
                          path, get_application_name(), get_application_pid());
                NULL_TERMINATE_BUFFER(msg);
                display_error(msg);
#endif
            }
        } else {
            /* notify failure only in debug builds, otherwise just return */
#ifdef DEBUG
            /* with early injection this becomes even more likely */
            if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) == DR_MARKER_FOUND
#    ifndef X64
                || (wow64 &&
                    read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) ==
                        DR_MARKER_FOUND)
#    endif
            ) {
                /* ok, early injection should always beat this */
#    if VERBOSE
                /* can't readily tell what was expected */
                _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg),
                          PRODUCT_NAME " ok if early injection, otherwise ERROR: "
                                       "double injection, " PRODUCT_NAME
                                       " (%s) is already loaded "
                                       "in process %s (%s), continuing\n",
                          path, get_application_name(), get_application_pid());
                NULL_TERMINATE_BUFFER(msg);
                display_error(msg);
#    endif /* VERBOSE */
            } else {
                /* if GetModuleHandle finds us but we don't have a marker
                 * we may have failed somehow */
                _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg),
                          PRODUCT_NAME
                          " Error: failed injection, " PRODUCT_NAME " (%s) is "
                          "loaded but not initialized in process %s (%s), continuing\n",
                          path, get_application_name(), get_application_pid());
                NULL_TERMINATE_BUFFER(msg);
                display_error(msg);
            }
#endif /* DEBUG */
            return false;
        }
    } else
        path[0] = 0;
    if (dll == NULL) {
#ifdef DEBUG
        int err = GetLastError();
        _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error %d loading %s\n",
                  err, path);
        NULL_TERMINATE_BUFFER(msg);
        display_error(msg);
#endif
        return false;
    } else {
        int_func_t init_func;
        void_func_t take_over_func;
        int res;
#ifndef X64
        if (x64_in_wow64) {
            init_func = (int_func_t)(ptr_uint_t) /*we know <4GB*/
                get_proc_address_64((uint64)dll, "dynamorio_app_init");
            take_over_func = (void_func_t)(ptr_uint_t) /*we know <4GB*/
                get_proc_address_64((uint64)dll, "dynamorio_app_take_over");
            VERBOSE_MESSAGE("dynamorio_app_init: 0x%08x; dynamorio_app_take_over: "
                            "0x%08x\n",
                            init_func, take_over_func);
        } else {
#endif
            init_func = (int_func_t)GetProcAddress(dll, "dynamorio_app_init");
            take_over_func = (void_func_t)GetProcAddress(dll, "dynamorio_app_take_over");
#ifndef X64
        }
#endif
        if (init_func == NULL || take_over_func == NULL) {
            /* unload the library so that it's clear DR is not in control
             * (o/w the DR library is in the process and it's not clear
             * what's going on)
             */
#ifndef X64
            if (x64_in_wow64) {
#    ifdef DEBUG
                bool ok =
#    endif
                    free_library_64(dll);
                ASSERT(ok);
            } else
#endif
                FreeLibrary(dll);
#ifdef DEBUG
            display_error("Error getting " PRODUCT_NAME " functions\n");
#endif
            return false;
        }
        VERBOSE_MESSAGE("about to inject dynamorio");
#ifndef X64
        if (x64_in_wow64)
            res = switch_modes_and_call(init_func, NULL, NULL, NULL);
        else
#endif
            res = (*init_func)();
        VERBOSE_MESSAGE("dynamorio_app_init() returned %d\n", res);
#ifndef X64
        if (x64_in_wow64)
            switch_modes_and_call(take_over_func, NULL, NULL, NULL);
        else
#endif
            (*take_over_func)();
        VERBOSE_MESSAGE("inside " PRODUCT_NAME " now\n");
    }
    return true;
}
예제 #4
0
파일: drmarker.c 프로젝트: AVGirl/dynamorio
/* For 32-bit build, supports looking for x64 marker (in WOW64 process).
 * For 64-bit build, only supports looking for x64 marker.
 */
static int
read_and_verify_dr_marker_common(HANDLE process, dr_marker_t *marker, bool x64)
{
    byte buf[8]; /* only needs to be 5, but dword pad just in case */
    size_t res;
    void *target = NULL;
#if !defined(NOT_DYNAMORIO_CORE) && !defined(NOT_DYNAMORIO_CORE_PROPER)
    GET_NTDLL(DR_MARKER_HOOKED_FUNCTION, DR_MARKER_HOOKED_FUNCTION_ARGS);
    void *hook_func = (void *)DR_MARKER_HOOKED_FUNCTION;
#else
    if (IF_X64_ELSE(!x64, x64 && !is_wow64_process(NT_CURRENT_PROCESS)))
        return DR_MARKER_ERROR;
    if (x64) {
# ifndef X64
        uint64 hook_func = get_proc_address_64
            (get_module_handle_64(L_DR_MARKER_HOOKED_DLL),
             DR_MARKER_HOOKED_FUNCTION_STRING);
        uint64 landing_pad = 0;
        if (hook_func == 0)
            return DR_MARKER_ERROR;
        if (!NT_SUCCESS(nt_wow64_read_virtual_memory64(process, hook_func, buf, 5, &res))
            || res != 5) {
            return DR_MARKER_ERROR;
        }
        if (buf[0] != OP_jmp_byte)
            return DR_MARKER_NOT_FOUND;

        /* jmp offset + EIP (after jmp = hook_func + size of jmp (5 bytes)) */
        /* for 64-bit, the target is stored in front of the trampoline */
        landing_pad = *(int *)&buf[1] + hook_func + 5 - 8;
         if (!NT_SUCCESS(nt_wow64_read_virtual_memory64(process, landing_pad, buf, 8,
                                                        &res)) ||
            res != 8U)
            return DR_MARKER_ERROR;
        /* trampoline address is stored at the top of the landing pad for 64-bit */
        target = (void *)PAGE_START(*(ptr_int_t *)buf);
    } else {
# endif /* !X64 */
        void *hook_func = (void *)GetProcAddress(GetModuleHandle(DR_MARKER_HOOKED_DLL),
                                                 DR_MARKER_HOOKED_FUNCTION_STRING);
#endif
        void *landing_pad;
        if (hook_func == NULL)
            return DR_MARKER_ERROR;
        if (!READ_FUNC(process, hook_func, buf, 5, &res) || res != 5)
            return DR_MARKER_ERROR;
        if (buf[0] != OP_jmp_byte)
            return DR_MARKER_NOT_FOUND;

        /* jmp offset + EIP (after jmp = hook_func + size of jmp (5 bytes)) */
        landing_pad = (void *)(*(int *)&buf[1] + (ptr_int_t)hook_func + 5);
        /* for 64-bit, the target is stored in front of the trampoline */
        if (x64)
            landing_pad = (byte *)landing_pad - 8;
        /* see emit_landing_pad_code() for layout of landing pad */
        if (!READ_FUNC(process, landing_pad, buf, (x64 ? 8 : 5), &res) ||
            res != (x64 ? 8U : 5U))
            return DR_MARKER_ERROR;
        if (x64) {
            /* trampoline address is stored at the top of the landing pad for 64-bit */
            target = (void *)PAGE_START(*(ptr_int_t *)buf);
        } else {
            /* jmp offset + EIP (after jmp = landing_pad + size of jmp (5 bytes)) */
            target = (void *)PAGE_START(*(int *)&buf[1] + (ptr_int_t)landing_pad + 5);
        }
#if defined(NOT_DYNAMORIO_CORE) || defined(NOT_DYNAMORIO_CORE_PROPER)
    }
#endif

    if (target == NULL)
        return DR_MARKER_ERROR;
    if (!READ_FUNC(process, target, marker, sizeof(dr_marker_t), &res) ||
        res != sizeof(dr_marker_t)) {
        return DR_MARKER_NOT_FOUND;
    }

    if (dr_marker_verify(process, marker)) {
        return DR_MARKER_FOUND;
    }

    return DR_MARKER_NOT_FOUND; /* probably some other hooker */
}
예제 #5
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;
}