├── .github
├── FUNDING.yml
└── workflows
│ ├── create-release.yml
│ └── gh-pages.yml
├── .gitignore
├── .gitlab-ci.yml
├── LICENSE
├── README.md
├── doc_src
├── .nojekyll
├── Makefile
├── _templates
│ └── layout.html
├── ch01_essentials.rst
├── ch02_registry.rst
├── ch03_event_logs.rst
├── ch06_databases.rst
├── conf.py
├── index.rst
└── make.bat
├── pyforhandbook
├── __init__.py
├── ch01_essentials
│ ├── __init__.py
│ ├── argparse_example.py
│ ├── csv_example.py
│ ├── logging_example.py
│ ├── open_files.py
│ └── recursion_example.py
├── ch02_registry
│ ├── NTUSER-WIN7-trustrecords.DAT
│ ├── __init__.py
│ ├── yarp_base.py
│ └── yarp_ntuser.py
├── ch03_event_logs
│ ├── __init__.py
│ └── using_python_evtx.py
├── ch06_databases
│ ├── __init__.py
│ └── opening_sqlite.py
└── version.py
├── requirements.txt
└── setup.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [chapinb]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: chapin
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/workflows/create-release.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Setup Python
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: '3.8'
18 |
19 | - name: Set env
20 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
21 |
22 | - name: Show version
23 | run: echo $RELEASE_VERSION && echo ${{ env.RELEASE_VERSION }}
24 |
25 | - name: Upgrade pip
26 | run: |
27 | # install pip=>20.1 to use "pip cache dir"
28 | python3 -m pip install --upgrade pip
29 |
30 | - name: Install dependencies
31 | run: python3 -m pip install -r ./requirements.txt
32 |
33 | - name: Build docs
34 | run: (cd doc_src && make html)
35 |
36 | - name: Compress docs
37 | run: (cd ./doc_src/_build/ && mv html pyforhandbook_html_docs_${{ env.RELEASE_VERSION }} && zip -qq -r ../../pyforhandbook_html_docs_${{ env.RELEASE_VERSION }}.zip pyforhandbook_html_docs_${{ env.RELEASE_VERSION }})
38 |
39 | - name: Create release
40 | id: create_release
41 | uses: actions/create-release@v1
42 | env:
43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44 | with:
45 | tag_name: ${{ github.ref }}
46 | release_name: Release ${{ github.ref }}
47 | draft: false
48 | prerelease: false
49 |
50 | - name: Upload docs to release
51 | uses: actions/upload-release-asset@v1
52 | env:
53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 | with:
55 | upload_url: ${{ steps.create_release.outputs.upload_url }}
56 | asset_path: pyforhandbook_html_docs_${{ env.RELEASE_VERSION }}.zip
57 | asset_name: pyforhandbook_html_docs_${{ env.RELEASE_VERSION }}.zip
58 | asset_content_type: application/zip
59 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | tags:
6 | - latest
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Setup Python
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: '3.8'
18 |
19 | - name: Upgrade pip
20 | run: |
21 | # install pip=>20.1 to use "pip cache dir"
22 | python3 -m pip install --upgrade pip
23 |
24 | - name: Install dependencies
25 | run: python3 -m pip install -r ./requirements.txt
26 |
27 | - name: Build docs
28 | run: (cd doc_src && make html)
29 |
30 | - name: Deploy
31 | uses: peaceiris/actions-gh-pages@v3
32 | with:
33 | github_token: ${{ secrets.GITHUB_TOKEN }}
34 | publish_dir: ./doc_src/_build/html
35 |
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Exclude locally built docs
2 | docs/
3 | .vscode/
4 | doc_src/_build/
5 | .idea/
6 |
7 | # Byte-compiled / optimized / DLL files
8 | __pycache__/
9 | *.py[cod]
10 | *$py.class
11 |
12 | # C extensions
13 | *.so
14 |
15 | # Distribution / packaging
16 | .Python
17 | build/
18 | develop-eggs/
19 | dist/
20 | downloads/
21 | eggs/
22 | .eggs/
23 | lib/
24 | lib64/
25 | parts/
26 | sdist/
27 | var/
28 | wheels/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 | MANIFEST
33 |
34 | # PyInstaller
35 | # Usually these files are written by a python script from a template
36 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
37 | *.manifest
38 | *.spec
39 |
40 | # Installer logs
41 | pip-log.txt
42 | pip-delete-this-directory.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | .hypothesis/
54 | .pytest_cache/
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 | local_settings.py
63 | db.sqlite3
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # pyenv
82 | .python-version
83 |
84 | # celery beat schedule file
85 | celerybeat-schedule
86 |
87 | # SageMath parsed files
88 | *.sage.py
89 |
90 | # Environments
91 | .env
92 | .venv
93 | env/
94 | venv/
95 | venv3/
96 | ENV/
97 | env.bak/
98 | venv.bak/
99 |
100 | # Spyder project settings
101 | .spyderproject
102 | .spyproject
103 |
104 | # Rope project settings
105 | .ropeproject
106 |
107 | # VSCode project settings
108 | .vscode
109 |
110 | # mkdocs documentation
111 | /site
112 |
113 | # mypy
114 | .mypy_cache/
115 |
116 | # Other files
117 | *.log
118 | *.csv
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: python:3.7.3-alpine
2 |
3 | pages:
4 | script:
5 | - apk --no-cache add make gcc musl-dev git
6 | - pip install -r dev_requirements.txt
7 | - pip install -r requirements.txt
8 | - cd doc_src
9 | - make html
10 | - mv _build/html ../public
11 | artifacts:
12 | paths:
13 | - public
14 | only:
15 | - master
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Chapin Bryce
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python Forensics Handbook
2 |
3 | A handy reference guide for building Python scripts to help out
4 | Digital Forensic, Incident Response, and other Cyber Security
5 | tools.
6 |
7 | ## Accessing the Handbook
8 |
9 | There are several ways you can access the content in this handbook:
10 |
11 | * Online at [https://chapinb.com/python-forensics-handboook](https://chapinb.com/python-forensics-handbook)
12 | * Accessing the latest release available GitHub at
13 | [https://github.com/chapinb/python-forensics-handbook/releases](https://github.com/chapinb/python-forensics-handbook/releases)
14 | and downloading the `pyforhandbook_html_docs_{version}.zip` file. You can
15 | then extract the arvhive and open `index.html` in your web browser.
16 | * Cloning the repository and checking out the gh-pages branch.
17 | * Building it yourself! See instructions below.
18 |
19 |
20 | ## Building the handbook
21 |
22 | To build this handbook, you will need Python 3.6 or later. To start, clone the master branch (or check out
23 | a tag for the release you want to build.) Then install all requirements by running `pip install -r requirements.txt`.
24 |
25 | Once that finishes, navigate to the `doc_src/` directory and run `make html`. This will run on Windows, Linux, or macOS.
26 | After the make command completes, you can view the built documentation within the `doc_src/_build/html` folder.
27 |
28 | ## Contributing
29 |
30 | Have an idea for a section or a function that you would like to share? Please follow the above steps in
31 | "Building the handbook", add your changes, and open a pull request to get it integrated in to the repository!
32 |
33 | More details on how to contribute coming soon. In the meantime, if you have any questions about the above, feel free
34 | to open an issue on Github or reach out to me on Twitter @chapindb.
35 |
--------------------------------------------------------------------------------
/doc_src/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chapinb/python-forensics-handbook/6f28f39639d8add4e553f8986edc75308a914b0b/doc_src/.nojekyll
--------------------------------------------------------------------------------
/doc_src/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = .
8 | BUILDDIR = _build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | html: Makefile
19 | # @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
20 | @$(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html"
21 |
22 | epub: Makefile
23 | @$(SPHINXBUILD) -M epub "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/doc_src/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 |
3 | {% block extrahead %}
4 | {{ super() }}
5 |
6 |
7 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/doc_src/ch01_essentials.rst:
--------------------------------------------------------------------------------
1 | Chapter 1 - Essential Scripts
2 | ==============================
3 | .. toctree::
4 | :maxdepth: 2
5 | :caption: Contents:
6 |
7 | Section 1.1 - Argparse Example
8 | --------------------------------
9 | .. automodule:: pyforhandbook.ch01_essentials.argparse_example
10 | :members:
11 |
12 | Section 1.2 - Logging Example
13 | --------------------------------
14 | .. automodule:: pyforhandbook.ch01_essentials.logging_example
15 | :members:
16 |
17 | Section 1.3 - Open Files
18 | ------------------------
19 | .. automodule:: pyforhandbook.ch01_essentials.open_files
20 | :members:
21 |
22 | Section 1.4 - CSV Example
23 | --------------------------------
24 | .. automodule:: pyforhandbook.ch01_essentials.csv_example
25 | :members:
26 |
27 | Section 1.5 - Directory Recursion
28 | ---------------------------------
29 | .. automodule:: pyforhandbook.ch01_essentials.recursion_example
30 | :members:
31 |
32 | Indices and tables
33 | --------------------------------
34 |
35 | * :ref:`genindex`
36 | * :ref:`modindex`
37 | * :ref:`search`
38 |
--------------------------------------------------------------------------------
/doc_src/ch02_registry.rst:
--------------------------------------------------------------------------------
1 | Chapter 2 - Registry Parsing
2 | ==============================
3 | .. toctree::
4 | :maxdepth: 2
5 | :caption: Contents:
6 |
7 | Section 2.1 - Opening a Hive
8 | --------------------------------
9 | .. automodule:: pyforhandbook.ch02_registry.yarp_base
10 | :members:
11 |
12 | Section 2.2 - Parsing Hive Values
13 | ----------------------------------
14 | .. automodule:: pyforhandbook.ch02_registry.yarp_ntuser
15 | :members:
16 |
17 | Indices and tables
18 | --------------------------------
19 |
20 | * :ref:`genindex`
21 | * :ref:`modindex`
22 | * :ref:`search`
23 |
--------------------------------------------------------------------------------
/doc_src/ch03_event_logs.rst:
--------------------------------------------------------------------------------
1 | Chapter 3 - Windows Event Log Parsing
2 | =====================================
3 | .. toctree::
4 | :maxdepth: 2
5 | :caption: Contents:
6 |
7 | Section 3.1 - Using python-evtx
8 | ----------------------------------
9 | .. automodule:: pyforhandbook.ch03_event_logs.using_python_evtx
10 | :members:
11 |
12 | Indices and tables
13 | --------------------------------
14 |
15 | * :ref:`genindex`
16 | * :ref:`modindex`
17 | * :ref:`search`
18 |
--------------------------------------------------------------------------------
/doc_src/ch06_databases.rst:
--------------------------------------------------------------------------------
1 | Chapter 6 - Sqlite & MacOS/Mobile/Browsers
2 | ==========================================
3 | .. toctree::
4 | :maxdepth: 2
5 | :caption: Contents:
6 |
7 | Section 6.1 - Opening Sqlite
8 | --------------------------------
9 | .. automodule:: pyforhandbook.ch06_databases.opening_sqlite
10 | :members:
11 |
12 | Indices and tables
13 | --------------------------------
14 |
15 | * :ref:`genindex`
16 | * :ref:`modindex`
17 | * :ref:`search`
18 |
--------------------------------------------------------------------------------
/doc_src/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # http://www.sphinx-doc.org/en/master/config
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import datetime
14 | from pyforhandbook import version as version_info
15 |
16 | # -- Project information -----------------------------------------------------
17 |
18 | project = 'Python Forensics Handbook'
19 | copyright = version_info.__copyright__
20 | author = version_info.__author__
21 |
22 | # The full version, including alpha/beta/rc tags
23 | release = version_info.__version__
24 | version = version_info.__version__
25 |
26 | # -- General configuration ---------------------------------------------------
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = [
32 | 'sphinx.ext.autodoc', 'sphinx.ext.napoleon'
33 | ]
34 |
35 | # Add any paths that contain templates here, relative to this directory.
36 | templates_path = ['_templates']
37 |
38 | # List of patterns, relative to source directory, that match files and
39 | # directories to ignore when looking for source files.
40 | # This pattern also affects html_static_path and html_extra_path.
41 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
42 |
43 |
44 | # -- Options for HTML output -------------------------------------------------
45 |
46 | # The theme to use for HTML and HTML Help pages. See the documentation for
47 | # a list of builtin themes.
48 | #
49 | # html_theme = 'alabaster'
50 |
51 | html_theme = 'sphinx_rtd_theme'
52 |
53 | # Add any paths that contain custom static files (such as style sheets) here,
54 | # relative to this directory. They are copied after the builtin static files,
55 | # so a file named "default.css" will overwrite the builtin "default.css".
56 | html_static_path = ['_static']
57 |
58 |
59 | ## Added by CBRYCE
60 |
61 | # Hide module names in docs
62 | add_module_names = False
63 |
64 | # Allow extensions to work in epub
65 | viewcode_enable_epub = True
66 |
--------------------------------------------------------------------------------
/doc_src/index.rst:
--------------------------------------------------------------------------------
1 | .. Python Forensics Handbook documentation master file, created by
2 | sphinx-quickstart on Tue May 28 07:12:54 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | The Python Forensics Handbook
7 | =============================
8 | --------------------------------------------------------
9 | A reference guide for developing Python scripts in DFIR
10 | --------------------------------------------------------
11 |
12 | .. toctree::
13 | :maxdepth: 1
14 | :caption: Table of Contents:
15 |
16 | ch01_essentials
17 | ch02_registry
18 | ch03_event_logs
19 | ch06_databases
20 |
21 | Handbook Sections
22 | ==============================
23 | .. automodule:: pyforhandbook
24 | :members:
25 |
26 | Indices and tables
27 | ==================
28 |
29 | * :ref:`genindex`
30 | * :ref:`modindex`
31 | * :ref:`search`
32 |
--------------------------------------------------------------------------------
/doc_src/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/pyforhandbook/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | .. note::
3 | IN DEVELOPMENT - More sections will release over the coming weeks/months/as
4 | time permits. Feel free to contribute as you have an idea or time to assist,
5 | otherwise stay tuned!
6 |
7 | This handbook consists of 7 sections covering common tasks for developing
8 | Python scripts for use in DFIR. Each section contains short,
9 | portable code blocks that can drop into a new script with minimal
10 | tweaking. This way, you can quickly build out your custom script
11 | without needing to re-invent the wheel each time.
12 |
13 | This handbook is not intended to be read in order - if anything
14 | this outline is the main launching point to find the correct page
15 | containing the code block you wish to reference.
16 |
17 | Please feel free to contribute your own sections with the snippets that have
18 | worked well for you, even if a similar section already exists. This handbook
19 | is hosted on GitHub at https://github.com/chapinb/python-forensics-handbook and
20 | available to read online at https://chapinb.com/python-forensics-handbook.
21 | Please consider submitting a pull request with your additions!
22 |
23 | Chapter 1 - Essential Script Elements
24 | -------------------------------------
25 |
26 | This chapter covers code blocks that are useful across scripts
27 | and are not DFIR specific, but solid practices to integrate into
28 | projects to allow for uniformity.
29 |
30 | * Argparse
31 | - Command line parameter handling
32 | * Logging
33 | - Writing status and error messages to the console and
34 | log file
35 | * Open Files
36 | - Read text files with varying UTF encodings.
37 | * CSV Generation
38 | - For better or worse, CSV reports are very common in DFIR
39 | and this code block covers several methods for
40 | generating a CSV
41 | * Recursive File Exploration
42 | - Quick example of code to explore directories and access
43 | nested files.
44 | * Parallel Processing
45 | - Simple implementation of multithreading and multiprocessing
46 |
47 | Chapter 2 - Registry Hives
48 | ------------------------------------
49 |
50 | In this chapter, we demonstrate how to open a registry hive, navigate through
51 | its keys, and interact with values to expose information for analysis.
52 |
53 | * Using yarp to open a single hive
54 | - Opening a hive and recovering data available in transaction logs
55 | * Parse registry hive keys and values
56 | - Building off our prior code to parse specific artifacts from an
57 | NTUSER.DAT hive, including string and binary values. Uses classes in a
58 | manner that is very flexible and permits extending functionality as
59 | needed with minimal effort.
60 | * Searching for a pattern across hive keys and values.
61 | - Looking for a provided pattern across the entire hive.
62 |
63 | Chapter 3 - Event Logs
64 | ----------------------
65 |
66 | The functions showcased in this chapter highlight methods to access events
67 | within Windows event log files, iterating over the events, and extracting
68 | useful records for further examination.
69 |
70 | * Using python-evtx
71 | - Opening evtx files
72 | - Iterating over events
73 | * Parsing Logins
74 | - Parse out the commonly investigated 4624/4672 events
75 |
76 | Chapter 4 - Text logs
77 | ---------------------
78 |
79 | * Handling IIS Logs
80 | - Parse common fields in IIS logs into a report
81 | * Handling Syslog
82 | - Parse common syslog formats into a report
83 | * Adding in GeoIP
84 | - Function to add GeoIP recognition
85 |
86 | Chapter 5 - API calls & JSON data
87 | ---------------------------------
88 |
89 | * VirusTotal
90 | * HybridAnalysis
91 | * Manipulating JSON
92 |
93 | Chapter 6 - Databases
94 | ------------------------------------------
95 |
96 | Databases are found within many applications and operating systems. This chapter
97 | covers methods to extract information from these common databases, along with
98 | functions that are purpose built to parse information from frequently seen
99 | database tables.
100 |
101 | * macOS Activity
102 | - KnowledgeC
103 | * Android SMS
104 | * Google Chrome History DB
105 |
106 | Chapter 7 - Opening forensic images
107 | --------------------------------------
108 |
109 | Media acquisition and preservation formats are very common within DFIR and
110 | the ability to extract specific contents from these files leads to faster
111 | analysis and simplified usage of the tool you are building. With these functions
112 | you can read files from a forensic image and pass them straight to your other
113 | utilities for further parsing.
114 |
115 | * LibEWF
116 | - Expose an E01 as a raw image
117 | * PyTSK
118 | - Read data from a raw image (MBR)
119 | - Read data from a file (hashing)
120 | - Iterate through folders (file listing)
121 | - Perform targeted reads (file signatures)
122 |
123 | """
124 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/__init__.py:
--------------------------------------------------------------------------------
1 | """Essential Script Snippets
2 |
3 | In this section, we will cover developing essential code snippets.
4 | These code blocks are commonly used in DFIR scripting to allow
5 | for users to provide parameters to the tool, logging of progress
6 | and errors, and generation of reports.
7 | """
8 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/argparse_example.py:
--------------------------------------------------------------------------------
1 | """Example for setting up arguments for your command line utility.
2 |
3 | Example Usage:
4 |
5 | ``$ python argparse.py``
6 |
7 | References:
8 |
9 | * https://docs.python.org/3/library/argparse.html
10 | * https://docs.python.org/3/library/os.html
11 | * https://docs.python.org/3/library/pathlib.html
12 |
13 | Argparse configuration
14 | ======================
15 |
16 | This function shows an example of creating an argparse instance
17 | with required and optional parameters. Further, it demonstrates
18 | how to set default values and boolean arguments. the ``argparse``
19 | module has many more features documented at
20 | https://docs.python.org/3/library/argparse.html
21 |
22 | .. literalinclude:: ../pyforhandbook/ch01_essentials/argparse_example.py
23 | :pyobject: setup_argparse
24 |
25 | """
26 | import argparse
27 | import os
28 | from pathlib import PurePath
29 |
30 | """
31 | Copyright 2019 Chapin Bryce
32 |
33 | Permission is hereby granted, free of charge, to any person
34 | obtaining a copy of this software and associated documentation
35 | files (the "Software"), to deal in the Software without
36 | restriction, including without limitation the rights to use, copy,
37 | modify, merge, publish, distribute, sublicense, and/or sell copies
38 | of the Software, and to permit persons to whom the Software is
39 | furnished to do so, subject to the following conditions:
40 |
41 | The above copyright notice and this permission notice shall be
42 | included in all copies or substantial portions of the Software.
43 |
44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
46 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
48 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
49 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
51 | DEALINGS IN THE SOFTWARE.
52 | """
53 |
54 | __author__ = "Chapin Bryce"
55 | __date__ = 20190527
56 | __license__ = "MIT Copyright 2019 Chapin Bryce"
57 | __desc__ = """Sample script to accept command line arguments."""
58 | __docs__ = [
59 | "https://docs.python.org/3/library/argparse.html",
60 | "https://docs.python.org/3/library/os.html",
61 | "https://docs.python.org/3/library/pathlib.html",
62 | ]
63 |
64 |
65 | def setup_argparse():
66 | # Setup a parser instance with common fields including a
67 | # description and epilog. The `formatter_class` instructs
68 | # argparse to show default values set for parameters.
69 | parser = argparse.ArgumentParser(
70 | description="Sample Argparse",
71 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
72 | epilog=f"Built by {__author__}, v.{__date__}",
73 | )
74 |
75 | # The simplest form of adding an argument, the name of the
76 | # parameter and a description of its form.
77 | parser.add_argument("INPUT_FILE", help="Input file to parse")
78 | parser.add_argument("OUTPUT_FOLDER", help="Folder to store output")
79 |
80 | # An optional argument with multiple methods of specifying
81 | # the parameter. Includes a default value
82 | parser.add_argument(
83 | "-l",
84 | "--log",
85 | help="Path to log file",
86 | default=os.path.abspath(
87 | os.path.join(
88 | PurePath(__file__).parent,
89 | PurePath(__file__).name.rsplit(".", 1)[0] + ".log",
90 | )
91 | ),
92 | )
93 |
94 | # An optional argument which does not accept a value, instead
95 | # just modifies functionality.
96 | parser.add_argument(
97 | "-v", "--verbose", action="store_true", help="Include debug log messages"
98 | )
99 |
100 | # Once we've specified our arguments we can parse them for
101 | # reference
102 | args = parser.parse_args()
103 |
104 | # Returning our parsed arguments for further use.
105 | return args
106 |
107 |
108 | # Only run if called directly (not imported)
109 | if __name__ == "__main__":
110 | cli_args = setup_argparse()
111 |
112 | # Show arguments
113 | print(f"Input file: {cli_args.INPUT_FILE}")
114 | print(f"Output folder: {cli_args.OUTPUT_FOLDER}")
115 | print(f"Log file: {cli_args.log}")
116 | print(f"Be verbose?: {cli_args.verbose}")
117 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/csv_example.py:
--------------------------------------------------------------------------------
1 | """Example for writing datasets into CSV files.
2 |
3 | Demonstrates source datasets comprised of lists of dictionaries
4 | and lists of lists as separate functions. Example data is
5 | provided in line and will generate two identical CSVs as output.
6 |
7 | Example Usage:
8 |
9 | ``$ python csv_example.py``
10 |
11 | References:
12 |
13 | * https://docs.python.org/3/library/csv.html
14 | * https://docs.python.org/3/library/os.html
15 |
16 |
17 | List of dictionaries to CSV
18 | ===========================
19 |
20 | Example ``data`` variable:
21 |
22 | ::
23 |
24 | [
25 | {'name': 'apple', 'quantity': 10, 'location': 'VT'},
26 | {'name': 'orange', 'quantity': 5, 'location': 'FL'}
27 | ]
28 |
29 | This first function shows an example of writing a list containing
30 | multiple dictionaries to a CSV file. You can optionally provide
31 | an ordered list of headers to filter what rows to show, or let the
32 | function use the keys of the first dictionary in the list to
33 | generate the header information. The latter option may produce
34 | a new order each iteration and is not preferred if you can
35 | determine the headers in advance.
36 |
37 | .. literalinclude:: ../pyforhandbook/ch01_essentials/csv_example.py
38 | :pyobject: write_csv_dicts
39 |
40 | List of ordered lists to CSV
41 | ============================
42 |
43 | Example ``data`` variable:
44 |
45 | ::
46 |
47 | [
48 | ['name', 'quantity', 'location'],
49 | ['apple', 10, 'VT'],
50 | ['orange', 5, 'FL']
51 | ]
52 |
53 | This function shows an example of writing a list containing
54 | multiple lists to a CSV file. You can optionally provide
55 | an ordered list of headers, or let the function use the values
56 | of the first element in the list to generate the header
57 | information. Unlike the dictionary option, you cannot filter
58 | column data by adjusting the provided headers, you must write all
59 | columns to the CSV.
60 |
61 | .. literalinclude:: ../pyforhandbook/ch01_essentials/csv_example.py
62 | :pyobject: write_csv_lists
63 |
64 |
65 | Docstring References
66 | ====================
67 | """
68 |
69 | import csv
70 |
71 | """
72 | Copyright 2019 Chapin Bryce
73 |
74 | Permission is hereby granted, free of charge, to any person
75 | obtaining a copy of this software and associated documentation
76 | files (the "Software"), to deal in the Software without
77 | restriction, including without limitation the rights to use, copy,
78 | modify, merge, publish, distribute, sublicense, and/or sell copies
79 | of the Software, and to permit persons to whom the Software is
80 | furnished to do so, subject to the following conditions:
81 |
82 | The above copyright notice and this permission notice shall be
83 | included in all copies or substantial portions of the Software.
84 |
85 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
87 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
88 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
89 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
91 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
92 | DEALINGS IN THE SOFTWARE.
93 | """
94 |
95 | __author__ = "Chapin Bryce"
96 | __date__ = 20190527
97 | __license__ = "MIT Copyright 2019 Chapin Bryce"
98 | __desc__ = """Sample script to write to CSV files."""
99 | __docs__ = [
100 | "https://docs.python.org/3/library/csv.html",
101 | "https://docs.python.org/3/library/os.html",
102 | ]
103 |
104 |
105 | def write_csv_dicts(outfile, data, headers=None):
106 | """Writes a list of dictionaries to a CSV file.
107 |
108 | Arguments:
109 | outfile (str): Path to output file
110 | data (list): List of dictionaries to write to file
111 | headers (list): Header row to use. If empty, will use the
112 | first dictionary in the `data` list.
113 |
114 | Example:
115 | >>> list_of_dicts = [
116 | >>> {'name': 'apple', 'quantity': 10, 'location': 'VT'},
117 | >>> {'name': 'orange', 'quantity': 5, 'location': 'FL'}
118 | >>> ]
119 | >>> write_csv_dicts('dict_test.csv', list_of_dicts)
120 | """
121 |
122 | if not headers:
123 | # Use the first line of data
124 | headers = [str(x) for x in data[0].keys()]
125 |
126 | with open(outfile, "w", newline="") as open_file:
127 | # Write only provided headers, ignore others
128 | csv_file = csv.DictWriter(open_file, headers, extrasaction="ignore")
129 | csv_file.writeheader()
130 | csv_file.writerows(data)
131 |
132 |
133 | def write_csv_lists(outfile, data, headers=None):
134 | """Writes a list of lists to a CSV file.
135 |
136 | Arguments:
137 | outfile (str): Path to output file
138 | data (list): List of lists to write to file
139 | headers (list): Header row to use. If empty, will use the
140 | first list in the `data` list.
141 |
142 | Examples:
143 | >>> fields = ['name', 'quantity', 'location']
144 | >>> list_of_lists = [
145 | >>> ['apple', 10, 'VT'],
146 | >>> ['orange', 5, 'FL']
147 | >>> ]
148 | >>> write_csv_lists('list_test.csv', list_of_lists, headers=fields)
149 | """
150 |
151 | with open(outfile, "w", newline="") as open_file:
152 | # Write only provided headers, ignore others
153 | csv_file = csv.writer(open_file)
154 | for count, entry in enumerate(data):
155 | if count == 0 and headers:
156 | # If headers are defined, write them, otherwise
157 | # continue as they will be written anyways
158 | csv_file.writerow(headers)
159 | csv_file.writerow(entry)
160 |
161 |
162 | if __name__ == "__main__":
163 | sample_dict_data = [
164 | {"id": "0", "city": "Boston", "state": "MA", "country": "USA"},
165 | {"id": "1", "city": "New York", "state": "NY", "country": "USA"},
166 | {"id": "2", "city": "Washington", "state": "DC", "country": "USA"},
167 | ]
168 |
169 | write_csv_dicts("dict_test.csv", sample_dict_data)
170 |
171 | header_row = ["id", "city", "state", "country"]
172 | sample_list_data = [
173 | ["0", "Boston", "MA", "USA"],
174 | ["1", "New York", "NY", "USA"],
175 | ["2", "Washington", "DC", "USA"],
176 | ]
177 |
178 | write_csv_lists("list_test.csv", sample_list_data, headers=header_row)
179 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/logging_example.py:
--------------------------------------------------------------------------------
1 | """Example for writing logging information to the console and a
2 | log file.
3 |
4 | Example Usage:
5 |
6 | ``$ python logging_example.py``
7 |
8 | References:
9 |
10 | * https://docs.python.org/3/library/logging.html
11 | * https://docs.python.org/3/library/os.html
12 |
13 | Logging configuration
14 | =====================
15 |
16 | This function shows an example of creating a logging instance that
17 | writes messages to both STDERR and a file, allowing your script
18 | to write content to STDOUT uninterrupted. Additionally, you can
19 | set different logging levels for the two handlers - generally you
20 | keep debugging information in the log file while writing more
21 | critical messages to the console in STDERR.
22 |
23 | .. literalinclude:: ../pyforhandbook/ch01_essentials/logging_example.py
24 | :pyobject: setup_logging
25 |
26 | Docstring References
27 | ====================
28 | """
29 | import logging
30 | import sys
31 |
32 | """
33 | Copyright 2019 Chapin Bryce
34 |
35 | Permission is hereby granted, free of charge, to any person
36 | obtaining a copy of this software and associated documentation
37 | files (the "Software"), to deal in the Software without
38 | restriction, including without limitation the rights to use, copy,
39 | modify, merge, publish, distribute, sublicense, and/or sell copies
40 | of the Software, and to permit persons to whom the Software is
41 | furnished to do so, subject to the following conditions:
42 |
43 | The above copyright notice and this permission notice shall be
44 | included in all copies or substantial portions of the Software.
45 |
46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
47 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
48 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
49 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
50 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
51 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
53 | DEALINGS IN THE SOFTWARE.
54 | """
55 |
56 | __author__ = "Chapin Bryce"
57 | __date__ = 20190527
58 | __license__ = "MIT Copyright 2019 Chapin Bryce"
59 | __desc__ = """Sample script to display and write logging
60 | messages."""
61 | __docs__ = [
62 | "https://docs.python.org/3/library/logging.html",
63 | "https://docs.python.org/3/library/os.html",
64 | ]
65 |
66 | logger = logging.getLogger(name=__name__)
67 |
68 |
69 | def setup_logging(logging_obj, log_file, verbose=False):
70 | """Function to setup logging configuration and test it.
71 |
72 | Args:
73 | logging_obj: A logging instance, returned from logging.getLogger().
74 | log_file: File path to write log messages to.
75 | verbose: Whether or not to enable the debug level in STDERR output.
76 |
77 | Examples:
78 | >>> sample_logger = logging.getLogger(name=__name__)
79 | >>> log_path = "sample.log"
80 | >>> setup_logging(sample_logger, log_path, verbose=True)
81 | >>> sample_logger.debug("This is a debug message")
82 | >>> sample_logger.info("This is an info message")
83 | >>> sample_logger.warning("This is a warning message")
84 | >>> sample_logger.error("This is a error message")
85 | >>> sample_logger.critical("This is a critical message")
86 | """
87 | logging_obj.setLevel(logging.DEBUG)
88 |
89 | # Logging formatter. Best to keep consistent for most use cases
90 | log_format = logging.Formatter(
91 | "%(asctime)s %(filename)s %(levelname)s %(module)s "
92 | "%(funcName)s %(lineno)d %(message)s"
93 | )
94 |
95 | # Setup STDERR logging, allowing you uninterrupted
96 | # STDOUT redirection
97 | stderr_handle = logging.StreamHandler(stream=sys.stderr)
98 | if verbose:
99 | stderr_handle.setLevel(logging.DEBUG)
100 | else:
101 | stderr_handle.setLevel(logging.INFO)
102 | stderr_handle.setFormatter(log_format)
103 |
104 | # Setup file logging
105 | file_handle = logging.FileHandler(log_file, "a")
106 | file_handle.setLevel(logging.DEBUG)
107 | file_handle.setFormatter(log_format)
108 |
109 | # Add handles
110 | logging_obj.addHandler(stderr_handle)
111 | logging_obj.addHandler(file_handle)
112 |
113 |
114 | if __name__ == "__main__":
115 | setup_logging(logger, "sample.log")
116 | logger.warning("This is a warning!")
117 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/open_files.py:
--------------------------------------------------------------------------------
1 | """Example for reading data from encoded text files.
2 |
3 | Demonstrates how to handle setting the proper encoding for
4 | UTF-8, UTF-16-LE, and UTF-16-BE with the ability to easily
5 | expand to support checking other file magic values/signatures.
6 |
7 | Example Usage:
8 |
9 | ``$ python open_files.py``
10 |
11 | References:
12 |
13 | * https://docs.python.org/3/library/io.html
14 |
15 |
16 | Open files with proper encoding
17 | ===============================
18 |
19 | This first function shows an example of opening a file after checking for a
20 | byte-order mark (BOM). While this method could be expanded to check for a file's
21 | magic value/file signature, this low-tech method will help with parsing a
22 | collection of files that may be UTF-8, UTF-16-LE, and UTF-16-BE, three very
23 | common text file encodings. Feel free to build and share on this.
24 |
25 | .. literalinclude:: ../pyforhandbook/ch01_essentials/open_files.py
26 | :pyobject: open_file
27 |
28 | Docstring References
29 | ====================
30 | """
31 |
32 | from io import open
33 |
34 | """
35 | Copyright 2019 Chapin Bryce
36 |
37 | Permission is hereby granted, free of charge, to any person
38 | obtaining a copy of this software and associated documentation
39 | files (the "Software"), to deal in the Software without
40 | restriction, including without limitation the rights to use, copy,
41 | modify, merge, publish, distribute, sublicense, and/or sell copies
42 | of the Software, and to permit persons to whom the Software is
43 | furnished to do so, subject to the following conditions:
44 |
45 | The above copyright notice and this permission notice shall be
46 | included in all copies or substantial portions of the Software.
47 |
48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
49 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
50 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
51 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
52 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
53 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
55 | DEALINGS IN THE SOFTWARE.
56 | """
57 |
58 | __author__ = "Chapin Bryce"
59 | __date__ = 20191103
60 | __license__ = "MIT Copyright 2019 Chapin Bryce"
61 | __desc__ = """Sample script to read encoded text files."""
62 | __docs__ = [
63 | "https://docs.python.org/3/library/csv.html",
64 | "https://docs.python.org/3/library/os.html",
65 | ]
66 |
67 |
68 | def open_file(input_file):
69 | """Opens an encoded text file and prints the contents
70 |
71 | Arguments:
72 | input_file (str): Path to file to open
73 | """
74 |
75 | test_encoding = open(input_file, "rb")
76 | bom = test_encoding.read(2)
77 | file_encoding = "utf-8"
78 | if bom == b"FEFF":
79 | file_encoding = "utf-16-le"
80 | elif bom == b"FFFE":
81 | file_encoding = "utf-16-be"
82 |
83 | with open(input_file, "r", encoding=file_encoding) as open_input_file:
84 | for raw_line in open_input_file:
85 | line = raw_line.strip()
86 | print(line)
87 |
88 |
89 | if __name__ == "__main__":
90 | import argparse
91 |
92 | parser = argparse.ArgumentParser(
93 | description=__desc__,
94 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
95 | epilog=f"Built by {__author__}, v.{__date__}",
96 | )
97 | parser.add_argument("INPUT_FILE", help="Text file to read")
98 | args = parser.parse_args()
99 |
100 | open_file(args.INPUT_FILE)
101 |
--------------------------------------------------------------------------------
/pyforhandbook/ch01_essentials/recursion_example.py:
--------------------------------------------------------------------------------
1 | """File recursion example.
2 |
3 | Demonstration of iterating through a directory to interact with
4 | files.
5 |
6 | Example Usage:
7 |
8 | ``$ python recursion_example.py``
9 |
10 | References:
11 |
12 | * https://docs.python.org/3/library/os.html
13 |
14 | List a directory
15 | ================
16 |
17 | This function shows an example of displaying all files and
18 | folders within a single directory. From here you can further
19 | interact with individual files and folders or iterate recursively
20 | by calling the function on identified subdirectories.
21 |
22 | .. literalinclude:: ../pyforhandbook/ch01_essentials/recursion_example.py
23 | :pyobject: list_directory
24 |
25 | List a directory recursively
26 | ============================
27 |
28 | This function shows an example of displaying all files and
29 | folders within a all directories. You don't need to worry about
30 | additional function calls as the ``os.walk()`` method handles
31 | the recursion on subdirectories and your logic can focus on
32 | handling the processing of files. This sample shows a method of
33 | counting the number of files, subdirectories, and files ending in
34 | ".py" as an example.
35 |
36 | .. literalinclude:: ../pyforhandbook/ch01_essentials/recursion_example.py
37 | :pyobject: iterate_files
38 |
39 | """
40 | import os
41 |
42 | """
43 | Copyright 2019 Chapin Bryce
44 |
45 | Permission is hereby granted, free of charge, to any person
46 | obtaining a copy of this software and associated documentation
47 | files (the "Software"), to deal in the Software without
48 | restriction, including without limitation the rights to use, copy,
49 | modify, merge, publish, distribute, sublicense, and/or sell copies
50 | of the Software, and to permit persons to whom the Software is
51 | furnished to do so, subject to the following conditions:
52 |
53 | The above copyright notice and this permission notice shall be
54 | included in all copies or substantial portions of the Software.
55 |
56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
57 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
58 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
59 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
60 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
61 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
63 | DEALINGS IN THE SOFTWARE.
64 | """
65 |
66 | __author__ = "Chapin Bryce"
67 | __date__ = 20190527
68 | __license__ = "MIT Copyright 2019 Chapin Bryce"
69 | __desc__ = """Sample script to iterate over a folder of files."""
70 | __docs__ = ["https://docs.python.org/3/library/os.html"]
71 |
72 |
73 | def list_directory(path):
74 | """List all file and folder entries in `path`.
75 |
76 | Args:
77 | path (str): A directory within a mounted file system. May be relative or
78 | absolute.
79 |
80 | Examples:
81 | >>> list_directory('.')
82 |
83 | """
84 | print(f"Files and folders in '{os.path.abspath(path)}':")
85 | # Quick and easy method for listing items within a single
86 | # folder.
87 | for entry in os.listdir(path):
88 | # Print all entry names
89 | print(f"\t{entry}")
90 |
91 |
92 | def iterate_files(path):
93 | """Recursively iterate over a path, findings all files within the folder
94 | and its subdirectories.
95 |
96 | Args:
97 | path (str): A directory within a mounted file system. May be relative or
98 | absolute.
99 |
100 | Examples:
101 | >>> number_of_py_files = 0
102 | >>> for f in iterate_files('../'):
103 | ... if f.endswith('.py'):
104 | ... number_of_py_files += 1
105 | >>> print(f"\t{number_of_py_files} python files found "
106 | ... f"in {os.path.abspath('../')}")
107 | """
108 | # Though `os.walk()` exposes a list of directories in the
109 | # current `root`, it is rarely used since we are generally
110 | # interested in the files found within the subdirectories.
111 | # For this reason, it is common to see `dirs` named `_`.
112 | # DO NOT NAME `dirs` as `dir` since `dir` is a reserved word!
113 | for root, dirs, files in os.walk(os.path.abspath(path)):
114 | # Both `dirs` and `files` are lists containing all entries
115 | # at the current `root`.
116 | for file_name in files:
117 | # To effectively reference a file, you should include
118 | # the below line which creates a full path reference
119 | # to the specific file, regardless of how nested it is
120 | # We can then hand `file_entry` off to other functions.
121 | yield os.path.join(root, file_name)
122 |
123 |
124 | if __name__ == "__main__":
125 | abspath = os.path.abspath
126 | print(f"Listing {abspath('.')}")
127 | list_directory(".")
128 | print(f"\nRecursively counting files in {abspath('../../')}")
129 | num_py_files = 0
130 | for file_entry in iterate_files("../"):
131 | if file_entry.endswith(".py"):
132 | num_py_files += 1
133 | print(f"\t{num_py_files} python files found in {abspath('../')}")
134 |
--------------------------------------------------------------------------------
/pyforhandbook/ch02_registry/NTUSER-WIN7-trustrecords.DAT:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chapinb/python-forensics-handbook/6f28f39639d8add4e553f8986edc75308a914b0b/pyforhandbook/ch02_registry/NTUSER-WIN7-trustrecords.DAT
--------------------------------------------------------------------------------
/pyforhandbook/ch02_registry/__init__.py:
--------------------------------------------------------------------------------
1 | """Registry Script Snippets
2 |
3 | Parsing registry hives is a common task for Windows host analysis.
4 | The ``yarp`` library is a robust library for parsing registry
5 | hives and this section will show examples of how to use it.
6 | """
7 |
--------------------------------------------------------------------------------
/pyforhandbook/ch02_registry/yarp_base.py:
--------------------------------------------------------------------------------
1 | """Using the `yarp` library to open Windows registry hives using a class
2 | structure that is very portable and flexible.
3 |
4 | Example Usage:
5 |
6 | ``$ python yarp_base.py {NTUSER HIVE}``
7 |
8 | References:
9 |
10 | * https://github.com/msuhanov/yarp
11 | * https://docs.python.org/3/library/struct.html
12 | * https://docs.python.org/3/library/datetime.html
13 |
14 |
15 | Opening a Registry Hive
16 | =======================
17 |
18 | This class demonstrates how to open a registry hive file with the `yarp` tool.
19 | This library not only allows us to open a single offline hive, but also
20 | leverage any available transaction logs to include additional information
21 | otherwise available on the Window's system. This class handles both the opening
22 | of the primary hive and attempted recovery of the transaction logs.
23 |
24 | .. literalinclude:: ../pyforhandbook/ch02_registry/yarp_base.py
25 | :pyobject: RegistryBase
26 |
27 | Docstring References
28 | ====================
29 | """
30 | # Installed via:
31 | # pip install https://github.com/msuhanov/yarp/archive/1.0.28.tar.gz
32 | from yarp import Registry, RegistryHelpers
33 |
34 |
35 | """
36 | Copyright 2019 Chapin Bryce
37 |
38 | Permission is hereby granted, free of charge, to any person
39 | obtaining a copy of this software and associated documentation
40 | files (the "Software"), to deal in the Software without
41 | restriction, including without limitation the rights to use, copy,
42 | modify, merge, publish, distribute, sublicense, and/or sell copies
43 | of the Software, and to permit persons to whom the Software is
44 | furnished to do so, subject to the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be
47 | included in all copies or substantial portions of the Software.
48 |
49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
51 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
52 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
53 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
54 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
56 | DEALINGS IN THE SOFTWARE.
57 | """
58 |
59 | __author__ = "Chapin Bryce"
60 | __date__ = 20190707
61 | __license__ = "MIT Copyright 2019 Chapin Bryce"
62 | __desc__ = """Registry parsing class that opens an offline hive."""
63 | __docs__ = [
64 | "https://github.com/msuhanov/yarp",
65 | "https://docs.python.org/3/library/datetime.html",
66 | "https://docs.python.org/3/library/struct.html",
67 | ]
68 |
69 |
70 | class RegistryBase:
71 | """Base class containing common registry parsing code. Will open a hive
72 | and attempt recovery using available transaction logs"""
73 |
74 | def __init__(self, reg_file):
75 | """Base __init__ method, responsible for opening a hive."""
76 | self.reg_file = reg_file
77 | self.tx_log_files = []
78 | self.hive = None
79 | self._open_hive()
80 |
81 | def _open_hive(self):
82 | """Open a registry hive with yarp. Must be an open file object with read
83 | permissions. Will attempt to recover the hive with transaction logs if
84 | present.
85 | """
86 | self.hive = Registry.RegistryHive(self.reg_file)
87 | self._recover_hive()
88 |
89 | def _recover_hive(self):
90 | """Search for transaction logs and attempt recovery of the hive."""
91 | hive_path = self.hive.registry_file.file_object.name
92 | tx_logs = RegistryHelpers.DiscoverLogFiles(hive_path)
93 | self.tx_log_files = []
94 | for tx_path in ["log_path", "log1_path", "log2_path"]:
95 | log_obj = None
96 | if getattr(tx_logs, tx_path, None):
97 | log_obj = open(getattr(tx_logs, tx_path), "rb")
98 | self.tx_log_files.append(log_obj)
99 | self.hive.recover_auto(*self.tx_log_files)
100 |
101 | def close(self):
102 | """Properly close a hive."""
103 | self.hive = None
104 | self.reg_file.close()
105 | for log in self.tx_log_files:
106 | if log:
107 | log.close()
108 |
109 |
110 | def main(reg_file):
111 | reg = RegistryBase(reg_file)
112 | print("Hive: " + reg.hive.registry_file.file_object.name)
113 | print("Last written time: " + reg.hive.last_written_timestamp().isoformat())
114 | print("Root key: " + reg.hive.root_key().name())
115 |
116 |
117 | if __name__ == "__main__":
118 | import argparse
119 |
120 | parser = argparse.ArgumentParser(
121 | description="Registry Parsing",
122 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
123 | epilog=f"Built by {__author__}, v.{__date__}",
124 | )
125 | parser.add_argument(
126 | "REG_FILE", help="Path to registry file", type=argparse.FileType("rb")
127 | )
128 | args = parser.parse_args()
129 | main(args.REG_FILE)
130 |
--------------------------------------------------------------------------------
/pyforhandbook/ch02_registry/yarp_ntuser.py:
--------------------------------------------------------------------------------
1 | """Using the `yarp` library to parse NTUSER.DAT Windows registry hives
2 | using a class structure that is very portable and flexible. Parses the
3 | MountPoints2 and TrustRecords keys for with string and binary values.
4 |
5 | Example Usage:
6 |
7 | ``$ python yarp_ntuser.py {NTUSER HIVE}``
8 |
9 | References:
10 |
11 | * https://github.com/msuhanov/yarp
12 | * https://docs.python.org/3/library/struct.html
13 | * https://docs.python.org/3/library/datetime.html
14 |
15 | Creating a Hive Specific Parser
16 | ===============================
17 |
18 | Since we have a strong base class providing functionality to open hives, we can
19 | build hive specific parsing classes that are tailored to handle artifacts
20 | distinct to a single hive type. In this case we set up a class to handle
21 | NTUSER.DAT files, though could get more specific on Windows versions, etc. In
22 | this class we store a few useful details including fixed values used by other
23 | methods and metadata about the class.
24 |
25 | .. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
26 | :pyobject: NTUSER.__init__
27 |
28 | Reading Hive String Values
29 | ==========================
30 |
31 | With an open hive, we can begin to parse values from a known key location
32 | within the hive. This method allows us to specify a key path and inspect each
33 | of the sub-keys. For each of the sub-keys, we can then get the names and data
34 | associated with each value in the key. Additionally we could - if needed -
35 | continue to recurse on sub-keys here. Instead we return this cursory information
36 | for the caller to display as they wish. Since the values within MountPoints2
37 | store string data, we don't need to perform further parsing of the record.
38 |
39 | .. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
40 | :pyobject: NTUSER.parse_mount_points2
41 |
42 | Reading Hive Binary Values
43 | ==========================
44 |
45 | Similarly to our prior example, we can get a key by path. In this case we don't
46 | have a sense of what Office versions are available in the key and have elected
47 | to iterate through each of those using the `parse_office_versions()` method.
48 | Using each of the versions, we then access the respective `TrustRecords` key.
49 | If found, we then parse the binary data (retrieved with the same `.data()`
50 | method) using Struct to extract a timestamp and integer marking whether a
51 | trusted macro was used. These parsed attributes are then returned to the caller
52 | to be displayed.
53 |
54 | .. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
55 | :pyobject: NTUSER.parse_trust_records
56 |
57 | Docstring References
58 | ====================
59 | """
60 |
61 | from datetime import datetime, timedelta
62 | import struct
63 |
64 | from pyforhandbook.ch02_registry.yarp_base import RegistryBase
65 |
66 |
67 | """
68 | Copyright 2019 Chapin Bryce
69 |
70 | Permission is hereby granted, free of charge, to any person
71 | obtaining a copy of this software and associated documentation
72 | files (the "Software"), to deal in the Software without
73 | restriction, including without limitation the rights to use, copy,
74 | modify, merge, publish, distribute, sublicense, and/or sell copies
75 | of the Software, and to permit persons to whom the Software is
76 | furnished to do so, subject to the following conditions:
77 |
78 | The above copyright notice and this permission notice shall be
79 | included in all copies or substantial portions of the Software.
80 |
81 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
82 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
83 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
84 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
85 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
86 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
87 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
88 | DEALINGS IN THE SOFTWARE.
89 | """
90 |
91 | __author__ = "Chapin Bryce"
92 | __date__ = 20190707
93 | __license__ = "MIT Copyright 2019 Chapin Bryce"
94 | __desc__ = """Registry parsing class that parses the NTUSER.DAT hive."""
95 | __docs__ = [
96 | "https://github.com/msuhanov/yarp",
97 | "https://docs.python.org/3/library/datetime.html",
98 | "https://docs.python.org/3/library/struct.html",
99 | ]
100 |
101 |
102 | class NTUSER(RegistryBase):
103 | """Class to handle the parsing of the NTUSER.DAT hive."""
104 |
105 | def __init__(self, reg_path):
106 | super().__init__(reg_path)
107 | self.hive_type = "NTUSER.DAT"
108 | self.macro_enabled_val = 2147483647
109 |
110 | def parse_mount_points2(self):
111 | """Demonstration of parsing values from a key by path."""
112 | key_path = (
113 | "Software\\Microsoft\\Windows\\CurrentVersion"
114 | "\\Explorer\\MountPoints2"
115 | )
116 | for mp in self.hive.find_key(key_path).subkeys():
117 | yield {
118 | "name": mp.name().replace("#", "\\"),
119 | "values": {x.name(): x.data() for x in mp.values()},
120 | "last_written": mp.last_written_timestamp(),
121 | }
122 |
123 | def parse_office_versions(self):
124 | """Get Office versions within an open Registry hive.
125 |
126 | Yields:
127 | (str): Office version number (ie. '15.0')
128 | """
129 | office_versions = self.hive.find_key("Software\\Microsoft\\Office")
130 | for sub_key in office_versions.subkeys():
131 | key_name = sub_key.name()
132 | try:
133 | _ = float(key_name)
134 | is_ver_num = True
135 | except ValueError:
136 | is_ver_num = False
137 |
138 | if is_ver_num:
139 | yield key_name
140 |
141 | def parse_trust_records(self):
142 | """Demonstration of parsing binary values within a key."""
143 | trust_record_path = (
144 | "Software\\Microsoft\\Office\\{OFFICE_VERSION}"
145 | "\\Word\\Security\\Trusted Documents\\TrustRecords"
146 | )
147 | for office_version in self.parse_office_versions():
148 | trust_rec_key = self.hive.find_key(
149 | trust_record_path.format(OFFICE_VERSION=office_version)
150 | )
151 | if not trust_rec_key:
152 | continue
153 |
154 | for rec in trust_rec_key.values():
155 | date_val, macro_enabled = struct.unpack("q12xI", rec.data())
156 | ms = date_val / 10.0
157 | dt_date = datetime(1601, 1, 1) + timedelta(microseconds=ms)
158 | yield {
159 | "doc": rec.name(),
160 | "dt": dt_date.isoformat(),
161 | "macro": macro_enabled == self.macro_enabled_val,
162 | }
163 |
164 |
165 | def main(reg_file):
166 | reg = NTUSER(reg_file)
167 | # Call an example parsing method and display the values from NTUSER keys
168 | print("{:=^30}".format(" MountPoints2 "))
169 | for mount_point in reg.parse_mount_points2():
170 | print(f"Found MountPoints2 path '{mount_point['name']}' with values:")
171 | value_str = "\tlast written time: {}\n".format(
172 | mount_point["last_written"].isoformat()
173 | )
174 | value_str += "\n".join(
175 | [f"\t{x}: {y}" for x, y in mount_point["values"].items()]
176 | )
177 | print(value_str)
178 |
179 | print("{:=^30}".format(" TrustRecords "))
180 | for tr in reg.parse_trust_records():
181 | print(f"Document: {tr['doc']}")
182 | print(f"\tCreated Date: {tr['dt']}")
183 | print(f"\tMacro Enabled: {tr['macro']}")
184 |
185 |
186 | if __name__ == "__main__":
187 | import argparse
188 |
189 | parser = argparse.ArgumentParser(
190 | description="Registry Parsing",
191 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
192 | epilog=f"Built by {__author__}, v.{__date__}",
193 | )
194 | parser.add_argument(
195 | "REG_FILE", help="Path to registry file", type=argparse.FileType("rb")
196 | )
197 | args = parser.parse_args()
198 | main(args.REG_FILE)
199 |
--------------------------------------------------------------------------------
/pyforhandbook/ch03_event_logs/__init__.py:
--------------------------------------------------------------------------------
1 | """Windows Event Log Snippets
2 |
3 | Parsing event logs is a common task for Windows host analysis.
4 | The ``python-evtx`` library is a robust library for parsing event logs
5 | and this section will show examples of how to leverage this library to
6 | answer common questions in the event log.
7 | """
8 |
--------------------------------------------------------------------------------
/pyforhandbook/ch03_event_logs/using_python_evtx.py:
--------------------------------------------------------------------------------
1 | """Example for opening EVTX files, iterating over events, and filtering events.
2 |
3 | Demonstrates how to open an EVTX file and get basic details about the event log.
4 | This section makes use of python-evtx, a python library for reading event log
5 | files. To install, run ``pip install python-evtx``.
6 |
7 | Other libraries for parsing these event logs exist and we welcome others to
8 | add snippets that showcase how to make use of them in reading EVTX files.
9 |
10 | Example Usage:
11 |
12 | ``$ python using_python_evtx.py System.evtx``
13 |
14 | References:
15 |
16 | * https://github.com/williballenthin/python-evtx
17 |
18 |
19 | Open Windows Event Logs (EVTX)
20 | ==============================
21 |
22 | This function shows an example of opening an EVTX file and parsing out several
23 | header metadata parameters about the file.
24 |
25 | .. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
26 | :pyobject: open_evtx
27 |
28 | Iterate over record XML data (EVTX)
29 | ===================================
30 |
31 | In this function, we iterate over the records within an EVTX file and expose
32 | the raw XML. This leverages a yield generator for
33 | low impact on resources.
34 |
35 | Additionally, if you would like to parse the XML, or interact with the child
36 | elements, you can enable it by assigning the `parse_xml` parameter as True,
37 | which will then call the ``.lxml()`` method on the individual event record.
38 | This requires the installation of the lxml Library, as it returns a lxml.etree
39 | object that you can interact with.
40 |
41 | .. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
42 | :pyobject: get_events
43 |
44 | Filtering records within events logs
45 | ====================================
46 |
47 | Now that we have :func:`get_events()`, we can begin to perform operations on
48 | the newly accessible data. In this function, we extract information from the
49 | LXML object, and use that to filter results based on Event ID and other fields
50 | within the results. You can easily extend this to support other fields,
51 | filters, and return values. Some examples include:
52 |
53 | - extracting all login and logoff events, with their session identifiers,
54 | then calculating the session durations
55 | - Identify PowerShell events and expose arguments for further processing
56 | (ie. Base64 decoding, shellcode analysis)
57 |
58 | .. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
59 | :pyobject: filter_events_json
60 |
61 | Docstring References
62 | ====================
63 | """
64 | import json
65 | from collections import OrderedDict
66 | import Evtx.Evtx as evtx
67 |
68 |
69 | """
70 | Copyright 2019 Chapin Bryce
71 |
72 | Permission is hereby granted, free of charge, to any person
73 | obtaining a copy of this software and associated documentation
74 | files (the "Software"), to deal in the Software without
75 | restriction, including without limitation the rights to use, copy,
76 | modify, merge, publish, distribute, sublicense, and/or sell copies
77 | of the Software, and to permit persons to whom the Software is
78 | furnished to do so, subject to the following conditions:
79 |
80 | The above copyright notice and this permission notice shall be
81 | included in all copies or substantial portions of the Software.
82 |
83 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
84 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
85 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
86 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
87 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
88 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
89 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
90 | DEALINGS IN THE SOFTWARE.
91 | """
92 |
93 | __author__ = "Chapin Bryce"
94 | __date__ = 20191103
95 | __license__ = "MIT Copyright 2019 Chapin Bryce"
96 | __desc__ = """Sample script to read EVTX files."""
97 | __docs__ = ["https://github.com/williballenthin/python-evtx"]
98 |
99 |
100 | def open_evtx(input_file):
101 | """Opens a Windows Event Log and displays common log parameters.
102 |
103 | Arguments:
104 | input_file (str): Path to evtx file to open
105 |
106 | Examples:
107 | >>> open_evtx("System.evtx")
108 | File version (major): 3
109 | File version (minor): 1
110 | File is ditry: True
111 | File is full: False
112 | Next record number: 10549
113 |
114 | """
115 |
116 | with evtx.Evtx(input_file) as open_log:
117 | header = open_log.get_file_header()
118 | properties = OrderedDict(
119 | [
120 | ("major_version", "File version (major)"),
121 | ("minor_version", "File version (minor)"),
122 | ("is_dirty", "File is dirty"),
123 | ("is_full", "File is full"),
124 | ("next_record_number", "Next record number"),
125 | ]
126 | )
127 |
128 | for key, value in properties.items():
129 | print(f"{value}: {getattr(header, key)()}")
130 |
131 |
132 | def get_events(input_file, parse_xml=False):
133 | """Opens a Windows Event Log and returns XML information from
134 | the event record.
135 |
136 | Arguments:
137 | input_file (str): Path to evtx file to open
138 | parse_xml (bool): If True, return an lxml object, otherwise a string
139 |
140 | Yields:
141 | (generator): XML information in object or string format
142 |
143 | Examples:
144 | >>> for event_xml in enumerate(get_events("System.evtx")):
145 | >>> print(event_xml)
146 |
147 | """
148 | with evtx.Evtx(input_file) as event_log:
149 | for record in event_log.records():
150 | if parse_xml:
151 | yield record.lxml()
152 | else:
153 | yield record.xml()
154 |
155 |
156 | def filter_events_json(event_data, event_ids, fields=None):
157 | """Provide events where the event id is found within the provided list
158 | of event ids. If found, it will return a JSON formatted object per event.
159 |
160 | If a list of fields are provided, it will filter the resulting JSON event
161 | object to contain only those fields.
162 |
163 | Arguments:
164 | event_data (genertor): Iterable containing event data as XML. Preferably
165 | the result of the :func:`get_events()` method.
166 | event_ids (list): A list of event identifiers. Each element should be a
167 | string value, even though the identifier is an integer.
168 | fields (list): Collection of fields from the XML data to include in the
169 | JSON output. Only supports top-level fields.
170 |
171 | Yields:
172 | (dict): A dictionary containing the filtered record information
173 |
174 | Example:
175 |
176 | >>> filtered_logins = filter_events_json(
177 | >>> get_events("System.evtx", parse_xml=True),
178 | >>> event_ids=['4624', '4625'],
179 | >>> fields=["SubjectUserName", "SubjectUserSid",
180 | >>> "SubjectDomainName", "TargetUserName", "TargetUserSid",
181 | >>> "TargetDomainName", "WorkstationName", "IpAddress",
182 | >>> "IpPort", "ProcessName"]
183 | >>> )
184 | >>> for filtered_login in filtered_logins:
185 | >>> print(json.dumps(filtered_login, indent=2))
186 |
187 | """
188 | for evt in event_data:
189 | system_tag = evt.find("System", evt.nsmap)
190 | event_id = system_tag.find("EventID", evt.nsmap)
191 | if event_id.text in event_ids:
192 | event_data = evt.find("EventData", evt.nsmap)
193 | json_data = {}
194 | for data in event_data.getchildren():
195 | if not fields or data.attrib["Name"] in fields:
196 | # If we don't have a specified field filter list, print all
197 | # Otherwise filter for only those fields within the list
198 | json_data[data.attrib["Name"]] = data.text
199 |
200 | yield json_data
201 |
202 |
203 | if __name__ == "__main__":
204 | import argparse
205 |
206 | parser = argparse.ArgumentParser(
207 | description=__desc__,
208 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
209 | epilog=f"Built by {__author__}, v.{__date__}",
210 | )
211 | parser.add_argument("EVTX_FILE", help="EVTX file to read")
212 | args = parser.parse_args()
213 |
214 | print("EVTX File Header Information")
215 | open_evtx(args.EVTX_FILE)
216 | print("EVTX File records")
217 | for count, event in enumerate(get_events(args.EVTX_FILE)):
218 | if count >= 3:
219 | break
220 | print(event)
221 |
222 | print("Filter for Login events")
223 | logins = filter_events_json(
224 | get_events(args.EVTX_FILE, parse_xml=True),
225 | event_ids=["4624"],
226 | fields=[
227 | "SubjectUserName",
228 | "SubjectUserSid",
229 | "SubjectDomainName",
230 | "TargetUserName",
231 | "TargetUserSid",
232 | "TargetDomainName",
233 | "WorkstationName",
234 | "IpAddress",
235 | "IpPort",
236 | "ProcessName",
237 | ],
238 | )
239 | for login in logins:
240 | print(json.dumps(login, indent=2))
241 |
--------------------------------------------------------------------------------
/pyforhandbook/ch06_databases/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chapinb/python-forensics-handbook/6f28f39639d8add4e553f8986edc75308a914b0b/pyforhandbook/ch06_databases/__init__.py
--------------------------------------------------------------------------------
/pyforhandbook/ch06_databases/opening_sqlite.py:
--------------------------------------------------------------------------------
1 | """Example for opening and exploring Sqlite databases.
2 |
3 | Example Usage:
4 |
5 | ``$ python opening_sqlite.py history_db``
6 |
7 | References:
8 |
9 | * https://docs.python.org/3/library/argparse.html
10 | * https://docs.python.org/3/library/os.html
11 | * https://docs.python.org/3/library/sqlite3.html
12 |
13 | Opening Sqlite configuration
14 | ============================
15 |
16 | This function shows an example of opening a Sqlite database with Python.
17 | Additional information regarding Sqlite modules can be
18 | seen at https://docs.python.org/3/library/sqlite3.html.
19 |
20 | .. literalinclude:: ../pyforhandbook/ch06_databases/opening_sqlite.py
21 | :pyobject: open_sqlite
22 |
23 | Listing Tables configuration
24 | ============================
25 |
26 | This function shows an example of listing available tables in an opened Sqlite
27 | database.
28 |
29 | .. literalinclude:: ../pyforhandbook/ch06_databases/opening_sqlite.py
30 | :pyobject: list_tables
31 | """
32 | import argparse
33 | import sqlite3
34 |
35 | """
36 | Copyright 2019 Brittney Argirakis
37 |
38 | Permission is hereby granted, free of charge, to any person
39 | obtaining a copy of this software and associated documentation
40 | files (the "Software"), to deal in the Software without
41 | restriction, including without limitation the rights to use, copy,
42 | modify, merge, publish, distribute, sublicense, and/or sell copies
43 | of the Software, and to permit persons to whom the Software is
44 | furnished to do so, subject to the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be
47 | included in all copies or substantial portions of the Software.
48 |
49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
51 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
52 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
53 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
54 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
56 | DEALINGS IN THE SOFTWARE.
57 | """
58 |
59 | __author__ = "Brittney Argirakis"
60 | __date__ = 20191126
61 | __license__ = "MIT Copyright 2019 Brittney Argirakis"
62 | __desc__ = """Sample script to open a SqLite DB."""
63 | __docs__ = [
64 | "https://docs.python.org/3/library/argparse.html",
65 | "https://docs.python.org/3/library/os.html",
66 | "https://docs.python.org/3/library/sqlite3.html",
67 | ]
68 |
69 |
70 | def open_sqlite(input_db):
71 | """Open a SQLite database
72 |
73 | Args:
74 | input_db: Path to a SQLite database to open
75 |
76 | Returns:
77 | A connection to a SQLite database
78 | """
79 | print("Provided Database: {}".format(input_db))
80 | return sqlite3.connect(input_db)
81 |
82 |
83 | def list_tables(conn):
84 | """List all tables in a SQLite database
85 |
86 | Args:
87 | conn: An open connection from a SQLite database
88 |
89 | Returns:
90 | list: List of table names found in the database
91 | """
92 | cur = conn.cursor()
93 | cur.execute("SELECT name FROM sqlite_master")
94 | return [i[0] for i in cur.fetchall()]
95 |
96 |
97 | if __name__ == "__main__":
98 | parser = argparse.ArgumentParser(
99 | description=__desc__,
100 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
101 | epilog=f"Built by {__author__}, v.{__date__}",
102 | )
103 | parser.add_argument("db", help="path to the database to read")
104 | args = parser.parse_args()
105 | connection = open_sqlite(args.db)
106 | listed_tables = list_tables(connection)
107 |
108 | print(listed_tables)
109 |
--------------------------------------------------------------------------------
/pyforhandbook/version.py:
--------------------------------------------------------------------------------
1 | __author__ = "Chapin Bryce"
2 | __authors__ = ["Chapin Bryce", "Brittney Argirakis"]
3 | __license__ = "MIT"
4 | __version__ = "0.1.2"
5 | __copyright__ = "2020, Chapin Bryce"
6 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | .
2 | alabaster==0.7.12
3 | Sphinx==3.2.1
4 | sphinx-rtd-theme==0.5.0
5 | black==20.8b1
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | from pyforhandbook import version
4 |
5 | setuptools.setup(
6 | name="pyforhandbook",
7 | version=version.__version__,
8 | author=version.__author__,
9 | author_email="python@chapinb.com",
10 | description="Handbook of Python functions for rapid development "
11 | "of forensic tools.",
12 | url="https://chapinb.com/python-forensic-handbook",
13 | packages=setuptools.find_packages(),
14 | install_requires=[
15 | 'yarp @ git+https://github.com/msuhanov/yarp@1.0.28#egg=yarp',
16 | 'python-evtx==0.6.1',
17 | 'lxml==4.5.2',
18 | ],
19 | classifiers=[
20 | "Programming Language :: Python :: 3.5",
21 | "Programming Language :: Python :: 3.6",
22 | "Programming Language :: Python :: 3.7",
23 | "Programming Language :: Python :: 3.8",
24 | "Programming Language :: Python :: 3 :: Only",
25 | "License :: OSI Approved :: MIT License",
26 | "Operating System :: OS Independent",
27 | "Development Status :: 5 - Production/Stable",
28 | "Intended Audience :: Telecommunications Industry",
29 | "Intended Audience :: System Administrators",
30 | "Intended Audience :: Other Audience",
31 | "Intended Audience :: Information Technology",
32 | "Intended Audience :: Developers",
33 | "Intended Audience :: Education",
34 | "Natural Language :: English",
35 | "Topic :: Scientific/Engineering :: Information Analysis",
36 | "Topic :: Security",
37 | "Topic :: Utilities",
38 | "Topic :: Documentation",
39 | "Topic :: Software Development :: Documentation"
40 | ]
41 | )
42 |
--------------------------------------------------------------------------------