Restructure of code for easier reference.
* Updated doc strings * Added examples * Addressed linting issues
This commit is contained in:
parent
77c41e1a55
commit
46cced1881
|
|
@ -1,4 +1,4 @@
|
|||
Section 1 - Essential Scripts
|
||||
Chapter 1 - Essential Scripts
|
||||
==============================
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -6,27 +6,27 @@ Section 1 - Essential Scripts
|
|||
|
||||
Section 1.1 - Argparse Example
|
||||
--------------------------------
|
||||
.. automodule:: pyforhandbook.section_01.argparse_example
|
||||
.. automodule:: pyforhandbook.ch01_essentials.argparse_example
|
||||
:members:
|
||||
|
||||
Section 1.2 - Logging Example
|
||||
--------------------------------
|
||||
.. automodule:: pyforhandbook.section_01.logging_example
|
||||
.. automodule:: pyforhandbook.ch01_essentials.logging_example
|
||||
:members:
|
||||
|
||||
Section 1.3 - Open Files
|
||||
------------------------
|
||||
.. automodule:: pyforhandbook.section_01.open_files
|
||||
.. automodule:: pyforhandbook.ch01_essentials.open_files
|
||||
:members:
|
||||
|
||||
Section 1.4 - CSV Example
|
||||
--------------------------------
|
||||
.. automodule:: pyforhandbook.section_01.csv_example
|
||||
.. automodule:: pyforhandbook.ch01_essentials.csv_example
|
||||
:members:
|
||||
|
||||
Section 1.5 - Directory Recursion
|
||||
---------------------------------
|
||||
.. automodule:: pyforhandbook.section_01.recursion_example
|
||||
.. automodule:: pyforhandbook.ch01_essentials.recursion_example
|
||||
:members:
|
||||
|
||||
Indices and tables
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Section 2 - Registry Parsing
|
||||
Chapter 2 - Registry Parsing
|
||||
==============================
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -6,12 +6,12 @@ Section 2 - Registry Parsing
|
|||
|
||||
Section 2.1 - Opening a Hive
|
||||
--------------------------------
|
||||
.. automodule:: pyforhandbook.section_02.yarp_base
|
||||
.. automodule:: pyforhandbook.ch02_registry.yarp_base
|
||||
:members:
|
||||
|
||||
Section 2.2 - Parsing Hive Values
|
||||
----------------------------------
|
||||
.. automodule:: pyforhandbook.section_02.yarp_ntuser
|
||||
.. automodule:: pyforhandbook.ch02_registry.yarp_ntuser
|
||||
:members:
|
||||
|
||||
Indices and tables
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Section 3 - Windows Event Log Parsing
|
||||
Chapter 3 - Windows Event Log Parsing
|
||||
=====================================
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -6,7 +6,7 @@ Section 3 - Windows Event Log Parsing
|
|||
|
||||
Section 3.1 - Using python-evtx
|
||||
----------------------------------
|
||||
.. automodule:: pyforhandbook.section_03.using_python_evtx
|
||||
.. automodule:: pyforhandbook.ch03_event_logs.using_python_evtx
|
||||
:members:
|
||||
|
||||
Indices and tables
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Section 6 - Sqlite & MacOS/Mobile/Browsers
|
||||
Chapter 6 - Sqlite & MacOS/Mobile/Browsers
|
||||
==========================================
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -6,7 +6,7 @@ Section 6 - Sqlite & MacOS/Mobile/Browsers
|
|||
|
||||
Section 6.1 - Opening Sqlite
|
||||
--------------------------------
|
||||
.. automodule:: pyforhandbook.section_06.opening_sqlite
|
||||
.. automodule:: pyforhandbook.ch06_databases.opening_sqlite
|
||||
:members:
|
||||
|
||||
Indices and tables
|
||||
|
|
@ -5,18 +5,18 @@
|
|||
|
||||
The Python Forensics Handbook
|
||||
=============================
|
||||
---------------------------------------------
|
||||
A guide for developing Python scripts in DFIR
|
||||
---------------------------------------------
|
||||
--------------------------------------------------------
|
||||
A reference guide for developing Python scripts in DFIR
|
||||
--------------------------------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Table of Contents:
|
||||
|
||||
section1
|
||||
section2
|
||||
section3
|
||||
section6
|
||||
ch01_essentials
|
||||
ch02_registry
|
||||
ch03_event_logs
|
||||
ch06_databases
|
||||
|
||||
Handbook Sections
|
||||
==============================
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
IN DEVELOPMENT - More sections will release over the coming weeks/months/as
|
||||
time permits. Feel free to contribute as you have an idea or time to assist,
|
||||
otherwise stay tuned!
|
||||
.. note::
|
||||
IN DEVELOPMENT - More sections will release over the coming weeks/months/as
|
||||
time permits. Feel free to contribute as you have an idea or time to assist,
|
||||
otherwise stay tuned!
|
||||
|
||||
This handbook has 7 sections covering common tasks for developing
|
||||
This handbook consists of 7 sections covering common tasks for developing
|
||||
Python scripts for use in DFIR. Each section contains short,
|
||||
portable code blocks that can drop into a new script with minimal
|
||||
tweaking. This way, you can quickly build out your custom script
|
||||
|
|
@ -19,7 +20,7 @@ is hosted on GitHub at https://github.com/chapinb/python-forensics-handbook and
|
|||
available to read online at https://chapinb.com/python-forensics-handbook.
|
||||
Please consider submitting a pull request with your additions!
|
||||
|
||||
Section 1 - Essential Script Elements
|
||||
Chapter 1 - Essential Script Elements
|
||||
-------------------------------------
|
||||
|
||||
This chapter covers code blocks that are useful across scripts
|
||||
|
|
@ -43,9 +44,12 @@ projects to allow for uniformity.
|
|||
* Parallel Processing
|
||||
- Simple implementation of multithreading and multiprocessing
|
||||
|
||||
Section 2 - Registry Hives with YARP
|
||||
Chapter 2 - Registry Hives
|
||||
------------------------------------
|
||||
|
||||
In this chapter, we demonstrate how to open a registry hive, navigate through
|
||||
its keys, and interact with values to expose information for analysis.
|
||||
|
||||
* Using yarp to open a single hive
|
||||
- Opening a hive and recovering data available in transaction logs
|
||||
* Parse registry hive keys and values
|
||||
|
|
@ -56,16 +60,20 @@ Section 2 - Registry Hives with YARP
|
|||
* Searching for a pattern across hive keys and values.
|
||||
- Looking for a provided pattern across the entire hive.
|
||||
|
||||
Section 3 - Event Logs
|
||||
Chapter 3 - Event Logs
|
||||
----------------------
|
||||
|
||||
The functions showcased in this chapter highlight methods to access events
|
||||
within Windows event log files, iterating over the events, and extracting
|
||||
useful records for further examination.
|
||||
|
||||
* Using python-evtx
|
||||
- Opening evtx files
|
||||
- Iterating over events
|
||||
* Parsing Logins (with types, levels, privs)
|
||||
* Parsing Logins
|
||||
- Parse out the commonly investigated 4624/4672 events
|
||||
|
||||
Section 4 - Text logs
|
||||
Chapter 4 - Text logs
|
||||
---------------------
|
||||
|
||||
* Handling IIS Logs
|
||||
|
|
@ -75,30 +83,41 @@ Section 4 - Text logs
|
|||
* Adding in GeoIP
|
||||
- Function to add GeoIP recognition
|
||||
|
||||
Section 5 - API calls & JSON data
|
||||
Chapter 5 - API calls & JSON data
|
||||
---------------------------------
|
||||
|
||||
* VirusTotal
|
||||
* HybridAnalysis
|
||||
* Manipulating JSON
|
||||
|
||||
Section 6 - SQLite & macOS/mobile/browsers
|
||||
Chapter 6 - Databases
|
||||
------------------------------------------
|
||||
|
||||
Databases are found within many applications and operating systems. This chapter
|
||||
covers methods to extract information from these common databases, along with
|
||||
functions that are purpose built to parse information from frequently seen
|
||||
database tables.
|
||||
|
||||
* macOS Activity
|
||||
- KnowledgeC
|
||||
* Andriod SMS
|
||||
* Google Chome History DB
|
||||
* Android SMS
|
||||
* Google Chrome History DB
|
||||
|
||||
Section 7 - Opening forensic images
|
||||
Chapter 7 - Opening forensic images
|
||||
--------------------------------------
|
||||
|
||||
Media acquisition and preservation formats are very common within DFIR and
|
||||
the ability to extract specific contents from these files leads to faster
|
||||
analysis and simplified usage of the tool you are building. With these functions
|
||||
you can read files from a forensic image and pass them straight to your other
|
||||
utilities for further parsing.
|
||||
|
||||
* LibEWF
|
||||
- Expose an E01 as a raw image
|
||||
* PyTSK
|
||||
- Read data from a raw image (MBR)
|
||||
- Read data from a file (hashing)
|
||||
- Iterate through folders (file listing)
|
||||
- Perform targetted reads (file sigs)
|
||||
- Perform targeted reads (file signatures)
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ how to set default values and boolean arguments. the ``argparse``
|
|||
module has many more features documented at
|
||||
https://docs.python.org/3/library/argparse.html
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/argparse_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/argparse_example.py
|
||||
:pyobject: setup_argparse
|
||||
|
||||
"""
|
||||
|
|
@ -51,44 +51,51 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190527
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to accept command line arguments.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to accept command line arguments."""
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/argparse.html',
|
||||
'https://docs.python.org/3/library/os.html',
|
||||
'https://docs.python.org/3/library/pathlib.html'
|
||||
"https://docs.python.org/3/library/argparse.html",
|
||||
"https://docs.python.org/3/library/os.html",
|
||||
"https://docs.python.org/3/library/pathlib.html",
|
||||
]
|
||||
|
||||
|
||||
def setup_argparse():
|
||||
# Setup a parser instance with common fields including a
|
||||
# description and epilog. The `formatter_class` instructs
|
||||
# argparse to show default values set for parameters.
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Sample Argparse',
|
||||
description="Sample Argparse",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
|
||||
# The simplest form of adding an argument, the name of the
|
||||
# parameter and a description of its form.
|
||||
parser.add_argument('INPUT_FILE', help="Input file to parse")
|
||||
parser.add_argument('OUTPUT_FOLDER',
|
||||
help="Folder to store output")
|
||||
parser.add_argument("INPUT_FILE", help="Input file to parse")
|
||||
parser.add_argument("OUTPUT_FOLDER", help="Folder to store output")
|
||||
|
||||
# An optional argument with multiple methods of specifying
|
||||
# the parameter. Includes a default value
|
||||
parser.add_argument('-l', '--log', help="Path to log file",
|
||||
default=os.path.abspath(os.path.join(
|
||||
PurePath(__file__).parent,
|
||||
PurePath(__file__).name.rsplit('.', 1)[0] + '.log'))
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--log",
|
||||
help="Path to log file",
|
||||
default=os.path.abspath(
|
||||
os.path.join(
|
||||
PurePath(__file__).parent,
|
||||
PurePath(__file__).name.rsplit(".", 1)[0] + ".log",
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
# An optional argument which does not accept a value, instead
|
||||
# just modifies functionality.
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='Include debug log messages')
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="store_true", help="Include debug log messages"
|
||||
)
|
||||
|
||||
# Once we've specified our arguments we can parse them for
|
||||
# reference
|
||||
|
|
@ -97,12 +104,13 @@ def setup_argparse():
|
|||
# Returning our parsed arguments for further use.
|
||||
return args
|
||||
|
||||
|
||||
# Only run if called directly (not imported)
|
||||
if __name__ == '__main__':
|
||||
args = setup_argparse()
|
||||
if __name__ == "__main__":
|
||||
cli_args = setup_argparse()
|
||||
|
||||
# Show arguments
|
||||
print(f'Input file: {args.INPUT_FILE}')
|
||||
print(f'Output folder: {args.OUTPUT_FOLDER}')
|
||||
print(f'Log file: {args.log}')
|
||||
print(f'Be verbose?: {args.verbose}')
|
||||
print(f"Input file: {cli_args.INPUT_FILE}")
|
||||
print(f"Output folder: {cli_args.OUTPUT_FOLDER}")
|
||||
print(f"Log file: {cli_args.log}")
|
||||
print(f"Be verbose?: {cli_args.verbose}")
|
||||
|
|
@ -31,10 +31,10 @@ multiple dictionaries to a CSV file. You can optionally provide
|
|||
an ordered list of headers to filter what rows to show, or let the
|
||||
function use the keys of the first dictionary in the list to
|
||||
generate the header information. The latter option may produce
|
||||
a new order each iteration and is not prefered if you can
|
||||
a new order each iteration and is not preferred if you can
|
||||
determine the headers in advance.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/csv_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/csv_example.py
|
||||
:pyobject: write_csv_dicts
|
||||
|
||||
List of ordered lists to CSV
|
||||
|
|
@ -58,7 +58,7 @@ information. Unlike the dictionary option, you cannot filter
|
|||
column data by adjusting the provided headers, you must write all
|
||||
columns to the CSV.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/csv_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/csv_example.py
|
||||
:pyobject: write_csv_lists
|
||||
|
||||
|
||||
|
|
@ -92,13 +92,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190527
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to write to CSV files.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to write to CSV files."""
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/csv.html',
|
||||
'https://docs.python.org/3/library/os.html'
|
||||
"https://docs.python.org/3/library/csv.html",
|
||||
"https://docs.python.org/3/library/os.html",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -110,18 +110,24 @@ def write_csv_dicts(outfile, data, headers=None):
|
|||
data (list): List of dictionaries to write to file
|
||||
headers (list): Header row to use. If empty, will use the
|
||||
first dictionary in the `data` list.
|
||||
|
||||
Example:
|
||||
>>> list_of_dicts = [
|
||||
>>> {'name': 'apple', 'quantity': 10, 'location': 'VT'},
|
||||
>>> {'name': 'orange', 'quantity': 5, 'location': 'FL'}
|
||||
>>> ]
|
||||
>>> write_csv_dicts('dict_test.csv', list_of_dicts)
|
||||
"""
|
||||
|
||||
if not headers:
|
||||
# Use the first line of data
|
||||
headers = [str(x) for x in data[0].keys()]
|
||||
|
||||
with open(outfile, 'w', newline="") as open_file:
|
||||
with open(outfile, "w", newline="") as open_file:
|
||||
# Write only provided headers, ignore others
|
||||
csvfile = csv.DictWriter(open_file, headers,
|
||||
extrasaction='ignore')
|
||||
csvfile.writeheader()
|
||||
csvfile.writerows(data)
|
||||
csv_file = csv.DictWriter(open_file, headers, extrasaction="ignore")
|
||||
csv_file.writeheader()
|
||||
csv_file.writerows(data)
|
||||
|
||||
|
||||
def write_csv_lists(outfile, data, headers=None):
|
||||
|
|
@ -132,35 +138,41 @@ def write_csv_lists(outfile, data, headers=None):
|
|||
data (list): List of lists to write to file
|
||||
headers (list): Header row to use. If empty, will use the
|
||||
first list in the `data` list.
|
||||
|
||||
Examples:
|
||||
>>> fields = ['name', 'quantity', 'location']
|
||||
>>> list_of_lists = [
|
||||
>>> ['apple', 10, 'VT'],
|
||||
>>> ['orange', 5, 'FL']
|
||||
>>> ]
|
||||
>>> write_csv_lists('list_test.csv', list_of_lists, headers=fields)
|
||||
"""
|
||||
|
||||
with open(outfile, 'w', newline="") as open_file:
|
||||
with open(outfile, "w", newline="") as open_file:
|
||||
# Write only provided headers, ignore others
|
||||
csvfile = csv.writer(open_file)
|
||||
csv_file = csv.writer(open_file)
|
||||
for count, entry in enumerate(data):
|
||||
if count == 0 and headers:
|
||||
# If headers are defined, write them, otherwise
|
||||
# continue as they will be written anyways
|
||||
csvfile.writerow(headers)
|
||||
csvfile.writerow(entry)
|
||||
csv_file.writerow(headers)
|
||||
csv_file.writerow(entry)
|
||||
|
||||
sample_dict_data = [
|
||||
{'id': '0', 'city': 'Boston', 'state': 'MA',
|
||||
'country': 'USA'},
|
||||
{'id': '1', 'city': 'New York', 'state': 'NY',
|
||||
'country': 'USA'},
|
||||
{'id': '2', 'city': 'Washington', 'state': 'DC',
|
||||
'country': 'USA'},
|
||||
]
|
||||
|
||||
write_csv_dicts('dict_test.csv', sample_dict_data)
|
||||
if __name__ == "__main__":
|
||||
sample_dict_data = [
|
||||
{"id": "0", "city": "Boston", "state": "MA", "country": "USA"},
|
||||
{"id": "1", "city": "New York", "state": "NY", "country": "USA"},
|
||||
{"id": "2", "city": "Washington", "state": "DC", "country": "USA"},
|
||||
]
|
||||
|
||||
header_row = ['id', 'city', 'state', 'country']
|
||||
sample_list_data = [
|
||||
['0', 'Boston', 'MA', 'USA'],
|
||||
['1', 'New York', 'NY', 'USA'],
|
||||
['2', 'Washington', 'DC', 'USA']
|
||||
]
|
||||
write_csv_dicts("dict_test.csv", sample_dict_data)
|
||||
|
||||
write_csv_lists('list_test.csv', sample_list_data,
|
||||
headers=header_row)
|
||||
header_row = ["id", "city", "state", "country"]
|
||||
sample_list_data = [
|
||||
["0", "Boston", "MA", "USA"],
|
||||
["1", "New York", "NY", "USA"],
|
||||
["2", "Washington", "DC", "USA"],
|
||||
]
|
||||
|
||||
write_csv_lists("list_test.csv", sample_list_data, headers=header_row)
|
||||
|
|
@ -20,7 +20,7 @@ set different logging levels for the two handlers - generally you
|
|||
keep debugging information in the log file while writing more
|
||||
critical messages to the console in STDERR.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/logging_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/logging_example.py
|
||||
:pyobject: setup_logging
|
||||
|
||||
Docstring References
|
||||
|
|
@ -53,61 +53,64 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190527
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to display and write logging
|
||||
messages.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to display and write logging
|
||||
messages."""
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/logging.html',
|
||||
'https://docs.python.org/3/library/os.html'
|
||||
"https://docs.python.org/3/library/logging.html",
|
||||
"https://docs.python.org/3/library/os.html",
|
||||
]
|
||||
|
||||
logger = None
|
||||
logger = logging.getLogger(name=__name__)
|
||||
|
||||
def setup_logging():
|
||||
"""Function to setup logging configuration and test it."""
|
||||
# Allow us to modify the `logger` variable within a function
|
||||
global logger
|
||||
|
||||
# Set logger object, uses module's name
|
||||
logger = logging.getLogger(name=__name__)
|
||||
def setup_logging(logging_obj, verbose=False):
|
||||
"""Function to setup logging configuration and test it.
|
||||
|
||||
# Set default logger level to DEBUG. You can change this later
|
||||
logger.setLevel(logging.DEBUG)
|
||||
Args:
|
||||
logging_obj: A logging instance, returned from logging.getLogger().
|
||||
verbose: Whether or not to enable the debug level in STDERR output.
|
||||
|
||||
# Logging formatter. Best to keep consistent for most usecases
|
||||
Examples:
|
||||
>>> sample_logger = logging.getLogger(name=__name__)
|
||||
>>> sample_logger = setup_logging(sample_logger)
|
||||
>>> sample_logger.debug("This is a debug message")
|
||||
>>> sample_logger.info("This is an info message")
|
||||
>>> sample_logger.warning("This is a warning message")
|
||||
>>> sample_logger.error("This is a error message")
|
||||
>>> sample_logger.critical("This is a critical message")
|
||||
"""
|
||||
logging_obj.setLevel(logging.DEBUG)
|
||||
|
||||
# Logging formatter. Best to keep consistent for most use cases
|
||||
log_format = logging.Formatter(
|
||||
'%(asctime)s %(filename)s %(levelname)s %(module)s '
|
||||
'%(funcName)s %(lineno)d %(message)s')
|
||||
"%(asctime)s %(filename)s %(levelname)s %(module)s "
|
||||
"%(funcName)s %(lineno)d %(message)s"
|
||||
)
|
||||
|
||||
# Setup STDERR logging, allowing you uninterrupted
|
||||
# STDOUT redirection
|
||||
stderr_handle = logging.StreamHandler(stream=sys.stderr)
|
||||
stderr_handle.setLevel(logging.INFO)
|
||||
if verbose:
|
||||
stderr_handle.setLevel(logging.DEBUG)
|
||||
else:
|
||||
stderr_handle.setLevel(logging.INFO)
|
||||
stderr_handle.setFormatter(log_format)
|
||||
|
||||
# Setup file logging
|
||||
file_handle = logging.FileHandler('sample.log', 'a')
|
||||
file_handle = logging.FileHandler("sample.log", "a")
|
||||
file_handle.setLevel(logging.DEBUG)
|
||||
file_handle.setFormatter(log_format)
|
||||
|
||||
# Add handles
|
||||
logger.addHandler(stderr_handle)
|
||||
logger.addHandler(file_handle)
|
||||
logging_obj.addHandler(stderr_handle)
|
||||
logging_obj.addHandler(file_handle)
|
||||
|
||||
# Sample log messages
|
||||
logger.debug("This is a debug message")
|
||||
logger.info("This is an info message")
|
||||
logger.warning("This is a warning message")
|
||||
logger.error("This is a error message")
|
||||
logger.critical("This is a critical message")
|
||||
return logging_obj
|
||||
|
||||
def sample_function():
|
||||
"""Sample function to demonstrate logging formatting."""
|
||||
logger.info("Called from a function")
|
||||
|
||||
sample_function()
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_logging()
|
||||
setup_logging(logger)
|
||||
logger.warning("This is a warning!")
|
||||
|
|
@ -22,7 +22,7 @@ magic value/file signature, this low-tech method will help with parsing a
|
|||
collection of files that may be UTF-8, UTF-16-LE, and UTF-16-BE, three very
|
||||
common text file encodings. Feel free to build and share on this.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/open_files.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/open_files.py
|
||||
:pyobject: open_file
|
||||
|
||||
Docstring References
|
||||
|
|
@ -30,7 +30,6 @@ Docstring References
|
|||
"""
|
||||
|
||||
from io import open
|
||||
import os
|
||||
|
||||
"""
|
||||
Copyright 2019 Chapin Bryce
|
||||
|
|
@ -56,13 +55,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20191103
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to read encoded text files.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to read encoded text files."""
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/csv.html',
|
||||
'https://docs.python.org/3/library/os.html'
|
||||
"https://docs.python.org/3/library/csv.html",
|
||||
"https://docs.python.org/3/library/os.html",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -73,28 +72,29 @@ def open_file(input_file):
|
|||
input_file (str): Path to file to open
|
||||
"""
|
||||
|
||||
test_encoding = open(input_file, 'rb')
|
||||
test_encoding = open(input_file, "rb")
|
||||
bom = test_encoding.read(2)
|
||||
file_encoding = 'utf-8'
|
||||
if bom == b'FEFF':
|
||||
file_encoding = 'utf-16-le'
|
||||
elif bom == b'FFFE':
|
||||
file_encoding = 'utf-16-be'
|
||||
file_encoding = "utf-8"
|
||||
if bom == b"FEFF":
|
||||
file_encoding = "utf-16-le"
|
||||
elif bom == b"FFFE":
|
||||
file_encoding = "utf-16-be"
|
||||
|
||||
with open(input_file, 'r', encoding=file_encoding) as fopen:
|
||||
for raw_line in fopen:
|
||||
with open(input_file, "r", encoding=file_encoding) as open_input_file:
|
||||
for raw_line in open_input_file:
|
||||
line = raw_line.strip()
|
||||
|
||||
print(line)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__desc__,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
parser.add_argument('INPUT_FILE', help="Text file to read")
|
||||
parser.add_argument("INPUT_FILE", help="Text file to read")
|
||||
args = parser.parse_args()
|
||||
|
||||
open_file(args.INPUT_FILE)
|
||||
|
|
@ -19,7 +19,7 @@ folders within a single directory. From here you can further
|
|||
interact with individual files and folders or iterate recursively
|
||||
by calling the function on identified subdirectories.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/recursion_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/recursion_example.py
|
||||
:pyobject: list_directory
|
||||
|
||||
List a directory recursively
|
||||
|
|
@ -33,7 +33,7 @@ handling the processing of files. This sample shows a method of
|
|||
counting the number of files, subdirectories, and files ending in
|
||||
".py" as an example.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_01/recursion_example.py
|
||||
.. literalinclude:: ../pyforhandbook/ch01_essentials/recursion_example.py
|
||||
:pyobject: iterate_files
|
||||
|
||||
"""
|
||||
|
|
@ -63,16 +63,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190527
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to iterate over a folder of files.'''
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/os.html'
|
||||
]
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to iterate over a folder of files."""
|
||||
__docs__ = ["https://docs.python.org/3/library/os.html"]
|
||||
|
||||
|
||||
def list_directory(path):
|
||||
"""List all file and folder entries in `path`."""
|
||||
"""List all file and folder entries in `path`.
|
||||
|
||||
Args:
|
||||
path (str): A directory within a mounted file system. May be relative or
|
||||
absolute.
|
||||
|
||||
Examples:
|
||||
>>> list_directory('.')
|
||||
|
||||
"""
|
||||
print(f"Files and folders in '{os.path.abspath(path)}':")
|
||||
# Quick and easy method for listing items within a single
|
||||
# folder.
|
||||
|
|
@ -80,7 +88,23 @@ def list_directory(path):
|
|||
# Print all entry names
|
||||
print(f"\t{entry}")
|
||||
|
||||
|
||||
def iterate_files(path):
|
||||
"""Recursively iterate over a path, findings all files within the folder
|
||||
and its subdirectories.
|
||||
|
||||
Args:
|
||||
path (str): A directory within a mounted file system. May be relative or
|
||||
absolute.
|
||||
|
||||
Examples:
|
||||
>>> number_of_py_files = 0
|
||||
>>> for f in iterate_files('../'):
|
||||
... if f.endswith('.py'):
|
||||
... number_of_py_files += 1
|
||||
>>> print(f"\t{number_of_py_files} python files found "
|
||||
... f"in {os.path.abspath('../')}")
|
||||
"""
|
||||
# Though `os.walk()` exposes a list of directories in the
|
||||
# current `root`, it is rarely used since we are generally
|
||||
# interested in the files found within the subdirectories.
|
||||
|
|
@ -89,23 +113,21 @@ def iterate_files(path):
|
|||
for root, dirs, files in os.walk(os.path.abspath(path)):
|
||||
# Both `dirs` and `files` are lists containing all entries
|
||||
# at the current `root`.
|
||||
for fentry in files:
|
||||
for file_name in files:
|
||||
# To effectively reference a file, you should include
|
||||
# the below line which creates a full path reference
|
||||
# to the specific file, regardless of how nested it is
|
||||
file_entry = os.path.join(root, fentry)
|
||||
# We can then hand `file_entry` off to other functions.
|
||||
yield file_entry
|
||||
yield os.path.join(root, file_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
abspath = os.path.abspath
|
||||
print(f"Listing {abspath('.')}")
|
||||
list_directory('.')
|
||||
print(f"\nRecurively counting files in {abspath('../../')}")
|
||||
list_directory(".")
|
||||
print(f"\nRecursively counting files in {abspath('../../')}")
|
||||
num_py_files = 0
|
||||
for file_entry in iterate_files('../../'):
|
||||
if file_entry.endswith('.py'):
|
||||
for file_entry in iterate_files("../"):
|
||||
if file_entry.endswith(".py"):
|
||||
num_py_files += 1
|
||||
print(f"\t{num_py_files} python files found "
|
||||
f"in {abspath('../../')}")
|
||||
print(f"\t{num_py_files} python files found in {abspath('../')}")
|
||||
|
|
@ -21,14 +21,12 @@ leverage any available transaction logs to include additional information
|
|||
otherwise available on the Window's system. This class handles both the opening
|
||||
of the primary hive and attempted recovery of the transaction logs.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_02/yarp_base.py
|
||||
.. literalinclude:: ../pyforhandbook/ch02_registry/yarp_base.py
|
||||
:pyobject: RegistryBase
|
||||
|
||||
Docstring References
|
||||
====================
|
||||
"""
|
||||
from datetime import datetime, timedelta
|
||||
import struct
|
||||
# Installed via:
|
||||
# pip install https://github.com/msuhanov/yarp/archive/1.0.28.tar.gz
|
||||
from yarp import Registry, RegistryHelpers
|
||||
|
|
@ -58,20 +56,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190707
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Registry parsing class that opens an offline hive.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Registry parsing class that opens an offline hive."""
|
||||
__docs__ = [
|
||||
'https://github.com/msuhanov/yarp',
|
||||
'https://docs.python.org/3/library/datetime.html',
|
||||
'https://docs.python.org/3/library/struct.html'
|
||||
"https://github.com/msuhanov/yarp",
|
||||
"https://docs.python.org/3/library/datetime.html",
|
||||
"https://docs.python.org/3/library/struct.html",
|
||||
]
|
||||
|
||||
|
||||
class RegistryBase():
|
||||
class RegistryBase:
|
||||
"""Base class containing common registry parsing code. Will open a hive
|
||||
and attempt recovery using available transaction logs"""
|
||||
|
||||
def __init__(self, reg_file):
|
||||
"""Base __init__ method, responsible for opening a hive."""
|
||||
self.reg_file = reg_file
|
||||
|
|
@ -92,10 +91,10 @@ class RegistryBase():
|
|||
hive_path = self.hive.registry_file.file_object.name
|
||||
tx_logs = RegistryHelpers.DiscoverLogFiles(hive_path)
|
||||
self.tx_log_files = []
|
||||
for tx_path in ['log_path', 'log1_path', 'log2_path']:
|
||||
for tx_path in ["log_path", "log1_path", "log2_path"]:
|
||||
log_obj = None
|
||||
if getattr(tx_logs, tx_path, None):
|
||||
log_obj = open(getattr(tx_logs, tx_path), 'rb')
|
||||
log_obj = open(getattr(tx_logs, tx_path), "rb")
|
||||
self.tx_log_files.append(log_obj)
|
||||
self.hive.recover_auto(*self.tx_log_files)
|
||||
|
||||
|
|
@ -117,12 +116,14 @@ def main(reg_file):
|
|||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Registry Parsing',
|
||||
description="Registry Parsing",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
parser.add_argument(
|
||||
"REG_FILE", help="Path to registry file", type=argparse.FileType("rb")
|
||||
)
|
||||
parser.add_argument('REG_FILE', help='Path to registry file',
|
||||
type=argparse.FileType('rb'))
|
||||
args = parser.parse_args()
|
||||
main(args.REG_FILE)
|
||||
|
|
@ -22,7 +22,7 @@ NTUSER.DAT files, though could get more specific on Windows versions, etc. In
|
|||
this class we store a few useful details including fixed values used by other
|
||||
methods and metadata about the class.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_02/yarp_ntuser.py
|
||||
.. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
|
||||
:pyobject: NTUSER.__init__
|
||||
|
||||
Reading Hive String Values
|
||||
|
|
@ -30,14 +30,14 @@ Reading Hive String Values
|
|||
|
||||
With an open hive, we can begin to parse values from a known key location
|
||||
within the hive. This method allows us to specify a key path and inspect each
|
||||
of the subkeys. For each of the subkeys, we can then get the names and data
|
||||
of the sub-keys. For each of the sub-keys, we can then get the names and data
|
||||
associated with each value in the key. Additionally we could - if needed -
|
||||
continue to recurse on subkeys here. Instead we return this cursory information
|
||||
continue to recurse on sub-keys here. Instead we return this cursory information
|
||||
for the caller to display as they wish. Since the values within MountPoints2
|
||||
store string data, we don't need to perform further parsing of the record.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_02/yarp_ntuser.py
|
||||
:pyobject: NTUSER.parse_mountpoints2
|
||||
.. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
|
||||
:pyobject: NTUSER.parse_mount_points2
|
||||
|
||||
Reading Hive Binary Values
|
||||
==========================
|
||||
|
|
@ -51,8 +51,8 @@ method) using Struct to extract a timestamp and integer marking whether a
|
|||
trusted macro was used. These parsed attributes are then returned to the caller
|
||||
to be displayed.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_02/yarp_ntuser.py
|
||||
:pyobject: NTUSER.parse_trustrecords
|
||||
.. literalinclude:: ../pyforhandbook/ch02_registry/yarp_ntuser.py
|
||||
:pyobject: NTUSER.parse_trust_records
|
||||
|
||||
Docstring References
|
||||
====================
|
||||
|
|
@ -61,11 +61,8 @@ Docstring References
|
|||
from datetime import datetime, timedelta
|
||||
import struct
|
||||
|
||||
import yarp
|
||||
try:
|
||||
from pyforhandbook.section_02.yarp_base import RegistryBase
|
||||
except ImportError:
|
||||
from yarp_base import RegistryBase
|
||||
from pyforhandbook.ch02_registry.yarp_base import RegistryBase
|
||||
|
||||
|
||||
"""
|
||||
Copyright 2019 Chapin Bryce
|
||||
|
|
@ -91,34 +88,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20190707
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Registry parsing class that parses the NTUSER.DAT hive.'''
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Registry parsing class that parses the NTUSER.DAT hive."""
|
||||
__docs__ = [
|
||||
'https://github.com/msuhanov/yarp',
|
||||
'https://docs.python.org/3/library/datetime.html',
|
||||
'https://docs.python.org/3/library/struct.html'
|
||||
"https://github.com/msuhanov/yarp",
|
||||
"https://docs.python.org/3/library/datetime.html",
|
||||
"https://docs.python.org/3/library/struct.html",
|
||||
]
|
||||
|
||||
|
||||
class NTUSER(RegistryBase):
|
||||
"""Class to handle the parsing of the NTUSER.DAT hive."""
|
||||
|
||||
def __init__(self, reg_path):
|
||||
super().__init__(reg_path)
|
||||
self.hive_type = 'NTUSER.DAT'
|
||||
self.hive_type = "NTUSER.DAT"
|
||||
self.macro_enabled_val = 2147483647
|
||||
|
||||
def parse_mountpoints2(self):
|
||||
def parse_mount_points2(self):
|
||||
"""Demonstration of parsing values from a key by path."""
|
||||
key_path = ('Software\\Microsoft\\Windows\\CurrentVersion'
|
||||
'\\Explorer\\MountPoints2')
|
||||
key_path = (
|
||||
"Software\\Microsoft\\Windows\\CurrentVersion"
|
||||
"\\Explorer\\MountPoints2"
|
||||
)
|
||||
for mp in self.hive.find_key(key_path).subkeys():
|
||||
mp_data = {}
|
||||
mp_data['name'] = mp.name().replace('#', '\\')
|
||||
mp_data['values'] = {x.name(): x.data() for x in mp.values()}
|
||||
mp_data['last_written'] = mp.last_written_timestamp()
|
||||
yield mp_data
|
||||
yield {
|
||||
"name": mp.name().replace("#", "\\"),
|
||||
"values": {x.name(): x.data() for x in mp.values()},
|
||||
"last_written": mp.last_written_timestamp(),
|
||||
}
|
||||
|
||||
def parse_office_versions(self):
|
||||
"""Get Office versions within an open Registry hive.
|
||||
|
|
@ -126,10 +126,9 @@ class NTUSER(RegistryBase):
|
|||
Yields:
|
||||
(str): Office version number (ie. '15.0')
|
||||
"""
|
||||
office_versions = self.hive.find_key('Software\\Microsoft\\Office')
|
||||
for subkey in office_versions.subkeys():
|
||||
key_name = subkey.name()
|
||||
is_ver_num = False
|
||||
office_versions = self.hive.find_key("Software\\Microsoft\\Office")
|
||||
for sub_key in office_versions.subkeys():
|
||||
key_name = sub_key.name()
|
||||
try:
|
||||
_ = float(key_name)
|
||||
is_ver_num = True
|
||||
|
|
@ -139,52 +138,61 @@ class NTUSER(RegistryBase):
|
|||
if is_ver_num:
|
||||
yield key_name
|
||||
|
||||
def parse_trustrecords(self):
|
||||
def parse_trust_records(self):
|
||||
"""Demonstration of parsing binary values within a key."""
|
||||
trust_record_path = 'Software\\Microsoft\\Office\\{OFFICE_VERSION}' \
|
||||
'\\Word\\Security\\Trusted Documents\\TrustRecords'
|
||||
trust_record_path = (
|
||||
"Software\\Microsoft\\Office\\{OFFICE_VERSION}"
|
||||
"\\Word\\Security\\Trusted Documents\\TrustRecords"
|
||||
)
|
||||
for office_version in self.parse_office_versions():
|
||||
trust_rec_key = self.hive.find_key(
|
||||
trust_record_path.format(OFFICE_VERSION=office_version))
|
||||
trust_record_path.format(OFFICE_VERSION=office_version)
|
||||
)
|
||||
if not trust_rec_key:
|
||||
continue
|
||||
|
||||
for rec in trust_rec_key.values():
|
||||
date_val, macro_enabled = struct.unpack('q12xI', rec.data())
|
||||
ms = date_val/10.0
|
||||
date_val, macro_enabled = struct.unpack("q12xI", rec.data())
|
||||
ms = date_val / 10.0
|
||||
dt_date = datetime(1601, 1, 1) + timedelta(microseconds=ms)
|
||||
yield {
|
||||
'doc': rec.name(),
|
||||
'dt': dt_date.isoformat(),
|
||||
'macro': macro_enabled == self.macro_enabled_val
|
||||
"doc": rec.name(),
|
||||
"dt": dt_date.isoformat(),
|
||||
"macro": macro_enabled == self.macro_enabled_val,
|
||||
}
|
||||
|
||||
|
||||
def main(reg_file):
|
||||
reg = NTUSER(reg_file)
|
||||
# Call an example parsing method and display the values from NTUSER keys
|
||||
print("{:=^30}".format(' MountPoints2 '))
|
||||
for mount_point in reg.parse_mountpoints2():
|
||||
print("{:=^30}".format(" MountPoints2 "))
|
||||
for mount_point in reg.parse_mount_points2():
|
||||
print(f"Found MountPoints2 path '{mount_point['name']}' with values:")
|
||||
value_str = '\tlast written time: {}\n'.format(
|
||||
mount_point["last_written"].isoformat())
|
||||
value_str = "\tlast written time: {}\n".format(
|
||||
mount_point["last_written"].isoformat()
|
||||
)
|
||||
value_str += "\n".join(
|
||||
[f"\t{x}: {y}" for x, y in mount_point['values'].items()])
|
||||
[f"\t{x}: {y}" for x, y in mount_point["values"].items()]
|
||||
)
|
||||
print(value_str)
|
||||
|
||||
print("{:=^30}".format(' TrustRecords '))
|
||||
for tr in reg.parse_trustrecords():
|
||||
print("{:=^30}".format(" TrustRecords "))
|
||||
for tr in reg.parse_trust_records():
|
||||
print(f"Document: {tr['doc']}")
|
||||
print(f"\tCreated Date: {tr['dt']}")
|
||||
print(f"\tMacro Enabled: {tr['macro']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Registry Parsing',
|
||||
description="Registry Parsing",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
parser.add_argument(
|
||||
"REG_FILE", help="Path to registry file", type=argparse.FileType("rb")
|
||||
)
|
||||
parser.add_argument('REG_FILE', help='Path to registry file',
|
||||
type=argparse.FileType('rb'))
|
||||
args = parser.parse_args()
|
||||
main(args.REG_FILE)
|
||||
main(args.REG_FILE)
|
||||
|
|
@ -22,7 +22,7 @@ Open Windows Event Logs (EVTX)
|
|||
This function shows an example of opening an EVTX file and parsing out several
|
||||
header metadata parameters about the file.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_03/using_python_evtx.py
|
||||
.. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
|
||||
:pyobject: open_evtx
|
||||
|
||||
Iterate over record XML data (EVTX)
|
||||
|
|
@ -38,7 +38,7 @@ which will then call the ``.lxml()`` method on the individual event record.
|
|||
This requires the installation of the lxml Library, as it returns a lxml.etree
|
||||
object that you can interact with.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_03/using_python_evtx.py
|
||||
.. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
|
||||
:pyobject: get_events
|
||||
|
||||
Filtering records within events logs
|
||||
|
|
@ -55,7 +55,7 @@ filters, and return values. Some examples include:
|
|||
- Identify PowerShell events and expose arguments for further processing
|
||||
(ie. Base64 decoding, shellcode analysis)
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_03/using_python_evtx.py
|
||||
.. literalinclude:: ../pyforhandbook/ch03_event_logs/using_python_evtx.py
|
||||
:pyobject: filter_events_json
|
||||
|
||||
Docstring References
|
||||
|
|
@ -90,13 +90,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__date__ = 20191103
|
||||
__license__ = 'MIT Copyright 2019 Chapin Bryce'
|
||||
__desc__ = '''Sample script to read EVTX files.'''
|
||||
__docs__ = [
|
||||
'https://github.com/williballenthin/python-evtx'
|
||||
]
|
||||
__license__ = "MIT Copyright 2019 Chapin Bryce"
|
||||
__desc__ = """Sample script to read EVTX files."""
|
||||
__docs__ = ["https://github.com/williballenthin/python-evtx"]
|
||||
|
||||
|
||||
def open_evtx(input_file):
|
||||
|
|
@ -117,13 +115,15 @@ def open_evtx(input_file):
|
|||
|
||||
with evtx.Evtx(input_file) as open_log:
|
||||
header = open_log.get_file_header()
|
||||
properties = OrderedDict([
|
||||
('major_version', 'File version (major)'),
|
||||
('minor_version', 'File version (minor)'),
|
||||
('is_dirty', 'File is ditry'),
|
||||
('is_full', 'File is full'),
|
||||
('next_record_number', 'Next record number')
|
||||
])
|
||||
properties = OrderedDict(
|
||||
[
|
||||
("major_version", "File version (major)"),
|
||||
("minor_version", "File version (minor)"),
|
||||
("is_dirty", "File is dirty"),
|
||||
("is_full", "File is full"),
|
||||
("next_record_number", "Next record number"),
|
||||
]
|
||||
)
|
||||
|
||||
for key, value in properties.items():
|
||||
print(f"{value}: {getattr(header, key)()}")
|
||||
|
|
@ -192,22 +192,23 @@ def filter_events_json(event_data, event_ids, fields=None):
|
|||
event_data = evt.find("EventData", evt.nsmap)
|
||||
json_data = {}
|
||||
for data in event_data.getchildren():
|
||||
if not fields or data.attrib['Name'] in fields:
|
||||
if not fields or data.attrib["Name"] in fields:
|
||||
# If we don't have a specified field filter list, print all
|
||||
# Otherwise filter for only those fields within the list
|
||||
json_data[data.attrib['Name']] = data.text
|
||||
json_data[data.attrib["Name"]] = data.text
|
||||
|
||||
yield json_data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__desc__,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
parser.add_argument('EVTX_FILE', help="EVTX file to read")
|
||||
parser.add_argument("EVTX_FILE", help="EVTX file to read")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("EVTX File Header Information")
|
||||
|
|
@ -221,10 +222,19 @@ if __name__ == "__main__":
|
|||
print("Filter for Login events")
|
||||
logins = filter_events_json(
|
||||
get_events(args.EVTX_FILE, parse_xml=True),
|
||||
event_ids=['4624'],
|
||||
fields=["SubjectUserName", "SubjectUserSid", "SubjectDomainName",
|
||||
"TargetUserName", "TargetUserSid", "TargetDomainName",
|
||||
"WorkstationName", "IpAddress", "IpPort", "ProcessName"]
|
||||
event_ids=["4624"],
|
||||
fields=[
|
||||
"SubjectUserName",
|
||||
"SubjectUserSid",
|
||||
"SubjectDomainName",
|
||||
"TargetUserName",
|
||||
"TargetUserSid",
|
||||
"TargetDomainName",
|
||||
"WorkstationName",
|
||||
"IpAddress",
|
||||
"IpPort",
|
||||
"ProcessName",
|
||||
],
|
||||
)
|
||||
for login in logins:
|
||||
print(json.dumps(login, indent=2))
|
||||
|
|
@ -17,19 +17,19 @@ This function shows an example of opening a Sqlite database with Python.
|
|||
Additional information regarding Sqlite modules can be
|
||||
seen at https://docs.python.org/3/library/sqlite3.html.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_06/opening_sqlite.py
|
||||
.. literalinclude:: ../pyforhandbook/ch06_databases/opening_sqlite.py
|
||||
:pyobject: open_sqlite
|
||||
|
||||
Listing Tables configuration
|
||||
============================
|
||||
|
||||
This function shows an example of listing available tables in an opened Sqlite database.
|
||||
This function shows an example of listing available tables in an opened Sqlite
|
||||
database.
|
||||
|
||||
.. literalinclude:: ../pyforhandbook/section_06/opening_sqlite.py
|
||||
.. literalinclude:: ../pyforhandbook/ch06_databases/opening_sqlite.py
|
||||
:pyobject: list_tables
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
"""
|
||||
|
|
@ -56,39 +56,53 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
__author__ = 'Brittney Argirakis'
|
||||
__author__ = "Brittney Argirakis"
|
||||
__date__ = 20191126
|
||||
__license__ = 'MIT Copyright 2019 Brittney Argirakis'
|
||||
__desc__ = '''Sample script to open a SqLite DB.'''
|
||||
__license__ = "MIT Copyright 2019 Brittney Argirakis"
|
||||
__desc__ = """Sample script to open a SqLite DB."""
|
||||
__docs__ = [
|
||||
'https://docs.python.org/3/library/argparse.html',
|
||||
'https://docs.python.org/3/library/os.html',
|
||||
'https://docs.python.org/3/library/sqlite3.html'
|
||||
"https://docs.python.org/3/library/argparse.html",
|
||||
"https://docs.python.org/3/library/os.html",
|
||||
"https://docs.python.org/3/library/sqlite3.html",
|
||||
]
|
||||
|
||||
def open_sqlite(inputdb):
|
||||
print("Provided Database: {}".format(inputdb))
|
||||
return sqlite3.connect(inputdb)
|
||||
|
||||
def open_sqlite(input_db):
|
||||
"""Open a SQLite database
|
||||
|
||||
Args:
|
||||
input_db: Path to a SQLite database to open
|
||||
|
||||
Returns:
|
||||
A connection to a SQLite database
|
||||
"""
|
||||
print("Provided Database: {}".format(input_db))
|
||||
return sqlite3.connect(input_db)
|
||||
|
||||
|
||||
def list_tables(conn):
|
||||
"""List all tables in a SQLite database
|
||||
|
||||
Args:
|
||||
conn: An open connection from a SQLite database
|
||||
|
||||
Returns:
|
||||
list: List of table names found in the database
|
||||
"""
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT name FROM sqlite_master")
|
||||
table_list = []
|
||||
for i in cur.fetchall():
|
||||
table_list.append(i[0])
|
||||
|
||||
return table_list
|
||||
return [i[0] for i in cur.fetchall()]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__desc__,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
epilog=f"Built by {__author__}, v.{__date__}"
|
||||
epilog=f"Built by {__author__}, v.{__date__}",
|
||||
)
|
||||
parser.add_argument("db", help="path to the database to read")
|
||||
args = parser.parse_args()
|
||||
conn = open_sqlite(args.db)
|
||||
listed_tables = list_tables(conn)
|
||||
connection = open_sqlite(args.db)
|
||||
listed_tables = list_tables(connection)
|
||||
|
||||
print(listed_tables)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
__author__ = 'Chapin Bryce'
|
||||
__authors__ = ['Chapin Bryce', 'Brittney Argirakis']
|
||||
__license__ = ''
|
||||
__version__ = '0.1.0'
|
||||
__copyright__ = '2020, Chapin Bryce'
|
||||
__author__ = "Chapin Bryce"
|
||||
__authors__ = ["Chapin Bryce", "Brittney Argirakis"]
|
||||
__license__ = ""
|
||||
__version__ = "0.1.0"
|
||||
__copyright__ = "2020, Chapin Bryce"
|
||||
|
|
|
|||
Loading…
Reference in New Issue