Skip to content

Instantly share code, notes, and snippets.

@jborean93
Last active February 11, 2025 23:09

Revisions

  1. jborean93 revised this gist Oct 24, 2022. 1 changed file with 184 additions and 194 deletions.
    378 changes: 184 additions & 194 deletions tls-keylogger.ps1
    Original file line number Diff line number Diff line change
    @@ -7,190 +7,10 @@ param (
    $LogPath
    )

    $SslHashHandshake = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions and TLS 1.2 with RFC 7627 session hashing.
    It is used to capture the client_random value in the TLS ClientHello
    message for future reference in the other hooked functions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$HandshakeHash,
    [IntPtr]$InputBytes,
    [int]$InputLength,
    [int]$Flags
    )

    # This buffer is the TLS packets, we are interested in the ClientHello
    # which is msgType 1 and version 0x0303 (TLS 1.2)
    $data = [byte[]]::new($InputLength)
    [System.Runtime.InteropServices.Marshal]::Copy($InputBytes, $data, 0, $data.Length)

    $msgType = $data[0]
    $version = if ($msgType -eq 1) {
    [System.BitConverter]::ToUInt16($data, 4)
    }

    if ($msgType -eq 1 -and $version -eq 0x0303) {
    $crandom = [byte[]]::new(32)
    [System.Buffer]::BlockCopy($data, 6, $crandom, 0, $crandom.Length)

    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
    $clientRandoms = $using:clientRandoms
    $clientRandoms[$tid] = $crandom
    }

    $this.Invoke($SslProvider, $HandshakeHash, $InputBytes, $InputLength, $Flags)
    }.ToString()
    $SslExpandTrafficKeys = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions twice.
    First is for the handshake traffic key and second for the first
    handshake traffic secret.
    The function is undocumented but Param4 is the client secret and Param5
    is the server secret.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4,
    [IntPtr]$Param5
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4, $Param5)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create($using:getSecretKeyFunc))
    $clientRandoms = $using:clientRandoms
    $stages = $using:stages
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    if ($stages[$tid]) {
    $suffix = 'TRAFFIC_SECRET_0'
    }
    else {
    $suffix = 'HANDSHAKE_TRAFFIC_SECRET'
    $stages[$tid] = $true
    }

    $clientSecret = Get-SecretKey -KeyPtr $Param4
    $serverSecret = Get-SecretKey -KeyPtr $Param5
    $cr = $clientRandoms[$tid]

    ($using:tlsWriter).WriteLine('CLIENT_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($clientSecret))
    ($using:tlsWriter).WriteLine('SERVER_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($serverSecret))

    $res
    }.ToString()
    $SslExpandExporterMasterKey = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions.
    Gets the exporter secret through the undocumented function.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create($using:getSecretKeyFunc))
    $clientRandoms = $using:clientRandoms
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    $secret = Get-SecretKey -KeyPtr $Param4
    $cr = $clientRandoms[$tid]

    ($using:tlsWriter).WriteLine('EXPORTER_SECRET {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $res
    }.ToString()
    $SslGenerateSessionKeys = {
    <#
    .DESCRIPTION
    Called for TLS 1.2 sessions.
    Gets the master secret key for TLS 1.2 sessions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$MasterKey,
    [IntPtr]$ReadKey,
    [IntPtr]$WriteKey,
    [IntPtr]$ParameterList,
    [int]$Flags
    )

    <#
    typedef struct _NCryptBufferDesc {
    ULONG ulVersion;
    ULONG cBuffers;
    PNCryptBuffer pBuffers;
    } NCryptBufferDesc, *PNCryptBufferDesc;
    typedef struct _NCryptBuffer {
    ULONG cbBuffer;
    ULONG BufferType;
    PVOID pvBuffer;
    } NCryptBuffer, *PNCryptBuffer;
    #>
    $bufferCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($ParameterList, 4)
    $bufferPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ParameterList, 8)
    for ($i = 0; $i -lt $bufferCount; $i++) {
    $bufferSize = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr)
    $bufferType = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr, 4)
    $bufferValuePtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($bufferPtr, 8)
    if ($bufferType -eq 20) {
    # NCRYPTBUFFER_SSL_CLIENT_RANDOM
    $cr = [byte[]]::new($bufferSize)
    [System.Runtime.InteropServices.Marshal]::Copy($bufferValuePtr, $cr, 0, $cr.Length)
    break
    }

    $bufferPtr = [IntPtr]::Add($bufferPtr, 8 + ([IntPtr]::Size))
    }

    # The client_random should always be in the ParameterList but the
    # fallback is still used.
    if (-not $cr) {
    $cr = ($using:clientRandoms)[([System.Threading.Thread]::CurrentThread.ManagedThreadId)]
    }

    $ssl5StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($MasterKey, 0x10)
    $secretPtr = [IntPtr]::Add($ssl5StructPtr,
    ([IntPtr]::Size -eq 8 ? 28 : 20))

    $secret = [byte[]]::new(48)
    [System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)

    ($using:tlsWriter).WriteLine('CLIENT_RANDOM {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $this.Invoke($SslProvider, $MasterKey, $ReadKey, $WriteKey, $ParameterList, $Flags)
    }.ToString()

    $LogPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($LogPath)
    $session = New-PSDetourSession -ProcessId lsass -ErrorAction Stop
    try {
    $modulePath = Join-Path (Get-Module -Name PSDetour).ModuleBase 'PSDetour.psd1'

    Invoke-Command -Session $session -ArgumentList $LogPath, $modulePath -ScriptBlock {
    Import-Module -Name $args[1]
    Invoke-Command -Session $session -ScriptBlock {

    Function Get-SecretKey {
    param([Parameter(Mandatory)][IntPtr]$KeyPtr)
    @@ -208,24 +28,194 @@ try {
    , $secret
    }

    $getSecretKeyFunc = ${function:Get-SecretKey}.ToString()
    $clientRandoms = [Hashtable]::Synchronized(@{})
    $stages = [Hashtable]::Synchronized(@{})

    $tlsLog = [System.IO.File]::Open($args[0], "Append", "Write", "Read")
    $tlsLog = [System.IO.File]::Open($using:LogPath, "Append", "Write", "Read")
    $tlsWriter = [System.IO.StreamWriter]::new($tlsLog)
    $tlsWriter.AutoFlush = $true
    $state = @{
    GetSecretKeyFunc = ${function:Get-SecretKey}.ToString()
    ClientRandoms = [Hashtable]::Synchronized(@{})
    Stages = [Hashtable]::Synchronized(@{})
    TlsWriter = $tlsWriter
    }

    $ncrypt = @{ DllName = 'ncrypt.dll' }
    $hooks = @(
    New-PSDetourHook @ncrypt -MethodName SslHashHandshake -Action ([ScriptBlock]::Create($using:SslHashHandshake))
    New-PSDetourHook @ncrypt -MethodName SslExpandTrafficKeys -Action ([ScriptBlock]::Create($using:SslExpandTrafficKeys))
    New-PSDetourHook @ncrypt -MethodName SslExpandExporterMasterKey -Action ([ScriptBlock]::Create($using:SslExpandExporterMasterKey))
    New-PSDetourHook @ncrypt -MethodName SslGenerateSessionKeys -Action ([ScriptBlock]::Create($using:SslGenerateSessionKeys))
    Start-PSDetour -State $state -Hook @(
    New-PSDetourHook @ncrypt -MethodName SslHashHandshake -Action {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions and TLS 1.2 with RFC 7627 session hashing.
    It is used to capture the client_random value in the TLS ClientHello
    message for future reference in the other hooked functions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$HandshakeHash,
    [IntPtr]$InputBytes,
    [int]$InputLength,
    [int]$Flags
    )

    # This buffer is the TLS packets, we are interested in the ClientHello
    # which is msgType 1 and version 0x0303 (TLS 1.2)
    $data = [byte[]]::new($InputLength)
    [System.Runtime.InteropServices.Marshal]::Copy($InputBytes, $data, 0, $data.Length)

    $msgType = $data[0]
    $version = if ($msgType -eq 1) {
    [System.BitConverter]::ToUInt16($data, 4)
    }

    if ($msgType -eq 1 -and $version -eq 0x0303) {
    $crandom = [byte[]]::new(32)
    [System.Buffer]::BlockCopy($data, 6, $crandom, 0, $crandom.Length)

    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
    $this.State.ClientRandoms[$tid] = $crandom
    }

    $this.Invoke($SslProvider, $HandshakeHash, $InputBytes, $InputLength, $Flags)
    }
    New-PSDetourHook @ncrypt -MethodName SslExpandTrafficKeys -Action {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions twice.
    First is for the handshake traffic key and second for the first
    handshake traffic secret.
    The function is undocumented but Param4 is the client secret and Param5
    is the server secret.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4,
    [IntPtr]$Param5
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4, $Param5)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create(
    $this.State.GetSecretKeyFunc))
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    if ($this.State.Stages[$tid]) {
    $suffix = 'TRAFFIC_SECRET_0'
    }
    else {
    $suffix = 'HANDSHAKE_TRAFFIC_SECRET'
    $this.State.Stages[$tid] = $true
    }

    $clientSecret = Get-SecretKey -KeyPtr $Param4
    $serverSecret = Get-SecretKey -KeyPtr $Param5
    $cr = $this.State.ClientRandoms[$tid]

    ($this.State.TlsWriter).WriteLine('CLIENT_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($clientSecret))
    ($this.State.TlsWriter).WriteLine('SERVER_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($serverSecret))

    $res
    }
    New-PSDetourHook @ncrypt -MethodName SslExpandExporterMasterKey -Action {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions.
    Gets the exporter secret through the undocumented function.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create(
    $this.State.GetSecretKeyFunc))
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    $secret = Get-SecretKey -KeyPtr $Param4
    $cr = $this.State.ClientRandoms[$tid]

    ($this.State.TlsWriter).WriteLine('EXPORTER_SECRET {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $res
    }
    New-PSDetourHook @ncrypt -MethodName SslGenerateSessionKeys -Action {
    <#
    .DESCRIPTION
    Called for TLS 1.2 sessions.
    Gets the master secret key for TLS 1.2 sessions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$MasterKey,
    [IntPtr]$ReadKey,
    [IntPtr]$WriteKey,
    [IntPtr]$ParameterList,
    [int]$Flags
    )

    <#
    typedef struct _NCryptBufferDesc {
    ULONG ulVersion;
    ULONG cBuffers;
    PNCryptBuffer pBuffers;
    } NCryptBufferDesc, *PNCryptBufferDesc;
    typedef struct _NCryptBuffer {
    ULONG cbBuffer;
    ULONG BufferType;
    PVOID pvBuffer;
    } NCryptBuffer, *PNCryptBuffer;
    #>
    $bufferCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($ParameterList, 4)
    $bufferPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ParameterList, 8)
    for ($i = 0; $i -lt $bufferCount; $i++) {
    $bufferSize = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr)
    $bufferType = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr, 4)
    $bufferValuePtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($bufferPtr, 8)
    if ($bufferType -eq 20) {
    # NCRYPTBUFFER_SSL_CLIENT_RANDOM
    $cr = [byte[]]::new($bufferSize)
    [System.Runtime.InteropServices.Marshal]::Copy($bufferValuePtr, $cr, 0, $cr.Length)
    break
    }

    $bufferPtr = [IntPtr]::Add($bufferPtr, 8 + ([IntPtr]::Size))
    }

    # The client_random should always be in the ParameterList but the
    # fallback is still used.
    if (-not $cr) {
    $cr = ($this.State.ClientRandoms)[([System.Threading.Thread]::CurrentThread.ManagedThreadId)]
    }

    $ssl5StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($MasterKey, 0x10)
    $secretPtr = [IntPtr]::Add($ssl5StructPtr,
    ([IntPtr]::Size -eq 8 ? 28 : 20))

    $secret = [byte[]]::new(48)
    [System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)

    ($this.State.TlsWriter).WriteLine('CLIENT_RANDOM {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $this.Invoke($SslProvider, $MasterKey, $ReadKey, $WriteKey, $ParameterList, $Flags)
    }
    )

    Start-PSDetour -Hook $hooks

    Write-Host "Press any key to stop TLS logging..."
    $null = $host.UI.RawUI.ReadKey()

    @@ -237,4 +227,4 @@ try {
    }
    finally {
    $session | Remove-PSSession
    }
    }
  2. jborean93 created this gist Oct 21, 2022.
    240 changes: 240 additions & 0 deletions tls-keylogger.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,240 @@
    #Requires -Module PSDetour

    [CmdletBinding()]
    param (
    [Parameter(Mandatory)]
    [string]
    $LogPath
    )

    $SslHashHandshake = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions and TLS 1.2 with RFC 7627 session hashing.
    It is used to capture the client_random value in the TLS ClientHello
    message for future reference in the other hooked functions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$HandshakeHash,
    [IntPtr]$InputBytes,
    [int]$InputLength,
    [int]$Flags
    )

    # This buffer is the TLS packets, we are interested in the ClientHello
    # which is msgType 1 and version 0x0303 (TLS 1.2)
    $data = [byte[]]::new($InputLength)
    [System.Runtime.InteropServices.Marshal]::Copy($InputBytes, $data, 0, $data.Length)

    $msgType = $data[0]
    $version = if ($msgType -eq 1) {
    [System.BitConverter]::ToUInt16($data, 4)
    }

    if ($msgType -eq 1 -and $version -eq 0x0303) {
    $crandom = [byte[]]::new(32)
    [System.Buffer]::BlockCopy($data, 6, $crandom, 0, $crandom.Length)

    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
    $clientRandoms = $using:clientRandoms
    $clientRandoms[$tid] = $crandom
    }

    $this.Invoke($SslProvider, $HandshakeHash, $InputBytes, $InputLength, $Flags)
    }.ToString()
    $SslExpandTrafficKeys = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions twice.
    First is for the handshake traffic key and second for the first
    handshake traffic secret.
    The function is undocumented but Param4 is the client secret and Param5
    is the server secret.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4,
    [IntPtr]$Param5
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4, $Param5)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create($using:getSecretKeyFunc))
    $clientRandoms = $using:clientRandoms
    $stages = $using:stages
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    if ($stages[$tid]) {
    $suffix = 'TRAFFIC_SECRET_0'
    }
    else {
    $suffix = 'HANDSHAKE_TRAFFIC_SECRET'
    $stages[$tid] = $true
    }

    $clientSecret = Get-SecretKey -KeyPtr $Param4
    $serverSecret = Get-SecretKey -KeyPtr $Param5
    $cr = $clientRandoms[$tid]

    ($using:tlsWriter).WriteLine('CLIENT_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($clientSecret))
    ($using:tlsWriter).WriteLine('SERVER_{0} {1} {2}', $suffix,
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($serverSecret))

    $res
    }.ToString()
    $SslExpandExporterMasterKey = {
    <#
    .DESCRIPTION
    Called for TLS 1.3 sessions.
    Gets the exporter secret through the undocumented function.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$Param1,
    [IntPtr]$Param2,
    [IntPtr]$Param3,
    [IntPtr]$Param4
    )

    $res = $this.Invoke($Param1, $Param2, $Param3, $Param4)

    $null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create($using:getSecretKeyFunc))
    $clientRandoms = $using:clientRandoms
    $tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId

    $secret = Get-SecretKey -KeyPtr $Param4
    $cr = $clientRandoms[$tid]

    ($using:tlsWriter).WriteLine('EXPORTER_SECRET {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $res
    }.ToString()
    $SslGenerateSessionKeys = {
    <#
    .DESCRIPTION
    Called for TLS 1.2 sessions.
    Gets the master secret key for TLS 1.2 sessions.
    #>
    [OutputType([int])]
    param (
    [IntPtr]$SslProvider,
    [IntPtr]$MasterKey,
    [IntPtr]$ReadKey,
    [IntPtr]$WriteKey,
    [IntPtr]$ParameterList,
    [int]$Flags
    )

    <#
    typedef struct _NCryptBufferDesc {
    ULONG ulVersion;
    ULONG cBuffers;
    PNCryptBuffer pBuffers;
    } NCryptBufferDesc, *PNCryptBufferDesc;
    typedef struct _NCryptBuffer {
    ULONG cbBuffer;
    ULONG BufferType;
    PVOID pvBuffer;
    } NCryptBuffer, *PNCryptBuffer;
    #>
    $bufferCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($ParameterList, 4)
    $bufferPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ParameterList, 8)
    for ($i = 0; $i -lt $bufferCount; $i++) {
    $bufferSize = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr)
    $bufferType = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr, 4)
    $bufferValuePtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($bufferPtr, 8)
    if ($bufferType -eq 20) {
    # NCRYPTBUFFER_SSL_CLIENT_RANDOM
    $cr = [byte[]]::new($bufferSize)
    [System.Runtime.InteropServices.Marshal]::Copy($bufferValuePtr, $cr, 0, $cr.Length)
    break
    }

    $bufferPtr = [IntPtr]::Add($bufferPtr, 8 + ([IntPtr]::Size))
    }

    # The client_random should always be in the ParameterList but the
    # fallback is still used.
    if (-not $cr) {
    $cr = ($using:clientRandoms)[([System.Threading.Thread]::CurrentThread.ManagedThreadId)]
    }

    $ssl5StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($MasterKey, 0x10)
    $secretPtr = [IntPtr]::Add($ssl5StructPtr,
    ([IntPtr]::Size -eq 8 ? 28 : 20))

    $secret = [byte[]]::new(48)
    [System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)

    ($using:tlsWriter).WriteLine('CLIENT_RANDOM {0} {1}',
    [System.Convert]::ToHexString($cr),
    [System.Convert]::ToHexString($secret))

    $this.Invoke($SslProvider, $MasterKey, $ReadKey, $WriteKey, $ParameterList, $Flags)
    }.ToString()

    $LogPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($LogPath)
    $session = New-PSDetourSession -ProcessId lsass -ErrorAction Stop
    try {
    $modulePath = Join-Path (Get-Module -Name PSDetour).ModuleBase 'PSDetour.psd1'

    Invoke-Command -Session $session -ArgumentList $LogPath, $modulePath -ScriptBlock {
    Import-Module -Name $args[1]

    Function Get-SecretKey {
    param([Parameter(Mandatory)][IntPtr]$KeyPtr)

    $dddbStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($KeyPtr)
    $ssl3StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($dddbStructPtr, 0x10)
    $uuurStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ssl3StructPtr, 0x20)
    $mskyStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($uuurStructPtr, 0x10)

    $secretLength = [System.Runtime.InteropServices.Marshal]::ReadInt32($mskyStructPtr, 0x10)
    $secretPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($mskyStructPtr, 0x18)

    $secret = [byte[]]::new($secretLength)
    [System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)
    , $secret
    }

    $getSecretKeyFunc = ${function:Get-SecretKey}.ToString()
    $clientRandoms = [Hashtable]::Synchronized(@{})
    $stages = [Hashtable]::Synchronized(@{})

    $tlsLog = [System.IO.File]::Open($args[0], "Append", "Write", "Read")
    $tlsWriter = [System.IO.StreamWriter]::new($tlsLog)
    $tlsWriter.AutoFlush = $true

    $ncrypt = @{ DllName = 'ncrypt.dll' }
    $hooks = @(
    New-PSDetourHook @ncrypt -MethodName SslHashHandshake -Action ([ScriptBlock]::Create($using:SslHashHandshake))
    New-PSDetourHook @ncrypt -MethodName SslExpandTrafficKeys -Action ([ScriptBlock]::Create($using:SslExpandTrafficKeys))
    New-PSDetourHook @ncrypt -MethodName SslExpandExporterMasterKey -Action ([ScriptBlock]::Create($using:SslExpandExporterMasterKey))
    New-PSDetourHook @ncrypt -MethodName SslGenerateSessionKeys -Action ([ScriptBlock]::Create($using:SslGenerateSessionKeys))
    )

    Start-PSDetour -Hook $hooks

    Write-Host "Press any key to stop TLS logging..."
    $null = $host.UI.RawUI.ReadKey()

    Stop-PSDetour

    $tlsWriter.Dispose()
    $tlsLog.Dispose()
    }
    }
    finally {
    $session | Remove-PSSession
    }