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