Thanks for the Memories: Identifying Malware from a Memory Capture

Thanks for the Memories: Identifying Malware from a Memory Capture

We've all seen attackers try and disguise their running malware as something legitimate. They might use a file name of a legitimate Windows file or even inject code into a legitimate process that's already running. Regardless of how it's done, that code has to run, which means it has to be in memory. Somewhere.

By Adam Bridge

Head of Response

02 Mar 2015

In this blog post we lay out a real-life examination of computer memory which enabled us to identify a keylogger that was running, what files were responsible for running it, and how it managed to ensure it was started every time the machine booted up. Not only did this provide us with previously unknown indicators of compromise, but also specific details with which we could assist the client in their remediation efforts.


During some hard disk forensics, one of our examiners found a text file which was clearly a log of a keylogger application. I won't share the gory details of what it contained - I'm sure you can imagine. By performing a search for the file name, the examiner found a hit in C:\Windows\MEMORY.DMP. This file stores debug information when a system failure occurs. The examiner passed this one over to me to see if I could do anything to help identify the keylogger.

I've anonymised the username for the purposes of this blog, replacing the username of the currently logged in user with 'theuser', but the file we're interested in is:


As is probably obvious, the file name is made up of logged-in-user-name + _tmp.dat.

In this case, as is the default, Windows had created a 'Kernel Memory Dump'. That is, not a full memory dump, but enough to help troubleshoot system failures. Unfortunately, at least to the best of my knowledge, there's not too much that can be done with a Kernel Memory Dump. However, that didn't deter us. The hard disk also contained a hiberfil.sys, which is the file that Windows uses when it goes into hibernation; it essentially contains a copy of RAM at the time the machine enters the hibernation state.

Enter Volatility

There are a few memory forensics tools out there, but the one I've seen prove itself time and time again is Volatility from the Volatility Foundation. Volatility is written in python, is free and is open source.

Armed with the latest version of Volatility (2.4, at the time of writing), I set about examining the hiberfil.sys. The first hurdle was compression - hiberfil.sys files are compressed, so Volatility has to decompress the file on the fly in order to analyse it. This is slow. Luckily, Volatility provides the imagecopy plugin which allows us to convert one type of memory dump (e.g. hiberfil) into a raw, uncompressed format - much faster.

C:\>python -f hiberfil.sys --profile=Win7SP1x64 imagecopy -O hiberfil.dd

Is the file into which the keystrokes are being logged actually referenced in memory?

With an uncompressed image, and contemporaneous notes underway, a good starting point was to see if the file was open in memory. For that, we use filescan:

C:\>python -f hiberfil.dd --profile=Win7SP1x64 filescan

And in the output, we look for our file:

Offset(P)  Pointers Handles Access Name
---------- -------- ------- ------ ----
0x1b66ef20 16       0       -W--w- \Device\HarddiskVolume1\Users\theuser\AppData\Local\Temp\theuser_tmp.dat

This is good news. The file was indeed open, for write access, when the memory dump was taken. At least we know we're not wasting our time.

Which processes are accessing the file?

The next step was to see which processes were accessing the file. A useful blog post from Andreas Schuster says that Volatility should be able to resolve the process(es) by following the _FILE_OBJECT structure. However, the blog post relates to quite an old version of Volatility and it doesn't seem to apply to the current version. Or it might be that it only works with handles, which in this case we didn't have. Regardless, there's always something else to try.

By using strings from Sysinternals we can find the positions (offsets) within the raw memory dump where the paths can be seen, and save them to a file:

C:\>strings -o -n 9 hiberfil.dd | findstr /I /L "\theuser_tmp.dat" >strings.txt

And the output, in strings.txt, looks something like this:


We can then use this strings file as an input to Volatility's strings plugin. The strings plugin takes a text file where each line contains a decimal offset and string to find, for example: 123456:some_text, and returns the process ID and virtual address where the string is found.


C:\>python -f hiberfil.dd --profile=Win7SP1x64 strings -s strings.txt >strings-output.txt

Gives us the following in strings-output.txt:

1125860523 [2000:008d74ab] \Device\HarddiskVolume1\Users\theuser\AppData\Local\Temp\theuser_tmp.dat
1160780915 [4628:1051ec73] c:\users\theuser\appdata\local\temp\theuser_tmp.dat
1167748112 [kernel:fa8001c4ec10] heuser\AppData\Local\Temp\theuser_tmp.dat:6E53BFF5-0001-412B-8407-E3AEDE763511:$DATA
1336598656 [kernel:f8a007b1b080] \Users\theuser\AppData\Local\Temp\theuser_tmp.dat
1542979888 [4628:0322d130] C:\Users\theuser\AppData\Local\Temp\theuser_tmp.dat
1575054952 [3056:025c3e68] \Device\HarddiskVolume1\Users\theuser\AppData\Local\Temp\theuser_tmp.dat

By working through the file, a unique list of processes accessing the strings can be drawn up:

  • kernel
  • 2000
  • 3056
  • 4628

The process IDs can be easily found by running the pslist plugin and checking the PID value:

C:\>python -f hiberfil.dd --profile=Win7SP1x64 pslist

The output gives us:

Offset(V)          Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------
0xfffffa8001719b30 System                    4      0    146     2056 ------      0 2014-09-15 07:35:03 UTC+0000
0xfffffa800305d040 smss.exe                320      4      2       33 ------      0 2014-09-15 07:35:03 UTC+0000
0xfffffa8004eeeb30 csrss.exe               456    448     10     1052      0      0 2014-09-15 07:35:10 UTC+0000
0xfffffa800639cb30 Rtvscan.exe            3056    612     24      531      0      1 2014-09-15 07:35:34 UTC+0000
0xfffffa8002d63490 ccSvcHst.exe           2000    612     36      408      0      1 2014-09-15 07:35:28 UTC+0000
0xfffffa80032a0b30 svchost.exe            2816    612     4       102      0      0 2014-09-15 07:35:33 UTC+0000
0xfffffa8003a766f0 explorer.exe           4628   4460     46     1118      1      0 2014-09-15 07:36:12 UTC+0000

So, kernel, ccSvcHst, Rtvscan and explorer all have references to the file path in their process memory space.

ccSvcHst and Rtvscan are both components of the Symantec endpoint protection software installed on the system. It's reasonable that the AV would be listing the file for a variety of reasons, but explorer is slightly odd. Explorer is spawned when one uses an open or save file dialog, but that seems unlikely for a malicious file. The explorer process definitely warrants a closer look.

A closer look at explorer.exe

Checking the md5 of explorer.exe we can be happy that explorer itself hasn't been tampered with. Perhaps a DLL is being injected. Of course, Volatility provides a plugin with which we can check what DLLs a process has loaded; the appropriately named dlllist.

C:\>python -f hiberfil.dd --profile=Win7SP1x64 dlllist --pid=4628
explorer.exe pid:   4628
Command line : C:\Windows\Explorer.EXE
Service Pack 1

Base                             Size          LoadCount Path
------------------ ------------------ ------------------ ----
0x00000000ffe90000           0x2c0000             0xffff C:\Windows\Explorer.EXE
0x00000000777b0000           0x1a9000             0xffff C:\Windows\SYSTEM32\ntdll.dll
0x0000000077690000           0x11f000             0xffff C:\Windows\system32\kernel32.dll
0x000007fefd900000            0x6b000             0xffff C:\Windows\system32\KERNELBASE.dll
0x000007fef5930000            0x15000                0x1 C:\Windows\Installer\WinInstall.dll

There were actually 215 DLLs loaded by explorer. (In case you're thinking 215 sounds suspiciously high, it's not. If you've got 'Process Explorer' to hand, check your own explorer.exe - I bet you've got around 200 loaded.)

So, scrolling down the list preparing myself to have to check the md5 of each, I noticed the one between the snips above: C:\Windows\Installer\WinInstall.dll. This folder is immediately suspicious: there aren't normally DLLs in the root of the Installer folder.

The easy option here would be to fire up the hard disk image and take a look at the file, but:

  1. we're showing off memory skills here, and
  2. I don't have the hard disk drive image.

But that's fine, because Volatility also includes a dlldump plugin. No prizes for guessing that it dumps DLLs.

C:\>python -f hiberfil.dd --profile=Win7SP1x64 dlldump --pid=4628 --dump-dir=4628
Process(V)         Name                 Module Base        Module Name          Result
------------------ -------------------- ------------------ -------------------- ------
0xfffffa8003a766f0 explorer.exe         0x00000000ffe90000 Explorer.EXE         OK: module.4628.74a766f0.ffe90000.dll
0xfffffa8003a766f0 explorer.exe         0x00000000777b0000 ntdll.dll            OK: module.4628.74a766f0.777b0000.dll
0xfffffa8003a766f0 explorer.exe         0x0000000002130000 apphelp.dll          OK: module.4628.74a766f0.2130000.dll
0xfffffa8003a766f0 explorer.exe         0x0000000180000000 igfxpph.dll          OK: module.4628.74a766f0.180000000.dll
0xfffffa8003a766f0 explorer.exe         0x000007fef5930000 WinInstall.dll       OK: module.4628.74a766f0.7fef5930000.dll

This article isn't the place to get into the analysis of the DLL, but if we do strings against the file we get some pretty damning clues...

[Right SHIFT]
[Left SHIFT]
[d/d/%d d:d:d] (%s)

It's easy enough to see:

  • Keylogger key mappings, e.g. the right shift key becomes [Right SHIFT]
  • A timestamp and message format string: [d/d/%d d:d:d] (%s)
  • The name of two Windows API functions to do with getting raw input.
  • A format string for our file name: %s\%s_tmp.dat
  • And generally, terms to do with 'logger'.

How is the DLL injected into explorer?

All that's left for us to do now is to see how the DLL is being injected into the explorer process.

I fully expected this to be a key/value pair in the Registry, so because we're staying in memory, let's use Volatility's dumpregistry plugin to, well, dump the Registry files from memory to disk:

C:\>python -f hiberfil.dd --profile=Win7SP1x64 dumpregistry --dump-dir=reg

We end up with a file listing like this:

Volume in drive C has no label.
 Volume Serial Number is 1234-5678

 Directory of C:\reg

14/01/2015  09:21    <DIR>          .
14/01/2015  09:21    <DIR>          ..
23/12/2014  12:10             8,192 registry.0xfffff8a00000f010.no_name.reg
23/12/2014  13:57        22,675,456 registry.0xfffff8a000023290.SYSTEM.reg
23/12/2014  12:10           282,624 registry.0xfffff8a00006b410.HARDWARE.reg
23/12/2014  12:10        66,224,128 registry.0xfffff8a001bf7410.SOFTWARE.reg
23/12/2014  12:10            40,960 registry.0xfffff8a004071010.SECURITY.reg
23/12/2014  12:10            36,864 registry.0xfffff8a0040dc410.SAM.reg
23/12/2014  12:10           245,760 registry.0xfffff8a004153410.NTUSERDAT.reg
23/12/2014  12:10           249,856 registry.0xfffff8a00431d410.NTUSERDAT.reg
23/12/2014  12:10         4,128,768 registry.0xfffff8a004ef8010.ntuserdat.reg
23/12/2014  12:10         6,733,824 registry.0xfffff8a005405010.UsrClassdat.reg
23/12/2014  12:10         5,808,128 registry.0xfffff8a00712f010.Syscachehve.reg
23/12/2014  12:10           286,720 registry.0xfffff8a007c9f010.DEFAULT.reg
              12 File(s)    106,721,280 bytes
               2 Dir(s)  69,660,389,376 bytes free

Next step, lets see if we can quickly identify which of these files contains a reference to WinInstall.dll:

C:\>strings.exe reg\*.reg | findstr /I /L WinInstall.dll
FINDSTR: Line 662056 is too long.

So one line is too long, that's normal, but there are no results. That is unexpected.

What about the folder in which the file resides:

C:\>strings.exe reg\*.reg | findstr /R C:\\Windows\\Installer\\.*dll
C:\reg\registry.0xfffff8a000023290.SYSTEM.reg: C:\Windows\Installer\adspack.dll
C:\reg\registry.0xfffff8a000023290.SYSTEM.reg: C:\Windows\Installer\adspack.dll
FINDSTR: Line 662056 is too long.

So now we have a SECOND DLL in the C:\Windows\Installer folder?! That's suspicious. Is THAT dll loaded by any processes?

Let's see if 'adspack.dll' appears anywhere.

C:\>python -f hiberfil.dd --profile=Win7SP1x64 dlllist >dlllist_all.txt
svchost.exe pid:   2816
Command line : C:\Windows\system32\svchost.exe -k netsvcs
Service Pack 1

Base                             Size          LoadCount Path
------------------ ------------------ ------------------ ----
0x00000000ffdb0000             0xb000             0xffff C:\Windows\system32\svchost.exe
0x00000000777b0000           0x1a9000             0xffff C:\Windows\SYSTEM32\ntdll.dll
0x0000000077690000           0x11f000             0xffff C:\Windows\system32\kernel32.dll
0x000007fef85e0000            0x25000                0x1 c:\windows\installer\adspack.dll

Our mystery dll is loaded by svchost - a service?

Let's grab a copy of this file:

C:\>python -f hiberfil.dd --profile=Win7SP1x64 dlldump --pid=2816 --dump-dir=2816
Process(V)         Name                 Module Base        Module Name          Result
------------------ -------------------- ------------------ -------------------- ------
0xfffffa80032a0b30 svchost.exe          0x00000000ffdb0000 svchost.exe          OK: module.2816.752a0b30.ffdb0000.dll
0xfffffa80032a0b30 svchost.exe          0x00000000777b0000 ntdll.dll            OK: module.2816.752a0b30.777b0000.dll
0xfffffa80032a0b30 svchost.exe          0x000007fef85e0000 adspack.dll          OK: module.2816.752a0b30.7fef85e0000.dll

And take a look at it in MiTeC's EXE Explorer.

A 'ServiceMain' function is exported...

...and there's a resource called 'DLL', which IS WinInstall.dll:

Analysis of this DLL shows that it runs as a service, drops WinInstall.dll and injects it into explorer.

We already know from above that this DLL is referenced in the SYSTEM hive, so it's a quick check (with MiTeC's Windows Registry Recovery) to see precisely where:

So, as we can see, a service called Ias is started at boot using adspack.dll.

Bullet-Point Summary

  • On start-up, a service called 'Ias' is started the binary for which is C:\Windows\Installer\adspack.dll.
  • This dll drops another dll, C:\Windows\Installer\WinInstall.dll, which is a keylogger.
  • This keylogger is injected into the explorer.exe process.

Contact and Follow-up

Adam is part of our Response team in Context's Cheltenham office. See the Contact page for how to get in touch.

Appendix: Get Lucky! A keylogger has to log key strokes.

As we're hunting a keylogger, we can consider something a keylogger has to do: log keystrokes that can't be represented by a printable character. For example: backspace, enter, home, end, etc. Often, as was the case in this instance, keyloggers represent these unprintable characters as the literal name within square brackets, for example: [ENTER]. We could have looked for that with strings again:

C:\>strings -o -n 11 hiberfil.dd | findstr /I /L "[BACKSPACE]" >backspace.txt

Which gives an output of:


OK! So, we have a couple of matches. Back to our strings plugin:

C:\>python -f hiberfil.dd --profile=Win7SP1x64 strings -s backspace.txt	
834782000 [4628:7fef593b330] [BACKSPACE]
1165946328 [2816:7fef85fcdd8][BACKSPACE]

Nice! So, we have two processes that contain the string:

  • 2816 svchost.exe
  • 4628 explorer.exe

So, explorer.exe has hit again, and svchost.exe is of course the process responsible for launching services. This would have identified our two processes too, but those unprintable characters could've been encoded in any kind of way - looks like our attackers were just a little lazy.

Subscribe for more Research like this

About Adam Bridge

Head of Response

CHECK IT Health Check Service
Cyber Essentials
CESG Certified Service
First - Improving Security Together
BSI ISO 9001 FS 581360
BSI ISO 27001 IS 553326
PCI - Approved Scanning Vendor
NCSC CCSC - Assured Service Provider
ASSURE Cyber Supplier - CAA