Enumerating 32-bit modules from 32-bit processes in WOW64

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Tue, 14 Jun 2005 08:14:03


When I enumerate the modules in a 32-bit process from either the same or
other 32-bit process running in WOW64, the behavior I'm seeing is that the
path reported from some of the modules points to the 64-bit system directory
(%windir%\system32), while others point to the WOW64 system directory
(%windir%\SysWOW64).

First, is anyone else seeing this behavior? And second, is this the
intended behavior?

There does seem to be some inconsistency in the paths -- if I run a 32-bit
program through a 32-bit de *** and watch the DLL load events, the paths
to the Windows modules point to the WOW64 system directory.

I've looked at a few different methods for enumerating modules in a process;
so far, this mix of system32/syswow64 paths appears via calls to the
Toolhelp functions, the PSAPI functions, and also calling NTDLL functions.

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Jochen Kal » Tue, 14 Jun 2005 15:08:54

i Philip!

Yes. Here is the code to repro the behaviour:

<code>
#include <windows.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

void PrintModules( DWORD processID )
{
HMODULE hMods[1024];
HANDLE hProcess;
DWORD cbNeeded;
unsigned int i;

_tprintf( _T("\nProcess ID: %u\n"), processID );
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ,
FALSE, processID);
if (NULL == hProcess)
return;
if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for ( i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
{
TCHAR szModName[MAX_PATH];
if ( GetModuleFileNameEx(
hProcess, hMods[i], szModName, sizeof(szModName)))
{
_tprintf(_T("\t%s (0x%p)\n"), szModName, hMods[i] );
}
}
}
CloseHandle( hProcess );
}


int _tmain(int argc, _TCHAR* argv[])
{
int pprocessID = GetCurrentProcessId();
if (argc > 1)
pprocessID = _tstol(argv[1]);

PrintModules(pprocessID);

_getch();
return 0;
}
</code>

Compiled with VS2005B2 (as win32-app).



This is a good question.

One thing you should consider:
If you try to open the file you will be redirected to the correct one
(because of Wow64FsRedirection).
So if you open "c:\windows\system32\psapi.dll" the correct file will be
opened (=> "c:\windows\syswow64\psapi.dll").



Yes. This is correct.

Here is the output for the above code:
<output>
EnumModules-Output for Win32-app running under wow64:
e:\enummodules\debug\EnumModules.exe (0x00400000)
C:\WINDOWS\system32\ntdll.dll (0x7D600000)
C:\WINDOWS\syswow64\kernel32.dll (0x7D4C0000)
C:\WINDOWS\system32\MSVCR80D.dll (0x10200000)
C:\WINDOWS\syswow64\msvcrt.dll (0x77BA0000)
C:\WINDOWS\system32\PSAPI.DLL (0x76B70000)
C:\WINDOWS\system32\tsappcmp.dll (0x71C20000)
C:\WINDOWS\syswow64\ADVAPI32.dll (0x77F50000)
C:\WINDOWS\syswow64\RPCRT4.dll (0x7DA20000)
</output>

And the same process from within VS2005B2:
<output>
Output from within VS2005B2:
'EnumModules.exe': Loaded 'E:\EnumModules\Debug\EnumModules.exe',
Symbols loaded.
'EnumModules.exe': Loaded 'NOT_AN_IMAGE', No symbols loaded.
'EnumModules.exe': Unloaded 'NOT_AN_IMAGE'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\ntdll32.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\kernel32.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\msvcr80d.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\msvcrt.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\psapi.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\tsappcmp.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\advapi32.dll'
'EnumModules.exe': Loaded 'C:\WINDOWS\SysWOW64\rpcrt4.dll'
</output>



I think the problem is the result of "GetModuleFileName(Ex)"...


--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/

 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Jochen Kal » Tue, 14 Jun 2005 20:35:16

i Philip!

Here is an example for retrieving the "correct" filenames:

<code>

#include <windows.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>


// For the following function see:
//http://msdn.microsoft.com/library/en-us/fileio/fs/obtaining_a_file_name_from_a_file_handle.asp
#define BUFSIZE 512
BOOL DisplayRealFileName(LPCTSTR szFileName)
{
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ |
FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;

BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH+1];
HANDLE hFileMap;

// Get the file size.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);

if( dwFileSizeLo == 0 && dwFileSizeHi == 0 )
{
_tprintf(_T("Cannot map a file with a length of zero.\n"));
CloseHandle(hFile);
return FALSE;
}

// Create a file mapping object.
hFileMap = CreateFileMapping(hFile,
NULL,
PAGE_READONLY,
0,
1,
NULL);

if (hFileMap)
{
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);

if (pMem)
{
if (GetMappedFileName (GetCurrentProcess(),
pMem,
pszFilename,
MAX_PATH))
{

// Translate path with device name to drive letters.
TCHAR szTemp[BUFSIZE];
szTemp[0] = '\0';

if (GetLogicalDriveStrings(BUFSIZE-1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;

do
{
// Copy the drive letter to the template string
*szDrive = *p;

// Look up each device name
if (QueryDosDevice(szDrive, szName, BUFSIZE))
{
size_t uNameLen = _tcslen(szName);

if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszFilename, szName,
uNameLen) == 0;

if (bFound)
{
// Reconstruct pszFilename using szTemp
// Replace device path with DOS path
TCHAR szTempFile[MAX_PATH];
_stprintf_s(szTempFile,
TEXT("%s%s"),
szDrive,
pszFilename+uNameLen);
_tcsncpy_s(pszFilename, szTempFile, MAX_PATH);
}
}
}

// Go to the next NULL character.
while (*p++);
} while (!bFound && *p); // end of string
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}

CloseHandle(hFileMap);
}
_tprintf(_T("File name is %s\n"), pszFilename);
CloseHandle(hFile);
return(bSuccess);
}



void PrintModules( DWORD processID )
{
HMODULE hMods[1024];
HANDLE hProcess;
DWORD cbNeeded;
unsigned int i;

_tprintf( _T("\nProcess ID: %u\n"), processID );
hProcess =
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Tue, 14 Jun 2005 22:24:22


process;
functions.

I forgot to mention that I believe (having read it somewhere I can't
remember) that the PSAPI function is looking at the module list directly
from the PEB, rather than going through NTDLL (for example). I haven't
verified this, though, and may look into dumping that part of the PEB when I
get a chance...

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Wed, 15 Jun 2005 10:05:23


Hi Jochen,

Thanks for the suggestion. I have an Operating Systems level subscription
this year, so I do have a couple of incidents I believe I could use. Now
that it appears that this is at least known behavior (and not something I've
screwed up on my own), what I'll want to find out is whether this will be
changed in the operating system(s) or not.

In the meantime, if you or anyone else happens to know whether the x64
behavior is the same as the IA-64 behavior, I am definitely curious.

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Jochen Kal » Wed, 15 Jun 2005 14:57:24

Hi Philip!

I assume that it will not be changed, because this might be a
"breaking-change" compared with older (real win32) apps!

Many win32-apps assume that they can load DLLs from the system32
directory; sometimes they have hardcoded the path to this directory.

And GetModuleFileName just returns this (original) path...

So I think it is a good idea to let the behaviour as it is...

Maybe the the fact that some names have the sysWow64 path inside the
names is becuase of the way the DLL is loaded (maybe is it is inidrectly
loaded, the OS will have to find the correct one... and then it will use
the sywow64-directory). But this is just a assumption.


Sorry, I don have an IA64 system... :-<

--
Greetings
Jochen

My blog about Win32 and .NET
http://www.yqcomputer.com/
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Wed, 15 Jun 2005 22:08:52


Hi Jochen,

I think that's a good assumption that this has to do with compatibility.
This is one of those cases where I didn't find the "backwards compatibility
directive" to be an intuitive thing. And looking at a list of loaded
modules for a couple of apps, the DLLs that point to "system32" do seem to
be ones that tend to be loaded explicitly rather than implicitly. The core
Win32 libraries point to syswow64 (KERNEL, USER, GDI), as do a few others.
NTDLL points to system32, but then that library is probably a special case.
The libraries that I'm loading explicitly (wtsapi32, iphlpapi,
ws2_32/wsock32) point to system32.

I'll probably invest a little time to check this out in more detail, but
this behavior may prove to be useful if it provides an indication (even it's
not a guarantee) of how a 32-bit DLL was loaded.

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Ivan Brugi » Wed, 15 Jun 2005 23:59:03

I will try very briefly to summarize how a 32-bit process is loaded.

- an EPROCESS is created, with the 32bit process `flag` set
- the image is mapped in the new address space
- the 64bit ntdll.dll is loaded
- the 64-bit ntdll.dll realizes this is a wow64 process
- it then loads the wow64 modules (wow64, wow64bin, wow64cpu)
and the wow64 modules runs the cpu simulation loop (let's not argue on
this name for now)
- the cpu simulation loop loads the 32-bit ntdll.dll, that carries on
process initialization as usual.
- during (32-bit) process initialization, ntdll loads the other modules
(from SysWoW64)

NtDll.dll is treated specially, given its role in process intialization.
So, to summarize, there are 2 list of modules for a 32-bit process under
wow,
and, 64-bit tlist.exe pointed to a 32-bit process clearly shows that.

The paths are always consistent for the "half of the world" that uses them.
Unless you disable redirection, opening `c:\windows\system32` from a 32-bit
process
will effectively go to `c:\windows\SysWOW64`, that is the correct result
from the point of view of the 32-bit process. Opening `c:\windows\syswow64`
is again correct for a 32-bit process.

However, as 64-bit process can be 32-bit aware, and, opening
`c:\windows\syswow64\xxx.dll` will really open the 32-bit binary,
and that may be expected and wanted, since 64-bit processes
can be designed to be aware of the wow64 subsystem, while
the coverse is not necessarely true.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.yqcomputer.com/




message

compatibility
core
case.
it's
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Thu, 16 Jun 2005 00:16:56


Hi Ivan,

Thanks for the information...can you clarify or elaborate, though, on why --
from the point of view of a 32-bit process -- some DLL paths point directly
to the syswow64 directory while others point to system32?

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Ivan Brugi » Thu, 16 Jun 2005 01:10:09

The rule of the thumb is the following:

ntdll.dll is special, and, it should be `c:\windows\system32\ntdll.dll`
always,
at least from the point of view of hte loader list.
(smss.exe has that different, for a reason that would take a long time to
explain)
in 32-bit processes under Wow64, the DllSearch path has
`c:\windows\syswow64` first,
so, unqualified laod will go over there.
Qualified loads will go to `c:\windows\system32`, internally redirected to
syswow64.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.yqcomputer.com/






why --
directly
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Thu, 16 Jun 2005 02:04:44


Hi Ivan,

Thanks for the clarification. Hope you don't mind one more: has this
behavior been changed from the IA-64 releases, or has it been the same since
the original IA-64 release?

Thanks,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Ivan Brugi » Thu, 16 Jun 2005 02:24:51

I don't have anymore an IA64 with win2003-gold handy to check this,
so, I cannot answer for sure.
I seem to recall the behavior of the loader has always been as described
below.

In any case, there has been a good amount work on the Wow64 subsystem
in order to achieve parity with the pure win32 environment,
in the x64 release timeframe, so, I would not be surprised if something
has/had changed
in the way results are presented.

The reason why this is a so much interesting detail, is still not clear in
this thread.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.yqcomputer.com/






to
to
since
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Thu, 16 Jun 2005 10:22:23


Hi Ivan,

Thanks. With regards to "why?" I'm a relative novice at 64-bit Windows (all
of three weeks on an XP x64 install), I work on a program that reports some
aspects of system information in the HijackThis vein, and one of the areas
of information is currently loaded modules in currently running process.
It's 32-bits, of course, and in part we'd be looking for 32-bit malware
modules in the 32-bit System directory (at least to begin with) and in case
we were trying to instruct someone to locate the file "manually," I believe
it would be clearer to explicitly point to syswow64 rather than system32.
Since this is the way it is, I'll need to do the translations in the
reporting. I just wanted to make sure it was known behavior and I also
wanted to try to understand better why it is implemented this way.

Thanks again,

Philip Sloss
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Jochen Kal » Thu, 15 Jun 2006 13:14:58

Hi Philip!


I suggest that you should contant MS product support and open an
"technical support request".
If you have MSDN subscription, you have up to 4 free incidents per year.

If you do not have MSDN-subcription please let me know...

--
Greetings
Jochen

My blog about Win32 and .NET
http://www.yqcomputer.com/
 
 
 

Enumerating 32-bit modules from 32-bit processes in WOW64

Post by Philip Slo » Thu, 15 Jun 2006 13:14:59


process;
functions.

Hi Jochen,

Thanks for verifying the behavior for me and for the suggestion of mapping
the load addresses back to the real filename. I may implement that for the
short term, but I've been trying to avoid directly crossing process
boundaries to keep the invasiveness minimal and your example appears to
require execution from the source process itself (aside from system
libraries, which probably have the same process-independent load addresses).
Of course, many of the system routines I'm calling already do this, but then
they have a larger QA staff than I do.

I guess I'm hoping that this inconsistency is not a requirement of WOW64,
but rather something that can be changed. I'm new to x64 and haven't had
the opportunity to use any IA-64 boxes...

Do you or anyone know if this behavior is also the same for WOW64 running on
IA-64 systems? Given the longer "in-service" time for the IA-64 version(s)
of Windows, if this behavior is the same there, then I would presume it's
less likely to change.

Thanks again,

Philip Sloss