mirror of
https://github.com/martanne/WinPmem-BitLocker
synced 2026-06-08 15:44:10 +00:00
Moved documentation site to its own directory and included kernel driver. (#2)
* Moved documentation site to its own directory * Initial commit of kernel driver code from Rekall.
This commit is contained in:
+3
-3
@@ -1,3 +1,3 @@
|
||||
[submodule "themes/hugo-whisper-theme"]
|
||||
path = themes/hugo-whisper-theme
|
||||
url = https://github.com/JugglerX/hugo-whisper-theme.git
|
||||
[submodule "site/themes/hugo-whisper-theme"]
|
||||
path = site/themes/hugo-whisper-theme
|
||||
url = https://github.com/JugglerX/hugo-whisper-theme.git
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,4 +1,11 @@
|
||||
The Pmem Memory acquisition tools.
|
||||
==================================
|
||||
# The Winpmem memory acquisition driver and userspace.
|
||||
|
||||
This is the documentation site for WinPmem
|
||||
Winpmem has been the default open source memory acquisition driver for
|
||||
windows for a long time. It used to live in the Rekall project, but
|
||||
has recently been separated into its own repository.
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
This code was originally developed within Google but was released
|
||||
under the Apache License.
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
winpmem.velocidex.com
|
||||
winpmem.velocidex.com
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Categories on WinPmem memory imager</title>
|
||||
<link>https://winpmem.velocidex.com/categories/</link>
|
||||
<description>Recent content in Categories on WinPmem memory imager</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>en-us</language>
|
||||
|
||||
<atom:link href="https://winpmem.velocidex.com/categories/index.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
|
||||
<language>en-us</language><atom:link href="https://winpmem.velocidex.com/categories/index.xml" rel="self" type="application/rss+xml" />
|
||||
</channel>
|
||||
</rss>
|
||||
</rss>
|
||||
|
||||
@@ -130,28 +130,23 @@
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<h1 class="title">Extracting streams from AFF4 volumes</h1>
|
||||
<div class="content">
|
||||
|
||||
|
||||
<p>Analysis tools which already support AFF4 will be able to use the AFF4
|
||||
<div class="content ">
|
||||
<p>Analysis tools which already support AFF4 will be able to use the AFF4
|
||||
image directly, but sometimes you may come across a tool which does
|
||||
not. In this case you will want to extract the RAW streams out of the
|
||||
AFF4 volume.</p>
|
||||
|
||||
<p>To extract streams from an AFF4 volume we use the <em>-e</em> flag.</p>
|
||||
|
||||
<h2 id="extract-streams-by-using-wild-cards">Extract streams by using wild cards</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -e '*/kallsyms' --export_dir /tmp/export/ /tmp/test.aff4</pre></div>
|
||||
<p>By default export directory is the current directory. The imager
|
||||
will create a directory structure under the export directory which
|
||||
contains all the matched files.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -e '*/kallsyms' --export_dir /tmp/export/ /tmp/test.aff4
|
||||
</code></pre></div><p>By default export directory is the current directory. The imager
|
||||
will create a directory structure under the export directory which
|
||||
contains all the matched files.</p>
|
||||
<h2 id="extract-streams-from-stdin">Extract streams from stdin</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -l test.aff4 | grep exe | \
|
||||
WinPmem.exe -e @ --export_dir export/ test.aff4</pre></div>
|
||||
<p>In the above example, we list the streams in the volume, then grep
|
||||
only the executable files, and extract them into the export
|
||||
directory.</p>
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -l test.aff4 | grep exe | \
|
||||
WinPmem.exe -e @ --export_dir export/ test.aff4
|
||||
</code></pre></div><p>In the above example, we list the streams in the volume, then grep
|
||||
only the executable files, and extract them into the export
|
||||
directory.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,7 +178,7 @@ AFF4 volume.</p>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+50
-60
@@ -130,75 +130,65 @@
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<h1 class="title">Acquiring Files</h1>
|
||||
<div class="content">
|
||||
|
||||
|
||||
<p>The WinPmem imager can also acquire multiple files into the AFF4
|
||||
<div class="content ">
|
||||
<p>The WinPmem imager can also acquire multiple files into the AFF4
|
||||
volume. These can be devices (such as disks using /dev/sda) or logical
|
||||
files.</p>
|
||||
|
||||
<h2 id="acquiring-a-disk-image">Acquiring a disk image</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -i \\.\c: -o output.aff4 -dd</pre></div>
|
||||
<p>Note that by default the aff4 imager will append new streams to the
|
||||
output volume if it already exists. This is useful for appending
|
||||
more relevant evidence after the initial acquisition is
|
||||
completed. If you dont want this behaviour you can specify the -t
|
||||
(–truncate) flag to truncate the output volume before adding the
|
||||
new stream.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -i \\.\c: -o output.aff4 -dd
|
||||
</code></pre></div><p>Note that by default the aff4 imager will append new streams to the
|
||||
output volume if it already exists. This is useful for appending
|
||||
more relevant evidence after the initial acquisition is
|
||||
completed. If you dont want this behaviour you can specify the -t
|
||||
(–truncate) flag to truncate the output volume before adding the
|
||||
new stream.</p>
|
||||
<h2 id="acquiring-multiple-logical-files">Acquiring multiple logical files</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem -i C:/Windows/*.exe -o output.aff4</pre></div>
|
||||
<p>Using glob expressions as input files will be expanded as required
|
||||
to include all filenames matching the globs. This works also on
|
||||
Windows which does not expand globs on the shell.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem -i C:/Windows/*.exe -o output.aff4
|
||||
</code></pre></div><p>Using glob expressions as input files will be expanded as required
|
||||
to include all filenames matching the globs. This works also on
|
||||
Windows which does not expand globs on the shell.</p>
|
||||
<h2 id="acquiring-files-newer-than-30-days-can-be-done-by-using-the-find">Acquiring files newer than 30 days can be done by using the find</h2>
|
||||
|
||||
<p>command on Linux or Powershell script on Windows.</p>
|
||||
<div class="highlight"><pre class="chroma"> find /usr/bin/ -ctime -30 | WinPmem -i @ -o /tmp/output.aff4</pre></div>
|
||||
<p>Or on Windows:</p>
|
||||
<div class="highlight"><pre class="chroma"> powershell -Command "Get-ChildItem f:\ | Where{$_.LastWriteTime -gt
|
||||
(Get-Date).AddDays(-7)} | SELECT Name | ft -hidetableheaders" |
|
||||
WinPmem -i @ -o /tmp/output.aff4</pre></div>
|
||||
<p>Using a single @ as the input filename, makes the aff4 imager read
|
||||
the list of files to acqiure from stdin. This allows for more
|
||||
sophisticated pre-processing and makes it easier to acquire files
|
||||
with spaces or special characters in their names (without having to
|
||||
worry about shell escapes). In the above example we use the find
|
||||
unix command to identify files newer than 30 days and also add them
|
||||
to the image.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> find /usr/bin/ -ctime -30 | WinPmem -i @ -o /tmp/output.aff4
|
||||
</code></pre></div><p>Or on Windows:</p>
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> powershell -Command "Get-ChildItem f:\ | Where{$_.LastWriteTime -gt
|
||||
(Get-Date).AddDays(-7)} | SELECT Name | ft -hidetableheaders" |
|
||||
WinPmem -i @ -o /tmp/output.aff4
|
||||
</code></pre></div><p>Using a single @ as the input filename, makes the aff4 imager read
|
||||
the list of files to acqiure from stdin. This allows for more
|
||||
sophisticated pre-processing and makes it easier to acquire files
|
||||
with spaces or special characters in their names (without having to
|
||||
worry about shell escapes). In the above example we use the find
|
||||
unix command to identify files newer than 30 days and also add them
|
||||
to the image.</p>
|
||||
<h2 id="enabling-multiple-threads">Enabling multiple threads</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -o /tmp/output.aff4 --threads 6</pre></div>
|
||||
<p>The aff4 imager uses a single thread by default, but if your
|
||||
machine has more cores, then you will see vastly better performance
|
||||
by allowing more threads to run. This is particularly important
|
||||
when using the default compression of the zlib compressor which
|
||||
needs more CPU resources.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -o /tmp/output.aff4 --threads 6
|
||||
</code></pre></div><p>The aff4 imager uses a single thread by default, but if your
|
||||
machine has more cores, then you will see vastly better performance
|
||||
by allowing more threads to run. This is particularly important
|
||||
when using the default compression of the zlib compressor which
|
||||
needs more CPU resources.</p>
|
||||
<h2 id="enabling-snappy-compression">Enabling snappy compression</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -o output.aff4 --compression snappy</pre></div>
|
||||
<p>The Snappy compression engine is much faster than the default zlib
|
||||
but trades off compression size. Enabling snappy compression will
|
||||
result in slightly larger images but should complete faster.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -o output.aff4 --compression snappy
|
||||
</code></pre></div><p>The Snappy compression engine is much faster than the default zlib
|
||||
but trades off compression size. Enabling snappy compression will
|
||||
result in slightly larger images but should complete faster.</p>
|
||||
<h2 id="splitting-an-image-into-multiple-volumes">Splitting an image into multiple volumes</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -o output.aff4 --split 650m</pre></div>
|
||||
<p>Some images are very large. By enabling splitting images it is
|
||||
possible to restrict the maximum size of each volume. The imager
|
||||
will close off each volume as it is done with it, and so you can
|
||||
start uploading, archiving each volume as soon as it is
|
||||
finished. Note that the same stream may be split across one or more
|
||||
volumes so you will need all volumes to properly extract the
|
||||
stream.</p>
|
||||
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -o output.aff4 --split 650m
|
||||
</code></pre></div><p>Some images are very large. By enabling splitting images it is
|
||||
possible to restrict the maximum size of each volume. The imager
|
||||
will close off each volume as it is done with it, and so you can
|
||||
start uploading, archiving each volume as soon as it is
|
||||
finished. Note that the same stream may be split across one or more
|
||||
volumes so you will need all volumes to properly extract the
|
||||
stream.</p>
|
||||
<h2 id="acquiring-into-standard-output">Acquiring into standard output</h2>
|
||||
<div class="highlight"><pre class="chroma"> WinPmem.exe -o - | gsutil cp - gs://rekall-test/test.aff4</pre></div>
|
||||
<p>If the output filename is specifies as a single dash (“-”), the
|
||||
imager writes the AFF4 volume to stdout. This allows the image to
|
||||
be piped to a different tool. The example above streams the image
|
||||
directly to a cloud storage bucket without needing to write a
|
||||
temporary local copy.</p>
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> WinPmem.exe -o - | gsutil cp - gs://rekall-test/test.aff4
|
||||
</code></pre></div><p>If the output filename is specifies as a single dash ("-"), the
|
||||
imager writes the AFF4 volume to stdout. This allows the image to
|
||||
be piped to a different tool. The example above streams the image
|
||||
directly to a cloud storage bucket without needing to write a
|
||||
temporary local copy.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,7 +220,7 @@ files.</p>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+17
-10
@@ -133,10 +133,7 @@
|
||||
<span class="overview">Overview</span>
|
||||
<h1 class="title">Winpmem Documentation</h1>
|
||||
<div class="content">
|
||||
|
||||
|
||||
<h2 id="documentation">Documentation</h2>
|
||||
|
||||
<h2 id="documentation">Documentation</h2>
|
||||
<p>This is the winpmem documentation.</p>
|
||||
|
||||
</div>
|
||||
@@ -145,36 +142,46 @@
|
||||
<div class="summary mb-2">
|
||||
|
||||
<h2 class="title-summary"><a href="https://winpmem.velocidex.com/docs/usage/">Overview</a></h2>
|
||||
<p> WinPmem is developed as part of the AFF4 imager project. AFF4 is an advanced, open forensic imaging format. The format has been …</p>
|
||||
<p>WinPmem is developed as part of the AFF4 imager project. AFF4 is an advanced, open forensic imaging format. The format has been standardized in the AFF4 Standard Specification, and it is increasing more supported by other tools.
|
||||
WinPmem by default will use AFF4 to store the memory image, but it is also possible to write the image in RAW format or ELF format. You might want to do this if you are going to use the image with tools that can not read AFF4 natively.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="summary mb-2">
|
||||
|
||||
<h2 class="title-summary"><a href="https://winpmem.velocidex.com/docs/memory/">Acquiring Memory</a></h2>
|
||||
<p>Acquiring memory using WinPmem is a very simple process:
|
||||
F:\&gt;winpmem.exe -o test.aff4 -dd 2019-05-17 02:26:22 I This is The WinPmem …</p>
|
||||
F:\>winpmem.exe -o test.aff4 -dd 2019-05-17 02:26:22 I This is The WinPmem memory imager. version 3.3rc1 2019-05-17 02:26:22 I Extracted 45368 bytes into C:\Users\test\AppData\Local\Temp\pmeB5CC.tmp 2019-05-17 02:26:22 I Driver Unloaded. 2019-05-17 02:26:22 I Loaded Driver C:\Users\test\AppData\Local\Temp\pmeB5CC.tmp 2019-05-17 02:26:22 I Setting acquisition mode 2 2019-05-17 02:26:22 I CR3: 0x00001AA000 5 memory ranges: Start 0x00001000 - Length 0x0009E000 Start 0x00100000 - Length 0x00002000 Start 0x00103000 - Length 0xBFDDD000 Start 0xBFF00000 - Length 0x00100000 Start 0x100000000 - Length 0x52400000 2019-05-17 02:26:22 W Output file test.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="summary mb-2">
|
||||
|
||||
<h2 class="title-summary"><a href="https://winpmem.velocidex.com/docs/files/">Acquiring Files</a></h2>
|
||||
<p> The WinPmem imager can also acquire multiple files into the AFF4 volume. These can be devices (such as disks using /dev/sda) or logical …</p>
|
||||
<p>The WinPmem imager can also acquire multiple files into the AFF4 volume. These can be devices (such as disks using /dev/sda) or logical files.
|
||||
Acquiring a disk image WinPmem.exe -i \\.\c: -o output.aff4 -dd Note that by default the aff4 imager will append new streams to the output volume if it already exists. This is useful for appending more relevant evidence after the initial acquisition is completed. If you dont want this behaviour you can specify the -t (–truncate) flag to truncate the output volume before adding the new stream.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="summary mb-2">
|
||||
|
||||
<h2 class="title-summary"><a href="https://winpmem.velocidex.com/docs/inspecting/">Inspecting AFF4 volumes</a></h2>
|
||||
<p>AFF4 volumes are based around the common Zip compression standard (for large volumes we use Zip64 extensions). Therefore it is possible to …</p>
|
||||
<p>AFF4 volumes are based around the common Zip compression standard (for large volumes we use Zip64 extensions). Therefore it is possible to examine AFF4 volumes using common zip utilities.
|
||||
⟫ unzip -l /tmp/test.aff4 Archive: /tmp/test.aff4 aff4://c7c60030-cc3e-43a6-b5d1-1551b29c9918 Length Date Time Name --------- ---------- ----- ---- 189432 2018-01-15 12:50 usr/bin/mksquashfs 951952 2018-01-15 12:50 usr/bin/x86_64-w64-mingw32-cpp ... 50929 2018-01-15 12:50 information.turtle ... --------- ------- 315748394 327 files We can see that each volume has a unique URN, and it contains a file called “information.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="summary mb-2">
|
||||
|
||||
<h2 class="title-summary"><a href="https://winpmem.velocidex.com/docs/extracting/">Extracting streams from AFF4 volumes</a></h2>
|
||||
<p> Analysis tools which already support AFF4 will be able to use the AFF4 image directly, but sometimes you may come across a tool which does …</p>
|
||||
<p>Analysis tools which already support AFF4 will be able to use the AFF4 image directly, but sometimes you may come across a tool which does not. In this case you will want to extract the RAW streams out of the AFF4 volume.
|
||||
To extract streams from an AFF4 volume we use the -e flag.
|
||||
Extract streams by using wild cards WinPmem.exe -e '*/kallsyms' --export_dir /tmp/export/ /tmp/test.aff4 By default export directory is the current directory.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,7 +209,7 @@ F:\&gt;winpmem.exe -o test.aff4 -dd 2019-05-17 02:26:22 I This is The WinPme
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-7
@@ -1,15 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Winpmem Documentation on WinPmem memory imager</title>
|
||||
<link>https://winpmem.velocidex.com/docs/</link>
|
||||
<description>Recent content in Winpmem Documentation on WinPmem memory imager</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>en-us</language>
|
||||
|
||||
<atom:link href="https://winpmem.velocidex.com/docs/index.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
|
||||
<language>en-us</language><atom:link href="https://winpmem.velocidex.com/docs/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Overview</title>
|
||||
<link>https://winpmem.velocidex.com/docs/usage/</link>
|
||||
@@ -62,4 +58,4 @@ Extract streams by using wild cards WinPmem.exe -e &#39;*/kallsyms&#39;
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
</rss>
|
||||
|
||||
@@ -130,11 +130,11 @@
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<h1 class="title">Inspecting AFF4 volumes</h1>
|
||||
<div class="content">
|
||||
<div class="content ">
|
||||
<p>AFF4 volumes are based around the common Zip compression standard (for
|
||||
large volumes we use Zip64 extensions). Therefore it is possible to
|
||||
examine AFF4 volumes using common zip utilities.</p>
|
||||
<div class="highlight"><pre class="chroma"> ⟫ unzip -l /tmp/test.aff4
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> ⟫ unzip -l /tmp/test.aff4
|
||||
Archive: /tmp/test.aff4
|
||||
aff4://c7c60030-cc3e-43a6-b5d1-1551b29c9918
|
||||
Length Date Time Name
|
||||
@@ -145,14 +145,13 @@ examine AFF4 volumes using common zip utilities.</p>
|
||||
50929 2018-01-15 12:50 information.turtle
|
||||
...
|
||||
--------- -------
|
||||
315748394 327 files</pre></div>
|
||||
<p>We can see that each volume has a unique URN, and it contains a file
|
||||
315748394 327 files
|
||||
</code></pre></div><p>We can see that each volume has a unique URN, and it contains a file
|
||||
called “information.turtle”. This file contains the AFF4 metadata for
|
||||
the volume as an RDF turtle file.</p>
|
||||
|
||||
<p>We can get the aff4 imager to display the metadata in the volume using
|
||||
the -V flag::</p>
|
||||
<div class="highlight"><pre class="chroma"> ⟫ aff4imager -V /tmp/test.aff4
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback"> ⟫ aff4imager -V /tmp/test.aff4
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix aff4: <http://aff4.org/Schema#> .
|
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
||||
@@ -171,32 +170,34 @@ the -V flag::</p>
|
||||
aff4:compressionMethod <https://www.ietf.org/rfc/rfc1950.txt> ;
|
||||
aff4:size 8531652608 ;
|
||||
aff4:stored <aff4://2d874721-267b-40fb-ac20-7bc22a8af883> ;
|
||||
a aff4:ImageStream .</pre></div>
|
||||
<p>Note that if we have multiple volumes (as in a split volume set) we
|
||||
a aff4:ImageStream .
|
||||
</code></pre></div><p>Note that if we have multiple volumes (as in a split volume set) we
|
||||
should list all volumes as parameters to -V.</p>
|
||||
|
||||
<p>In the above output we see some interesting artifacts of the AFF4 format:</p>
|
||||
|
||||
<ol>
|
||||
<li><p>All streams within the AFF4 volume have a unique URN. The imager
|
||||
<li>
|
||||
<p>All streams within the AFF4 volume have a unique URN. The imager
|
||||
creates the URNs based on their original filename, but this is just
|
||||
a convenience. The imager also stores the original filename (which
|
||||
might contain backslashes on windows).</p></li>
|
||||
|
||||
<li><p>Smaller files (e.g. <em>/proc/kallsyms</em>) are stored as AFF4 Segments
|
||||
might contain backslashes on windows).</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Smaller files (e.g. <em>/proc/kallsyms</em>) are stored as AFF4 Segments
|
||||
which are just regular zip archive members. This means you can
|
||||
extract Smaller files using normal zip tools.</p></li>
|
||||
|
||||
<li><p>Larger files are stored as AFF4 ImageStream. This type of storage
|
||||
extract Smaller files using normal zip tools.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Larger files are stored as AFF4 ImageStream. This type of storage
|
||||
chunks the file data into 32kb chunks, and stores groups of chunks
|
||||
in their own zip segment.</p></li>
|
||||
|
||||
<li><p>Finally sparse images (such as memory images) are stores as an AFF4
|
||||
in their own zip segment.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Finally sparse images (such as memory images) are stores as an AFF4
|
||||
Map. The map does not actually store any data itself (the data is
|
||||
stored by the stream <em>/proc/kcore/data</em>) but it specifies a
|
||||
transformation of its underlying stream.</p></li>
|
||||
transformation of its underlying stream.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>Finally using the -l flag enables a listing of all Image streams from the volume.</p>
|
||||
|
||||
</div>
|
||||
@@ -229,7 +230,7 @@ transformation of its underlying stream.</p></li>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+29
-22
@@ -130,9 +130,9 @@
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<h1 class="title">Acquiring Memory</h1>
|
||||
<div class="content">
|
||||
<div class="content ">
|
||||
<p>Acquiring memory using WinPmem is a very simple process:</p>
|
||||
<div class="highlight"><pre class="chroma">F:\>winpmem.exe -o test.aff4 -dd
|
||||
<div class="highlight"><pre class="chroma"><code class="language-fallback" data-lang="fallback">F:\>winpmem.exe -o test.aff4 -dd
|
||||
2019-05-17 02:26:22 I This is The WinPmem memory imager. version 3.3rc1
|
||||
2019-05-17 02:26:22 I Extracted 45368 bytes into C:\Users\test\AppData\Local\Temp\pmeB5CC.tmp
|
||||
2019-05-17 02:26:22 I Driver Unloaded.
|
||||
@@ -154,35 +154,42 @@
|
||||
2019-05-17 02:26:22 I Dumping Range 2 (Starts at 0x103000, length 0xbfddd000
|
||||
2019-05-17 02:26:23 I Reading 2103000 32 MiB / 4387 (25 MiB/s)
|
||||
2019-05-17 02:26:24 I Reading 4103000 64 MiB / 4387 (26 MiB/s)
|
||||
2019-05-17 02:26:25 I Reading 6103000 96 MiB / 4387 (28 MiB/s)</pre></div>
|
||||
<ul>
|
||||
<li><p>The <code>-o</code> flag instructs WinPmem to create a new AFF4 volume with the
|
||||
name <code>test.aff4</code>.</p></li>
|
||||
|
||||
<li><p>The -d flag instructs WinPmem to produce more vebose output (twice
|
||||
for progress reporting).</p></li>
|
||||
|
||||
<li><p>We see that WinPmem extracts the kernel driver into the temporary
|
||||
2019-05-17 02:26:25 I Reading 6103000 96 MiB / 4387 (28 MiB/s)
|
||||
</code></pre></div><ul>
|
||||
<li>
|
||||
<p>The <code>-o</code> flag instructs WinPmem to create a new AFF4 volume with the
|
||||
name <code>test.aff4</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The -d flag instructs WinPmem to produce more vebose output (twice
|
||||
for progress reporting).</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>We see that WinPmem extracts the kernel driver into the temporary
|
||||
directory and loads it into the kernel. The driver provides access
|
||||
to raw memory via a number of acquisition methods but the default is
|
||||
usually the best.</p></li>
|
||||
|
||||
<li><p>WinPmem then displays the detected physical memory ranges and
|
||||
continues to dump each range.</p></li>
|
||||
|
||||
<li><p>By default WinPmem uses 2 threads to compress the image, however
|
||||
usually the best.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>WinPmem then displays the detected physical memory ranges and
|
||||
continues to dump each range.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>By default WinPmem uses 2 threads to compress the image, however
|
||||
most machines can use multiple cores. Using the <code>--thread</code> flag you
|
||||
may increase this number. Using more threads will result in quicker
|
||||
compression, and therefore quicker imaging.</p></li>
|
||||
|
||||
<li><p>By default WinPmem will use an AFF4 image and will store the memory
|
||||
compression, and therefore quicker imaging.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>By default WinPmem will use an AFF4 image and will store the memory
|
||||
image using an AFF4 Map object with a compressed backing stream. If
|
||||
you want to produce a raw file format or an elf file, you may
|
||||
specify the <code>--format elf</code> or <code>--format raw</code> to produce these image
|
||||
formats. Note that due to limitations in these image formats it is
|
||||
not possible to include additional streams. Neither of these formats
|
||||
support compression either (but ELF format supports sparse data
|
||||
runs).</p></li>
|
||||
runs).</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -215,7 +222,7 @@ runs).</p></li>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+22
-25
@@ -130,64 +130,61 @@
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<h1 class="title">Overview</h1>
|
||||
<div class="content">
|
||||
|
||||
|
||||
<p>WinPmem is developed as part of the AFF4 imager project. AFF4 is an
|
||||
<div class="content ">
|
||||
<p>WinPmem is developed as part of the AFF4 imager project. AFF4 is an
|
||||
advanced, open forensic imaging format. The format has been
|
||||
standardized in the <a href="https://github.com/aff4/Standard">AFF4 Standard Specification</a>, and it is increasing more supported by other tools.</p>
|
||||
|
||||
<p>WinPmem by default will use AFF4 to store the memory image, but it is
|
||||
also possible to write the image in RAW format or ELF format. You
|
||||
might want to do this if you are going to use the image with tools
|
||||
that can not read AFF4 natively. However, note that using these
|
||||
imaging format will cause WinPmem to skip acquisition of multiple
|
||||
files and metadata making the image harder to use.</p>
|
||||
|
||||
<h2 id="why-use-aff4">Why use AFF4?</h2>
|
||||
|
||||
<p>The AFF4 format is specifically designed to support a wide range of
|
||||
use cases in forensic evidence capture and preservation. Some of the
|
||||
benefits of using AFF4 over another proprietary format are</p>
|
||||
|
||||
<ul>
|
||||
<li><p>The AFF4 format is an opened standard with many open source
|
||||
<li>
|
||||
<p>The AFF4 format is an opened standard with many open source
|
||||
implementations in a variery of languages. The format itself is very
|
||||
simple and so it will always be possible to extract data even
|
||||
without an proper implementation.</p></li>
|
||||
|
||||
<li><p>AFF4 storage is designed around the widely used Zip file
|
||||
without an proper implementation.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>AFF4 storage is designed around the widely used Zip file
|
||||
format. This means that you can use typical Zip recovery utilities
|
||||
to repair corrupted images, inspect the content of images etc.</p></li>
|
||||
|
||||
<li><p>AFF4 supports multiple streams within the same volume. This allows
|
||||
to repair corrupted images, inspect the content of images etc.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>AFF4 supports multiple streams within the same volume. This allows
|
||||
multiple sources of evidence to be captured into the same case
|
||||
file. For example, related memory, disk and logical file streams can
|
||||
be handled simultaneously. WinPmem uses this property to store
|
||||
memory images in the same volume as important files like drivers and
|
||||
kernel image, thus assisting the analysis phase.</p></li>
|
||||
|
||||
<li><p>AFF4 supports sparse streams (using the Map stream type). This is
|
||||
kernel image, thus assisting the analysis phase.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>AFF4 supports sparse streams (using the Map stream type). This is
|
||||
useful for representing memory images (which might have gaps in
|
||||
them) as well as simply representing read errors (rather than simply
|
||||
zero padding them).</p></li>
|
||||
|
||||
<li><p>AFF4 supports arbitrary metadata as RDF statements.</p></li>
|
||||
zero padding them).</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>AFF4 supports arbitrary metadata as RDF statements.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>The Pmem memory acquisition suite includes the <em>WinPmem</em>, <em>OSXPmem</em>
|
||||
and <em>LinPmem</em> tool. Each tool implements memory acquisition for its
|
||||
respective OS but all use largely the same options. All the below
|
||||
commands should also work on any tools in the pmem suite. For
|
||||
simplicity we demonstrate with WinPmem.</p>
|
||||
|
||||
<p>You can download_ the latest release of the aff4 imager through the
|
||||
<a href="https://github.com/Velocidex/c-aff4/releases">project’s release page</a>. Releases are
|
||||
statically built for their respective platforms in order to ensure
|
||||
that they can be easily deployed with minimal system requirements -
|
||||
even on very old systems. You do not need to include any runtime
|
||||
dependencies (like Visual C runtimes).</p>
|
||||
|
||||
<p>The following section describes how to use the imager effectively. You
|
||||
can get some helpful messages from the imager itself when run with the
|
||||
<em>–help</em> flag.</p>
|
||||
@@ -222,7 +219,7 @@ can get some helpful messages from the imager itself when run with the
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta name="generator" content="Hugo 0.56.0-DEV" />
|
||||
<meta name="generator" content="Hugo 0.75.1" />
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<title>WinPmem memory imager</title>
|
||||
@@ -154,7 +154,7 @@ include WinPmem, OSXPmem and LinPmem.</p>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-7
@@ -1,15 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>The Pmem Suite on WinPmem memory imager</title>
|
||||
<link>https://winpmem.velocidex.com/</link>
|
||||
<description>Recent content in The Pmem Suite on WinPmem memory imager</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>en-us</language>
|
||||
|
||||
<atom:link href="https://winpmem.velocidex.com/index.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
|
||||
<language>en-us</language><atom:link href="https://winpmem.velocidex.com/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Overview</title>
|
||||
<link>https://winpmem.velocidex.com/docs/usage/</link>
|
||||
@@ -62,4 +58,4 @@ Extract streams by using wild cards WinPmem.exe -e &#39;*/kallsyms&#39;
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
</rss>
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
var body=document.querySelector('body')
|
||||
var menuTrigger=document.querySelector('#toggle-main-menu-mobile');var menuContainer=document.querySelector('#main-menu-mobile');menuTrigger.onclick=function(){menuContainer.classList.toggle('open');menuTrigger.classList.toggle('is-active')
|
||||
body.classList.toggle('lock-scroll')}
|
||||
var content=document.querySelector('.content.anchor-link-enabled')
|
||||
if(content){addHeaderAnchors(content);}
|
||||
function addHeaderAnchors(content){var headers=content.querySelectorAll('h1, h2, h3, h4');var linkSvg=' <svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"></path><path d="M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z"/></svg>';var anchorForId=function(id){var anchor=document.createElement('a');anchor.classList.add('header-anchor');anchor.href="#"+id;anchor.innerHTML=linkSvg;return anchor;};for(var h=0;h<headers.length;h++){var header=headers[h];if(typeof header.id!=="undefined"&&header.id!==""){header.appendChild(anchorForId(header.id));}}}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
var body=document.querySelector('body')
|
||||
var menuTrigger=document.querySelector('#toggle-main-menu-mobile');var menuContainer=document.querySelector('#main-menu-mobile');menuTrigger.onclick=function(){menuContainer.classList.toggle('open');menuTrigger.classList.toggle('is-active')
|
||||
body.classList.toggle('lock-scroll')}
|
||||
+2
-4
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
|
||||
@@ -28,16 +28,14 @@
|
||||
|
||||
<url>
|
||||
<loc>https://winpmem.velocidex.com/categories/</loc>
|
||||
<priority>0</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://winpmem.velocidex.com/tags/</loc>
|
||||
<priority>0</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://winpmem.velocidex.com/</loc>
|
||||
</url>
|
||||
|
||||
</urlset>
|
||||
</urlset>
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/js/scripts.min.bf1e1f7ae8e03db5f012356e825843facdff51c0a559cb0d27fe2bbe1db405c2.js"></script>
|
||||
<script type="text/javascript" src="/js/scripts.min.1237ff71925bb8625c97a9af8db4c54525258bedfd7c47493daaff723bea755e.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-7
@@ -1,14 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Tags on WinPmem memory imager</title>
|
||||
<link>https://winpmem.velocidex.com/tags/</link>
|
||||
<description>Recent content in Tags on WinPmem memory imager</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>en-us</language>
|
||||
|
||||
<atom:link href="https://winpmem.velocidex.com/tags/index.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
|
||||
<language>en-us</language><atom:link href="https://winpmem.velocidex.com/tags/index.xml" rel="self" type="application/rss+xml" />
|
||||
</channel>
|
||||
</rss>
|
||||
</rss>
|
||||
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Please note: The Winpmem project is release under the Apache license. This means
|
||||
that all files under the path tools/windows/winpmem/ are released under the
|
||||
Apache license, despite residing in the Rekall code repository (Which is GPL).
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
WinPMEM - a kernel mode driver for gaining access to physical memory.
|
||||
Version 1.3
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
This is a physical memory imager which implements the following features:
|
||||
|
||||
- This code builds on 64 bit windows as well as 32 bit windows.
|
||||
|
||||
- A read device interface is used instead of writing the image from the kernel
|
||||
like some other imagers. This allows us to have complex userspace imager
|
||||
(e.g. copy across network, hash etc), as well as run analysis on the live
|
||||
system (e.g. rekall can be run directly on the device).
|
||||
|
||||
- The userspace component can write crash dumps of 32 and 64 bit systems.
|
||||
|
||||
- The image can be written to stdout (using an output filename of "-"). This can
|
||||
then be piped using the usual ways (netcat, ssh etc).
|
||||
|
||||
|
||||
The files in this directory (Including the winpmem sources and signed binaries),
|
||||
are available under the following license: Apache License, Version 2.0
|
||||
|
||||
How to use these drivers.
|
||||
=========================
|
||||
|
||||
There are two user space programs that can be used to acquire memory images. The
|
||||
python acquitision program demonstrates how to use the driver from python and
|
||||
can be easily extended to handle the image in another way (e.g. compress, hash,
|
||||
send across the network etc).
|
||||
|
||||
The winpmem_1.3.exe program is a self contained binary with both 32 and 64 bit
|
||||
drivers embedded. It is the easiest and most recommended to use for incident
|
||||
response since it requires no external dependencies, and automatically installs
|
||||
the correct driver.
|
||||
|
||||
The Python acquitision tool winpmem.py
|
||||
--------------------------------------
|
||||
|
||||
This release comes with signed binary drivers and a python program which uses
|
||||
this driver to create raw images. (Note that in order to load the driver on 64
|
||||
bit windows the driver _must_ be signed). Below we assume we are running on a 64
|
||||
bit system. The driver for 32 bit systems can be found in
|
||||
binaries\i386\winpmem_32.sys.
|
||||
|
||||
To get help about the acquisition tool:
|
||||
|
||||
c:\..> winpmem.py -h
|
||||
Usage: winpmem.py [options]
|
||||
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
-d FILE, --driver=FILE
|
||||
The driver location (winpmem.sys)
|
||||
-f FILE, --filename=FILE
|
||||
write image to FILE
|
||||
-n NAME, --name=NAME The name of the device.
|
||||
-m MODE, --mode=MODE The acquisition mode. Can be (physical or iospace)
|
||||
-l, --load Only load the driver and immediately quit. (Useful
|
||||
just before attaching with rekall)
|
||||
-u, --unload Unload the driver and immediately quit.
|
||||
|
||||
To acquire memory into the file foobar.raw:
|
||||
c:\..> winpmem.py -d binaries\amd64\winpmem_64.sys -f foobar.raw
|
||||
CR3 = 0x187000
|
||||
Memory ranges:
|
||||
Start Length
|
||||
0x1000 0x9E000
|
||||
0x100000 0x6F6FB000
|
||||
Imaging to foo.raw
|
||||
|
||||
Padding from 0x0 to 0x1000
|
||||
|
||||
|
||||
0000MB .
|
||||
Padding from 0x9F000 to 0x100000
|
||||
|
||||
................................................
|
||||
0050MB ..................................................
|
||||
0100MB ..................................................
|
||||
0150MB ..................................................
|
||||
0200MB ..................................................
|
||||
0250MB ..................................................
|
||||
|
||||
Note that CR3 and the valid physical memory ranges are reported.
|
||||
|
||||
Normally when acquiring memory we load the driver, acquire the memory, then
|
||||
unload the driver. To use rekall on the raw memory we need to leave the
|
||||
driver loaded:
|
||||
|
||||
c:\..> winpmem.py -d binaries\amd64\winpmem.sys -l
|
||||
Loaded the winpmem driver. You can now attach rekall to \\.\pmem
|
||||
|
||||
and once we finished with it we can unload it (Make sure to exit rekall
|
||||
before unloading the driver):
|
||||
|
||||
c:\..> winpmem.py -d binaries\amd64\winpmem.sys -u
|
||||
(1073, 'CreateService', 'The specified service already exists.')
|
||||
unloaded winpmem driver.
|
||||
|
||||
|
||||
The self contained binary winpmem.exe
|
||||
-------------------------------------
|
||||
|
||||
This program is easiest to use for incident response since it requires no other
|
||||
dependencies than the executable itself. The program will load the correct
|
||||
driver (32 bit or 64 bit) automatically.
|
||||
|
||||
c:\..> winpmem_1.3.exe -h
|
||||
Winpmem - A memory imager for windows.
|
||||
Copyright Michael Cohen (scudette@gmail.com) 2012.
|
||||
|
||||
Version 1.3. Built Nov 12 2012
|
||||
Usage:
|
||||
winpmem_1.3.exe [option] [output path]
|
||||
|
||||
Option:
|
||||
-l Load the driver and exit.
|
||||
-u Unload the driver and exit.
|
||||
-h Display this help.
|
||||
-w Turn on/off write mode.
|
||||
-1 Use MmMapIoSpace method.
|
||||
-2 Use \\Device\PhysicalMemory method (Default).
|
||||
-d Produce a crashdump file.
|
||||
|
||||
|
||||
NOTE: an output filename of - will write the image to STDOUT.
|
||||
|
||||
Examples:
|
||||
winpmem_1.3.exe physmem.raw
|
||||
Writes a raw image to physmem.raw
|
||||
|
||||
winpmem_1.3.exe -d - | nc 192.168.1.1 80
|
||||
Writes a crashdump file to netcat for network transport. Output is supressed
|
||||
here because STDOUT is redirected.
|
||||
|
||||
To load the driver and exit:
|
||||
c:\..> winpmem.exe -l
|
||||
Loaded Driver.
|
||||
|
||||
To unload the driver and exit:
|
||||
c:\..> winpmem.exe -u
|
||||
Driver Unloaded.
|
||||
|
||||
To acquire a raw image using the MmMapIoSpace method:
|
||||
c:\..> winpmem_1.3.exe -1 myimage.raw
|
||||
|
||||
The driver will be automatically unloaded after the image is acquired.
|
||||
|
||||
To acquire an image in crashdump format:
|
||||
|
||||
c:\..>winpmem_1.3.exe -d c:\temp\test.dmp
|
||||
Driver Unloaded.
|
||||
Loaded Driver C:\Users\mic\AppData\Local\Temp\win6C6.tmp.
|
||||
Will write a crash dump file
|
||||
CR3: 0x0000187000
|
||||
2 memory ranges:
|
||||
Start 0x00001000 - Length 0x0009E000
|
||||
Start 0x00100000 - Length 0x6F6FB000
|
||||
|
||||
|
||||
00% 0x00001000 .
|
||||
|
||||
00% 0x00100000 ..................................................
|
||||
02% 0x03300000 ..................................................
|
||||
05% 0x06500000 ..................................................
|
||||
...
|
||||
92% 0x67300000 ..................................................
|
||||
95% 0x6A500000 ..................................................
|
||||
98% 0x6D700000 .................................
|
||||
Driver Unloaded.
|
||||
|
||||
Experimental write support
|
||||
--------------------------
|
||||
|
||||
As from Version 1.1, the winpmem drivers support writing to memory as well as
|
||||
reading. This capability is a great learning tool since many rootkit hiding
|
||||
techniques can be emulated by writing to memory directly. For example the
|
||||
following Rekall session illustrates changing the name of the binary:
|
||||
|
||||
WinXPSP2x86:pmem 03:10:40> task = session.profile._EPROCESS(0x82079c18)
|
||||
WinXPSP2x86:pmem 03:10:57> task.ImageFileName
|
||||
Out<3> [String:ImageFileName]: 'cmd.exe\x00'
|
||||
WinXPSP2x86:pmem 03:11:15> task.ImageFileName = "foo.exe\x00"
|
||||
WinXPSP2x86:pmem 03:11:21> task.ImageFileName
|
||||
Out<6> [String:ImageFileName]: 'foo.exe\x00'
|
||||
|
||||
NOTE: Since this is a rather dangerous capability, the signed binary drivers
|
||||
have write support disabled. The unsigned binaries (really self signed with a
|
||||
test certificate) can not load on a regular system due to them being test self
|
||||
signed. You can allow the unsigned drivers to be loaded on a test system by
|
||||
issuing (see
|
||||
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553484(v=vs.85).aspx):
|
||||
|
||||
Bcdedit.exe -set TESTSIGNING ON
|
||||
|
||||
and reboot. You will see a small "Test Mode" text on the desktop to remind you
|
||||
that this machine is configured for test signed drivers.
|
||||
|
||||
Alternatively you can test this on XP or Vista32 which have no driver signing
|
||||
restrictions.
|
||||
|
||||
Additionally, Write support must also be enabled at load time:
|
||||
|
||||
winpmem_1.3-write.exe -w -l
|
||||
|
||||
This will load the drivers and turn on write support. Then we can run rekall
|
||||
interactively, as usual on the raw device:
|
||||
|
||||
vol.exe --profile Win7SP1x64 --file \\.\pmem
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
TARGETNAME=winpmem
|
||||
TARGETTYPE=DRIVER
|
||||
TARGETPATH=release
|
||||
|
||||
TARGETLIBS=$(DDK_LIB_PATH)\wdmsec.lib
|
||||
DDKROOT=$(BASEDIR)
|
||||
LINKER_FLAGS=$(LINKER_FLAGS) /PDBALTPATH:x.pdb
|
||||
INCLUDES=$(DDK_INC_PATH);
|
||||
|
||||
SOURCES= winpmem.c \
|
||||
read.c \
|
||||
api.c \
|
||||
kd.c \
|
||||
pte_mmap.c \
|
||||
pte_mmap_windows.c \
|
||||
pci.c
|
||||
@@ -0,0 +1 @@
|
||||
This directory contains the MSVC 14 project files for building the driver.
|
||||
@@ -0,0 +1,87 @@
|
||||
;
|
||||
; winpmem.inf
|
||||
;
|
||||
|
||||
[Version]
|
||||
Signature="$WINDOWS NT$"
|
||||
Class=USBDevice
|
||||
ClassGuid={88BAE032-5A81-49f0-BC3D-A4FF138216D6}
|
||||
Provider=%ManufacturerName%
|
||||
CatalogFile=winpmem.cat
|
||||
DriverVer=
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir = 12
|
||||
winpmem_Device_CoInstaller_CopyFiles = 11
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DiskName%,,,""
|
||||
|
||||
[SourceDisksFiles]
|
||||
winpmem.sys = 1,,
|
||||
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames
|
||||
|
||||
;*****************************************
|
||||
; Install Section
|
||||
;*****************************************
|
||||
|
||||
[Manufacturer]
|
||||
%ManufacturerName%=Standard,NT$ARCH$
|
||||
|
||||
[Standard.NT$ARCH$]
|
||||
%winpmem.DeviceDesc%=winpmem_Device, USB\VID_vvvv&PID_pppp
|
||||
|
||||
[winpmem_Device.NT]
|
||||
CopyFiles=Drivers_Dir
|
||||
|
||||
[Drivers_Dir]
|
||||
winpmem.sys
|
||||
|
||||
[winpmem_Device.NT.HW]
|
||||
AddReg=winpmem_AddReg
|
||||
|
||||
[winpmem_AddReg]
|
||||
; By default, USBDevice class uses iProduct descriptor to name the device in
|
||||
; Device Manager on Windows 8 and higher.
|
||||
; Uncomment for this device to use %DeviceName% on Windows 8 and higher:
|
||||
;HKR,,FriendlyName,,%winpmem.DeviceDesc%
|
||||
|
||||
;-------------- Service installation
|
||||
[winpmem_Device.NT.Services]
|
||||
AddService = winpmem,%SPSVCINST_ASSOCSERVICE%, winpmem_Service_Inst
|
||||
|
||||
; -------------- winpmem driver install sections
|
||||
[winpmem_Service_Inst]
|
||||
DisplayName = %winpmem.SVCDESC%
|
||||
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||
StartType = 3 ; SERVICE_DEMAND_START
|
||||
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||
ServiceBinary = %12%\winpmem.sys
|
||||
|
||||
;
|
||||
;--- winpmem_Device Coinstaller installation ------
|
||||
;
|
||||
|
||||
[winpmem_Device.NT.CoInstallers]
|
||||
AddReg=winpmem_Device_CoInstaller_AddReg
|
||||
CopyFiles=winpmem_Device_CoInstaller_CopyFiles
|
||||
|
||||
[winpmem_Device_CoInstaller_AddReg]
|
||||
HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"
|
||||
|
||||
[winpmem_Device_CoInstaller_CopyFiles]
|
||||
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll
|
||||
|
||||
[winpmem_Device.NT.Wdf]
|
||||
KmdfService = winpmem, winpmem_wdfsect
|
||||
[winpmem_wdfsect]
|
||||
KmdfLibraryVersion = $KMDFVERSION$
|
||||
|
||||
[Strings]
|
||||
SPSVCINST_ASSOCSERVICE= 0x00000002
|
||||
ManufacturerName="<Your manufacturer name>" ;TODO: Replace with your manufacturer name
|
||||
ClassName="Universal Serial Bus devices"
|
||||
DiskName = "winpmem Installation Disk"
|
||||
winpmem.DeviceDesc = "winpmem Device"
|
||||
winpmem.SVCDESC = "winpmem Service"
|
||||
REG_MULTI_SZ = 0x00010000
|
||||
@@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\api.c" />
|
||||
<ClCompile Include="..\kd.c" />
|
||||
<ClCompile Include="..\pci.c" />
|
||||
<ClCompile Include="..\pte_mmap.c" />
|
||||
<ClCompile Include="..\pte_mmap_windows.c" />
|
||||
<ClCompile Include="..\read.c" />
|
||||
<ClCompile Include="..\winpmem.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8BAC86C0-A3EA-4652-92BF-C51C0DF5D86C}</ProjectGuid>
|
||||
<TemplateGuid>{8c0e3d8b-df43-455b-815a-4a0e72973bc6}</TemplateGuid>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<RootNamespace>winpmem</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WppEnabled>true</WppEnabled>
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppKernelMode>true</WppKernelMode>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
<SupportJustMyCode>false</SupportJustMyCode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);usbdex.lib;ntstrsafe.lib;wdmsec.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WppEnabled>true</WppEnabled>
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppKernelMode>true</WppKernelMode>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);usbdex.lib;ntstrsafe.lib;wdmsec.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WppEnabled>true</WppEnabled>
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppKernelMode>true</WppKernelMode>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
<SupportJustMyCode>false</SupportJustMyCode>
|
||||
<LanguageStandard>stdcpp14</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);usbdex.lib;ntstrsafe.lib;wdmsec.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WppEnabled>true</WppEnabled>
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppKernelMode>true</WppKernelMode>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<LanguageStandard>stdcpp14</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);usbdex.lib;ntstrsafe.lib;wdmsec.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<FilesToPackage Include="$(TargetPath)" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Driver Files">
|
||||
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
|
||||
<Extensions>inf;inv;inx;mof;mc;</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\api.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\kd.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\pci.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\pte_mmap.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\pte_mmap_windows.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\read.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\winpmem.c">
|
||||
<Filter>Driver Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<EnableVerifier>False</EnableVerifier>
|
||||
<AllDrivers>False</AllDrivers>
|
||||
<VerifyProjectOutput>True</VerifyProjectOutput>
|
||||
<VerifyDrivers />
|
||||
<VerifyFlags>133563</VerifyFlags>
|
||||
<TestCertificate>CN="WDKTestCert mic,131384300355942867" | 7EA426631F0B9F382BA48901017D8EF0D2B1AD4F</TestCertificate>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "winpmem.h"
|
||||
#include "api.h"
|
||||
#include "kd.h"
|
||||
|
||||
struct Pmem_KernelExports_t Pmem_KernelExports;
|
||||
|
||||
|
||||
NTSTATUS PmemGetProcAddresses() {
|
||||
void *image_base = KernelGetModuleBaseByPtr(NtBuildNumber);
|
||||
char key[sizeof(OBFUSCATION_KEY)] = OBFUSCATION_KEY;
|
||||
void *api = NULL;
|
||||
|
||||
|
||||
RtlZeroMemory(&Pmem_KernelExports, sizeof(Pmem_KernelExports));
|
||||
|
||||
if (!image_base) {
|
||||
goto error;
|
||||
};
|
||||
|
||||
// If any of the below APIs fail, they will have a NULL pointer in the export
|
||||
// table - this will cause that acquisition method to be rejected.
|
||||
if (image_base) {
|
||||
{
|
||||
int i;
|
||||
char parameter[sizeof(X_MmGetPhysicalMemoryRanges)] =
|
||||
X_MmGetPhysicalMemoryRanges;
|
||||
|
||||
|
||||
for(i=0; i<min(sizeof(parameter), sizeof(key)); i++) {
|
||||
parameter[i] ^= key[i];
|
||||
};
|
||||
|
||||
WinDbgPrint("Fetching API %s\n", parameter);
|
||||
api = KernelGetProcAddress(image_base, parameter);
|
||||
Pmem_KernelExports.MmGetPhysicalMemoryRanges = api;
|
||||
};
|
||||
|
||||
{
|
||||
int i;
|
||||
char parameter[sizeof(X_MmGetVirtualForPhysical)] =
|
||||
X_MmGetVirtualForPhysical;
|
||||
|
||||
for(i=0; i<min(sizeof(parameter), sizeof(key)); i++) {
|
||||
parameter[i] ^= key[i];
|
||||
};
|
||||
|
||||
WinDbgPrint("Fetching API %s\n", parameter);
|
||||
api = KernelGetProcAddress(image_base, parameter);
|
||||
Pmem_KernelExports.MmGetVirtualForPhysical = api;
|
||||
};
|
||||
|
||||
{
|
||||
int i;
|
||||
char parameter[sizeof(X_MmMapIoSpace)] = X_MmMapIoSpace;
|
||||
for(i=0; i<min(sizeof(parameter), sizeof(key)); i++) {
|
||||
parameter[i] ^= key[i];
|
||||
};
|
||||
|
||||
WinDbgPrint("Fetching API %s\n", parameter);
|
||||
api = KernelGetProcAddress(image_base, parameter);
|
||||
Pmem_KernelExports.MmMapIoSpace = api;
|
||||
};
|
||||
|
||||
{
|
||||
int i;
|
||||
char parameter[sizeof(X_MmUnmapIoSpace)] = X_MmUnmapIoSpace;
|
||||
for(i=0; i<min(sizeof(parameter), sizeof(key)); i++) {
|
||||
parameter[i] ^= key[i];
|
||||
};
|
||||
|
||||
WinDbgPrint("Fetching API %s\n", parameter);
|
||||
api = KernelGetProcAddress(image_base, parameter);
|
||||
Pmem_KernelExports.MmUnmapIoSpace = api;
|
||||
};
|
||||
};
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
error:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// This module uses obfuscated GetProcAddress calls to load certain functions
|
||||
// which are usually only used by memory acquisition tools. This makes it a
|
||||
// little bit harder to recognize us as an acquisition tool.
|
||||
#ifndef _API_H_
|
||||
#define _API_H_
|
||||
|
||||
#include <ntifs.h>
|
||||
#include <wdmsec.h>
|
||||
#include <initguid.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Select a better/different key or obfuscation scheme here.
|
||||
#define OBFUSCATION_KEY "\x43\x73\x17\x54\x19\x3C\x42\xE5\xA2\x6B\x7C\x8A\x1C\x2E\x5C\x2C\x95\xCD\xC8\x3A\x68\x54\xEE\xC9\x36\x55\xCB\xE9\x4D\x98\x05\x04\x20\x5E\x77\x0F\xCD\x09\x35\xAF\x4C\xF8\x45\x22\x12\x30\x8F\xCB\x55\xC4"
|
||||
|
||||
#define X_MmGetPhysicalMemoryRanges "\x0E\x1E\x50\x31\x6D\x6C\x2A\x9C\xD1\x02\x1F\xEB\x70\x63\x39\x41\xFA\xBF\xB1\x68\x09\x3A\x89\xAC\x45\x55"
|
||||
#define X_MmGetVirtualForPhysical "\x0E\x1E\x50\x31\x6D\x6A\x2B\x97\xD6\x1E\x1D\xE6\x5A\x41\x2E\x7C\xFD\xB4\xBB\x53\x0B\x35\x82\xC9"
|
||||
#define X_MmMapIoSpace "\x0E\x1E\x5A\x35\x69\x75\x2D\xB6\xD2\x0A\x1F\xEF\x1C"
|
||||
#define X_MmUnmapIoSpace "\x0E\x1E\x42\x3A\x74\x5D\x32\xAC\xCD\x38\x0C\xEB\x7F\x4B\x5C"
|
||||
|
||||
struct Pmem_KernelExports_t {
|
||||
PHYSICAL_MEMORY_RANGE *(*MmGetPhysicalMemoryRanges)();
|
||||
void *(*MmGetVirtualForPhysical)(PHYSICAL_ADDRESS);
|
||||
unsigned char *(*MmMapIoSpace)(LARGE_INTEGER, int, int);
|
||||
void (*MmUnmapIoSpace)(unsigned char *, int);
|
||||
};
|
||||
|
||||
extern struct Pmem_KernelExports_t Pmem_KernelExports;
|
||||
|
||||
NTSTATUS PmemGetProcAddresses();
|
||||
|
||||
#endif _API_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
// This module uses obfuscated GetProcAddress calls to load certain functions
|
||||
// which are usually only used by memory acquisition tools. This makes it a
|
||||
// little bit harder to recognize us as an acquisition tool.
|
||||
#ifndef _API_H_
|
||||
#define _API_H_
|
||||
|
||||
#include <ntifs.h>
|
||||
#include <wdmsec.h>
|
||||
#include <initguid.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Select a better/different key or obfuscation scheme here.
|
||||
#define OBFUSCATION_KEY "%(key)s"
|
||||
|
||||
#define X_MmGetPhysicalMemoryRanges "%(MmGetPhysicalMemoryRanges)s"
|
||||
#define X_MmGetVirtualForPhysical "%(MmGetVirtualForPhysical)s"
|
||||
#define X_MmMapIoSpace "%(MmMapIoSpace)s"
|
||||
#define X_MmUnmapIoSpace "%(MmUnmapIoSpace)s"
|
||||
|
||||
struct Pmem_KernelExports_t {
|
||||
PHYSICAL_MEMORY_RANGE *(*MmGetPhysicalMemoryRanges)();
|
||||
void *(*MmGetVirtualForPhysical)(PHYSICAL_ADDRESS);
|
||||
unsigned char *(*MmMapIoSpace)(LARGE_INTEGER, int, int);
|
||||
void (*MmUnmapIoSpace)(unsigned char *, int);
|
||||
};
|
||||
|
||||
extern struct Pmem_KernelExports_t Pmem_KernelExports;
|
||||
|
||||
NTSTATUS PmemGetProcAddresses();
|
||||
|
||||
#endif _API_H_
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "kd.h"
|
||||
|
||||
/*
|
||||
This code inspired and updated from
|
||||
http://alter.org.ua/docs/nt_kernel/procaddr/#KernelGetModuleBaseByPtr
|
||||
|
||||
We try to locate the kernel's base address. We do this by looking for an MZ
|
||||
header a short space before the location of a known exported symbol.
|
||||
|
||||
Unfortunately we might hit unmapped kernel memory which will blue screen so we
|
||||
need to check if the kernel address is at all valid.
|
||||
*/
|
||||
|
||||
IMAGE_DOS_HEADER *KernelGetModuleBaseByPtr(IN void *in_section) {
|
||||
unsigned char *p;
|
||||
IMAGE_DOS_HEADER *dos = NULL;
|
||||
IMAGE_NT_HEADERS *nt;
|
||||
int count = 0;
|
||||
|
||||
p = (unsigned char *)((uintptr_t)in_section & ~(PAGE_SIZE-1));
|
||||
|
||||
for(;p;p -= PAGE_SIZE) {
|
||||
count ++;
|
||||
|
||||
// Dont go back too far.
|
||||
if (count > 0x800) {
|
||||
return NULL;
|
||||
};
|
||||
|
||||
__try {
|
||||
dos = (IMAGE_DOS_HEADER *)p;
|
||||
|
||||
// If this address is not mapped in, there will be a BSOD
|
||||
// PAGE_FAULT_IN_NONPAGED_AREA so we check first.
|
||||
if(!MmIsAddressValid(dos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(dos->e_magic != 0x5a4d) // MZ
|
||||
continue;
|
||||
|
||||
nt = (IMAGE_NT_HEADERS *)((uintptr_t)dos + dos->e_lfanew);
|
||||
if((uintptr_t)nt >= (uintptr_t)in_section)
|
||||
continue;
|
||||
|
||||
if((uintptr_t)nt <= (uintptr_t)dos)
|
||||
continue;
|
||||
|
||||
if(!MmIsAddressValid(nt)) {
|
||||
continue;
|
||||
}
|
||||
if(nt->Signature != 0x00004550) // PE
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
// Ignore potential errors.
|
||||
} __except(EXCEPTION_CONTINUE_EXECUTION) {}
|
||||
}
|
||||
|
||||
return dos;
|
||||
}
|
||||
|
||||
/* Resolve a kernel function by name.
|
||||
*/
|
||||
void *KernelGetProcAddress(void *image_base, char *func_name) {
|
||||
void *func_address = NULL;
|
||||
|
||||
__try {
|
||||
int size = 0;
|
||||
IMAGE_DOS_HEADER *dos =(IMAGE_DOS_HEADER *)image_base;
|
||||
IMAGE_NT_HEADERS *nt =(IMAGE_NT_HEADERS *)((uintptr_t)image_base + dos->e_lfanew);
|
||||
|
||||
IMAGE_DATA_DIRECTORY *expdir = (IMAGE_DATA_DIRECTORY *)
|
||||
(nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT);
|
||||
|
||||
IMAGE_EXPORT_DIRECTORY *exports =(PIMAGE_EXPORT_DIRECTORY)
|
||||
((uintptr_t)image_base + expdir->VirtualAddress);
|
||||
|
||||
uintptr_t addr = (uintptr_t)exports-(uintptr_t)image_base;
|
||||
|
||||
// These are arrays of RVA addresses.
|
||||
unsigned int *functions = (unsigned int *)((uintptr_t)image_base +
|
||||
exports->AddressOfFunctions);
|
||||
|
||||
unsigned int *names = (unsigned int *)((uintptr_t)image_base +
|
||||
exports->AddressOfNames);
|
||||
|
||||
short *ordinals = (short *)((uintptr_t)image_base +
|
||||
exports->AddressOfNameOrdinals);
|
||||
|
||||
unsigned int max_name = exports->NumberOfNames;
|
||||
unsigned int max_func = exports->NumberOfFunctions;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < max_name; i++) {
|
||||
unsigned int ord = ordinals[i];
|
||||
if(i >= max_name || ord >= max_func) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (functions[ord] < addr || functions[ord] >= addr + size) {
|
||||
if (strcmp((char *)image_base + names[i], func_name) == 0) {
|
||||
func_address = (char *)image_base + functions[ord];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
func_address = NULL;
|
||||
}
|
||||
|
||||
return func_address;
|
||||
} // end KernelGetProcAddress()
|
||||
|
||||
|
||||
/* Search for a section by name.
|
||||
|
||||
Returns the mapped virtual memory section or NULL if not found.
|
||||
*/
|
||||
IMAGE_SECTION_HEADER *GetSection(IMAGE_DOS_HEADER *image_base, char *name) {
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)
|
||||
((uintptr_t)image_base + image_base->e_lfanew);
|
||||
int i;
|
||||
int number_of_sections = nt->FileHeader.NumberOfSections;
|
||||
|
||||
IMAGE_SECTION_HEADER *sections = (IMAGE_SECTION_HEADER *)
|
||||
((uintptr_t)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (i=0; i<number_of_sections; i++) {
|
||||
if(!strcmp((char *)sections[i].Name, name))
|
||||
return §ions[i];
|
||||
};
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Enumerate the KPCR blocks from all CPUs.
|
||||
*/
|
||||
|
||||
int GetKPCR(struct PmemMemoryInfo *info) {
|
||||
__int64 active_processors = KeQueryActiveProcessors();
|
||||
int i;
|
||||
|
||||
for (i=0; i < 32; i++) {
|
||||
info->KPCR[i].QuadPart = 0;
|
||||
};
|
||||
|
||||
for (i=0; i < 32; i++) {
|
||||
if (active_processors & ((__int64)1 << i)) {
|
||||
KeSetSystemAffinityThread((__int64)1 << i);
|
||||
#if _WIN64 || __amd64__
|
||||
//64 bit uses gs and _KPCR.Self is at 0x18:
|
||||
info->KPCR[i].QuadPart = (uintptr_t)__readgsqword(0x18);
|
||||
#else
|
||||
//32 bit uses fs and _KPCR.SelfPcr is at 0x1c:
|
||||
info->KPCR[i].QuadPart = (uintptr_t)__readfsword(0x1c);
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
KeRevertToUserAffinityThread();
|
||||
|
||||
return 1;
|
||||
};
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _WINPMEM_KD_H
|
||||
#define _WINPMEM_KD_H
|
||||
|
||||
#include "winpmem.h"
|
||||
#include "ntimage.h"
|
||||
|
||||
IMAGE_DOS_HEADER *KernelGetModuleBaseByPtr(IN void *in_section);
|
||||
|
||||
void *KernelGetProcAddress(void *image_base, char *func_name);
|
||||
|
||||
int GetKPCR(struct PmemMemoryInfo *info);
|
||||
|
||||
#endif // _WINPMEM_KD_H
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
import random
|
||||
|
||||
class Interpolator(object):
|
||||
|
||||
def __init__(self):
|
||||
self.key = [random.randint(1, 255) for _ in range(50)]
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item == "key":
|
||||
return "".join(["\\x%02X" % x for x in self.key])
|
||||
|
||||
# Add a NULL terminator
|
||||
item += "\x00"
|
||||
|
||||
# Xor with the key.
|
||||
return "".join(["\\x%02X" % (ord(y) ^ x)
|
||||
for x, y in zip(self.key, item)])
|
||||
|
||||
|
||||
def interpolate_file(filename):
|
||||
data = open(filename + ".in", "rb").read()
|
||||
|
||||
with open(filename, "wb") as fd:
|
||||
fd.write(data % Interpolator())
|
||||
|
||||
interpolate_file("api.h")
|
||||
Executable
+399
@@ -0,0 +1,399 @@
|
||||
#include "pci.h"
|
||||
|
||||
u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset) {
|
||||
u32 v;
|
||||
__outdword(PCI_CONFIG_ADDRESS,
|
||||
0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset);
|
||||
v = __indword(PCI_CONFIG_DATA);
|
||||
return v;
|
||||
}
|
||||
|
||||
void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset, u32 value) {
|
||||
__outdword(PCI_CONFIG_ADDRESS,
|
||||
0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset);
|
||||
__outdword(PCI_CONFIG_DATA, value);
|
||||
}
|
||||
|
||||
void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 value) {
|
||||
__outdword(PCI_CONFIG_ADDRESS,
|
||||
0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset);
|
||||
__outword(PCI_CONFIG_DATA, value);
|
||||
}
|
||||
u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset) {
|
||||
u8 v;
|
||||
__outdword(PCI_CONFIG_ADDRESS,
|
||||
0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset);
|
||||
v = __inbyte(PCI_CONFIG_DATA + (offset&3));
|
||||
return v;
|
||||
}
|
||||
|
||||
u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset) {
|
||||
u16 v;
|
||||
__outdword(PCI_CONFIG_ADDRESS,
|
||||
0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset);
|
||||
v = __inword(PCI_CONFIG_DATA + (offset&2));
|
||||
return v;
|
||||
}
|
||||
|
||||
u32 get_base_register_size(u8 bus, u8 slot, u8 func, u8 offset) {
|
||||
__int32 base = read_pci_config(bus, slot, func, offset);
|
||||
u32 mask = 0;
|
||||
u16 command =0;
|
||||
|
||||
if (base == 0) return 0;
|
||||
|
||||
command = read_pci_config_16(bus, slot, func, PCI_COMMAND);
|
||||
|
||||
// Disable IO and memory bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, 0);
|
||||
|
||||
// Write to config space all 0xFFFFFFFF
|
||||
write_pci_config(bus, slot, func, offset, 0xFFFFFFFF);
|
||||
mask = read_pci_config(bus, slot, func, offset) & 0xFFFFFFF0;
|
||||
write_pci_config(bus, slot, func, offset, base);
|
||||
|
||||
// Reenable bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, command);
|
||||
|
||||
return ~mask + base;
|
||||
};
|
||||
|
||||
|
||||
#if WINPMEM_PCI_DEBUG
|
||||
// Debugging functions for dumping out PCI configuration data.
|
||||
|
||||
void dump_interesting_fields(u8 bus, u8 slot, u8 func) {
|
||||
u16 vendor_id = read_pci_config_16(bus, slot, func, PCI_VENDOR_ID);
|
||||
u16 device_id = read_pci_config_16(bus, slot, func, PCI_DEVICE_ID);
|
||||
|
||||
WinDbgPrint("pci 0000:%02x:%02x.%d ", bus, slot, func);
|
||||
WinDbgPrint("Id %04x:%04x config space:\n", vendor_id, device_id);
|
||||
};
|
||||
|
||||
void dump_bar(u8 bus, u8 slot, u8 func) {
|
||||
u16 vendor_id = read_pci_config_16(bus, slot, func, PCI_VENDOR_ID);
|
||||
u16 device_id = read_pci_config_16(bus, slot, func, PCI_DEVICE_ID);
|
||||
u32 base0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
|
||||
u32 base1 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1);
|
||||
u32 base2 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_2);
|
||||
u32 base3 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_3);
|
||||
u32 base4 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_4);
|
||||
u32 base5 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_5);
|
||||
|
||||
|
||||
WinDbgPrint("Base Addresses: %08X %08X %08X %08X %08X %08X\n",
|
||||
base0, base1, base2, base3, base4, base5);
|
||||
|
||||
WinDbgPrint("Masks: %08X %08X %08X %08X %08X %08X\n",
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_0),
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_1),
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_2),
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_3),
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_4),
|
||||
get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_5));
|
||||
}
|
||||
|
||||
void DumpConfigSpace(u8 bus, u8 slot, u8 func) {
|
||||
|
||||
u8 i, j;
|
||||
|
||||
for(i=0; i<0x80; i+=0x10) {
|
||||
WinDbgPrint("0x%02X ", i);
|
||||
for(j=i; j<i+0x10; j++) {
|
||||
u8 b = read_pci_config_byte(bus, slot, func, j);
|
||||
WinDbgPrint("%02X ", b);
|
||||
};
|
||||
WinDbgPrint("\n");
|
||||
}
|
||||
};
|
||||
|
||||
void DumpRuns(struct PmemMemoryInfo *info) {
|
||||
int i;
|
||||
for(i=0; i<info->NumberOfRuns.QuadPart; i++) {
|
||||
WinDbgPrint("0x%llx 0x%llx 0x%llx\n", info->Run[i].BaseAddress.QuadPart,
|
||||
info->Run[i].BaseAddress.QuadPart + info->Run[i].NumberOfBytes.QuadPart,
|
||||
info->Run[i].NumberOfBytes.QuadPart);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static NTSTATUS InsertMemoryHole(struct PmemMemoryInfo *info, int len,
|
||||
u64 start, u64 end) {
|
||||
int i;
|
||||
|
||||
#if WINPMEM_PCI_DEBUG
|
||||
WinDbgPrint("Inserting memory hole at Start %llX end %llx\n", start, end);
|
||||
#endif
|
||||
|
||||
// Round start and end to page boundaries.
|
||||
start = start & 0xFFFFFFFFFFFFF000;
|
||||
end = end | 0xFFF;
|
||||
|
||||
if (start == end){
|
||||
goto exit;
|
||||
};
|
||||
|
||||
for (i=0; i<info->NumberOfRuns.QuadPart; i++) {
|
||||
PHYSICAL_MEMORY_RANGE Run = info->Run[i];
|
||||
u64 run_start = Run.BaseAddress.QuadPart;
|
||||
u64 run_end = Run.BaseAddress.QuadPart + Run.NumberOfBytes.QuadPart;
|
||||
|
||||
if (run_start < start && start < run_end) {
|
||||
// Make some room for a new entry.
|
||||
RtlMoveMemory(&info->Run[i+1], &info->Run[i],
|
||||
sizeof(Run) * (info->NumberOfRuns.LowPart - i));
|
||||
|
||||
info->NumberOfRuns.QuadPart++;
|
||||
|
||||
info->Run[i].NumberOfBytes.QuadPart = start - run_start - 1;
|
||||
info->Run[i+1].BaseAddress.QuadPart = start;
|
||||
info->Run[i+1].NumberOfBytes.QuadPart = run_end - start - 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
if (run_start < end && end < run_end) {
|
||||
info->Run[i].BaseAddress.QuadPart = end + 1;
|
||||
info->Run[i].NumberOfBytes.QuadPart = run_end - end - 1;
|
||||
goto exit;
|
||||
};
|
||||
};
|
||||
|
||||
exit:
|
||||
|
||||
#if WINPMEM_PCI_DEBUG
|
||||
DumpRuns(info);
|
||||
#endif
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
|
||||
static NTSTATUS DumpBaseAddressRegister32(u8 bus, u8 slot, u8 func, u8 offset,
|
||||
struct PmemMemoryInfo *info, int len) {
|
||||
u32 mask = 0;
|
||||
u32 base = read_pci_config(bus, slot, func, offset);
|
||||
u16 command = read_pci_config_16(bus, slot, func, PCI_COMMAND);
|
||||
|
||||
// Disable IO and memory bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, 0);
|
||||
|
||||
write_pci_config(bus, slot, func, offset, 0xFFFFFFFF);
|
||||
mask = read_pci_config(bus, slot, func, offset) & 0xFFFFFFF0;
|
||||
write_pci_config(bus, slot, func, offset, base);
|
||||
|
||||
// Reenable bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, command);
|
||||
|
||||
base = base & 0xFFFFFFF0;
|
||||
return InsertMemoryHole(info, len, base, ~mask + base);
|
||||
};
|
||||
|
||||
|
||||
static NTSTATUS DumpBaseAddressRegister64(u8 bus, u8 slot, u8 func, u8 offset,
|
||||
struct PmemMemoryInfo *info, int len) {
|
||||
u64 base = read_pci_config(bus, slot, func, offset);
|
||||
u32 base_high = read_pci_config(bus, slot, func, offset + sizeof(u32));
|
||||
u32 mask = 0;
|
||||
u32 mask_high = 0;
|
||||
u64 end = 0;
|
||||
u16 command = read_pci_config_16(bus, slot, func, PCI_COMMAND);
|
||||
|
||||
// Disable IO and memory bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, 0);
|
||||
|
||||
// Check the lower word first.
|
||||
write_pci_config(bus, slot, func, offset, 0xFFFFFFFF);
|
||||
mask = read_pci_config(bus, slot, func, offset) & 0xFFFFFFF0;
|
||||
write_pci_config(bus, slot, func, offset, (u32)base);
|
||||
|
||||
// Check the upper 32 bit word.
|
||||
write_pci_config(bus, slot, func, offset + sizeof(u32), 0xFFFFFFFF);
|
||||
mask_high = read_pci_config(bus, slot, func, offset + sizeof(u32));
|
||||
write_pci_config(bus, slot, func, offset + sizeof(u32), (u32)base_high);
|
||||
|
||||
// Reenable bus access.
|
||||
write_pci_config_16(bus, slot, func, PCI_COMMAND, command);
|
||||
|
||||
base = ((base & 0xFFFFFFF0) | ((u64)base_high) << 32);
|
||||
end = ~(mask | ((u64)mask_high) << 32) + base;
|
||||
|
||||
return InsertMemoryHole(info, len, base, end);
|
||||
};
|
||||
|
||||
|
||||
// Advances the offset depending on the size of the base address register.
|
||||
static NTSTATUS DumpBaseAddressRegister(u8 bus, u8 slot, u8 func, u8 *offset,
|
||||
struct PmemMemoryInfo *info, int len) {
|
||||
u64 base = read_pci_config(bus, slot, func, *offset) & 0xFFFFFFFF;
|
||||
|
||||
if (base == 0) {
|
||||
*offset += sizeof(u32);
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
// We skip IO space registers since they are not interesting for memory
|
||||
// acquisition.
|
||||
if ((base & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
*offset += sizeof(u32);
|
||||
|
||||
// 64 bit base address register.
|
||||
} else if ((base & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
|
||||
DumpBaseAddressRegister64(bus, slot, func, *offset, info, len);
|
||||
*offset += sizeof(u64);
|
||||
|
||||
// 32 bit memspace or io space base address register.
|
||||
} else if ((base & PCI_BASE_ADDRESS_SPACE) ==
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY) {
|
||||
DumpBaseAddressRegister32(bus, slot, func, *offset, info, len);
|
||||
*offset += sizeof(u32);
|
||||
|
||||
} else {
|
||||
// Something else - Just advance the offset one word forward.
|
||||
*offset += sizeof(u32);
|
||||
};
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static NTSTATUS DumpStandardHeader(u8 bus, u8 slot, u8 func,
|
||||
struct PmemMemoryInfo *info, int len) {
|
||||
u8 offset;
|
||||
|
||||
// For standard devices we just go over all their base address registers.
|
||||
for(offset = PCI_BASE_ADDRESS_0; offset <= PCI_BASE_ADDRESS_5;) {
|
||||
DumpBaseAddressRegister(bus, slot, func, &offset, info, len);
|
||||
};
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
|
||||
static NTSTATUS DumpPCIBridge(u8 bus, u8 slot, u8 func,
|
||||
struct PmemMemoryInfo *info, int len) {
|
||||
u8 offset;
|
||||
u64 base;
|
||||
u64 limit;
|
||||
|
||||
// Support direct Bridge BARs.
|
||||
for(offset = PCI_BASE_ADDRESS_0; offset <= PCI_BASE_ADDRESS_1;) {
|
||||
DumpBaseAddressRegister(bus, slot, func, &offset, info, len);
|
||||
};
|
||||
|
||||
// I/O base and limit registers are not interesting for memory acquisition.
|
||||
|
||||
// Memory base and limit registers.
|
||||
base = read_pci_config_16(bus, slot, func, PCI_MEMORY_BASE);
|
||||
if (base != 0) {
|
||||
base = (base & 0xFFF0) << 20;
|
||||
limit = read_pci_config_16(bus, slot, func, PCI_MEMORY_LIMIT);
|
||||
|
||||
limit = (limit & 0xFFF0) << 20 | 0xFFFFFF;
|
||||
if (limit > base) {
|
||||
InsertMemoryHole(info, len, base, limit);
|
||||
};
|
||||
};
|
||||
|
||||
// Prefetcheable Memory base and limit registers.
|
||||
base = read_pci_config_16(bus, slot, func, PCI_PREF_MEMORY_BASE);
|
||||
if (base != 0) {
|
||||
// Determine if the base register is 32 or 64 bit.
|
||||
if ((base & 0xF) == 0) {
|
||||
base = (base & 0xFFF0) << 20;
|
||||
} else {
|
||||
base = ((base & 0xFFF0) << 20) |
|
||||
((u64)read_pci_config(bus, slot, func, PCI_PREF_BASE_UPPER32) << 32);
|
||||
};
|
||||
|
||||
limit = read_pci_config_16(bus, slot, func, PCI_PREF_MEMORY_LIMIT);
|
||||
|
||||
// Determine if the limit register is 32 or 64 bit.
|
||||
if ((limit & 0xF) == 0) {
|
||||
limit = (limit & 0xFFF0) << 20 | 0xFFFFFF;
|
||||
} else {
|
||||
limit = ((limit & 0xFFF0) << 20) | 0xFFFFFF |
|
||||
((u64)read_pci_config(bus, slot, func, PCI_PREF_LIMIT_UPPER32) << 32);
|
||||
};
|
||||
|
||||
if (limit > base) {
|
||||
InsertMemoryHole(info, len, base, limit);
|
||||
};
|
||||
};
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Uses direct PCI probing to add accessible physical memory ranges.
|
||||
*/
|
||||
NTSTATUS PCI_AddMemoryRanges(struct PmemMemoryInfo *info, int len) {
|
||||
int required_length = (sizeof(struct PmemMemoryInfo) +
|
||||
sizeof(PHYSICAL_MEMORY_RANGE));
|
||||
unsigned int bus, slot, func;
|
||||
|
||||
if (len < required_length) {
|
||||
return STATUS_INFO_LENGTH_MISMATCH;
|
||||
};
|
||||
|
||||
// Initialize the physical memory range.
|
||||
info->NumberOfRuns.QuadPart = 1;
|
||||
info->Run[0].BaseAddress.QuadPart = 0;
|
||||
info->Run[0].NumberOfBytes.QuadPart = -1;
|
||||
|
||||
for (bus = 0; bus < 256; bus++) {
|
||||
for (slot = 0; slot < 32; slot++) {
|
||||
for (func = 0; func < 8; func++) {
|
||||
u8 type;
|
||||
|
||||
u16 vendor_id = read_pci_config_16((u8)bus, (u8)slot, (u8)func,
|
||||
PCI_VENDOR_ID);
|
||||
|
||||
// Device not present.
|
||||
if (vendor_id == 0xffff)
|
||||
continue;
|
||||
|
||||
type = read_pci_config_byte((u8)bus, (u8)slot, (u8)func,
|
||||
PCI_HEADER_TYPE);
|
||||
|
||||
// Standard header.
|
||||
if ((type & 0x1f) == 0) {
|
||||
|
||||
#if WINPMEM_PCI_DEBUG
|
||||
WinDbgPrint("PCI Type %X\n", type);
|
||||
dump_interesting_fields((u8)bus, (u8)slot, (u8)func);
|
||||
dump_bar((u8)bus, (u8)slot, (u8)func);
|
||||
DumpConfigSpace((u8)bus, (u8)slot, (u8)func);
|
||||
#endif
|
||||
DumpStandardHeader((u8)bus, (u8)slot, (u8)func, info, len);
|
||||
|
||||
// PCI-PCI bridge.
|
||||
} else if ((type & 0x1f) == 1) {
|
||||
|
||||
#if WINPMEM_PCI_DEBUG
|
||||
WinDbgPrint("PCI Type %X\n", type);
|
||||
dump_interesting_fields((u8)bus, (u8)slot, (u8)func);
|
||||
DumpConfigSpace((u8)bus, (u8)slot, (u8)func);
|
||||
#endif
|
||||
|
||||
DumpPCIBridge((u8)bus, (u8)slot, (u8)func, info, len);
|
||||
|
||||
} else {
|
||||
WinDbgPrint("Unknown header PCI at 0000:%02x:%02x.%d type %d\n",
|
||||
bus, slot, func, type);
|
||||
};
|
||||
|
||||
// This is not a multi function device.
|
||||
if (func == 0 && (type & 0x80) == 0) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
// Implements a raw pci access method for communicating with the pci
|
||||
// controller directly.
|
||||
#include "winpmem.h"
|
||||
|
||||
#define PCI_VENDOR_ID 0x00 /* 16 bits */
|
||||
#define PCI_DEVICE_ID 0x02 /* 16 bits */
|
||||
#define PCI_COMMAND 0x04 /* 16 bits */
|
||||
#define PCI_COMMAND_IO 0x01
|
||||
#define PCI_COMMAND_MEMORY 0x02
|
||||
|
||||
#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
|
||||
#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
|
||||
#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
|
||||
#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
|
||||
#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
|
||||
#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
|
||||
#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
|
||||
#define PCI_BASE_ADDRESS_SPACE_IO 0x01
|
||||
#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
|
||||
#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
|
||||
#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
|
||||
#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
|
||||
#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
|
||||
#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
|
||||
#define PCI_BASE_ADDRESS_MEM_MASK (~(pciaddr_t)0x0f)
|
||||
#define PCI_BASE_ADDRESS_IO_MASK (~(pciaddr_t)0x03)
|
||||
/* bit 1 is reserved if address_space = 1 */
|
||||
|
||||
|
||||
#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
|
||||
#define PCI_IO_LIMIT 0x1d
|
||||
#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
|
||||
#define PCI_MEMORY_LIMIT 0x22
|
||||
#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
|
||||
#define PCI_PREF_MEMORY_LIMIT 0x26
|
||||
#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
|
||||
#define PCI_PREF_LIMIT_UPPER32 0x2c
|
||||
#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
|
||||
#define PCI_IO_LIMIT_UPPER16 0x32
|
||||
|
||||
|
||||
#define PCI_CONFIG_DATA 0xCFC
|
||||
#define PCI_CONFIG_ADDRESS 0xCF8
|
||||
|
||||
#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */
|
||||
#define PCI_HEADER_TYPE 0x0e /* 8 bits */
|
||||
|
||||
NTSTATUS PCI_AddMemoryRanges(struct PmemMemoryInfo *info, int len);
|
||||
Executable
+231
@@ -0,0 +1,231 @@
|
||||
// This module contains helper functions to directly edit the page tables of a
|
||||
// process, enabling it to map physical memory independent of the operating
|
||||
// system.
|
||||
//
|
||||
// Warning: This code directly writes to the kernel page tables and executes
|
||||
// priviledged instructions as invlpg. It will only run in ring 0.
|
||||
//
|
||||
//
|
||||
// Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
// Copyright 2014 - 2017 Google Inc.
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
// Authors: Johannes Stüttgen (johannes.stuettgen@gmail.com)
|
||||
// Michael Cohen <mike@velocidex.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "pte_mmap.h"
|
||||
#include "winpmem.h"
|
||||
|
||||
|
||||
// Edit the page tables to point a virtual address to a specific physical page.
|
||||
//
|
||||
// Args:
|
||||
// self: The this pointer to the object using this function.
|
||||
// target: The physical address to map to.
|
||||
//
|
||||
// Returns:
|
||||
// PTE_SUCCESS or PTE_ERROR
|
||||
//
|
||||
static PTE_STATUS pte_remap_rogue_page(PTE_MMAP_OBJ *self, PHYS_ADDR target) {
|
||||
if (self->rogue_page.pointer == NULL) {
|
||||
// We do not have a rogue page to remap at all.
|
||||
return PTE_ERROR;
|
||||
}
|
||||
// Can only remap pages, addresses must be page aligned.
|
||||
if (((!target) & PAGE_MASK) || self->rogue_page.offset) {
|
||||
WinDbgPrint("Failed to map %#016llx, "
|
||||
"only page aligned remapping is supported!",
|
||||
target);
|
||||
return PTE_ERROR;
|
||||
}
|
||||
|
||||
WinDbgPrintDebug("Remapping pte at %p to %#016llx",
|
||||
self->rogue_pte,
|
||||
target);
|
||||
// Change the pte to point to the new offset.
|
||||
self->rogue_pte->page_frame = PAGE_TO_PFN(target);
|
||||
// Flush the old pte from the tlbs in the system.
|
||||
self->flush_tlbs_page_(self->rogue_page.pointer);
|
||||
|
||||
return PTE_SUCCESS;
|
||||
}
|
||||
|
||||
// Parse a 64 bit page table entry and print it.
|
||||
static void print_pte_contents(PTE_MMAP_OBJ *self, PTE_LOGLEVEL loglevel,
|
||||
PTE *pte) {
|
||||
WinDbgPrint("Virtual Address:%#016llx\n"
|
||||
"\tpresent: %lld\n"
|
||||
"\trw: %lld\n"
|
||||
"\tuser: %lld\n"
|
||||
"\twrite_through:%lld\n"
|
||||
"\tcache_disable:%lld\n"
|
||||
"\taccessed: %lld\n"
|
||||
"\tdirty: %lld\n"
|
||||
"\tpat: %lld\n"
|
||||
"\tglobal: %lld\n"
|
||||
"\txd: %lld\n"
|
||||
"\tpfn: %010llx",
|
||||
(pte_uint64)pte,
|
||||
(pte_uint64)pte->present,
|
||||
(pte_uint64)pte->rw,
|
||||
(pte_uint64)pte->user,
|
||||
(pte_uint64)pte->write_through,
|
||||
(pte_uint64)pte->cache_disable,
|
||||
(pte_uint64)pte->accessed,
|
||||
(pte_uint64)pte->dirty,
|
||||
(pte_uint64)pte->pat,
|
||||
(pte_uint64)pte->global,
|
||||
(pte_uint64)pte->xd,
|
||||
(pte_uint64)pte->page_frame);
|
||||
}
|
||||
|
||||
// Traverses the page tables to find the pte for a given virtual address.
|
||||
//
|
||||
// Args:
|
||||
// self: The this pointer for the object calling this function.
|
||||
// vaddr: The virtual address to resolve the pte for
|
||||
// pte: A pointer to a pointer which will be set with the address of the pte,
|
||||
// if found.
|
||||
//
|
||||
// Returns:
|
||||
// PTE_SUCCESS or PTE_ERROR
|
||||
//
|
||||
static PTE_STATUS virt_find_pte(PTE_MMAP_OBJ *self, void *addr,
|
||||
PTE **pte) {
|
||||
PTE_CR3 cr3;
|
||||
PML4E *pml4;
|
||||
PML4E *pml4e;
|
||||
PDPTE *pdpt;
|
||||
PDPTE *pdpte;
|
||||
PDE *pd;
|
||||
PDE *pde;
|
||||
PTE *pt;
|
||||
VIRT_ADDR vaddr;
|
||||
PTE_STATUS status = PTE_ERROR;
|
||||
|
||||
vaddr.pointer = addr;
|
||||
|
||||
WinDbgPrint("Resolving PTE for Address:%#016llx", vaddr);
|
||||
|
||||
// Get contents of cr3 register to get to the PML4
|
||||
cr3 = self->get_cr3_();
|
||||
|
||||
WinDbgPrint("Kernel CR3 is %p", cr3);
|
||||
WinDbgPrint("Kernel PML4 is at %p physical",
|
||||
PFN_TO_PAGE(cr3.pml4_p));
|
||||
|
||||
// Resolve the PML4
|
||||
pml4 = (PML4E *)self->phys_to_virt_(PFN_TO_PAGE(cr3.pml4_p));
|
||||
WinDbgPrint("kernel PML4 is at %p virtual", pml4);
|
||||
|
||||
// Resolve the PDPT
|
||||
pml4e = (pml4 + vaddr.pml4_index);
|
||||
|
||||
WinDbgPrint("PML4 entry %d is at %p", vaddr.pml4_index,
|
||||
pml4e);
|
||||
|
||||
if (!pml4e->present) {
|
||||
|
||||
WinDbgPrint("Error, address %#016llx has no valid mapping in PML4:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (PTE *)pml4e);
|
||||
goto error;
|
||||
}
|
||||
WinDbgPrint("PML4[%#010x]: %p)", vaddr.pml4_index, pml4e);
|
||||
|
||||
|
||||
pdpt = (PDPTE *)self->phys_to_virt_(PFN_TO_PAGE(pml4e->pdpt_p));
|
||||
WinDbgPrintDebug("Points to PDPT: %p)", pdpt);
|
||||
|
||||
// Resolve the PDT
|
||||
pdpte = (pdpt + vaddr.pdpt_index);
|
||||
if (!pdpte->present) {
|
||||
WinDbgPrint("Error, address %#016llx has no valid mapping in PDPT:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (PTE *)pdpte);
|
||||
goto error;
|
||||
}
|
||||
if (pdpte->page_size) {
|
||||
WinDbgPrint("Error, address %#016llx belongs to a 1GB page:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (PTE *)pdpte);
|
||||
goto error;
|
||||
}
|
||||
WinDbgPrint("PDPT[%#010x]: %p)", vaddr.pdpt_index, pdpte);
|
||||
pd = (PDE *)self->phys_to_virt_(PFN_TO_PAGE(pdpte->pd_p));
|
||||
WinDbgPrint("Points to PD: %p)", pd);
|
||||
|
||||
// Resolve the PT
|
||||
pde = (pd + vaddr.pd_index);
|
||||
if (!pde->present) {
|
||||
WinDbgPrint("Error, address %#016llx has no valid mapping in PD:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (PTE *)pde);
|
||||
goto error;
|
||||
}
|
||||
if (pde->page_size) {
|
||||
WinDbgPrint("Error, address %#016llx belongs to a 2MB page:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (PTE *)pde);
|
||||
goto error;
|
||||
}
|
||||
|
||||
WinDbgPrint("PD [%#010x]: %p)", vaddr.pd_index, pde);
|
||||
pt = (PTE *)self->phys_to_virt_(PFN_TO_PAGE(pde->pt_p));
|
||||
WinDbgPrint("Points to PT: %p)", pt);
|
||||
|
||||
// Get the PTE and Page Frame
|
||||
*pte = (pt + vaddr.pt_index);
|
||||
if (! (*pte)->present) {
|
||||
WinDbgPrint("Error, address %#016llx has no valid mapping in PT:",
|
||||
vaddr.value);
|
||||
self->print_pte_(self, PTE_ERR, (*pte));
|
||||
goto error;
|
||||
}
|
||||
WinDbgPrint("PT [%#010x]: %p)", vaddr.pt_index, *pte);
|
||||
|
||||
status = PTE_SUCCESS;
|
||||
error:
|
||||
return status;
|
||||
}
|
||||
|
||||
// Initializer for objects of this class. Takes care of the non-abstract parts
|
||||
// of the object.
|
||||
//
|
||||
// Args:
|
||||
// self: Pointer to the allocated memory for this object.
|
||||
//
|
||||
void pte_mmap_init(PTE_MMAP_OBJ *self) {
|
||||
// store this pointer
|
||||
self->self = self;
|
||||
// store non-abstract functions in vtable
|
||||
self->remap_page = pte_remap_rogue_page;
|
||||
self->find_pte_ = virt_find_pte;
|
||||
self->print_pte_ = print_pte_contents;
|
||||
// Initialize attributes
|
||||
self->loglevel = PTE_BUILD_LOGLEVEL;
|
||||
}
|
||||
|
||||
// Call this before freeing the object or the rogue_page.
|
||||
// Will reset the page table entry for the rogue page.
|
||||
//
|
||||
// Args:
|
||||
// self: Pointer to the allocated memory for this object.
|
||||
//
|
||||
void pte_mmap_cleanup(PTE_MMAP_OBJ *self) {
|
||||
WinDbgPrintDebug("Restoring pte to original mapping (%#016llx)",
|
||||
self->original_addr);
|
||||
self->remap_page(self, self->original_addr);
|
||||
self->free_rogue_page_(self->rogue_page.pointer);
|
||||
}
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
// Directly manipulates the page tables to map physical memory into the kernel.
|
||||
// Notice: This is only an abstract base class and cannot be used standalone.
|
||||
// Use the actual implementation for your operating system (eg. pte_mmap_linux).
|
||||
//
|
||||
// Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
// Copyright 2014 - 2017 Google Inc.
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
// Author: Johannes Stüttgen (johannes.stuettgen@gmail.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _REKALL_DRIVER_PTE_MMAP_H_
|
||||
#define _REKALL_DRIVER_PTE_MMAP_H_
|
||||
|
||||
#define PAGE_MASK (~(PAGE_SIZE-1))
|
||||
|
||||
#define PFN_TO_PAGE(pfn) (pfn << PAGE_SHIFT)
|
||||
#define PAGE_TO_PFN(pfn) (pfn >> PAGE_SHIFT)
|
||||
|
||||
typedef unsigned __int64 pte_uint64;
|
||||
|
||||
#pragma warning(disable:4214)
|
||||
#pragma warning(disable:4201) /*nonstandard extension used: nameless struct/union */
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef union CR3_ {
|
||||
pte_uint64 value;
|
||||
struct {
|
||||
pte_uint64 ignored_1 : 3;
|
||||
pte_uint64 write_through : 1;
|
||||
pte_uint64 cache_disable : 1;
|
||||
pte_uint64 ignored_2 : 7;
|
||||
pte_uint64 pml4_p :40;
|
||||
pte_uint64 reserved :12;
|
||||
};
|
||||
} PTE_CR3;
|
||||
|
||||
typedef union VIRT_ADDR_ {
|
||||
pte_uint64 value;
|
||||
void *pointer;
|
||||
struct {
|
||||
pte_uint64 offset : 12;
|
||||
pte_uint64 pt_index : 9;
|
||||
pte_uint64 pd_index : 9;
|
||||
pte_uint64 pdpt_index : 9;
|
||||
pte_uint64 pml4_index : 9;
|
||||
pte_uint64 reserved : 16;
|
||||
};
|
||||
} VIRT_ADDR;
|
||||
|
||||
typedef pte_uint64 PHYS_ADDR;
|
||||
|
||||
typedef union PML4E_ {
|
||||
pte_uint64 value;
|
||||
struct {
|
||||
pte_uint64 present : 1;
|
||||
pte_uint64 rw : 1;
|
||||
pte_uint64 user : 1;
|
||||
pte_uint64 write_through : 1;
|
||||
pte_uint64 cache_disable : 1;
|
||||
pte_uint64 accessed : 1;
|
||||
pte_uint64 ignored_1 : 1;
|
||||
pte_uint64 reserved_1 : 1;
|
||||
pte_uint64 ignored_2 : 4;
|
||||
pte_uint64 pdpt_p :40;
|
||||
pte_uint64 ignored_3 :11;
|
||||
pte_uint64 xd : 1;
|
||||
};
|
||||
} PML4E;
|
||||
|
||||
typedef union PDPTE_ {
|
||||
pte_uint64 value;
|
||||
struct {
|
||||
pte_uint64 present : 1;
|
||||
pte_uint64 rw : 1;
|
||||
pte_uint64 user : 1;
|
||||
pte_uint64 write_through : 1;
|
||||
pte_uint64 cache_disable : 1;
|
||||
pte_uint64 accessed : 1;
|
||||
pte_uint64 dirty : 1;
|
||||
pte_uint64 page_size : 1;
|
||||
pte_uint64 ignored_2 : 4;
|
||||
pte_uint64 pd_p :40;
|
||||
pte_uint64 ignored_3 :11;
|
||||
pte_uint64 xd : 1;
|
||||
};
|
||||
} PDPTE;
|
||||
|
||||
typedef union PDE_ {
|
||||
pte_uint64 value;
|
||||
struct {
|
||||
pte_uint64 present : 1;
|
||||
pte_uint64 rw : 1;
|
||||
pte_uint64 user : 1;
|
||||
pte_uint64 write_through : 1;
|
||||
pte_uint64 cache_disable : 1;
|
||||
pte_uint64 accessed : 1;
|
||||
pte_uint64 dirty : 1;
|
||||
pte_uint64 page_size : 1;
|
||||
pte_uint64 ignored_2 : 4;
|
||||
pte_uint64 pt_p :40;
|
||||
pte_uint64 ignored_3 :11;
|
||||
pte_uint64 xd : 1;
|
||||
};
|
||||
} PDE;
|
||||
|
||||
typedef union PTE_ {
|
||||
pte_uint64 value;
|
||||
VIRT_ADDR vaddr;
|
||||
struct {
|
||||
pte_uint64 present : 1;
|
||||
pte_uint64 rw : 1;
|
||||
pte_uint64 user : 1;
|
||||
pte_uint64 write_through : 1;
|
||||
pte_uint64 cache_disable : 1;
|
||||
pte_uint64 accessed : 1;
|
||||
pte_uint64 dirty : 1;
|
||||
pte_uint64 pat : 1;
|
||||
pte_uint64 global : 1;
|
||||
pte_uint64 ignored_1 : 3;
|
||||
pte_uint64 page_frame :40;
|
||||
pte_uint64 ignored_3 :11;
|
||||
pte_uint64 xd : 1;
|
||||
};
|
||||
} PTE;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Loglevels to exclude debug messages from production builds.
|
||||
typedef enum PTE_LOGLEVEL_ {
|
||||
PTE_ERR = 0,
|
||||
PTE_LOG,
|
||||
PTE_DEBUG
|
||||
} PTE_LOGLEVEL;
|
||||
|
||||
// The default loglevel for this build
|
||||
#define PTE_BUILD_LOGLEVEL PTE_LOG
|
||||
|
||||
// Operating system independent error checking.
|
||||
typedef enum PTE_STATUS_ {
|
||||
PTE_SUCCESS = 0,
|
||||
PTE_ERROR,
|
||||
PTE_ERROR_HUGE_PAGE,
|
||||
PTE_ERROR_RO_PTE
|
||||
} PTE_STATUS;
|
||||
|
||||
// Functions and data for directly manipulating the page tables.
|
||||
// Create an object of this type by mallocing some memory and then calling
|
||||
// pte_mmap_init() on it.
|
||||
typedef struct PTE_MMAP_OBJ_ {
|
||||
// this pointer.
|
||||
struct PTE_MMAP_OBJ_ *self;
|
||||
// Public
|
||||
PTE_STATUS (*remap_page)(struct PTE_MMAP_OBJ_ *self, PHYS_ADDR target);
|
||||
// Private
|
||||
void *(*get_rogue_page_)(void);
|
||||
void (*free_rogue_page_)(void *page);
|
||||
void *(*phys_to_virt_)(PHYS_ADDR addr);
|
||||
PTE_STATUS (*find_pte_)(struct PTE_MMAP_OBJ_ *self, void *vaddr, PTE **pte);
|
||||
void (*flush_tlbs_page_)(void *page);
|
||||
PTE_CR3 (*get_cr3_)(void);
|
||||
void (*print_pte_)(struct PTE_MMAP_OBJ_ *self, PTE_LOGLEVEL loglevel,
|
||||
PTE *pte);
|
||||
// Internal Attributes
|
||||
VIRT_ADDR rogue_page;
|
||||
PTE *rogue_pte;
|
||||
PHYS_ADDR original_addr;
|
||||
PTE_LOGLEVEL loglevel;
|
||||
} PTE_MMAP_OBJ;
|
||||
|
||||
// Initializer for newly created objects.
|
||||
void pte_mmap_init(PTE_MMAP_OBJ *self);
|
||||
// Call this before freeing obj or the rogue_page.
|
||||
// Will reset the page table entry for the rogue page.
|
||||
void pte_mmap_cleanup(PTE_MMAP_OBJ *self);
|
||||
|
||||
#endif // _REKALL_DRIVER_PTE_MMAP_H_
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
// Subclass of the pte_mmap module. Contains implementations for the Linux
|
||||
// specific part of the module.
|
||||
//
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
// Author: Johannes Stüttgen (johannes.stuettgen@gmail.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "pte_mmap_linux.h"
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
// Get a free, non-paged page of memory.
|
||||
static void *pte_get_rogue_page(void) {
|
||||
return vmalloc(PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Frees a single page allocated with vmalloc().
|
||||
static void pte_free_rogue_page(void *page) {
|
||||
vfree(page);
|
||||
return;
|
||||
}
|
||||
|
||||
// Makes use of the fact that the page tables are always mapped in the direct
|
||||
// Kernel memory map.
|
||||
static void *pte_phys_to_virt(PHYS_ADDR address) {
|
||||
return phys_to_virt(address);
|
||||
}
|
||||
|
||||
// Flushes the tlb entry for a specific page.
|
||||
static void pte_flush_tlb_page(void *addr) {
|
||||
__asm__ __volatile__("invlpg %0;"
|
||||
:
|
||||
:"m"(addr)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
// Flush a specific page from all cpus by sending an ipi.
|
||||
static void pte_flush_all_tlbs_page(void *page) {
|
||||
on_each_cpu(pte_flush_tlb_page, page, 1);
|
||||
}
|
||||
|
||||
/* Get the contents of the CR3 register. */
|
||||
static CR3 pte_get_cr3(void) {
|
||||
CR3 cr3;
|
||||
__asm__ __volatile__("mov %%cr3, %0;": "=r"(cr3.value));
|
||||
return cr3;
|
||||
}
|
||||
|
||||
// Print messages to the kernel msg ring buffer.
|
||||
static void pte_log_print(PTE_MMAP_OBJ *self, PTE_LOGLEVEL loglevel,
|
||||
const char *fmt, ...) {
|
||||
va_list argptr;
|
||||
|
||||
if (self->loglevel < loglevel) {
|
||||
return;
|
||||
}
|
||||
if (loglevel == PTE_ERR) {
|
||||
printk("PTE MMAP Error: ");
|
||||
}
|
||||
|
||||
va_start(argptr, fmt);
|
||||
vprintk(fmt, argptr);
|
||||
va_end(argptr);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
// Initializer that fills an operating system specific vtable,
|
||||
// allocates memory, etc.
|
||||
PTE_MMAP_OBJ *pte_mmap_linux_new(void) {
|
||||
PTE_MMAP_OBJ *self = NULL;
|
||||
|
||||
// Allocate the object
|
||||
self = kmalloc(GFP_KERNEL, sizeof(PTE_MMAP_OBJ));
|
||||
// Let the superconstructor set up the internal stuff
|
||||
pte_mmap_init(self);
|
||||
// Fill the virtual function into the vtable
|
||||
self->get_rogue_page_ = pte_get_rogue_page;
|
||||
self->free_rogue_page_ = pte_free_rogue_page;
|
||||
self->phys_to_virt_ = pte_phys_to_virt;
|
||||
self->flush_tlbs_page_ = pte_flush_all_tlbs_page;
|
||||
self->get_cr3_ = pte_get_cr3;
|
||||
self->log_print_ = pte_log_print;
|
||||
// Initialize attributes that rely on memory allocation
|
||||
self->rogue_page.pointer = self->get_rogue_page_();
|
||||
self->log_print_(self, PTE_DEBUG, "Looking up PTE for rogue page: %p",
|
||||
self->rogue_page);
|
||||
if (self->find_pte_(self, self->rogue_page.pointer, &self->rogue_pte)) {
|
||||
self->log_print_(self, PTE_ERR,
|
||||
"Failed to find the PTE for the rogue page, "
|
||||
"might be inside huge page, aborting...");
|
||||
goto error;
|
||||
}
|
||||
self->log_print_(self, PTE_DEBUG, "Found rogue pte at %p", self->rogue_pte);
|
||||
// Back up the address this pte points to for cleaning up later.
|
||||
self->original_addr = PFN_TO_PAGE(self->rogue_pte->page_frame);
|
||||
|
||||
return self;
|
||||
|
||||
error:
|
||||
pte_mmap_linux_delete(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Will reset the page table entry for the rogue page and free the object.
|
||||
void pte_mmap_linux_delete(PTE_MMAP_OBJ *self) {
|
||||
pte_mmap_cleanup(self);
|
||||
kfree(self);
|
||||
}
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
// Linux specific implementation of the pte_mmap module.
|
||||
//
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
// Author: Johannes Stüttgen (johannes.stuettgen@gmail.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef _VOLATILITY_DRIVER_PTE_MMAP_LINUX_H_
|
||||
#define _VOLATILITY_DRIVER_PTE_MMAP_LINUX_H_
|
||||
|
||||
#ifndef __linux
|
||||
#error "This module is linux specific. Use the pte_mmap subclass for your OS"
|
||||
#endif
|
||||
#ifndef __LP64__
|
||||
#error "This module contains x86-64 code and cannot run on your machine"
|
||||
#endif
|
||||
|
||||
#include "pte_mmap.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
// Initializer that fills an operating system specific vtable,
|
||||
// allocates memory, etc.
|
||||
PTE_MMAP_OBJ *pte_mmap_linux_new(void);
|
||||
|
||||
// Call this before freeing obj or the rogue_page.
|
||||
// Will reset the page table entry for the rogue page.
|
||||
void pte_mmap_linux_delete(PTE_MMAP_OBJ *self);
|
||||
|
||||
#endif // _VOLATILITY_DRIVER_PTE_MMAP_LINUX_H_
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
// Subclass of the pte_mmap module. Contains implementations for the Windows
|
||||
// specific part of the module.
|
||||
//
|
||||
// Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
// Copyright 2014 - 2017 Google Inc.
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Author: Johannes Stüttgen (johannes.stuettgen@gmail.com)
|
||||
// Author: Michael Cohen (scudette@gmail.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "pte_mmap_windows.h"
|
||||
static char rogue_page[PAGE_SIZE * 3] = "";
|
||||
static char *page_aligned_space = NULL;
|
||||
static MDL *rogue_mdl = NULL;
|
||||
|
||||
// Get a free, non-paged page of memory. On windows we can not
|
||||
// allocate from pool because pool storage is controlled by large page
|
||||
// PTEs. So we just use a static page in the driver executable.
|
||||
static void *pte_get_rogue_page(void) {
|
||||
if (page_aligned_space == NULL) {
|
||||
// We would ideally like to just allocate memory from non paged
|
||||
// pool but on Windows 7, non paged pool is allocated from large
|
||||
// pages and we wont be able to use the page in PTE remapping. So
|
||||
// this code creates an MDL from a static buffer within the
|
||||
// driver's data section. We must ensure that this buffer is not
|
||||
// paged out though so we must call MmGetSystemAddressForMdlSafe
|
||||
// to ensure the MDL is locked into memory.
|
||||
|
||||
// page_aligned_space is the first page aligned offset within the
|
||||
// buffer.
|
||||
page_aligned_space = &rogue_page[0];
|
||||
page_aligned_space += PAGE_SIZE - ((__int64)&rogue_page[0]) % PAGE_SIZE;
|
||||
|
||||
// MDL is for a single page.
|
||||
rogue_mdl = IoAllocateMdl(page_aligned_space, PAGE_SIZE,
|
||||
FALSE, FALSE, NULL);
|
||||
if (!rogue_mdl) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
// This locks the physical page into memory.
|
||||
MmProbeAndLockPages(rogue_mdl, KernelMode, IoReadAccess);
|
||||
page_aligned_space = MmMapLockedPagesSpecifyCache(
|
||||
rogue_mdl, KernelMode, MmCached, NULL, 0, NormalPagePriority);
|
||||
return page_aligned_space;
|
||||
|
||||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
NTSTATUS ntStatus = GetExceptionCode();
|
||||
|
||||
WinDbgPrint("Exception while locking rogue_mdl %#08X\n", ntStatus);
|
||||
IoFreeMdl(rogue_mdl);
|
||||
rogue_mdl = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return page_aligned_space;
|
||||
}
|
||||
|
||||
// Frees a single page allocated with vmalloc(). Rogue page is static
|
||||
// we do not free it.
|
||||
static void pte_free_rogue_page(void *page) {
|
||||
if (rogue_mdl) {
|
||||
MmUnlockPages(rogue_mdl);
|
||||
IoFreeMdl(rogue_mdl);
|
||||
rogue_mdl = NULL;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// Makes use of the fact that the page tables are always mapped in the direct
|
||||
// Kernel memory map.
|
||||
static void *pte_phys_to_virt(PHYS_ADDR address) {
|
||||
PHYSICAL_ADDRESS phys_address;
|
||||
|
||||
phys_address.QuadPart = address;
|
||||
//return phys_to_virt(address);
|
||||
// TODO(scudette): Use PFNDB here.
|
||||
return Pmem_KernelExports.MmGetVirtualForPhysical(phys_address);
|
||||
}
|
||||
|
||||
// Flushes the tlb entry for a specific page.
|
||||
static void pte_flush_tlb_page(void *addr) {
|
||||
// Use compiler instrinsic.
|
||||
__invlpg(addr);
|
||||
}
|
||||
|
||||
// Flush a specific page from all cpus by sending an ipi.
|
||||
static void pte_flush_all_tlbs_page(void *page) {
|
||||
// TODO: Make this work.
|
||||
pte_flush_tlb_page(page);
|
||||
}
|
||||
|
||||
/* Get the contents of the CR3 register. */
|
||||
static PTE_CR3 pte_get_cr3(void) {
|
||||
PTE_CR3 result;
|
||||
result.value = __readcr3();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Will reset the page table entry for the rogue page and free the object.
|
||||
void pte_mmap_windows_delete(PTE_MMAP_OBJ *self) {
|
||||
pte_mmap_cleanup(self);
|
||||
ExFreePool(self);
|
||||
}
|
||||
|
||||
// Initializer that fills an operating system specific vtable,
|
||||
// allocates memory, etc.
|
||||
PTE_MMAP_OBJ *pte_mmap_windows_new(void) {
|
||||
PTE_MMAP_OBJ *self = NULL;
|
||||
|
||||
// Allocate the object
|
||||
self = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PTE_MMAP_OBJ),
|
||||
PMEM_POOL_TAG);
|
||||
|
||||
if (!self) return NULL;
|
||||
|
||||
// Let the superconstructor set up the internal stuff
|
||||
pte_mmap_init(self);
|
||||
|
||||
// Fill the virtual function into the vtable
|
||||
self->get_rogue_page_ = pte_get_rogue_page;
|
||||
|
||||
self->free_rogue_page_ = pte_free_rogue_page;
|
||||
self->phys_to_virt_ = pte_phys_to_virt;
|
||||
self->flush_tlbs_page_ = pte_flush_all_tlbs_page;
|
||||
self->get_cr3_ = pte_get_cr3;
|
||||
|
||||
|
||||
// Initialize attributes that rely on memory allocation
|
||||
self->rogue_page.pointer = self->get_rogue_page_();
|
||||
if (self->rogue_page.pointer == NULL) {
|
||||
goto error;
|
||||
}
|
||||
WinDbgPrintDebug("Looking up PTE for rogue page: %p",
|
||||
self->rogue_page);
|
||||
if (self->find_pte_(self, self->rogue_page.pointer, &self->rogue_pte)) {
|
||||
WinDbgPrint("Failed to find the PTE for the rogue page, "
|
||||
"might be inside huge page, aborting...");
|
||||
goto error;
|
||||
}
|
||||
WinDbgPrintDebug("Found rogue pte at %p", self->rogue_pte);
|
||||
|
||||
// Back up the address this pte points to for cleaning up later.
|
||||
self->original_addr = PFN_TO_PAGE(self->rogue_pte->page_frame);
|
||||
|
||||
return self;
|
||||
|
||||
error:
|
||||
pte_mmap_windows_delete(self);
|
||||
return NULL;
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#include "winpmem.h"
|
||||
#include "pte_mmap.h"
|
||||
|
||||
void pte_mmap_windows_delete(PTE_MMAP_OBJ *self);
|
||||
PTE_MMAP_OBJ *pte_mmap_windows_new(void);
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
Copyright 2014-2017 Google Inc.
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "read.h"
|
||||
|
||||
static int EnsureExtensionHandle(PDEVICE_EXTENSION extension) {
|
||||
NTSTATUS NtStatus;
|
||||
UNICODE_STRING PhysicalMemoryPath;
|
||||
OBJECT_ATTRIBUTES MemoryAttributes;
|
||||
|
||||
/* Make sure we have a valid handle now. */
|
||||
if(!extension->MemoryHandle) {
|
||||
RtlInitUnicodeString(&PhysicalMemoryPath, L"\\Device\\PhysicalMemory");
|
||||
|
||||
InitializeObjectAttributes(&MemoryAttributes,
|
||||
&PhysicalMemoryPath,
|
||||
OBJ_KERNEL_HANDLE,
|
||||
(HANDLE) NULL,
|
||||
(PSECURITY_DESCRIPTOR) NULL);
|
||||
|
||||
NtStatus = ZwOpenSection(&extension->MemoryHandle,
|
||||
SECTION_MAP_READ, &MemoryAttributes);
|
||||
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
WinDbgPrint("Failed ZwOpenSection(MemoryHandle) => %08X\n", NtStatus);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static LONG PhysicalMemoryPartialRead(IN PDEVICE_EXTENSION extension,
|
||||
LARGE_INTEGER offset, PCHAR buf,
|
||||
ULONG count) {
|
||||
ULONG page_offset = offset.QuadPart % PAGE_SIZE;
|
||||
ULONG to_read = min(PAGE_SIZE - page_offset, count);
|
||||
PUCHAR mapped_buffer = NULL;
|
||||
SIZE_T ViewSize = PAGE_SIZE;
|
||||
NTSTATUS NtStatus;
|
||||
|
||||
if (EnsureExtensionHandle(extension)) {
|
||||
/* Map page into the Kernel AS */
|
||||
NtStatus = ZwMapViewOfSection(extension->MemoryHandle, (HANDLE) -1,
|
||||
&mapped_buffer, 0L, PAGE_SIZE, &offset,
|
||||
&ViewSize, ViewUnmap, 0, PAGE_READONLY);
|
||||
|
||||
if (NT_SUCCESS(NtStatus)) {
|
||||
RtlCopyMemory(buf, mapped_buffer + page_offset, to_read);
|
||||
ZwUnmapViewOfSection((HANDLE)-1, mapped_buffer);
|
||||
|
||||
} else {
|
||||
WinDbgPrint("Failed to Map page at 0x%llX\n", offset.QuadPart);
|
||||
RtlZeroMemory(buf, to_read);
|
||||
};
|
||||
};
|
||||
|
||||
return to_read;
|
||||
};
|
||||
|
||||
|
||||
// Read a single page using MmMapIoSpace.
|
||||
static LONG MapIOPagePartialRead(IN PDEVICE_EXTENSION extension,
|
||||
LARGE_INTEGER offset, PCHAR buf,
|
||||
ULONG count) {
|
||||
extension;
|
||||
ULONG page_offset = offset.QuadPart % PAGE_SIZE;
|
||||
ULONG to_read = min(PAGE_SIZE - page_offset, count);
|
||||
PUCHAR mapped_buffer = NULL;
|
||||
LARGE_INTEGER ViewBase;
|
||||
|
||||
// Round to page size
|
||||
ViewBase.QuadPart = offset.QuadPart - page_offset;
|
||||
|
||||
// Map exactly one page.
|
||||
mapped_buffer = Pmem_KernelExports.MmMapIoSpace(
|
||||
ViewBase, PAGE_SIZE, MmNonCached);
|
||||
|
||||
if (mapped_buffer) {
|
||||
try {
|
||||
// Be extra careful here to not produce a BSOD.
|
||||
RtlCopyMemory(buf, mapped_buffer+page_offset, to_read);
|
||||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
WinDbgPrintDebug("Unable to read %d bytes from %p for %p\n", to_read, source, offset.QuadPart - page_offset);
|
||||
Pmem_KernelExports.MmUnmapIoSpace(mapped_buffer, PAGE_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Pmem_KernelExports.MmUnmapIoSpace(mapped_buffer, PAGE_SIZE);
|
||||
return to_read;
|
||||
} else {
|
||||
// Failed to map page, return an error.
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Read a single page using direct PTE mapping.
|
||||
static LONG PTEMmapPartialRead(IN PDEVICE_EXTENSION extension,
|
||||
LARGE_INTEGER offset, PCHAR buf,
|
||||
ULONG count) {
|
||||
ULONG page_offset = offset.QuadPart % PAGE_SIZE;
|
||||
ULONG to_read = min(PAGE_SIZE - page_offset, count);
|
||||
LARGE_INTEGER ViewBase;
|
||||
LONG result = -1;
|
||||
|
||||
// Round to page size
|
||||
ViewBase.QuadPart = offset.QuadPart - page_offset;
|
||||
|
||||
// Map exactly one page.
|
||||
if(extension->pte_mmapper &&
|
||||
extension->pte_mmapper->remap_page(extension->pte_mmapper,
|
||||
offset.QuadPart - page_offset) ==
|
||||
PTE_SUCCESS) {
|
||||
char *source = (char *)(extension->pte_mmapper->rogue_page.value + page_offset);
|
||||
try {
|
||||
// Be extra careful here to not produce a BSOD. We would rather
|
||||
// return an error than a BSOD.
|
||||
RtlCopyMemory(buf, source, to_read);
|
||||
result = to_read;
|
||||
|
||||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
WinDbgPrintDebug("Unable to read %d bytes from %p for %p\n", to_read, source, offset.QuadPart - page_offset);
|
||||
}
|
||||
}
|
||||
// Failed to map page, or an exception occurred - error out.
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
static NTSTATUS DeviceRead(IN PDEVICE_EXTENSION extension, LARGE_INTEGER offset,
|
||||
PCHAR buf, ULONG count, OUT ULONG *total_read,
|
||||
LONG (*handler)(IN PDEVICE_EXTENSION, LARGE_INTEGER,
|
||||
PCHAR, ULONG)) {
|
||||
int result = 0;
|
||||
|
||||
*total_read = 0;
|
||||
|
||||
ExAcquireFastMutex(&extension->mu);
|
||||
while(*total_read < count) {
|
||||
result = handler(extension, offset, buf, count - *total_read);
|
||||
|
||||
/* Error Occured. */
|
||||
if(result < 0)
|
||||
goto error;
|
||||
|
||||
/* No data available. */
|
||||
if(result==0) {
|
||||
break;
|
||||
};
|
||||
|
||||
offset.QuadPart += result;
|
||||
buf += result;
|
||||
*total_read += result;
|
||||
};
|
||||
|
||||
ExReleaseFastMutex(&extension->mu);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
error:
|
||||
ExReleaseFastMutex(&extension->mu);
|
||||
return STATUS_IO_DEVICE_ERROR;
|
||||
};
|
||||
|
||||
|
||||
NTSTATUS PmemRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
|
||||
PVOID Buf; //Buffer provided by user space.
|
||||
ULONG BufLen; //Buffer length for user provided buffer.
|
||||
LARGE_INTEGER BufOffset; // The file offset requested from userspace.
|
||||
PIO_STACK_LOCATION pIoStackIrp;
|
||||
PDEVICE_EXTENSION extension;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
ULONG total_read = 0;
|
||||
|
||||
// We must be running in PASSIVE_LEVEL or we bluescreen here. We
|
||||
// theoretically should always be running at PASSIVE_LEVEL here, but
|
||||
// in case we ended up here at the wrong IRQL its better to bail
|
||||
// than to bluescreen.
|
||||
if(KeGetCurrentIrql() != PASSIVE_LEVEL) {
|
||||
status = STATUS_ABANDONED;
|
||||
goto exit;
|
||||
};
|
||||
|
||||
extension = DeviceObject->DeviceExtension;
|
||||
|
||||
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
|
||||
BufLen = pIoStackIrp->Parameters.Read.Length;
|
||||
BufOffset = pIoStackIrp->Parameters.Read.ByteOffset;
|
||||
Buf = (PCHAR)(Irp->AssociatedIrp.SystemBuffer);
|
||||
|
||||
switch(extension->mode) {
|
||||
|
||||
// Read using the physical memory handle.
|
||||
case ACQUISITION_MODE_PHYSICAL_MEMORY:
|
||||
status = DeviceRead(extension, BufOffset, Buf, BufLen, &total_read,
|
||||
PhysicalMemoryPartialRead);
|
||||
break;
|
||||
|
||||
case ACQUISITION_MODE_MAP_IO_SPACE:
|
||||
status = DeviceRead(extension, BufOffset, Buf, BufLen, &total_read,
|
||||
MapIOPagePartialRead);
|
||||
break;
|
||||
|
||||
case ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE:
|
||||
case ACQUISITION_MODE_PTE_MMAP:
|
||||
if (extension->pte_mmapper == NULL) {
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto exit;
|
||||
};
|
||||
status = DeviceRead(extension, BufOffset, Buf, BufLen, &total_read,
|
||||
PTEMmapPartialRead);
|
||||
break;
|
||||
|
||||
default:
|
||||
WinDbgPrint("Acquisition mode %u not supported.\n", extension->mode);
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
BufLen = 0;
|
||||
}
|
||||
|
||||
exit:
|
||||
Irp->IoStatus.Status = status;
|
||||
Irp->IoStatus.Information = total_read;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#if PMEM_WRITE_ENABLED == 1
|
||||
|
||||
NTSTATUS PmemWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
|
||||
PVOID Buf; //Buffer provided by user space.
|
||||
ULONG BufLen; //Buffer length for user provided buffer.
|
||||
LARGE_INTEGER BufOffset; // The file offset requested from userspace.
|
||||
PIO_STACK_LOCATION pIoStackIrp;
|
||||
PDEVICE_EXTENSION extension;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
SIZE_T ViewSize = PAGE_SIZE;
|
||||
PUCHAR mapped_buffer = NULL;
|
||||
ULONG page_offset = 0;
|
||||
LARGE_INTEGER offset;
|
||||
|
||||
// We must be running in PASSIVE_LEVEL or we bluescreen here. We
|
||||
// theoretically should always be running at PASSIVE_LEVEL here, but
|
||||
// in case we ended up here at the wrong IRQL its better to bail
|
||||
// than to bluescreen.
|
||||
if(KeGetCurrentIrql() != PASSIVE_LEVEL) {
|
||||
status = STATUS_ABANDONED;
|
||||
goto exit;
|
||||
};
|
||||
|
||||
extension = DeviceObject->DeviceExtension;
|
||||
|
||||
if (!extension->WriteEnabled) {
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
WinDbgPrint("Write mode not enabled.\n");
|
||||
goto exit;
|
||||
};
|
||||
|
||||
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
|
||||
BufLen = pIoStackIrp->Parameters.Write.Length;
|
||||
|
||||
// Where to write exactly.
|
||||
BufOffset = pIoStackIrp->Parameters.Write.ByteOffset;
|
||||
Buf = (PCHAR)(Irp->AssociatedIrp.SystemBuffer);
|
||||
|
||||
page_offset = BufOffset.QuadPart % PAGE_SIZE;
|
||||
offset.QuadPart = BufOffset.QuadPart - page_offset; // Page aligned.
|
||||
|
||||
// How much we need to write rounded up to the next page.
|
||||
ViewSize = BufLen + page_offset;
|
||||
ViewSize += PAGE_SIZE - (ViewSize % PAGE_SIZE);
|
||||
|
||||
/* Map memory into the Kernel AS */
|
||||
if (EnsureExtensionHandle(extension)) {
|
||||
status = ZwMapViewOfSection(extension->MemoryHandle, (HANDLE) -1,
|
||||
&mapped_buffer, 0L, PAGE_SIZE, &offset,
|
||||
&ViewSize, ViewUnmap, 0, PAGE_READWRITE);
|
||||
|
||||
if (NT_SUCCESS(status)) {
|
||||
RtlCopyMemory(mapped_buffer + page_offset, Buf, BufLen);
|
||||
ZwUnmapViewOfSection((HANDLE)-1, mapped_buffer);
|
||||
} else {
|
||||
WinDbgPrint("Failed to map view %lld %ld (%ld).\n", offset, ViewSize,
|
||||
status);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
Irp->IoStatus.Status = status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
This file implement methods for reading through the pmem device.
|
||||
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __READ_H
|
||||
#define __READ_H
|
||||
|
||||
#include "winpmem.h"
|
||||
|
||||
/* Read a page through the PhysicalMemory device. */
|
||||
LONG PhysicalMemoryPartialRead(IN PDEVICE_EXTENSION extension,
|
||||
LARGE_INTEGER offset, PCHAR buf, ULONG count);
|
||||
|
||||
/* Read a large buffer by concatenating lots of small reads. */
|
||||
NTSTATUS DeviceRead(IN PDEVICE_EXTENSION extension, LARGE_INTEGER offset,
|
||||
PCHAR buf, ULONG count, OUT ULONG *total_read,
|
||||
LONG (*handler)(IN PDEVICE_EXTENSION,
|
||||
LARGE_INTEGER, PCHAR, ULONG)
|
||||
);
|
||||
|
||||
|
||||
/* Actual read handler. */
|
||||
__drv_dispatchType(IRP_MJ_READ)
|
||||
DRIVER_DISPATCH PmemRead;
|
||||
NTSTATUS PmemRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
|
||||
|
||||
__drv_dispatchType(IRP_MJ_WRITE)
|
||||
DRIVER_DISPATCH PmemWrite;
|
||||
NTSTATUS PmemWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
Copyright 2014-2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "winpmem.h"
|
||||
#include "pte_mmap_windows.h"
|
||||
#include "api.h"
|
||||
#include "read.h"
|
||||
#include "kd.h"
|
||||
//#include "pci.h"
|
||||
|
||||
|
||||
// The following globals are populated in the kernel context from DriverEntry
|
||||
// and reported to the user context.
|
||||
|
||||
// The kernel CR3
|
||||
LARGE_INTEGER CR3;
|
||||
|
||||
DRIVER_UNLOAD IoUnload;
|
||||
VOID IoUnload(IN PDRIVER_OBJECT DriverObject) {
|
||||
UNICODE_STRING DeviceLinkUnicodeString;
|
||||
PDEVICE_OBJECT pDeviceObject = NULL;
|
||||
PDEVICE_EXTENSION ext = NULL;
|
||||
|
||||
if (DriverObject == NULL)
|
||||
return;
|
||||
|
||||
pDeviceObject = DriverObject->DeviceObject;
|
||||
ext=(PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
|
||||
|
||||
RtlInitUnicodeString (&DeviceLinkUnicodeString, L"\\??\\" PMEM_DEVICE_NAME);
|
||||
IoDeleteSymbolicLink (&DeviceLinkUnicodeString);
|
||||
|
||||
if(ext->pte_mmapper)
|
||||
pte_mmap_windows_delete(ext->pte_mmapper);
|
||||
|
||||
IoDeleteDevice(pDeviceObject);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Gets information about the memory layout.
|
||||
|
||||
- The Physical memory address ranges.
|
||||
*/
|
||||
NTSTATUS AddMemoryRanges(struct PmemMemoryInfo *info, int len) {
|
||||
PPHYSICAL_MEMORY_RANGE MmPhysicalMemoryRange;
|
||||
int number_of_runs = 0;
|
||||
int required_length;
|
||||
|
||||
// Enumerate address ranges.
|
||||
MmPhysicalMemoryRange = Pmem_KernelExports.MmGetPhysicalMemoryRanges();
|
||||
|
||||
if (MmPhysicalMemoryRange == NULL) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
};
|
||||
|
||||
/** Find out how many ranges there are. */
|
||||
for(number_of_runs=0;
|
||||
(MmPhysicalMemoryRange[number_of_runs].BaseAddress.QuadPart) ||
|
||||
(MmPhysicalMemoryRange[number_of_runs].NumberOfBytes.QuadPart);
|
||||
number_of_runs++);
|
||||
|
||||
required_length = (sizeof(struct PmemMemoryInfo) +
|
||||
number_of_runs * sizeof(PHYSICAL_MEMORY_RANGE));
|
||||
|
||||
/* Do we have enough space? */
|
||||
if(len < required_length) {
|
||||
return STATUS_INFO_LENGTH_MISMATCH;
|
||||
};
|
||||
|
||||
RtlZeroMemory(info, required_length);
|
||||
|
||||
info->NumberOfRuns.QuadPart = number_of_runs;
|
||||
RtlCopyMemory(&info->Run[0], MmPhysicalMemoryRange,
|
||||
number_of_runs * sizeof(PHYSICAL_MEMORY_RANGE));
|
||||
|
||||
ExFreePool(MmPhysicalMemoryRange);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
__drv_dispatchType(IRP_MJ_CREATE)
|
||||
DRIVER_DISPATCH wddCreate;
|
||||
static NTSTATUS wddCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
|
||||
// PDEVICE_EXTENSION ext=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||||
//PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (!DeviceObject || !Irp) {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
|
||||
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
|
||||
__drv_dispatchType(IRP_MJ_CLOSE)
|
||||
DRIVER_DISPATCH wddClose;
|
||||
static NTSTATUS wddClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
|
||||
PDEVICE_EXTENSION ext=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||||
// PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (!DeviceObject || !Irp) {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if(ext->MemoryHandle != 0) {
|
||||
ZwClose(ext->MemoryHandle);
|
||||
ext->MemoryHandle = 0;
|
||||
};
|
||||
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
|
||||
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
|
||||
DRIVER_DISPATCH wddDispatchDeviceControl;
|
||||
NTSTATUS wddDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject,
|
||||
IN PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IrpStack;
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
ULONG IoControlCode;
|
||||
PVOID IoBuffer;
|
||||
PDEVICE_EXTENSION ext;
|
||||
ULONG InputLen, OutputLen;
|
||||
|
||||
// We must be running in PASSIVE_LEVEL or we bluescreen here. We
|
||||
// theoretically should always be running at PASSIVE_LEVEL here, but
|
||||
// in case we ended up here at the wrong IRQL its better to bail
|
||||
// than to bluescreen.
|
||||
if(KeGetCurrentIrql() != PASSIVE_LEVEL) {
|
||||
status = STATUS_ABANDONED;
|
||||
goto exit;
|
||||
};
|
||||
|
||||
ext = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||||
|
||||
Irp->IoStatus.Information = 0;
|
||||
|
||||
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
IoBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||||
OutputLen = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
InputLen = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
|
||||
IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
|
||||
|
||||
switch ((IoControlCode & 0xFFFFFF0F)) {
|
||||
|
||||
// The old deprecated ioctrl interface for backwards
|
||||
// compatibility. Do not use for new code.
|
||||
case IOCTL_GET_INFO_DEPRECATED: {
|
||||
char *buffer = ExAllocatePoolWithTag(NonPagedPoolNx, 0x1000, PMEM_POOL_TAG);
|
||||
|
||||
if (buffer) {
|
||||
struct DeprecatedPmemMemoryInfo *info = (void *)IoBuffer;
|
||||
struct PmemMemoryInfo *memory_info = (void *)buffer;
|
||||
|
||||
status = AddMemoryRanges(memory_info, 0x1000);
|
||||
if (status != STATUS_SUCCESS) {
|
||||
ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
|
||||
goto exit;
|
||||
};
|
||||
|
||||
info->CR3.QuadPart = CR3.QuadPart;
|
||||
info->NumberOfRuns = (unsigned long)memory_info->NumberOfRuns.QuadPart;
|
||||
|
||||
// Is there enough space in the user supplied buffer?
|
||||
if (OutputLen < (info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE) +
|
||||
sizeof(struct DeprecatedPmemMemoryInfo))) {
|
||||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||||
ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
|
||||
goto exit;
|
||||
};
|
||||
|
||||
// Copy the runs over.
|
||||
RtlCopyMemory(&info->Run[0], &memory_info->Run[0],
|
||||
info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE));
|
||||
|
||||
// This is the total length of the response.
|
||||
Irp->IoStatus.Information =
|
||||
sizeof(struct DeprecatedPmemMemoryInfo) +
|
||||
info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE);
|
||||
|
||||
WinDbgPrint("Returning info on the system memory using deprecated "
|
||||
"interface!\n");
|
||||
|
||||
ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
|
||||
status = STATUS_SUCCESS;
|
||||
};
|
||||
}; break;
|
||||
|
||||
// Return information about memory layout etc through this ioctrl.
|
||||
case IOCTL_GET_INFO: {
|
||||
struct PmemMemoryInfo *info = (void *)IoBuffer;
|
||||
|
||||
if (OutputLen < sizeof(struct PmemMemoryInfo)) {
|
||||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||||
goto exit;
|
||||
};
|
||||
|
||||
// Ensure we clear the buffer first.
|
||||
RtlZeroMemory(IoBuffer, sizeof(struct PmemMemoryInfo));
|
||||
|
||||
#if 0
|
||||
// Get the memory ranges according to the mode.
|
||||
if (ext->mode == ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE) {
|
||||
status = PCI_AddMemoryRanges(info, OutputLen);
|
||||
} else {
|
||||
status = AddMemoryRanges(info, OutputLen);
|
||||
}
|
||||
#else
|
||||
status = AddMemoryRanges(info, OutputLen);
|
||||
#endif
|
||||
if (status != STATUS_SUCCESS) {
|
||||
goto exit;
|
||||
};
|
||||
|
||||
WinDbgPrint("Returning info on the system memory.\n");
|
||||
|
||||
// We are currently running in user context which means __readcr3() will
|
||||
// return the process CR3. So we return the kernel CR3 we found
|
||||
// when loading.
|
||||
info->CR3.QuadPart = CR3.QuadPart;
|
||||
|
||||
info->NtBuildNumber.QuadPart = *NtBuildNumber;
|
||||
info->NtBuildNumberAddr.QuadPart = (uintptr_t)NtBuildNumber;
|
||||
info->KernBase.QuadPart = (uintptr_t)KernelGetModuleBaseByPtr(NtBuildNumber);
|
||||
|
||||
// Fill in KPCR.
|
||||
GetKPCR(info);
|
||||
|
||||
// This is the length of the response.
|
||||
Irp->IoStatus.Information =
|
||||
sizeof(struct PmemMemoryInfo) +
|
||||
info->NumberOfRuns.LowPart * sizeof(PHYSICAL_MEMORY_RANGE);
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
}; break;
|
||||
|
||||
case IOCTL_SET_MODE: {
|
||||
WinDbgPrint("Setting Acquisition mode.\n");
|
||||
|
||||
/* First u32 is the acquisition mode. */
|
||||
if (InputLen >= sizeof(u32)) {
|
||||
enum PMEM_ACQUISITION_MODE mode = *(u32 *)IoBuffer;
|
||||
|
||||
switch(mode) {
|
||||
case ACQUISITION_MODE_PHYSICAL_MEMORY:
|
||||
// These are all the requirements for this method.
|
||||
if (!Pmem_KernelExports.MmGetPhysicalMemoryRanges) {
|
||||
WinDbgPrint("Kernel APIs required for this method are not "
|
||||
"available.");
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
} else {
|
||||
WinDbgPrint("Using physical memory device for acquisition.\n");
|
||||
status = STATUS_SUCCESS;
|
||||
ext->mode = mode;
|
||||
};
|
||||
break;
|
||||
|
||||
case ACQUISITION_MODE_MAP_IO_SPACE:
|
||||
if (!Pmem_KernelExports.MmGetPhysicalMemoryRanges ||
|
||||
!Pmem_KernelExports.MmMapIoSpace ||
|
||||
!Pmem_KernelExports.MmUnmapIoSpace) {
|
||||
WinDbgPrint("Kernel APIs required for this method are not "
|
||||
"available.");
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
} else {
|
||||
WinDbgPrint("Using MmMapIoSpace for acquisition.\n");
|
||||
status = STATUS_SUCCESS;
|
||||
ext->mode = mode;
|
||||
};
|
||||
break;
|
||||
|
||||
case ACQUISITION_MODE_PTE_MMAP:
|
||||
if (!Pmem_KernelExports.MmGetVirtualForPhysical ||
|
||||
!Pmem_KernelExports.MmGetPhysicalMemoryRanges ||
|
||||
!ext->pte_mmapper) {
|
||||
WinDbgPrint("Kernel APIs required for this method are not "
|
||||
"available.");
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
} else {
|
||||
WinDbgPrint("Using PTE Remapping for acquisition.\n");
|
||||
status = STATUS_SUCCESS;
|
||||
ext->mode = mode;
|
||||
};
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE:
|
||||
if (!Pmem_KernelExports.MmGetVirtualForPhysical ||
|
||||
!ext->pte_mmapper) {
|
||||
WinDbgPrint("Kernel APIs required for this method are not "
|
||||
"available.");
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
} else {
|
||||
WinDbgPrint("Using PTE Remapping with PCI probe for acquisition.\n");
|
||||
status = STATUS_SUCCESS;
|
||||
ext->mode = mode;
|
||||
};
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
WinDbgPrint("Invalid acquisition mode %d.\n", mode);
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
};
|
||||
|
||||
} else {
|
||||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||||
};
|
||||
}; break;
|
||||
|
||||
#if PMEM_WRITE_ENABLED == 1
|
||||
case IOCTL_WRITE_ENABLE: {
|
||||
ext->WriteEnabled = !ext->WriteEnabled;
|
||||
WinDbgPrint("Write mode is %d. Do you know what you are doing?\n",
|
||||
ext->WriteEnabled);
|
||||
status = STATUS_SUCCESS;
|
||||
}; break;
|
||||
#endif
|
||||
|
||||
default: {
|
||||
WinDbgPrint("Invalid IOCTRL %d\n", IoControlCode);
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
};
|
||||
}
|
||||
|
||||
exit:
|
||||
Irp->IoStatus.Status = status;
|
||||
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
||||
return status;
|
||||
}
|
||||
|
||||
DRIVER_INITIALIZE DriverEntry;
|
||||
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,
|
||||
IN PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
UNICODE_STRING DeviceName, DeviceLink;
|
||||
NTSTATUS NtStatus;
|
||||
PDEVICE_OBJECT DeviceObject = NULL;
|
||||
PDEVICE_EXTENSION extension;
|
||||
|
||||
RegistryPath; // Unused
|
||||
|
||||
WinDbgPrint("WinPMEM - " PMEM_VERSION " - Physical memory acquisition\n");
|
||||
|
||||
#if PMEM_WRITE_ENABLED == 1
|
||||
WinDbgPrint("WinPMEM write support available!");
|
||||
#endif
|
||||
|
||||
WinDbgPrint("Copyright (c) 2018, Velocidex Innovations <mike@velocidex.com>\n");
|
||||
WinDbgPrint("Copyright (c) 2017, Google Inc.\n");
|
||||
|
||||
// Initialize import tables:
|
||||
if(PmemGetProcAddresses() != STATUS_SUCCESS) {
|
||||
WinDbgPrint("Failed to initialize import table. Aborting.\n");
|
||||
goto error;
|
||||
};
|
||||
|
||||
RtlInitUnicodeString (&DeviceName, L"\\Device\\" PMEM_DEVICE_NAME);
|
||||
|
||||
// We create our secure device.
|
||||
// http://msdn.microsoft.com/en-us/library/aa490540.aspx
|
||||
NtStatus = IoCreateDeviceSecure(DriverObject,
|
||||
sizeof(DEVICE_EXTENSION),
|
||||
&DeviceName,
|
||||
FILE_DEVICE_UNKNOWN,
|
||||
FILE_DEVICE_SECURE_OPEN,
|
||||
FALSE,
|
||||
&SDDL_DEVOBJ_SYS_ALL_ADM_ALL,
|
||||
&GUID_DEVCLASS_PMEM_DUMPER,
|
||||
&DeviceObject);
|
||||
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
WinDbgPrint ("IoCreateDevice failed. => %08X\n", NtStatus);
|
||||
return NtStatus;
|
||||
}
|
||||
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = wddCreate;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = wddClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = wddDispatchDeviceControl;
|
||||
DriverObject->MajorFunction[IRP_MJ_READ] = PmemRead;
|
||||
|
||||
#if PMEM_WRITE_ENABLED == 1
|
||||
{
|
||||
// Make sure that the drivers with write support are clearly marked as such.
|
||||
static char TAG[] = "Write Supported";
|
||||
}
|
||||
|
||||
// Support writing.
|
||||
DriverObject->MajorFunction[IRP_MJ_WRITE] = PmemWrite;
|
||||
#endif
|
||||
DriverObject->DriverUnload = IoUnload;
|
||||
|
||||
// Use buffered IO - a bit slower but simpler to implement, and more
|
||||
// efficient for small reads.
|
||||
SetFlag(DeviceObject->Flags, DO_BUFFERED_IO );
|
||||
ClearFlag(DeviceObject->Flags, DO_DIRECT_IO );
|
||||
ClearFlag(DeviceObject->Flags, DO_DEVICE_INITIALIZING);
|
||||
|
||||
RtlInitUnicodeString (&DeviceLink, L"\\??\\" PMEM_DEVICE_NAME);
|
||||
|
||||
NtStatus = IoCreateSymbolicLink (&DeviceLink, &DeviceName);
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
IoDeleteSymbolicLink(&DeviceLink);
|
||||
NtStatus = IoCreateSymbolicLink(&DeviceLink, &DeviceName);
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
WinDbgPrint("IoCreateSymbolicLink failed. => %08X\n", NtStatus);
|
||||
IoDeleteDevice(DeviceObject);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate globals in kernel context.
|
||||
CR3.QuadPart = __readcr3();
|
||||
|
||||
// Initialize the device extension with safe defaults.
|
||||
extension = DeviceObject->DeviceExtension;
|
||||
extension->mode = ACQUISITION_MODE_PHYSICAL_MEMORY;
|
||||
extension->MemoryHandle = 0;
|
||||
|
||||
#if _WIN64
|
||||
// Disable pte mapping for 32 bit systems.
|
||||
extension->pte_mmapper = pte_mmap_windows_new();
|
||||
if (extension->pte_mmapper == NULL) {
|
||||
IoDeleteDevice(DeviceObject);
|
||||
goto error;
|
||||
}
|
||||
extension->pte_mmapper->loglevel = PTE_ERR;
|
||||
extension->mode = ACQUISITION_MODE_PTE_MMAP;
|
||||
#else
|
||||
extension->pte_mmapper = NULL;
|
||||
#endif
|
||||
|
||||
ExInitializeFastMutex(&extension->mu);
|
||||
|
||||
WinDbgPrint("Driver intialization completed.");
|
||||
return NtStatus;
|
||||
|
||||
error:
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Copyright 2018 Velocidex Innovations <mike@velocidex.com>
|
||||
Copyright 2014-2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _WINPMEM_H_
|
||||
#define _WINPMEM_H_
|
||||
|
||||
/* These should be changed for incident response purposes to prevent trivial
|
||||
* rootkit subversion.
|
||||
*/
|
||||
#define SILENT_OPERATION 0
|
||||
|
||||
// Really verbose debugging.
|
||||
#define VERBOSE_DEBUG 0
|
||||
|
||||
#define PMEM_DEVICE_NAME L"pmem"
|
||||
#define PMEM_VERSION "v1.6.3"
|
||||
#define PMEM_POOL_TAG 0x4d454d50
|
||||
|
||||
// In order to enable writing this must be set to 1 and the
|
||||
// appropriate IOCTL must be sent to switch the driver to write mode.
|
||||
#define PMEM_WRITE_ENABLED 0
|
||||
|
||||
#include <ntifs.h>
|
||||
#include <wdmsec.h>
|
||||
#include <initguid.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pte_mmap.h"
|
||||
#include "api.h"
|
||||
|
||||
// Some standard integer sizes.
|
||||
typedef unsigned __int64 u64;
|
||||
typedef unsigned __int32 u32;
|
||||
typedef unsigned __int16 u16;
|
||||
typedef unsigned __int8 u8;
|
||||
|
||||
#define MI_CONVERT_PHYSICAL_TO_PFN(Pa) (Pa >> 12)
|
||||
|
||||
//
|
||||
// IOCTL
|
||||
//
|
||||
// This is used to query the driver about memory stats.
|
||||
#define IOCTL_GET_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x103, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
#define IOCTL_SET_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x101, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
#define IOCTL_WRITE_ENABLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x102, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
// This is the old deprecated interface. Use IOCTL_GET_INFO instead.
|
||||
#define IOCTL_GET_INFO_DEPRECATED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x100, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
// This is the structure which is returned.
|
||||
#pragma pack(push, 2)
|
||||
|
||||
extern PUSHORT NtBuildNumber;
|
||||
|
||||
/* This is the format of the deprecated IOCTL_GET_INFO_DEPRECATED
|
||||
call.
|
||||
*/
|
||||
struct DeprecatedPmemMemoryInfo {
|
||||
LARGE_INTEGER CR3;
|
||||
LARGE_INTEGER KPCR;
|
||||
ULONG NumberOfRuns;
|
||||
|
||||
// A Null terminated array of ranges.
|
||||
PHYSICAL_MEMORY_RANGE Run[1];
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct PmemMemoryInfo {
|
||||
LARGE_INTEGER CR3;
|
||||
LARGE_INTEGER NtBuildNumber; // Version of this kernel.
|
||||
|
||||
LARGE_INTEGER KernBase; // The base of the kernel image.
|
||||
|
||||
|
||||
// The following are deprecated and will not be set by the driver. It is safer
|
||||
// to get these during analysis from NtBuildNumberAddr below.
|
||||
LARGE_INTEGER KDBG; // The address of KDBG
|
||||
|
||||
// Support up to 32 processors for KPCR.
|
||||
LARGE_INTEGER KPCR[32];
|
||||
|
||||
LARGE_INTEGER PfnDataBase;
|
||||
LARGE_INTEGER PsLoadedModuleList;
|
||||
LARGE_INTEGER PsActiveProcessHead;
|
||||
|
||||
// END DEPRECATED.
|
||||
|
||||
// The address of the NtBuildNumber integer - this is used to find the kernel
|
||||
// base quickly.
|
||||
LARGE_INTEGER NtBuildNumberAddr;
|
||||
|
||||
// As the driver is extended we can add fields here maintaining
|
||||
// driver alignment..
|
||||
LARGE_INTEGER Padding[0xfe];
|
||||
|
||||
LARGE_INTEGER NumberOfRuns;
|
||||
|
||||
// A Null terminated array of ranges.
|
||||
PHYSICAL_MEMORY_RANGE Run[1];
|
||||
};
|
||||
|
||||
|
||||
enum PMEM_ACQUISITION_MODE {
|
||||
// Use the MmMapIoSpace API.
|
||||
ACQUISITION_MODE_MAP_IO_SPACE = 0,
|
||||
|
||||
// Map the \\.\PhysicalMemory device.
|
||||
ACQUISITION_MODE_PHYSICAL_MEMORY = 1,
|
||||
|
||||
// Use direct page table manipulation.
|
||||
ACQUISITION_MODE_PTE_MMAP = 2,
|
||||
|
||||
// Use direct page table manipulation with PCI memory map probing
|
||||
ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE = 3
|
||||
};
|
||||
|
||||
struct PmemMemoryControl {
|
||||
u32 mode; //really: enum PMEM_ACQUISITION_MODE mode but we want to enforce
|
||||
//standard struct sizes.;
|
||||
};
|
||||
|
||||
|
||||
/* When we are silent we do not emit any debug messages. */
|
||||
#if SILENT_OPERATION == 1
|
||||
#define WinDbgPrint(fmt, ...)
|
||||
#define vWinDbgPrintEx(x, ...)
|
||||
#else
|
||||
#define WinDbgPrint DbgPrint
|
||||
#define vWinDbgPrintEx vDbgPrintEx
|
||||
#endif
|
||||
|
||||
# if VERBOSE_DEBUG == 1
|
||||
#define WinDbgPrintDebug DbgPrint
|
||||
# else
|
||||
#define WinDbgPrintDebug(fmt, ...)
|
||||
#endif
|
||||
|
||||
// Add verbose debugging to PCI code.
|
||||
#define WINPMEM_PCI_DEBUG 0
|
||||
|
||||
|
||||
/*
|
||||
Our Device Extension Structure.
|
||||
*/
|
||||
typedef struct _DEVICE_EXTENSION {
|
||||
/* If we read from \\Device\\PhysicalMemory, this is the handle to that. */
|
||||
HANDLE MemoryHandle;
|
||||
|
||||
/* How we should acquire memory. */
|
||||
enum WDD_ACQUISITION_MODE mode;
|
||||
|
||||
int WriteEnabled;
|
||||
|
||||
/* Hold a handle to the pte_mmap object. */
|
||||
PTE_MMAP_OBJ *pte_mmapper;
|
||||
|
||||
FAST_MUTEX mu;
|
||||
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
|
||||
|
||||
// 5e1ce668-47cb-410e-a664-5c705ae4d71b
|
||||
DEFINE_GUID(GUID_DEVCLASS_PMEM_DUMPER,
|
||||
0x5e1ce668L,
|
||||
0x47cb,
|
||||
0x410e,
|
||||
0xa6, 0x64, 0x5c, 0x70, 0x5a, 0xe4, 0xd7, 0x1b);
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!python
|
||||
"""This is the user space program which is required for dumping images using the
|
||||
winpmem driver.
|
||||
|
||||
Copyright 2012 Michael Cohen <scudette@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
__author__ = "Michael Cohen <scudette@gmail.com>"
|
||||
|
||||
import win32service
|
||||
import win32file
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import optparse
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option("-d", "--driver",
|
||||
help="The driver location (winpmem.sys)", metavar="FILE")
|
||||
|
||||
parser.add_option("-f", "--filename", dest="filename", default="pmemdump.raw",
|
||||
help="write image to FILE", metavar="FILE")
|
||||
|
||||
parser.add_option("-n", "--name", default="pmem",
|
||||
help="The name of the device.")
|
||||
|
||||
parser.add_option("-m", "--mode", default="physical",
|
||||
help="The acquisition mode. Can be (physical or iospace)")
|
||||
|
||||
parser.add_option("-l", "--load", default=False, action="store_true",
|
||||
help="Only load the driver and immediately quit. "
|
||||
"(Useful just before attaching with volatility)")
|
||||
|
||||
parser.add_option("-u", "--unload", default=False, action="store_true",
|
||||
help="Unload the driver and immediately quit. ")
|
||||
|
||||
|
||||
def CTL_CODE(DeviceType, Function, Method, Access):
|
||||
return (DeviceType<<16) | (Access << 14) | (Function << 2) | Method
|
||||
|
||||
|
||||
# IOCTLS for interacting with the driver.
|
||||
CTRL_IOCTRL = CTL_CODE(0x22, 0x101, 0, 3)
|
||||
INFO_IOCTRL = CTL_CODE(0x22, 0x103, 0, 3)
|
||||
INFO_IOCTRL_DEPRECATED = CTL_CODE(0x22, 0x100, 0, 3)
|
||||
|
||||
|
||||
class Image(object):
|
||||
"""This class abstracts the image."""
|
||||
buffer_size = 1024 * 1024
|
||||
|
||||
def __init__(self, fd):
|
||||
self.fd = fd
|
||||
self.SetMode()
|
||||
self.ParseMemoryRuns()
|
||||
|
||||
# Tell the driver what acquisition mode we want.
|
||||
self.GetInfo()
|
||||
#self.GetInfoDeprecated()
|
||||
|
||||
def GetInfoDeprecated(self):
|
||||
result = win32file.DeviceIoControl(self.fd, INFO_IOCTRL_DEPRECATED, "",
|
||||
1024, None)
|
||||
fmt_string = "QQl"
|
||||
offset = struct.calcsize(fmt_string)
|
||||
|
||||
cr3, kpcr, number_of_runs = struct.unpack_from(fmt_string, result)
|
||||
for x in range(number_of_runs):
|
||||
start, length = struct.unpack_from("QQ", result, x * 16 + offset)
|
||||
print("0x%X\t\t0x%X" % (start, length))
|
||||
|
||||
FIELDS = (["CR3", "NtBuildNumber", "KernBase", "KDBG"] +
|
||||
["KPCR%02d" % i for i in range(32)] +
|
||||
["PfnDataBase", "PsLoadedModuleList", "PsActiveProcessHead"] +
|
||||
["Padding%s" % i for i in range(0xff)] +
|
||||
["NumberOfRuns"])
|
||||
|
||||
def ParseMemoryRuns(self):
|
||||
self.runs = []
|
||||
|
||||
result = win32file.DeviceIoControl(
|
||||
self.fd, INFO_IOCTRL, "", 102400, None)
|
||||
|
||||
fmt_string = "Q" * len(self.FIELDS)
|
||||
self.memory_parameters = dict(zip(self.FIELDS, struct.unpack_from(
|
||||
fmt_string, result)))
|
||||
|
||||
self.dtb = self.memory_parameters["CR3"]
|
||||
self.kdbg = self.memory_parameters["KDBG"]
|
||||
|
||||
offset = struct.calcsize(fmt_string)
|
||||
|
||||
for x in range(self.memory_parameters["NumberOfRuns"]):
|
||||
start, length = struct.unpack_from("QQ", result, x * 16 + offset)
|
||||
self.runs.append((start, length))
|
||||
|
||||
def GetInfo(self):
|
||||
for k, v in sorted(self.memory_parameters.items()):
|
||||
if k.startswith("Pad"):
|
||||
continue
|
||||
|
||||
if not v: continue
|
||||
|
||||
print("%s: \t%#08x (%s)" % (k, v, v))
|
||||
|
||||
print("Memory ranges:")
|
||||
print("Start\t\tEnd\t\tLength")
|
||||
|
||||
for start, length in self.runs:
|
||||
print("0x%X\t\t0x%X\t\t0x%X" % (start, start+length, length))
|
||||
|
||||
def SetMode(self):
|
||||
if FLAGS.mode == "iospace":
|
||||
mode = 0
|
||||
elif FLAGS.mode == "physical":
|
||||
mode = 1
|
||||
elif FLAGS.mode == "pte":
|
||||
mode = 2
|
||||
elif FLAGS.mode == "pte_pci":
|
||||
mode = 3
|
||||
else:
|
||||
raise RuntimeError("Mode %s not supported" % FLAGS.mode)
|
||||
|
||||
win32file.DeviceIoControl(
|
||||
self.fd, CTRL_IOCTRL, struct.pack("I", mode), 0, None)
|
||||
|
||||
def PadWithNulls(self, outfd, length):
|
||||
while length > 0:
|
||||
to_write = min(length, self.buffer_size)
|
||||
outfd.write("\x00" * to_write)
|
||||
length -= to_write
|
||||
|
||||
def DumpWithRead(self, output_filename):
|
||||
"""Read the image and write all the data to a raw file."""
|
||||
with open(output_filename, "wb") as outfd:
|
||||
offset = 0
|
||||
for start, length in self.runs:
|
||||
if start > offset:
|
||||
print("\nPadding from 0x%X to 0x%X\n" % (offset, start))
|
||||
self.PadWithNulls(outfd, start - offset)
|
||||
|
||||
offset = start
|
||||
end = start + length
|
||||
while offset < end:
|
||||
to_read = min(self.buffer_size, end - offset)
|
||||
win32file.SetFilePointer(self.fd, offset, 0)
|
||||
|
||||
_, data = win32file.ReadFile(self.fd, to_read)
|
||||
outfd.write(data)
|
||||
|
||||
offset += to_read
|
||||
|
||||
offset_in_mb = offset/1024/1024
|
||||
if not offset_in_mb % 50:
|
||||
sys.stdout.write("\n%04dMB\t" % offset_in_mb)
|
||||
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
|
||||
def main():
|
||||
"""Load the driver and image the memory."""
|
||||
# Check the driver is somewhere
|
||||
if not FLAGS.driver or not os.access(FLAGS.driver, os.R_OK):
|
||||
print("You must specify a valid driver file.")
|
||||
sys.exit(-1)
|
||||
|
||||
# Must have absolute path here.
|
||||
driver = os.path.join(os.getcwd(), FLAGS.driver)
|
||||
hScm = win32service.OpenSCManager(
|
||||
None, None, win32service.SC_MANAGER_CREATE_SERVICE)
|
||||
|
||||
try:
|
||||
hSvc = win32service.CreateService(
|
||||
hScm, FLAGS.name, FLAGS.name,
|
||||
win32service.SERVICE_ALL_ACCESS,
|
||||
win32service.SERVICE_KERNEL_DRIVER,
|
||||
win32service.SERVICE_DEMAND_START,
|
||||
win32service.SERVICE_ERROR_IGNORE,
|
||||
driver,
|
||||
None, 0, None, None, None)
|
||||
except win32service.error as e:
|
||||
print(e)
|
||||
hSvc = win32service.OpenService(hScm, FLAGS.name,
|
||||
win32service.SERVICE_ALL_ACCESS)
|
||||
|
||||
# Make sure the service is stopped.
|
||||
try:
|
||||
win32service.ControlService(hSvc, win32service.SERVICE_CONTROL_STOP)
|
||||
except win32service.error:
|
||||
pass
|
||||
|
||||
if FLAGS.unload:
|
||||
print("unloaded winpmem driver.")
|
||||
return
|
||||
|
||||
try:
|
||||
win32service.StartService(hSvc, [])
|
||||
except win32service.error as e:
|
||||
print("%s: will try to continue" % e)
|
||||
|
||||
if FLAGS.load:
|
||||
fd = win32file.CreateFile(
|
||||
"\\\\.\\" + FLAGS.name,
|
||||
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
||||
win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE,
|
||||
None,
|
||||
win32file.OPEN_EXISTING,
|
||||
win32file.FILE_ATTRIBUTE_NORMAL,
|
||||
None)
|
||||
|
||||
print(r"Loaded the winpmem driver. You can now attach "
|
||||
r"volatility to \\.\pmem")
|
||||
image = Image(fd)
|
||||
|
||||
return
|
||||
|
||||
try:
|
||||
fd = win32file.CreateFile(
|
||||
"\\\\.\\" + FLAGS.name,
|
||||
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
||||
win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE,
|
||||
None,
|
||||
win32file.OPEN_EXISTING,
|
||||
win32file.FILE_ATTRIBUTE_NORMAL,
|
||||
None)
|
||||
|
||||
try:
|
||||
t = time.time()
|
||||
image = Image(fd)
|
||||
print("Imaging to %s" % FLAGS.filename)
|
||||
image.DumpWithRead(FLAGS.filename)
|
||||
print("\nCompleted in %s seconds" % (time.time() - t))
|
||||
finally:
|
||||
win32file.CloseHandle(fd)
|
||||
finally:
|
||||
try:
|
||||
win32service.ControlService(hSvc, win32service.SERVICE_CONTROL_STOP)
|
||||
except win32service.error:
|
||||
pass
|
||||
win32service.DeleteService(hSvc)
|
||||
win32service.CloseServiceHandle(hSvc)
|
||||
|
||||
if __name__ == "__main__":
|
||||
(FLAGS, args) = parser.parse_args()
|
||||
main()
|
||||
@@ -0,0 +1,4 @@
|
||||
all:
|
||||
rm -rf ../docs/
|
||||
hugo
|
||||
echo winpmem.velocidex.com > ../docs/CNAME
|
||||
@@ -0,0 +1,4 @@
|
||||
The Pmem Memory acquisition tools.
|
||||
==================================
|
||||
|
||||
This is the documentation site for WinPmem
|
||||
@@ -2,7 +2,7 @@ baseURL = "https://winpmem.velocidex.com/"
|
||||
languageCode = "en-us"
|
||||
title = "WinPmem memory imager"
|
||||
theme = "hugo-whisper-theme"
|
||||
publishDir = "docs"
|
||||
publishDir = "../docs"
|
||||
pygmentsCodeFences = true
|
||||
pygmentsCodefencesGuessSyntax = true
|
||||
pygmentsUseClasses = true
|
||||
Submodule
+1
Submodule site/themes/hugo-whisper-theme added at 6638af48a3
Submodule themes/hugo-whisper-theme deleted from c679e0b497
Reference in New Issue
Block a user