Public/Invoke-OffsetThreatScanRegion.ps1

function Invoke-OffsetThreatScanRegion {
    <#
    .SYNOPSIS
        Discovers multiple independently-detectable byte regions in a file via in-memory AMSI scanning.

    .DESCRIPTION
        Splits the file into contiguous byte segments and scans each in isolation through AMSI
        (AmsiScanBuffer, entirely in memory - nothing detected is written to disk, so Microsoft
        Defender real-time protection is never triggered and is never altered). Segments that
        independently trigger detection are returned as regions; by default each hit is bisected
        within its segment to map the exact triggering boundary to an absolute file offset.

        This finds regions that trigger on their own. It can miss signatures that only fire in
        full-file context or that straddle a segment boundary, so treat the regions as leads to
        confirm with Invoke-OffsetThreatScan and manual validation, not as a complete list of
        every triggering byte. AMSI (in-memory) is the only supported engine here; Microsoft
        Defender file scanning would require writing detected content to disk.

    .PARAMETER FilePath
        The file to analyse.

    .PARAMETER SegmentCount
        Number of contiguous segments to divide the file into (default 8).

    .PARAMETER NoRefine
        Skip the within-segment boundary bisection; report segment-level hits only.

    .EXAMPLE
        Invoke-OffsetThreatScanRegion .\payload.bin -SegmentCount 16 |
            Select-Object -ExpandProperty DetectedRegions
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath,

        [ValidateRange(1, 4096)]
        [int]$SegmentCount = 8,

        [ValidateRange(1, 10)]
        [int]$RepeatCount = 2,

        [switch]$NoRefine,

        [switch]$NoProgress
    )

    if (-not (Test-OIIsWindows)) {
        throw 'Threat-provider scanning is supported only on Windows. Offset inspection remains cross-platform.'
    }

    $resolvedPath = (Resolve-Path -LiteralPath $FilePath -ErrorAction Stop).Path
    $item = Get-Item -LiteralPath $resolvedPath -ErrorAction Stop
    if ($item.PSIsContainer) { throw "Path is a directory, not a file: $resolvedPath" }

    $fileSize = $item.Length
    if ($fileSize -gt [int]::MaxValue) {
        throw 'AMSI region scanning supports files up to 2 GB because scan content is represented by managed arrays.'
    }

    Initialize-OIAmsiInterop

    # Capture the private response converter as a command reference so it resolves
    # when the scanner closure runs inside the boundary search (bare private-function
    # names do not resolve in that invocation scope).
    $convertFromAmsiResponseCommand = Get-Command -Name ConvertFrom-OIAmsiResponse -CommandType Function -ErrorAction Stop

    $session = $null
    $stream = $null
    $segments = @()
    $regions = @()
    $fileSha256 = $null
    $errorMessage = $null
    try {
        $session = New-Object OffsetInspect.Interop.AmsiSession('OffsetInspect/3.0')
        $stream = [System.IO.File]::Open(
            $resolvedPath,
            [System.IO.FileMode]::Open,
            [System.IO.FileAccess]::Read,
            [System.IO.FileShare]::Read
        )
        $fileSha256 = Get-OIStreamSha256 -Stream $stream
        $segments = @(Split-OIByteRange -TotalLength $fileSize -SegmentCount $SegmentCount)

        $sessionReference = $session
        $contentName = $resolvedPath
        $bufferScanner = {
            param([byte[]]$Bytes, [int]$Length)
            return (& $convertFromAmsiResponseCommand -Response $sessionReference.ScanBytePrefix($Bytes, $Length, $contentName))
        }.GetNewClosure()

        $regions = @(Find-OIDetectionSegment `
            -Stream $stream `
            -Segment $segments `
            -BufferScanner $bufferScanner `
            -RepeatCount $RepeatCount `
            -Refine:(-not $NoRefine) `
            -NoProgress:$NoProgress)
    }
    catch {
        $errorMessage = $_.Exception.Message
    }
    finally {
        if ($null -ne $session) {
            try { $session.Dispose() }
            catch { Write-Verbose "AMSI session cleanup failed: $($_.Exception.Message)" }
        }
        if ($null -ne $stream) { $stream.Dispose() }
    }

    $warnings = @(
        'Multi-region discovery reports independently-detectable byte segments via in-memory AMSI scanning. ' +
        'It can miss signatures that only fire in full-file context or that straddle a segment boundary; ' +
        'confirm regions with Invoke-OffsetThreatScan and manual validation.'
    )

    $result = [pscustomobject]@{
        Success             = ($null -eq $errorMessage)
        File                = $resolvedPath
        FileSize            = $fileSize
        FileSha256          = $fileSha256
        Engine              = 'AMSI'
        ScanMode            = 'RawBytes'
        SegmentCount        = @($segments).Count
        DetectedRegionCount = @($regions).Count
        DetectedRegions     = @($regions)
        Warnings            = $warnings
        Error               = $errorMessage
    }
    $result.PSObject.TypeNames.Insert(0, 'OffsetInspect.ThreatRegionResult')
    return $result
}