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); } }