volatilityfoundation / volatility

An advanced memory forensics framework
http://volatilityfoundation.org/
GNU General Public License v2.0
7.17k stars 1.27k forks source link

dlllist still failing on wow64 processes for Win10x64 #490

Open gleeda opened 6 years ago

gleeda commented 6 years ago

The reason for this is that the wow64process is a NoneObject pointer to a valid address. For example, I have a 32bit process:

$ python vol.py -f win10x64.vmem --kdbg=0xf802ac1f9758 --profile=Win10x64 -n rundll32.exe  pslist
Offset(V)          Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit                          
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xffffe000392ac480 rundll32.exe           5568   2104      2        0      1      1 2018-01-28 15:44:19 UTC+0000 

# we see only the 64bit dlls:

$python vol.py -f --kdbg=0xf802ac1f9758 --profile=Win10x64   -p 5568 dlllist
Volatility Foundation Volatility Framework 2.6
************************************************************************
rundll32.exe pid:   5568
Command line : rundll32  "\\vmware-host\Shared Folders\misc code\consl\Debug\consl.dll",dll_wWinMain

Base                             Size          LoadCount LoadTime                       Path
------------------ ------------------ ------------------ ------------------------------ ----
0x0000000000040000            0x12000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\SysWOW64\rundll32.exe
0x00007ff8fda80000           0x1c2000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\SYSTEM32\ntdll.dll
0x000000005eba0000            0x4f000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\wow64.dll
0x000000005ebf0000            0x73000                0x6 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\wow64win.dll
0x000000005eb90000             0x8000                0x6 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\wow64cpu.dll

$ python vol.py -f win10x64.vmem --kdbg=0xf802ac1f9758 --profile=Win10x64 -p 5568 volshell 

In [1]: wow64process = proc().Wow64Process
In [2]: wow64process
Out[2]: <NoneObject pointer to [0x7F243000]>

In [3]: wow64process.is_valid()
Out[3]: False

# use only the value, not the object:
In [4]: peb32 = obj.Object("_PEB32", offset = wow64process.v(), vm = proc().get_process_address_space(), name = "Peb32", parent = self)

In [5]: peb32
Out[5]: [CType Peb32] @ 0x7F243000

In [6]: table_name = "_LDR32_DATA_TABLE_ENTRY"

In [7]: list_member = "InLoadOrderModuleList"

In [8]: link_member = "InLoadOrderLinks"

# now we see the other dlls, including the 32bit DLL I'm interested in:
In [9]: for module in proc()._get_modules(peb32.Ldr.m(list_member), table_name, link_member):
    ...:     print module.FullDllName
    ...:     
    ...:     
C:\Windows\SysWOW64\rundll32.exe
C:\Windows\SYSTEM32\ntdll.dll

[snip]
\\vmware-host\Shared Folders\misc code\consl\Debug\consl.dll
C:\Windows\system32\uxtheme.dll
C:\Windows\system32\dwmapi.dll

Therefore, we'll have to figure out a better way to check for validity, or figure out why the wow64process is a NoneObject https://github.com/volatilityfoundation/volatility/blob/master/volatility/plugins/overlays/windows/windows.py#L412

Also, this class still has an error, even if we just put "True" in the if statement checking validity:

 def Peb32(self):
        """ Returns a _PEB object which is using the process address space.

        The PEB structure is referencing back into the process address
        space so we need to switch address spaces when we look at
        it. This method ensures this happens automatically.
        """
        wow64process = self.Wow64Process

        if True: #wow64process.is_valid():
            process_ad = self.get_process_address_space()
            if process_ad:

                # starting with windows 10 the Wow64Process member
                # points to an _EWOW64PROCESS with a Peb
                try: 
                    offset = wow64process.Peb  #<-- this doesn't fail like it should
                except AttributeError:
                    offset = wow64process.v()

                # which means that this doesn't work, since it has the wrong object.
                # therefore, instead of relying on a try/except, we should actually check the profile
                # to force the wow64process pointer
                peb32 = obj.Object("_PEB32", offset = offset, vm = process_ad, name = "Peb32", parent = self)

                if peb32.is_valid():
                    return peb32

        return obj.NoneObject("Peb32 not found")

If i just manually set the offset to wow64process.v() for this process, I get what I expect for dlllist:

$ python vol.py -f win10x64.vmem --kdbg=0xf802ac1f9758 --profile=Win10x64   -p 5568 dlllist
Volatility Foundation Volatility Framework 2.6
************************************************************************
rundll32.exe pid:   5568
Command line : rundll32  "\\vmware-host\Shared Folders\misc code\consl\Debug\consl.dll",dll_wWinMain

Base                             Size          LoadCount LoadTime                       Path
------------------ ------------------ ------------------ ------------------------------ ----
0x0000000000040000            0x12000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\SysWOW64\rundll32.exe
0x00007ff8fda80000           0x1c2000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\SYSTEM32\ntdll.dll
0x000000005eba0000            0x4f000             0xffff 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\wow64.dll
0x000000005ebf0000            0x73000                0x6 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\wow64win.dll
[snip]
0x0000000056ae0000            0x93000                0x6 2018-01-28 15:44:19 UTC+0000   \\vmware-host\Shared Folders\misc code\consl\Debug\consl.dll
0x0000000071eb0000            0x75000                0x6 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\uxtheme.dll
0x0000000071f30000            0x1d000                0x6 2018-01-28 15:44:19 UTC+0000   C:\Windows\system32\dwmapi.dll
gleeda commented 6 years ago

so here's an idea. We'll check the profile and use the appropriate structure, and we can also verify that the address of is valid in the case of a NoneObject with a valid pointer:

diff --git a/volatility/plugins/overlays/windows/windows.py b/volatility/plugins/overlays/windows/windows.py
index dd424776..a04f162a 100644
--- a/volatility/plugins/overlays/windows/windows.py
+++ b/volatility/plugins/overlays/windows/windows.py
@@ -408,22 +408,22 @@ class _EPROCESS(obj.CType, ExecutiveObjectMixin):
         it. This method ensures this happens automatically.
         """
         wow64process = self.Wow64Process
-
-        if wow64process.is_valid():
-            process_ad = self.get_process_address_space()
-            if process_ad:
-
-                # starting with windows 10 the Wow64Process member
-                # points to an _EWOW64PROCESS with a Peb
+        process_ad = self.get_process_address_space()
+        if process_ad and (wow64process.is_valid() or process_ad.is_valid_address(wow64process.v())):
+            # starting with windows 10 the Wow64Process member
+            # points to an _EWOW64PROCESS with a Peb
+            if process_ad.profile.metadata.get('major', 0) == 6 and process_ad.profile.metadata.get('minor', 0) >= 4:
+                offset = wow64process.v()
+            else:
                 try:
                     offset = wow64process.Peb
                 except AttributeError:
-                    offset = wow64process
+                    offset = wow64process.v()

-                peb32 = obj.Object("_PEB32", offset = offset, vm = process_ad, name = "Peb32", parent = self)
+            peb32 = obj.Object("_PEB32", offset = offset, vm = process_ad, name = "Peb32", parent = self)

-                if peb32.is_valid():
-                    return peb32
+            if peb32.is_valid():
+                return peb32

         return obj.NoneObject("Peb32 not found")
iMHLv2 commented 6 years ago

Gleeda, thanks for reporting. We'll get it into the queue. It may take a couple of days, but we'll update shortly.

gleeda commented 6 years ago

I can push the changes. I just wanted to make sure no one had any objections for my line of thinking :-)

iMHLv2 commented 6 years ago

Gleeda, I can reproduce and agree the Peb32 method needs refactoring. We're bulk testing a couple variations of your patch. Do you happen to have any Vista SP0-SP1 or 2003 SP1-SP2 systems with Wow64 processes? Those need special handling too, and I think one of these variations should cover it, but I'm not able to confirm ATM.

gleeda commented 6 years ago

I should have machines or samples fitting those criteria. Let me check and let me know when you want me to test them

iMHLv2 commented 6 years ago

Try that branch out above. The semantics are definitely strange. If you create two objects of different types at the same address, is_valid() will return False for one but True for the other (i.e. Pointer vs address).

You'll see the special handling for the Vista and 2003 profiles (_EWOW64PROCESS) which is what I'm most curious about, because we've never referenced that struct in plugins before, but of course give a shout if it doesn't work properly on any other profiles as well.

iMHLv2 commented 6 years ago

@gleeda Were you able to test on any Vista SP0-SP1 or 2003 SP1-SP2 systems?

iMHLv2 commented 6 years ago

Thoroughly tested with exception of Vista SP0-SP1 and 2003 SP1-SP2 (but the old version wouldn't handle those at all, so this new patch is inevitably better, but I can't confirm if its perfect yet).