FEX Core SDK
  • Documentation
  • C# Reference
Search Results for

    Quick Start Guide - Python

    Get from SDK download to working forensic analysis in 10 minutes.


    Prerequisites

    • Python 3.14+ (standard library only - no pip install needed)
    • FEX Core SDK extracted to a local directory
    • Windows x64 or Linux x64 (or Docker)

    Step 1: Set Up Your Project

    Create a working directory and copy (or symlink) the Python SDK:

    my-project/
    ├── core/              # Copy from sdk/python/core/
    │   ├── __init__.py
    │   ├── constants.py
    │   ├── fexcore.py
    │   └── loader.py
    ├── my_script.py       # Your code
    

    On Windows, ensure FEX.Core.dll and its dependencies are in bin/Win64/ relative to the SDK root (already included in the SDK package). The loader searches standard locations automatically.

    On Linux, place libfexcore.so in /usr/local/lib/ or set LD_LIBRARY_PATH.


    Step 2: Read Image Metadata

    Create my_script.py:

    #!/usr/bin/env python3
    """Example: Read forensic image metadata."""
    
    import json
    import os
    import sys
    from core import FexCore
    from core.loader import load_library
    
    def main():
        if len(sys.argv) < 2:
            print("Usage: python my_script.py <image_path>")
            sys.exit(1)
    
        image_path = sys.argv[1]
    
        # Load the FEX Core library (auto-detects Windows DLL or Linux .so)
        lib, lib_path = load_library()
        fex = FexCore(lib)
        print(f"Loaded: {lib_path}")
    
        # FEX2 license key from GetData — set FEX_LICENSE_KEY in your environment
        # (or pull it from your secret store). See ../license-keys.md.
        key = os.environ["FEX_LICENSE_KEY"]
    
        # Known issue (Windows): InitLibrary uses GetCurrentDirectory rather than
        # GetModuleFileName when locating its license context, so it returns -20
        # (RESULT_INVALIDKEY) when the calling process's CWD is not the same
        # directory as FEX.Core.dll — even with a valid key. Workaround: chdir
        # to the DLL directory across the init_library call. The fix will move
        # to the DLL itself in a future release.
        prev_cwd = os.getcwd()
        os.chdir(os.path.dirname(lib_path))
        try:
            fex.init_library(key)
        finally:
            os.chdir(prev_cwd)
        print("Library initialized")
    
        # Open the forensic image
        image_id = fex.open_image(image_path)
        if image_id < 0:
            print(f"Error opening image: {image_id}")
            sys.exit(1)
        print(f"Image opened (ID: {image_id})")
    
        # Get image metadata as JSON
        info_str = fex.image_info_json(image_id)
        info = json.loads(info_str)
    
        print("\nImage Metadata:")
        print(f"  Description:  {info.get('description', 'N/A')}")
        print(f"  Image Type:   {info.get('imageType', 'N/A')}")
        print(f"  Image Kind:   {info.get('imageKind', 'N/A')}")
        print(f"  Device Size:  {info.get('deviceSize', 'N/A')}")
    
        # Clean up
        fex.close_image(image_id)
        print("\nDone.")
    
    if __name__ == "__main__":
        main()
    

    Run it:

    python my_script.py path/to/evidence.E01
    

    Expected output:

    Loaded: C:\FEX-SDK\bin\Win64\FEX.Core.dll
    Library initialized
    Image opened (ID: 0)
    
    Image Metadata:
      Description:  My Evidence
      Image Type:   E01
      Image Kind:   Physical
      Device Size:  536870912000
    
    Done.
    

    Step 3: Create a CSV File Listing

    This example reads the filesystem and writes a CSV with full paths, sizes, and timestamps.

    #!/usr/bin/env python3
    """Example: Export file listing to CSV."""
    
    import csv
    import json
    import os
    import sys
    from datetime import datetime, timedelta
    from core import FexCore
    from core.loader import load_library
    from core.constants import FILESTATUS_FOLDER, FILESTATUS_DELETED
    
    # TFileRecord date fields are Windows FILETIME: 100-nanosecond intervals
    # since 1601-01-01 UTC. Zero means "not set".
    FILETIME_EPOCH = datetime(1601, 1, 1)
    
    def filetime_to_datetime(raw_int64):
        """Convert Windows FILETIME int64 to Python datetime (UTC)."""
        if raw_int64 == 0:
            return None
        try:
            return FILETIME_EPOCH + timedelta(microseconds=raw_int64 / 10)
        except (ValueError, OverflowError):
            return None
    
    def format_datetime(dt):
        """Format datetime as ISO string, or empty string if None."""
        if dt is None:
            return ""
        return dt.strftime("%Y-%m-%d %H:%M:%S")
    
    def main():
        if len(sys.argv) < 3:
            print("Usage: python csv_listing.py <image_path> <output.csv>")
            sys.exit(1)
    
        image_path = sys.argv[1]
        csv_path = sys.argv[2]
    
        # Load and initialize (FEX2 key from environment — see ../license-keys.md).
        # Windows: chdir to DLL directory across init_library — see the first
        # example for the workaround rationale.
        lib, lib_path = load_library()
        fex = FexCore(lib)
        prev_cwd = os.getcwd()
        os.chdir(os.path.dirname(lib_path))
        try:
            fex.init_library(os.environ["FEX_LICENSE_KEY"])
        finally:
            os.chdir(prev_cwd)
    
        # Open image
        image_id = fex.open_image(image_path)
        if image_id < 0:
            print(f"Error opening image: {image_id}")
            sys.exit(1)
    
        # Read filesystem (returns (result_code, file_count); records are fetched separately below)
        result, file_count = fex.read_file_system(image_id)
        if result != 0:
            print(f"Error reading filesystem: {result}")
            sys.exit(1)
        print(f"Filesystem read: {file_count} entries")
    
        # Get file records (includes full paths via V2 API)
        result, records = fex.get_file_system_records(image_id)
    
        # Write CSV
        with open(csv_path, "w", newline="", encoding="utf-8") as f:
            writer = csv.writer(f)
            writer.writerow([
                "Index", "Path", "Filename", "LogicalSize", "PhysicalSize",
                "Created", "Modified", "Accessed", "IsFolder", "IsDeleted"
            ])
    
            for rec in records:
                is_folder = bool(rec["status"] & FILESTATUS_FOLDER)
                is_deleted = bool(rec["status"] & FILESTATUS_DELETED)
    
                writer.writerow([
                    rec["_index"],
                    rec.get("path", ""),
                    rec["filename"],
                    rec["logicalSize"],
                    rec["physicalSize"],
                    format_datetime(filetime_to_datetime(rec["createdDate"])),
                    format_datetime(filetime_to_datetime(rec["modifiedDate"])),
                    format_datetime(filetime_to_datetime(rec["accessedDate"])),
                    is_folder,
                    is_deleted,
                ])
    
        print(f"Written {len(records)} records to {csv_path}")
    
        fex.close_image(image_id)
    
    if __name__ == "__main__":
        main()
    

    Run it:

    python csv_listing.py path/to/evidence.E01 file_listing.csv
    

    Output CSV:

    Index,Path,Filename,LogicalSize,PhysicalSize,Created,Modified,Accessed,IsFolder,IsDeleted
    0,,Root,0,0,,,,True,False
    1,Documents,Documents,0,0,2024-01-15 10:30:00,2024-01-15 10:30:00,,True,False
    2,Documents\report.pdf,report.pdf,1572864,1576960,2024-01-15 11:00:00,2024-01-15 11:05:00,2024-01-15 11:05:00,False,False
    ...
    

    Step 4: Extract All Files

    Use the included fex_extract.py tool for bulk extraction:

    # Extract all files to a folder
    python fex_extract.py path/to/evidence.E01 ./extracted_files
    
    # Skip deleted files
    python fex_extract.py path/to/evidence.E01 ./extracted_files --skip-deleted
    
    # Dry run (show what would be extracted without writing)
    python fex_extract.py path/to/evidence.E01 ./extracted_files --dry-run
    
    # Verbose output
    python fex_extract.py path/to/evidence.E01 ./extracted_files --verbose
    

    Expected output:

    FEX Extract - Bulk File Extraction
    ============================================================
    Opening image: evidence.E01
    Reading filesystem... 1,247 files found
    Extracting to: ./extracted_files
    
      [  1/1247] Documents\report.pdf (1.5 MB)
      [  2/1247] Documents\notes.txt (2.0 KB)
      [  3/1247] Pictures\photo.jpg (100.0 KB)
      ...
    
    ============================================================
    Extraction complete: 1,200 files extracted, 47 folders created
    Total size: 2.5 GB
    Errors: 0
    

    Files are extracted preserving the original directory structure. The tool automatically:

    • Preserves folder hierarchy from the forensic image
    • Truncates files to logical size (strips cluster slack)
    • Sanitizes Unicode filenames for cross-platform compatibility
    • Handles path length limits

    Step 5: Write Your Own Extraction Logic

    For custom extraction (e.g., filtering by file type or size):

    ⚠️ Note on bulk extraction. The example below uses fex.read_file_data(), which wraps the DLL's ReadFileData — fine for the small demo case. For extracting hundreds or thousands of files, switch to the handle-based stream API (RequestFileStream / ReadFileStream / ReleaseFileStream) to avoid unbounded memory growth. See the Python example in Common Workflows — Workflow 3.

    #!/usr/bin/env python3
    """Example: Extract only PDF files larger than 1 KB."""
    
    import os
    import sys
    from core import FexCore
    from core.loader import load_library
    from core.constants import FILESTATUS_FOLDER, RESULT_OK
    
    READ_CHUNK = 1024 * 1024  # 1 MB chunks
    
    def extract_file(fex, image_id, file_index, logical_size, output_path):
        """Extract a single file by reading in chunks."""
        os.makedirs(os.path.dirname(output_path), exist_ok=True)
        written = 0
        with open(output_path, "wb") as f:
            offset = 0
            while offset < logical_size:
                chunk_size = min(READ_CHUNK, logical_size - offset)
                result, bytes_read, buf = fex.read_file_data(
                    image_id, file_index, offset, chunk_size
                )
                if result != RESULT_OK or bytes_read == 0:
                    break
                f.write(bytes(buf[:bytes_read]))
                written += bytes_read
                offset += bytes_read
        # Truncate to exact logical size
        if written > logical_size:
            with open(output_path, "r+b") as f:
                f.truncate(logical_size)
    
    def main():
        image_path = sys.argv[1]
        output_dir = sys.argv[2]
    
        # Windows: chdir to DLL directory across init_library — see the first
        # example for the workaround rationale.
        lib, lib_path = load_library()
        fex = FexCore(lib)
        prev_cwd = os.getcwd()
        os.chdir(os.path.dirname(lib_path))
        try:
            fex.init_library(os.environ["FEX_LICENSE_KEY"])
        finally:
            os.chdir(prev_cwd)
    
        image_id = fex.open_image(image_path)
        result, _ = fex.read_file_system(image_id)
        result, records = fex.get_file_system_records(image_id)
    
        extracted = 0
        for rec in records:
            # Skip folders
            if rec["status"] & FILESTATUS_FOLDER:
                continue
            # Filter: only PDFs larger than 1 KB
            filename = rec["filename"].lower()
            if not filename.endswith(".pdf") or rec["logicalSize"] < 1024:
                continue
    
            path = rec.get("path", rec["filename"])
            output_path = os.path.join(output_dir, path)
            print(f"Extracting: {path} ({rec['logicalSize']} bytes)")
            extract_file(fex, image_id, rec["_index"], rec["logicalSize"], output_path)
            extracted += 1
    
        fex.close_image(image_id)
        print(f"\nExtracted {extracted} PDF files")
    
    if __name__ == "__main__":
        main()
    

    API Reference

    The Python SDK wraps the FEX Core C API. Key methods on the FexCore class:

    Method Description
    init_library(key) Initialize with license key
    open_image(path) Open forensic image, returns image_id
    close_image(image_id) Close an opened image
    image_info_json(image_id) Get image metadata as JSON string
    read_file_system(image_id) Read filesystem, returns (result, file_count)
    get_file_system_records(image_id) Get all file records as list of dicts
    read_file_data(image_id, index, offset, size) Read file content bytes
    size_of_file(image_id, index) Get logical file size
    get_file_path(image_id, index) Get full path for a file
    call_v2_json(func, *args) Call any V2 function returning JSON

    For the full C API reference, see V2 API Documentation.


    Next Steps

    • Docker Guide - Deploy on Linux with Docker
    • V2 API Reference - Complete API documentation
    • V3 API (Custom Fields) - Flexible property queries
    • Error Codes - All error codes with descriptions
    • Architecture Overview - API design and memory patterns
    In this article
    Back to top © GetData Forensics