A file that wasn’t there


One of our minions (he was introduced in this blog entry a while ago) recently came to us asking for advice: he was about to automate yet another task, by using his Python-fu, and realized that he misses entries in the file system as well as in the registry.

Notably, he only sees this behaviour on 64bit-versions of the Windows operating system:

Windows Explorer (64bit) vs Python application (32bit)
Left: Windows Explorer (64bit) lists several folders and files.   Right: Python application (32bit) only lists the folder Microsoft.

The left image shows the folder C:\Windows\System32\Tasks as seen in the Windows Explorer, the right image as seen in a simple 32bit-python application. Only the subfolder Microsoft is listed there. Something is amiss.

 

Below is the code to produce the right image, when executed in a 32bit-version of Python:

import glob, os
for pathfilename in glob.glob(r"C:\Windows\System32\Tasks\*"):
    print pathfilename

File System Redirection

The operating system mechanism responsible for this behaviour is known as File System Redirection. It was introduced many years ago to allow for a rather smooth co-existence of 32bit- and 64bit-applications on Windows 64bit. By no means, this is something new, but every now and then, questions regarding this are being asked.

With help of the excellent tool Total Commander, the above observation can be nicely visualized and understood. If we use the 64bit-version of Total Commander, we will get the same output as Windows Explorer (see right pane below):

Total Commander (64bit) Tasks

On the left pane, we see an excerpt of the directory listing of C:\Windows. Note that there is a folder C:\Windows\System32 as well as a folder C:\Windows\SysWOW64. Total Commander can be used to calculate the real size of these folders:

C:\Windows\System32 (64bit-view): 1’259’317 kB
C:\Windows\SysWOW64 (64bit-view): 901’928 kB

If we use the 32bit-version of Total Commander, we will get the same output as our 32bit-python application (see right pane below):

Total Commander (32bit) Tasks

On the left pane, we see an excerpt of the directory listing of C:\Windows, but from a 32bit-point of view. Interestingly, there is yet another Sys*-folder: C:\Windows\Sysnative. Again, using Total Commander, the real size of these folders can be calculated:

C:\Windows\Sysnative (32bit-view): 1’259’317 kB
C:\Windows\System32 (32bit-view): 901’928 kB
C:\Windows\SysWOW64 (32bit-view): 901’928 kB

Left: Size of C:\Windows\System32 (64bit) Right: Size of C:\Windows\System32 (32bit)
Left: Size of C:\Windows\System32 (64bit).                                                                 Right: Size of C:\Windows\System32 (32bit).

A 64bit-application (such as Windows Explorer) on Windows 64bit sees the physical (real, native) directory listing. For 32bit-applications to run smoothly in this environment, Windows uses File System Redirection: Access to C:\Windows\System32 is redirected to C:\Windows\SysWOW64 such that a 32bit-application loads the correct 32bit-version of system files. Therefore, the size of C:\Windows\System32 equals the size of C:\Windows\SysWOW64 when analyzed using the 32bit-version of Total Commander.

But what if a 32bit-applications wants to access the physical (real, native) folder C:\Windows\System32? Conveniently, for 32bit-applications there is a virtual folder C:\Windows\Sysnative to access the native system folder:

import glob, os
for pathfilename in glob.glob(r"C:\Windows\System32\Tasks\*"):
    print pathfilename
for pathfilename in glob.glob(r"C:\Windows\SysWOW64\Tasks\*"):
    print pathfilename
for pathfilename in glob.glob(r"C:\Windows\Sysnative\Tasks\*"):
    print pathfilename
Python application (32bit), listing of all three System32, SysWOW64 and Sysnative-directories
Python application (32bit), listing of all three system folders System32, SysWOW64 and Sysnative.

Registry Redirection

Unsurprisingly, the same effect can be seen when analyzing the Registry. It is known as Registry Redirection. The left image shows the registry key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree as seen in the Registry Editor (a 64bit-application), the right image as seen in a simple 32bit-python application.

Registry Editor (64bit) vs Python application (32bit)
Left: Registry Editor (64bit) shows several subkeys                             Right: Python application (32bit) does not list any subkey.

 

The code is as follows, executed in a 32bit-version of Python:

import _winreg
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree",0,_winreg.KEY_READ)
nr_keys, nr_values, lastmodified = _winreg.QueryInfoKey(key)
print r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
print "\t#Keys: %u #Values: %u" % (nr_keys,nr_values)

There must be a way to have access to the 64bit-view also from a 32bit-application. Indeed, Windows provides such a mechanism, and it is very simple: instead of accessing the registry with a the flag _KEY_READ, one can use the flags _KEY_READ|_KEY_WOW64_64KEY. The flag _KEY_WOW64_64KEY (0x100) makes sure that the 64bit-key is accessed, regardless whether by an 32bit-application or an 64bit-application. Correspondingly, the flag _KEY_WOW64_32KEY (0x200) makes sure that the 32bit-key is accessed, regardless whether by an 32bit-application or an 64bit-application:

import _winreg
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree",0,_winreg.KEY_READ)
nr_keys, nr_values, lastmodified = _winreg.QueryInfoKey(key)
print r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
print "\t#Keys: %u #Values: %u" % (nr_keys,nr_values)

key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree",0,_winreg.KEY_READ|_winreg.KEY_WOW64_64KEY)
nr_keys, nr_values, lastmodified = _winreg.QueryInfoKey(key)
print r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree (64bit-view)"
print "\t#Keys: %u #Values: %u" % (nr_keys,nr_values)
for i in xrange(nr_keys):
    print _winreg.EnumKey(key, i)
for i in xrange(nr_values):
    print _winreg.EnumValue(key,i)
python_32bit_reg_tree_64bitview
Python application (32bit) shows keys when using the 64bit-view.

 

Our minion is happy. He now knows how to look for all files and registry keys, regardless whether they are redirected or not. Not a single entry, be it malicious or not, will escape his Python-fu. Or will it?