x64 process detected as ARM64 process in Windows 11 for ARM running in Parallels

Discussion in 'Windows Virtual Machine' started by soleon, Jul 14, 2026.

  1. soleon

    soleon Bit poster

    Messages:
    1
    I have run into what looks like an emulation bug in the Windows 11 ARM64 guest: the Win32 API IsWow64Process2 cannot distinguish an emulated x64 process from a native ARM64 one. It reports every process as "running natively." This breaks any tool that uses this API to detect a target process's architecture.

    Environment
    • Host: MacBook Air M5 with Parallels App Store Edition 1.26.3 (23929)
    • Parallels Tools: 1.26.3 (23929), VM type "Parallels ARM Virtual Machine"
    • Guest: Windows 11 Pro 25H2, ARM64, build 26200.8737
    What I expected
    On physical Windows-on-ARM hardware, calling IsWow64Process2(hProcess, &processMachine, &nativeMachine) on an emulated x64 process returns:
    • processMachine = IMAGE_FILE_MACHINE_AMD64 (0x8664)
    • nativeMachine = IMAGE_FILE_MACHINE_ARM64 (0xAA64)
    For a native ARM64 process it returns processMachine = IMAGE_FILE_MACHINE_UNKNOWN (0). That difference is exactly how you tell an emulated x64 process from a native ARM64 one.

    What actually happens in the Parallels guest
    For a confirmed x64 process, the call returns:
    • processMachine = IMAGE_FILE_MACHINE_UNKNOWN (0)
    • nativeMachine = IMAGE_FILE_MACHINE_ARM64 (0xAA64)
    which is identical to what a native ARM64 process returns. So, there is no way to tell the two apart. I verified the target really was x64 two independent ways: its on-disk PE header machine field is 0x8664, and it had loaded the x64 .NET runtime from C:\Program Files\dotnet\x64\...\coreclr.dll. I also confirmed the calling process was genuinely native ARM64 (not itself emulated), and I got the same wrong result whether the caller was native ARM64 or emulated x64.

    Minimal repro (C#)
    Compile as a console app and pass a PID. Run it against a known x64 process (any x64 app running under emulation) and against a native ARM64 process and compare.
    using System;
    using System.Runtime.InteropServices;

    class Program
    {
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool IsWow64Process2(IntPtr hProcess, out ushort processMachine, out ushort nativeMachine);
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr OpenProcess(uint access, bool inherit, int pid);
    [DllImport("kernel32.dll")]
    static extern bool CloseHandle(IntPtr h);

    static string Name(ushort m) => m switch
    {
    0x0 => "UNKNOWN(0)",
    0x14c => "I386/x86",
    0x8664 => "AMD64/x64",
    0xAA64 => "ARM64",
    _ => "0x" + m.ToString("X")
    };

    static void Main(string[] args)
    {
    int pid = int.Parse(args[0]);
    IntPtr h = OpenProcess(0x1000 /* QueryLimitedInformation */, false, pid);
    if (h == IntPtr.Zero) { Console.WriteLine("OpenProcess failed: " + Marshal.GetLastWin32Error()); return; }
    bool ok = IsWow64Process2(h, out ushort pm, out ushort nm);
    CloseHandle(h);
    Console.WriteLine($"ok={ok} processMachine={Name(pm)} nativeMachine={Name(nm)}");
    }
    }


    Observed output
    • Against a native ARM64 process: processMachine=UNKNOWN(0) nativeMachine=ARM64 (correct)
    • Against an emulated x64 process: processMachine=UNKNOWN(0) nativeMachine=ARM64 (wrong; should be processMachine=AMD64/x64)
    Impact
    Any software that relies on IsWow64Process2 to determine another process's architecture misbehaves. In my case a WPF debugging tool picks an injector/DLL by target architecture; because every process looks like native ARM64, it loads an ARM64 DLL into an x64 process, and Windows correctly refuses to load it. Debuggers, profilers, and DLL-injection based tools would all be affected. The same API on real Windows-on-ARM hardware works correctly, so this appears specific to the Parallels x64 emulation layer rather than to Windows itself.

    Questions
    1. Is this a known limitation of the current x64 emulation, and is a fix planned?
    2. Is there a supported way to correctly detect an emulated x64 (or x86) guest process's architecture from another process?
    Happy to provide more diagnostics or test a build. Thanks.
     

Share This Page