en-US/PSDetour.dll-Help.xml

<?xml version="1.0" encoding="utf-8"?>
<helpItems schema="maml" xmlns="http://msh">
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>New-PSDetourHook</command:name>
      <command:verb>New</command:verb>
      <command:noun>PSDetourHook</command:noun>
      <maml:description>
        <maml:para>Create a PSDetour hook from a scriptblock.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Creates a hook object from a PowerShell scriptblock to be used as a hook for a native C function. The scriptblock should contain an `[OutputType([...])]` attribute to denote what the return value is of the C function. It should also contain a `param()` block with parameters for each C function argument and their types. When the hook is run, the `$this` variable contains information about the hook itself and can be used to invoke the actual API through the `Invoke` method.</maml:para>
      <maml:para>Take care when typing the return and parameter types as a misconfiguration can cause the whole process to crash. A parameter can be marked with the `MarshalAs` attribute to denote the marshalling type behaviour to use. Currently only the `UnmanagedType`, `In`, and `Out` attributes can be set.</maml:para>
      <maml:para>See about_PSDetourMarshalling (./about_PSDetourMarshalling.md)for more information around how to define a hook and its input/output types.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>New-PSDetourHook</maml:name>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
          <maml:name>DllName</maml:name>
          <maml:description>
            <maml:para>The DLL name or path where the C function to hook is defined.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
          <maml:name>MethodName</maml:name>
          <maml:description>
            <maml:para>The C function name/symbol to hook.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="3" aliases="ScriptBlock">
          <maml:name>Action</maml:name>
          <maml:description>
            <maml:para>The scriptblock to run when the C function is called. The `[OutputType([type])]` should be defined as the return value type of the C function. Parameters should also be defined in the `param()` block for each argument of the C function and their respective types. Any variables that reference variables outside of the `-Action` should be prefixed with `$using:VariableName`.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue>
          <dev:type>
            <maml:name>ScriptBlock</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="3" aliases="ScriptBlock">
        <maml:name>Action</maml:name>
        <maml:description>
          <maml:para>The scriptblock to run when the C function is called. The `[OutputType([type])]` should be defined as the return value type of the C function. Parameters should also be defined in the `param()` block for each argument of the C function and their respective types. Any variables that reference variables outside of the `-Action` should be prefixed with `$using:VariableName`.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue>
        <dev:type>
          <maml:name>ScriptBlock</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
        <maml:name>DllName</maml:name>
        <maml:description>
          <maml:para>The DLL name or path where the C function to hook is defined.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
        <maml:name>MethodName</maml:name>
        <maml:description>
          <maml:para>The C function name/symbol to hook.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>None</maml:name>
        </dev:type>
        <maml:description>
          <maml:para></maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>PSDetour.Commands.ScriptBlockHook</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>An object that contains the necessary information for `Start-PSDetour` to setup the required hooks.</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-- Example 1 - Hook CreateDirectoryW with string marshalling --</maml:title>
        <dev:code>PS C:\&gt; New-PSDetourHook -DllName Kernel32 -MethodName CreateDirectoryW -Action {
... [OutputType([bool])]
... param (
... [System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::LPWStr)]
... [string]$PathName,
... [IntPtr]$SecurityAttributes
... )
...
... # Calling $this.Invoke will invoke the actual function and return the value
... $this.Invoke($PathName, $SecurityAttributes)
... }</dev:code>
        <dev:remarks>
          <maml:para>This example will hook the CreateDirectoryW (https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectoryw). The `OutputType` is denoted as `bool` to reflect the `BOOL` return value of the function. The `$PathName` parameter is documented as a `string` and is also marked as an `UnmanagedType.LPWStr` to help dotnet know it's a Unicode string. Without this attribute the string will be an `ANSI` string giving the wrong value. The `$this.Invoke` method is called to invoke the real `CreateDirectoryW` and the output is implicitly returned back to the caller. Only the last output from the scriptblock action is going to be returned, in this case there is only the output from `$this.Invoke`.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>--- Example 2 - Hook CreateDirectoryW with raw IntPtr paths ---</maml:title>
        <dev:code>PS C:\&gt; New-PSDetourHook -DllName Kernel32 -MethodName CreateDirectoryW -Action {
... [OutputType([bool])]
... param (
... [IntPtr]$PathName,
... [IntPtr]$SecurityAttributes
... )
...
... # Calling $this.Invoke will invoke the actual function and return the value
... $res = $this.Invoke($PathName, $SecurityAttributes)
...
... if (-not $res) {
... $path = "\\?\" + [System.Runtime.InteropServices.Marshal]::PtrToStringUni($PathName)
... $newPath = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($path)
... $res = $this.Invoke($newPath, $SecurityAttributes)
... [System.Runtime.InteropServices.Marshal]::FreeHGlobal($newPath)
... }
...
... $res
... }</dev:code>
        <dev:remarks>
          <maml:para>This is like the first example but the `$Pathname` is coming in as a raw `IntPtr`. This `IntPtr` can be passed along directly but it can also be converted back to a string using normal dotnet methods. In this example it will try to invoke the C function and have a fallback to prefix the path with `\?` if that fails.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/jborean93/PSDetour/blob/main/docs/en-US/New-PSDetourHook.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>New-PSDetourSession</command:name>
      <command:verb>New</command:verb>
      <command:noun>PSDetourSession</command:noun>
      <maml:description>
        <maml:para>Creates a PSRemoting session to another process.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Creates a PSRemoting session to another process by injecting a running version of PowerShell into a new thread. This can be used to run commands remotely in any process and not just PowerShell.</maml:para>
      <maml:para>Due to the nature of how PSDetour hooks into the remote process, this is something that cannot be undone. Once a process has been tainted it will continue to run the PowerShell listener until the target process ends. See about_PSDetourSessions (./about_PSDetourSessions.md)for more information.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>New-PSDetourSession</maml:name>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
          <maml:name>ProcessId</maml:name>
          <maml:description>
            <maml:para>The process to create the remote session to. This can either be the process id, name, or `Process` object. If specifying by string/name, only one process of that name must exist. If there is none or multiple then the cmdlet will output an error.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">ProcessIntString[]</command:parameterValue>
          <dev:type>
            <maml:name>ProcessIntString[]</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>ApplicationArguments</maml:name>
          <maml:description>
            <maml:para>Any custom arguments to set in the remote session under `$PSSenderInfo.ApplicationArguments`. The keys must be a string and the values can only be primitive types like integers, strings, etc.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">PSPrimitiveDictionary</command:parameterValue>
          <dev:type>
            <maml:name>PSPrimitiveDictionary</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>Name</maml:name>
          <maml:description>
            <maml:para>Custom name to call the remote runspace.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>OpenTimeoutMS</maml:name>
          <maml:description>
            <maml:para>The timeout in milliseconds to wait until the session has been opened.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">Int32</command:parameterValue>
          <dev:type>
            <maml:name>Int32</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>ApplicationArguments</maml:name>
        <maml:description>
          <maml:para>Any custom arguments to set in the remote session under `$PSSenderInfo.ApplicationArguments`. The keys must be a string and the values can only be primitive types like integers, strings, etc.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">PSPrimitiveDictionary</command:parameterValue>
        <dev:type>
          <maml:name>PSPrimitiveDictionary</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>Name</maml:name>
        <maml:description>
          <maml:para>Custom name to call the remote runspace.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>OpenTimeoutMS</maml:name>
        <maml:description>
          <maml:para>The timeout in milliseconds to wait until the session has been opened.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">Int32</command:parameterValue>
        <dev:type>
          <maml:name>Int32</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
        <maml:name>ProcessId</maml:name>
        <maml:description>
          <maml:para>The process to create the remote session to. This can either be the process id, name, or `Process` object. If specifying by string/name, only one process of that name must exist. If there is none or multiple then the cmdlet will output an error.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">ProcessIntString[]</command:parameterValue>
        <dev:type>
          <maml:name>ProcessIntString[]</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>PSDetour.ProcessIntString[]</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>The process can be passed in by input.</maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>System.Management.Automation.Runspaces.PSSession</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>The PSSession object that can be used with `Invoke-Command`, `Enter-PSSession`, or other PSRemoting cmdlets. Make sure to use `Remove-PSSession` to shut down the session when it's no longer needed.</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para>Currently only x64 process targets are supported. ARM or x86 based processes will most likely fail and crash the target process. See Host Error Codes (https://github.com/dotnet/runtime/blob/main/docs/design/features/host-error-codes.md)for a list of return codes that may be used to debug hosting errors.</maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------- Example 1 - Create a remote session to notepad --------</maml:title>
        <dev:code>PS C:\&gt; $session = New-PSDetourSsession -ProcessId notepad
PS C:\&gt; Enter-PSSession -Session $session</dev:code>
        <dev:remarks>
          <maml:para>Creates a PSSession to the process called `notepad` and create an interactive prompt for that session. Note: This will only work if there is only one process called `notepad`.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>---- Example 2 - Create a remote session to a process by id ----</maml:title>
        <dev:code>PS C:\&gt; $session = New-PSDetourSsession -ProcessId 1234
PS C:\&gt; Invoke-Command -Session $session -ScriptBlock { $pid }
PS C:\&gt; $session | Remove-PSSession</dev:code>
        <dev:remarks>
          <maml:para>Creates a PSSession to the process with the id `1234` and runs the `$pid` command in the session.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/jborean93/PSDetour/blob/main/docs/en-US/New-PSDetourHook.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>Start-PSDetour</command:name>
      <command:verb>Start</command:verb>
      <command:noun>PSDetour</command:noun>
      <maml:description>
        <maml:para>Starts a detour session.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Starts a detour session with all the hooks defined in the current session. Use `New-PSDetourHook` to define the hooks to setup for the session. Once started, the detours will be in place until `Stop-PSDetour` is called.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>Start-PSDetour</maml:name>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
          <maml:name>Hook</maml:name>
          <maml:description>
            <maml:para>The hooks from `New-PSDetourHook` to setup in the detour session.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">DetourHook[]</command:parameterValue>
          <dev:type>
            <maml:name>DetourHook[]</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>State</maml:name>
          <maml:description>
            <maml:para>A custom object to inject into the running hooks accessible under `$this.State`. This can be any object that will be accessible in the hook context regardless of whether it is running in the same Runspace or a custom one. Alternatively, the `$using:var` syntax can be used which will force the hook to run in a new Runspace with the injected variable present.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">Object</command:parameterValue>
          <dev:type>
            <maml:name>Object</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
        <maml:name>Hook</maml:name>
        <maml:description>
          <maml:para>The hooks from `New-PSDetourHook` to setup in the detour session.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">DetourHook[]</command:parameterValue>
        <dev:type>
          <maml:name>DetourHook[]</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>State</maml:name>
        <maml:description>
          <maml:para>A custom object to inject into the running hooks accessible under `$this.State`. This can be any object that will be accessible in the hook context regardless of whether it is running in the same Runspace or a custom one. Alternatively, the `$using:var` syntax can be used which will force the hook to run in a new Runspace with the injected variable present.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">Object</command:parameterValue>
        <dev:type>
          <maml:name>Object</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>PSDetour.Commands.ScriptBlockHook[]</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>The hook as input by value or by name.</maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>System.Object</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>None</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- Example 1 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $hook | Start-PSDetour
PS C:\&gt; ... do stuff
PS C:\&gt; Stop-PSDetour</dev:code>
        <dev:remarks>
          <maml:para>Starts the detour session with the hooks specified and does work that should be hooked before stopping the session</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/jborean93/PSDetour/blob/main/docs/en-US/Start-PSDetour.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>Stop-PSDetour</command:name>
      <command:verb>Stop</command:verb>
      <command:noun>PSDetour</command:noun>
      <maml:description>
        <maml:para>Stops a PSDetour session and removes all active hooks.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Stops the active PSDetour session that was started by `Start-PSDetour`.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>Stop-PSDetour</maml:name>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters />
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>None</maml:name>
        </dev:type>
        <maml:description>
          <maml:para></maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>System.Object</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>None</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- Example 1 --------------------------</maml:title>
        <dev:code>PS C:\&gt; Stop-PSDetour</dev:code>
        <dev:remarks>
          <maml:para>Stops the detour session.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/jborean93/PSDetour/blob/main/docs/en-US/Start-PSDetour.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
</helpItems>