Sunday, March 11, 2012

Powershell Live-Memory Analysis Tools: Dump-Memory, Dump-Strings, Check-MemoryProtection

 I’m releasing three new tools for Powershell that may be of use for those performing live-memory forensics or for penetration testers trying to pull sensitive information from memory. Dump-Memory will simply dump the contents of memory to stdout or to a raw binary file on disk. Dump-Strings is like Sysinternals Strings but it operates on memory. It will dump the strings of any readable portion of memory in both Ascii and Unicode format. Lastly, Check-MemoryProtection is more of a helper function that will return the memory page protections of any address. All of these scripts operate entirely within memory unless you explicitly choose to write to disk.

Note: For Dump-Memory and Dump-Strings to work, the Check-MemoryProtection function must be defined. It is used to ensure that an access violation doesn’t occur if you try to access an inaccessible portion of memory.

Download here: Memory-Tools.ps1

I’m always open to feature requests. Just leave a comment if there is a feature you want implemented and I will try to make that happen. For example, one feature I might consider adding is a ‘-Force’ switch that would forcibly change the protections on the requested range of memory. Currently, these tools use only the minimum level of privilege needed to query the memory of a remote process.

Here are the help files for each tool:

Dump-Memory


Dump-Strings


Check-MemoryProtection

26 comments:

  1. Very nice. You'll soon have rewritten all of the SysInternals suite in PowerShell :)

    A few comments:

    - You might consider making this a module (mainly just rename to PSM1), since PowerShell modules offer tons of benefit compared to dotting a script to get its features. For example, "MemoryForensics.psm1", and prefix your nouns with "MF".
    - Take a look at the standard verbs (Get-Verb) - all of your function names have direct standard verb mappings that will be more familiar to PowerShell users. (I.e.: Check-MemoryProtection = Test-MfMemoryProtection)
    - The Add-Type cmdlet solves the need for explicit CompileAssemblyFromSource (etc). It also does type caching, so you don't have to worry about the try / catch bits you're doing.
    - Kudos on the help comments. I've found that it's helpful to do the help comments in a multi-line comment so that people can copy and paste examples without having to delete the '#' character at the beginning of every line.
    - For commands that deal with standard PowerShell concepts (i.e.: processes), consider updating specific parameters to support ValueFromPipelineByPropertyName (or ValueFromPipeline). This gives cool experiences like:

    Get-Process -name Notepad | Export-MfMemoryStrings

    (BTW, defining parameters to have a ParameterSetName will ensure that users can't supply parameters that you intend to be mutually exclusive)

    Great stuff!

    Lee Holmes [MSFT]

    ReplyDelete
  2. Hey Lee,

    Thanks for the awesome feedback! I'm definitely going to take every suggestion to heart...especially for the upcoming Scripting Games. :D

    I'll definitely start using Add-Type more. The try-catch bit seemed ugly to me when I wrote it. I always want to make sure that the compilation variables are set just right though so that nothing touches the disk.

    Becoming a good Powershell scripter really is a marathon and not a sprint. I can already see how I've evolved from when I released PowerSyringe.

    Thanks again!

    ~Matt

    ReplyDelete
  3. Sorry to disappoint you - despite declaring '$compileParams.GenerateInMemory = $True', the .NET Framework emits both a .CS file and .DLL for a few fleeting moments. The only workaround is Reflection.Emit, or the upcoming 'Roslyn' C# compiler.

    Lee

    ReplyDelete
  4. Hi Matt

    Great set of scripts. I get an error page though when I try to download the script.

    Thanks

    Doug

    ReplyDelete
  5. Thanks, Doug.

    Hmm. I just downloaded the script anonymously from IE and Chrome with no issue. What is the error you're getting?

    ReplyDelete
  6. All is good.

    The 'No preview available' screen threw me.

    I clicked File|Download and got the scripts.

    Thanks

    Doug

    ReplyDelete
  7. This is good post.
    I'd like to test your tools. But, not open download page.
    What are some ways to open the page?

    ReplyDelete
  8. proneer,

    Just click on File->Download. Or press Ctrl+S. I guess Google Docs isn't the best way to distribute my code. Meh. I'm lazy.

    ReplyDelete
  9. How about an actual replacement for the strings utility? Something to dump strings from a binary with powershell.

    ReplyDelete
  10. My intent with the tool was to perform operations solely in memory. I'll leave it as an exercise for any other Powershell geek out there to implement string dumping from a file. It wouldn't be terribly difficult. You would just read the file in as a FileStream and perform the same binary regexes that I did in memory.

    I don't want to replicate the functionality of too many tools out there. After all, even the in-memory string dumping was already done in Process Explorer. :/

    ReplyDelete
  11. Any additional resources (youtube videos, or other) you can provide to get this working ? I downloaded and have been trying to use the Dump-memory tool to dump the memory of various process ID's. However I've not been successful in getting it to work. I can shoot you logs of the powershell ISE if needed. I'm sure it is user error, but I would still like to get this going in powershell vs some other tool. Regards

    ReplyDelete
  12. Sorry no video or other resources. I assume you're making sure to provide '-ProcessId' for the PID you'd like to query. Otherwise, you're just referencing memory in powershell.exe. Also, make sure you're running at least PS v2. How are you executing the script? PS>.\Memory-Tools.ps1 or are you just running the script in the ISE and running Dump-Memory? Lastly, make sure you're using Dump-Memory on a valid address. 'Dump-Memory 0x7ffe0000 0x300' should work every time. Use that a a basis for troubleshooting. If it's still not working, let me know specific problems you're having. Good luck!

    ReplyDelete
  13. Thanks Matt. OK so I figured out I was passing a bogus PID,so I'm on to the the next problem. How do I find the correct base address and offset for a PID ? What I'm trying to do is for a pen test, use your script to dump memory from a putty client that is known to store clear text passwords in memory. Thanks for your help.

    ReplyDelete
  14. Thanks Matt. The tool seems to be working now, thanks for the help. I was passing a bad PID. I have another question which if you know of a power shell method for getting the base address and offset for a specific PID ? I'm working on a authorized pen test and I'd like to use your tool to dump the memory of a running putty application that is known to save credentials in memory. Thanks again for all your help.

    ReplyDelete
  15. I have an easy answer for your questions. What you're trying to accomplish in putty however may not be as straightforward as you'd like.

    Let's go with putty.exe as our example. To list the loaded modules and their respective base addresses in a readable format, do the following:

    $Proc = Get-Process putty
    $Proc.Modules | Format-Table -AutoSize ModuleName, @{ Label="Base"; Expression={ $_.BaseAddress.ToString("X$([IntPtr]::Size)") } }

    Regarding the dumping of a cleartext password in memory, dumping strings from loaded modules isn't going to cut it since the string is likely stored in the stack or heap somewhere. Unfortunately, these tools don't currently walk the structures necessary to extract heap and stack regions. My (ugly) solution which is only realistic on 32-bit consists of the following:

    $Proc = Get-Process putty
    $ModuleAddrs = @{}
    $Proc.Modules | % { $ModuleAddrs[[IntPtr]($_.BaseAddress)] = $_.ModuleMemorySize }


    for ($i = 0; $i -lt 2147483647; $i += 4096) {

    if ($ModuleAddrs.ContainsKey([IntPtr]($i))) {
    $i += $ModuleAddrs[[IntPtr]($i)]
    }

    $Protections = Check-MemoryProtection $i

    if ($Protections.Protect -ne [Winapi.Kernel32+AllocationProtectEnum]::PAGE_NOACCESS -And $Protections.State -eq [Winapi.Kernel32+StateEnum]::MEM_COMMIT) {
    Dump-Strings $i 4096 -ErrorAction SilentlyContinue
    }

    }

    This is going to dump strings from the entire 4GB of commited virtual memory minus what's in the loaded modules. :/ That's the best I got unless you know for sure the memory region where the password is. Until then, good luck finding a needle in a haystack. :D

    ReplyDelete
  16. Matt,

    thanks for your help. I'd like to find a pattern, of where the credentials will be stored. I dumped the putty memory using Process Explorer, with an existing putty session, however right now I can't find the credentials at all. I'll have do to some more research, not sure if they are doing an encoding or not. Thx,

    ReplyDelete
  17. Matt,

    Awesome scripts but getting some odd results and wondering if you could assist: I'm using Dumb-Strings to get the strings of running processes but I'm only returning about 30 results out of approximately 85 processes. In troubleshooting, it appears that the MainModule property of a lot of the processes are blank. Thus, I'm not able to read the BaseAddress or ModuleMemorySize properties to get the data I want. I'm running the PowerShell as an elevated Administrator on a Win 7 Enterprise 64-bit system. Can you explain why I'm not able to get these regions? Also, I ran ProcExp from the same Security Context and am able to get the strings that way. Any help would be appreciated. Thx.

    ReplyDelete
    Replies
    1. I suspect that at least some of the processes you trying to read from are protected processes in which case you won't be able to get a handle to the process even if all you're asking for is PROCESS_VM_READ permission. ProcExp can doesn't have a problem with those processes because it has the benefit of being able to read memory from the kernel as a device driver.

      Out of curiousity, have tried providing an address explicitly rather than relying upon MainModule being present?

      I wouldn't claim that Dump-Strings is an awesome script. I wrote it when I was still a n00b and I haven't touched it since. ;)

      Delete
    2. Haha -- meant DUMP-strings... It turns out that for whatever reason, running as NT Authority\System resolves the problem (I should've looked at the before). However, it threw me off because ProcExp could see the strings without NT Authority\System.

      I'll try figuring out how to get memory locations from PID instead of relying on MainModule and that'll probably fix the problem I'm experiencing.

      And I would claim it's an awesome script. It's the first PowerShell script I've seen (in PowerShell) to dump strings for analysis and considering what we're going to be using it for, it could be more than awesome for us.

      It's probably terribly simple to you but this is awesome for the rest of us. =D

      Delete
  18. Ah... we strip out SeDebugPrivilege from the Local Administrator. I'm pretty sure that's why I can't see it from Local Admin but we can from System. Just wanted to share in case anyone else runs into this.

    ReplyDelete
    Replies
    1. Ah. That makes total sense. Thanks for sharing!

      Delete
  19. Hi,

    Is it possible to dump any memory ? Not only one process.

    Thank you,

    ReplyDelete
    Replies
    1. What do you mean by dump "any" memory? Are you referring to physical memory?

      Delete
    2. Yes, I want to dump the physical memory. It's possible ?

      Delete
    3. PowerShell runs in user mode so no, you can't use PowerShell alone to dump physical memory. You could use it to interface with a driver that dumps memory though. At that point though, PowerShell probably wouldn't provide much value-add.

      Delete
  20. Ok. Thank you very much :)

    ReplyDelete