[Wine] Dynamic forking in Win32

rsn10100 wineforum-user at winehq.org
Mon Sep 20 04:31:14 CDT 2010


I created an application that uses dynamic forking and it works perfect in windows, but fails in wine.  Is this a bug or is this feature disabled in Wine?

I don't have wine myself but someone we have testing the app for us does.  It's difficult to debug over the net so I was hoping for some help here...

After adding some debug info we've traced a few failure points to a call to ZwUnmapViewOfSection and so I thought Wine might need the memory protections changed... No dice.  A call to VirtualProtectEx then fails after adding it in...

Any ideas on why this won't work in Wine?

Here is some code where I added a lot of extra output for debugging...

Code:

static void doFork(
    EXE_FILE *exe,
    LPVOID ptrLoc,
    DWORD imageSize,
    char *target)
{
    PROCESS_INFORMATION pi;
    CONTEXT ctx;
    PROCINFO childInfo;

    if (createChild(&pi, &ctx, &childInfo, target)) {
        LPVOID v = (LPVOID)NULL;
        DWORD r, old_protection = 0;
        MEMORY_BASIC_INFORMATION basic_info;

        printf("Original EXE loaded (PID = %d).\n", (int)pi.dwProcessId);
        printf("Original Base Addr = %X, Size = %X\n",
               (int)childInfo.baseAddr, (int)childInfo.imageSize);

        if (VirtualQueryEx(
            pi.hProcess,
            (LPCVOID)childInfo.baseAddr,
            &basic_info,
            sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
            ErrorExit("VirtualQueryEx: Failed!\n");
            TerminateProcess(pi.hProcess, 0);
            exit(1);
        }
        else {
            printf(
                "Before:\n"
                "Process basic info: 0x%08X\n"
                "       BaseAddress: 0x%08X\n"
                "    AllocationBase: 0x%08X\n"
                " AllocationProtect: 0x%08X\n"
                "        RegionSize: 0x%08X\n",
                basic_info.BaseAddress,
                basic_info.AllocationBase,
                basic_info.AllocationProtect,
                basic_info.RegionSize);

            printf("             State: 0x%08X\n", basic_info.State);
            print_flags(basic_info.State);
            printf("           Protect: 0x%08X\n", basic_info.Protect);
            print_flags(basic_info.Protect);
            printf("              Type: 0x%08X\n", basic_info.Type);
            print_flags(basic_info.Type);
        }
        printf("\n");

        old_protection = basic_info.Protect;
        if (r = VirtualProtectEx(
            pi.hProcess,
            (LPVOID)childInfo.baseAddr,
            childInfo.imageSize,
            PAGE_EXECUTE_READWRITE,
            &old_protection) == 0) {
            eprintf("VirtualProtectEx: Failed!\nReturn: %d\n", r);
        }

        if (VirtualQueryEx(
            pi.hProcess,
            (LPCVOID)childInfo.baseAddr,
            &basic_info,
            sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
            ErrorExit("VirtualQueryEx: Failed!\n");
            TerminateProcess(pi.hProcess, 0);
            exit(1);
        }
        else {
            printf(
                "After:\n"
                "Process basic info: 0x%08X\n"
                "       BaseAddress: 0x%08X\n"
                "    AllocationBase: 0x%08X\n"
                " AllocationProtect: 0x%08X\n"
                "        RegionSize: 0x%08X\n",
                basic_info.BaseAddress,
                basic_info.AllocationBase,
                basic_info.AllocationProtect,
                basic_info.RegionSize);

            printf("             State: 0x%08X\n", basic_info.State);
            print_flags(basic_info.State);
            printf("           Protect: 0x%08X\n", basic_info.Protect);
            print_flags(basic_info.Protect);
            printf("              Type: 0x%08X\n", basic_info.Type);
            print_flags(basic_info.Type);
        }

        if (exe->peXH->imageBase == childInfo.baseAddr &&
            imageSize <= childInfo.imageSize) {
            /* if new EXE has same baseaddr and its size is <= to the
             * original EXE, just overwrite it in memory
             */
            v = (LPVOID)childInfo.baseAddr;
            DWORD oldProtect;
            VirtualProtectEx(
                pi.hProcess,
                (LPVOID)childInfo.baseAddr,
                childInfo.imageSize,
                PAGE_EXECUTE_READWRITE,
                &oldProtect);

            printf("Using Existing Mem for New EXE at %X\n", (uint)v);
        }
        else {
            /* get address of ZwUnmapViewOfSection */
            PTRZwUnmapViewOfSection pZwUnmapViewOfSection =
                (PTRZwUnmapViewOfSection)GetProcAddress(
                    GetModuleHandle("ntdll.dll"), "ZwUnmapViewOfSection");

            if (!pZwUnmapViewOfSection) {
                eprintf("Could not load ZwUnmapViewOfSection from ntdll.dll\n");
                TerminateProcess(pi.hProcess, 0);
                exit(1);
            }

            /* try to unmap the original EXE image */
            if (pZwUnmapViewOfSection(
                    pi.hProcess, (LPVOID)childInfo.baseAddr) ==
                        STATUS_ACCESS_DENIED) {
                eprintf("Unmap returned 'STATUS_ACCESS_DENIED'\n");
                TerminateProcess(pi.hProcess, 0);
                exit(1);
            }
            else {
                /* allocate memory for the new EXE image at the
                 * prefered imagebase.
                 */
                v = VirtualAllocEx(
                    pi.hProcess,
                    (LPVOID)exe->peXH->imageBase,
                    imageSize,
                    MEM_RESERVE | MEM_COMMIT,
                    PAGE_EXECUTE_READWRITE);

                if (v)
                    printf(
                        "Unmapped and Allocated Mem for New EXE at %X\n",
                        (uint)v);
            }
        }

        if (!v && hasRelocationTable(exe->peXH)) {
            /* if unmap failed but EXE is relocatable, then we try to load the
             * EXE at another location
             */
            v = VirtualAllocEx(
                    pi.hProcess, (void *)NULL, imageSize,
                    MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

            if (v) {
                printf(
                    "Allocated Mem for New EXE at %X. EXE will be relocated.\n",
                    (uint)v);

                /* we've got to do the relocation ourself if we load the image
                 * at another memory location.
                 */
                doRelocation(exe, ptrLoc, (DWORD)v);
            }
        }

        printf("EIP = %X\n", (uint)ctx.Eip);
        printf("EAX = %X\n", (uint)ctx.Eax);
        printf("EBX = %X\n", (uint)ctx.Ebx);    /* EBX points to PEB */
        printf("ECX = %X\n", (uint)ctx.Ecx);
        printf("EDX = %X\n", (uint)ctx.Edx);

        if (v) {
            /* patch the EXE base addr in PEB (PEB + 8 holds process base addr) */
            DWORD *pebInfo = (DWORD *)ctx.Ebx;
            DWORD wrote;

            printf("New EXE Image Size = %X\n", (uint)imageSize);
            if (!WriteProcessMemory(
                    pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote)) {
                ErrorExit("Could not write to process memory...\n");
                TerminateProcess(pi.hProcess, 0);
                exit(1);
            }

            /* patch the base addr in the PE header of the EXE that we
             * load ourselves.
             */
            PE_ExtHeader *peXH =
                (PE_ExtHeader *)((DWORD)exe->mzH->offsetToPE +
                sizeof(PE_Header) + (DWORD)ptrLoc);

            peXH->imageBase = (DWORD)v;

            if (WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, NULL)) {
                printf("New EXE image injected into process.\n");

                ctx.ContextFlags = CONTEXT_FULL;
                //ctx.Eip = (DWORD)v + ((DWORD)dllLoaderWritePtr - (DWORD)ptrLoc);

                if ((DWORD)v == childInfo.baseAddr) {
                    /* eax holds new entry point */
                    ctx.Eax =
                        (DWORD)exe->peXH->imageBase +
                        exe->peXH->addressOfEntryPoint;
                }
                else {
                    /* in this case, the DLL was not loaded at the baseaddr,
                     * i.e. manual relocation was performed. */
                    /* eax holds new entry point */
                    ctx.Eax = (DWORD)v + exe->peXH->addressOfEntryPoint;
                }

                printf("********> EIP = %X\n", (uint)ctx.Eip);
                printf("********> EAX = %X\n", (uint)ctx.Eax);

                SetThreadContext(pi.hThread, &ctx);

                ResumeThread(pi.hThread);
                printf("Process resumed (PID = %d).\n", 
                         (uint)pi.dwProcessId);
            }
            else {
                ErrorExit("WriteProcessMemory failed\n");
                TerminateProcess(pi.hProcess, 0);
            }
        }
        else {
            ErrorExit("Load failed. X2.\n");
            TerminateProcess(pi.hProcess, 0);
        }
    }
    else {
        eprintf("CreateProcess failed.\nCannot load %s\n", target);
        exit(1);
    }
}










More information about the wine-users mailing list