A PE-header census found x86 and x64 mixed into System32
In this article
Reading 4,324 DLLs in System32 produced 4,155 ARM64 files, 122 x86 files, 46 x64 files, and one ARM32 file.
Because this was C:\Windows\System32 on ARM64 Windows, I expected almost everything to be ARM64. The contents looked more mixed than expected. Among EXEs there were 696 ARM64 files but also four x64 and two x86; among DLLs, the x86 count rose to 122. I had assumed that a folder with such a clean name would have an equally clean architecture split.
The most striking result was one remaining ARM32 file. The file was C:\Windows\System32\DriverStore\FileRepository\prnms006.inf_arm64_8bcb2c96990be129\SendToOneNoteFilter.dll. It was neither ARM64 nor x86, and the only ARM32 result was under a printer driver.
The measurement date was 2026-06-23. I ran it on the night of June 23, 2026, on a Surface Pro 11th Edition with Windows 11 Pro 10.0.26200 ARM64. Classification used only the PE-header Machine field, not file names. What I expected to be a short investigation turned into more questions once I started looking at the gap between folder names and file contents.
I read only the headers
The procedure was simple: open the EXEs and DLLs under C:\Windows\System32 and C:\Windows\SysWOW64, then read the PE Machine field. The first two bytes are MZ; offset 0x3C contains the offset to the PE header. From there I checked for PE\0\0; the two bytes four bytes later are Machine.
def read_machine(path):
with open(path, "rb") as f:
mz = f.read(2)
if mz != b"MZ":
return None
f.seek(0x3C)
pe_offset = int.from_bytes(f.read(4), "little")
f.seek(pe_offset)
if f.read(4) != b"PE\0\0":
return None
machine = int.from_bytes(f.read(2), "little")
return {
0xAA64: "ARM64",
0x8664: "x64",
0x014C: "x86",
0x01C4: "ARM32",
0xA641: "ARM64EC",
0xA64E: "ARM64X",
}.get(machine, hex(machine))
This avoids being led by file or folder names. I did not assume that a file in System32 was ARM64 or that a file in SysWOW64 was x86. That is why the x86 and x64 files under System32 became visible. Trusting folder names alone would have hidden this entire part.
As described below, this may not be enough to identify ARM64X. Looking only at the COFF header's Machine field was an explicit measurement condition.
Results by folder
The scan covered 702 EXEs in System32, 4,324 DLLs in System32, and 330 EXEs in SysWOW64. I will explain later why this read was too shallow to find ARM64X. The SysArm32 EXE folder did not exist, so no count was available.
Capacity percentages came from bytes_by_machine. For System32 DLLs, ARM64 is 96.1% by file count but 93.2% by bytes. There are 122 x86 files, but they account for 5.7% of the bytes, so they have a somewhat lighter presence by size than by count. I spent a while wondering why some architectures were mixed in the opposite direction.
| Target | count | ARM64 | x64 | x86 | ARM32 | Capacity note |
|---|---|---|---|---|---|---|
System32 *.exe |
702 files | 696 files / 99.1% / 636494216 bytes | 4 files / 0.6% / 764264 bytes | 2 files / 0.3% / 250704 bytes | 0 files | ARM64 roughly 99.8% |
System32 *.dll |
4324 files | 4155 files / 96.1% / 4685434672 bytes | 46 files / 1.1% / 52961288 bytes | 122 files / 2.8% / 286007728 bytes | 1 file / 0.0% / 77312 bytes | x86 roughly 5.7% |
SysWOW64 *.exe |
330 files | 2 files / 0.6% / 1119856 bytes | 0 files | 328 files / 99.4% / 51695656 bytes | 0 files | x86 roughly 97.9% |
SysArm32 *.exe |
none | 0 files | 0 files | 0 files | 0 files | Folder absent |
The four x64 EXEs in System32 were C:\Windows\System32\dns-sd.exe, C:\Windows\System32\dpnsvr.exe, C:\Windows\System32\fixmapix64.exe, and C:\Windows\System32\wbem\WmiPrvSEx64.exe. The two x86 files were C:\Windows\System32\DriverStore\FileRepository\devicestelemetryservicedriver.inf_arm64_1ed8bdd395254b72\AppLauncher.exe and C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe.
Examples of x64 DLLs included C:\Windows\System32\ccmcore.dll, C:\Windows\System32\ccmperf.dll, C:\Windows\System32\CcmUsrCse.dll, C:\Windows\System32\dfshim.dll, and C:\Windows\System32\directml_x64.dll. Examples of x86 DLLs were C:\Windows\System32\AuthFWSnapin.dll, C:\Windows\System32\AuthFWWizFwk.dll, C:\Windows\System32\enterpriseetw.dll, C:\Windows\System32\ETWCoreUIComponentsResources.dll, and C:\Windows\System32\Microsoft-Windows-AppModelExecEvents.dll.
SysWOW64 also contained the opposite mixture
If SysWOW64 is treated as the x86 location suggested by its name, 328 x86 files is unsurprising. What stood out was the two ARM64 files mixed in. They were C:\Windows\SysWOW64\edit.exe and C:\Windows\SysWOW64\sudo.exe. That is only 0.6% of the 330 EXEs by count, but the mixture is real. Small exceptions are enough to trip up a hard-coded assumption.
By size, SysWOW64 contained 51,695,656 bytes of x86 files and 1,119,856 bytes of ARM64 files. That is 97.9% x86 and 2.1% ARM64. Even with only two files, names such as sudo.exe and edit.exe make it easy to make the wrong architecture call from the folder name.
System32 showed the same pattern. powershell_ise.exe is below System32 but was x86. WmiPrvSEx64.exe includes x64 in its name and was also x64 in its Machine field. Names and headers sometimes agree, but they should not be treated as guaranteed to agree. The mismatch was more common than I expected.
The failed attempt to find ARM64X
I am keeping this as a measurement failure. The lookup table included ARM64EC 0xA641 and ARM64X 0xA64E. Yet both results were zero, which looked too clean and made me uneasy afterward.
At first I thought perhaps ARM64X was almost absent from my Windows 11 Pro 10.0.26200 installation. But I had heard that some System32 DLLs use ARM64X, so zero looked too perfect. The lone printer-driver ARM32 result made the situation even more suspicious. It took time to realize that looking only at the COFF header's Machine field may make ARM64X appear as ARM64. I started to suspect that I was reading too shallow a layer. This function reads only the PE file's surface Machine; it does not inspect the additional ARM64X structure.
Properly resolving this would require following another header, the load configuration, or ARM64X-specific metadata. I did not do that this time. That is why the verdict is “conditional,” not “worked.” The scan worked for finding the x86, x64, and ARM32 mixture, but it is not sufficient as an ARM64X detector. The locations of the exceptions mattered more to me than their count.
How I use the result
After this, I stopped trusting folder names too much in Windows on ARM investigations. System32 is not a box containing only ARM64, and SysWOW64 is not a box containing only x86. They are mostly what their names suggest, but there are exceptions ranging from a few files to hundreds. Before this investigation, I had been too willing to rely on the names.
When troubleshooting whether something is native or emulated, I would not stop at the location reported by where.exe. Reading two bytes from a PE header is cheap. Scanning all files as I did can expose a mixture that folder names hide. The starting point for this investigation was precisely the decision to inspect Machine rather than assume from the path.
The most interesting result was the single ARM32 DLL in System32. I do not know whether it remains for compatibility or because of the driver layout. If I measure this again, I want to add a correct ARM64X detector and recount the same 702 EXEs and 4,324 DLLs.