当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] PowerShell 和备用数据流的周五乐趣

作者:精品下载站 日期:2024-12-14 08:06:13 浏览:16 分类:玩电脑

PowerShell 和备用数据流的周五乐趣


我一直对交替数据流有着一种特殊的迷恋。这是一项已经存在很长时间的 NTFS 功能。该功能也称为 ADS,允许用户将数据写入文件的隐藏分支。您几乎可以在备用数据流中存储任何内容,而不会影响报告的文件大小。与任何事情一样,总是有可能受到虐待或更糟的情况。但我假设你有足够的安全措施。您无法禁用 ADS,所以也许我们可以用它做一些有用的事情。

寻找流

第一步是学习如何识别文件中的备用数据流。您可以使用 Get-Item 和 Streams 参数。幸运的是,该参数接受通配符。

[玩转系统] PowerShell 和备用数据流的周五乐趣

流 :$DATA 是文件内容的默认流。您会在每个文件上找到它。这是一个包含第二个数据流的文件。

[玩转系统] PowerShell 和备用数据流的周五乐趣

我将这个简单的 PowerShell 脚本放在一起,以便更轻松地识别额外的备用数据流。

#requires -version 5.1

Param([string]$Path = "*.*")

Get-Item -Path $path -stream * | Where-Object {$_.stream -ne ':$DATA'} |
Select-Object @{Name="Path";Expression = {$_.filename}},
Stream,@{Name="Size";Expression={$_.length}}

[玩转系统] PowerShell 和备用数据流的周五乐趣

获取流内容

我可以看到这些文件有备用数据流。但它们里面有什么?我们看看吧。

[玩转系统] PowerShell 和备用数据流的周五乐趣

Stream 参数不接受通配符,因此您需要提前知道名称。没问题。我将使用我的脚本。

C:\scripts\Get-AlternateDataStream.ps1 .\*.db | Select Path,Stream,@{Name="Content";Expression={Get-Content $_.path -stream $_.stream}}

[玩转系统] PowerShell 和备用数据流的周五乐趣

您可以轻松地将我的代码转换为 PowerShell 函数。

创建备用数据流

要创建您自己的备用数据流,您可以使用其他内容 cmdlet。

dir *.zip | Set-Content -Stream "source" -value "over the rainbow"

[玩转系统] PowerShell 和备用数据流的周五乐趣

我们可以存储什么样的独立于文件内容的信息?版本号怎么样?

set-content .\Get-AboutOnline.ps1 -Stream version -Value "0.9.0"

或者也许是作者信息?

set-content .\Get-AboutOnline.ps1 -Stream author -Value "Jeff Hicks"

[玩转系统] PowerShell 和备用数据流的周五乐趣

添加此信息不会更改文件内容,也不会更改文件大小。但它会更新 LastModfiedTime。

清除流

您可以轻松清除流的内容。

clear-content .\vm.db -Stream secret

溪流本身什么也没有留下。要完全删除流,请使用Remove-Item。

remove-item .\vm.db -Stream secret

使用备用数据流编写脚本

有了这些信息,为什么不构建一些工具来通过我们的 PowerShell 脚本使用备用数据流呢?我围绕 Set-Content 编写了一个包装函数来添加版本备用数据流。

Function Set-VersionStream {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,
        [parameter(Mandatory, HelpMessage = "Specify a version string")]
        [alias("version")]
        [string]$Value,
        [Parameter(HelpMessage = "Specify the name of the version stream. The default is 'versionInfo'.")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        if (-Not ($psboundparameters.containskey["Stream"])) {
            $psboundparameters.Add("Stream", "versionInfo")
        }
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding version $value to stream $($psboundparameters['Stream']) in $Path"
        Set-Content @psboundparameters
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Set-VersionStream

默认流名称是 versionInfo,但您可以指定其他名称或修改代码。

[玩转系统] PowerShell 和备用数据流的周五乐趣

我可以对作者信息做类似的事情。

Function Set-AuthorStream {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,
        [parameter(Mandatory, HelpMessage = "Specify an author string")]
        [alias("author")]
        [string]$Value,
        [Parameter(HelpMessage = "Specify the name of the author stream. The default is 'authorInfo'")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        if (-Not ($psboundparameters.containskey["Stream"])) {
            $psboundparameters.Add("Stream", "authorInfo")
        }
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding author $value to stream $($psboundparameters['Stream']) in $Path"
        Set-Content @psboundparameters
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Set-AuthorStream

[玩转系统] PowerShell 和备用数据流的周五乐趣

该函数接受管道输入,因此我可以快速在多个文件上设置它。

dir *.ps1 | Set-AuthorStream -Value "Jeff Hicks"

由于我有一个命令来设置该值,因此使用一个命令来获取该值可能会很方便,并且可以做的不仅仅是给我一个简单的字符串。

Function Get-AuthorStream {
    [cmdletbinding()]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,

        [Parameter(HelpMessage = "Specify the name of the author stream. The default is 'authorInfo'")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        $splat = @{
            ErrorAction = "Stop"
        }

        if ($psboundparameters.containskey["Stream"]) {
            $splat.Add("Stream", $psboundparameters.containskey["Stream"])
        }
        else {
            $splat.Add("Stream", "authorInfo")
        }
    } #begin

    Process {
        foreach ($item in $path) {
            $splat['Path'] = Convert-Path $item
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting author stream $($psboundparameters['Stream']) from $item"
            Try {
                $info = Get-Content @splat
                [pscustomobject]@{
                    Path       = $splat.path
                    AuthorInfo = $info
                }
            }
            Catch {
                Write-Warning "The stream $Stream was not found on $path"
            }
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close get-AuthorStream

[玩转系统] PowerShell 和备用数据流的周五乐趣

但是,由于我可以在备用数据流中存储的内容没有限制,为什么不存储更丰富的内容呢?看起来像这样的东西:

[玩转系统] PowerShell 和备用数据流的周五乐趣

我编写了一个概念验证函数,将此信息作为 JSON 存储在备用数据字符串中。

Function Set-AuthorStreamJson {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,
        [parameter(Mandatory, HelpMessage = "Specify an author name")]
        [string]$Author,
        [parameter(Mandatory)]
        [string]$Company,
        [parameter(Mandatory)]
        [string]$Version,
        [string]$Comment,
        [Parameter(HelpMessage = "Enter the creation tile. It will saved as a UTC formatted string")]
        [datetime]$Created = (Get-Date),
        [Parameter(HelpMessage = "Specify the name of the author stream. The default is 'authorInfo'")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        $splat = @{
            ErrorAction = "Stop"
        }

        if ($psboundparameters.containskey["Stream"]) {
            $splat.Add("Stream", $psboundparameters["Stream"])
        }
        else {
            $splat.Add("Stream", "authorInfo")
        }

        $json = [PSCustomObject]@{
            Author  = $author
            Company = $company
            Version = $version
            Created = "{0:u}" -f $created
            Comment = $comment
        } | ConvertTo-Json

    } #begin

    Process {
        foreach ($item in $Path) {
            $splat["Path"] = Convert-Path $item
            $splat["Value"] = $json
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding author information for $author to stream $($splat['Stream']) in $item"
            write-verbose $json
            Set-Content @splat
        }

    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Set-AuthorStreamJson

[玩转系统] PowerShell 和备用数据流的周五乐趣

该流是一个 JSON 字符串,我可以将其转换为对象。或者我可以使用 PowerShell 函数。

Function Get-AuthorStreamJson {
    [cmdletbinding()]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,

        [Parameter(HelpMessage = "Specify the name of the author stream. The default is 'authorInfo'")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        $splat = @{
            ErrorAction = "Stop"
        }

        if ($psboundparameters.containskey["Stream"]) {
            $splat.Add("Stream", $psboundparameters["Stream"])
        }
        else {
            $splat.Add("Stream", "authorInfo")
        }
    } #begin

    Process {
        foreach ($item in $path) {
            $splat['Path'] = Convert-Path $item
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting author stream $($splat.stream) from $item"
            Try {
                $info = Get-Content @splat
                $out = $info | ConvertFrom-Json
                $out | Add-Member -MemberType NoteProperty -Name Path -Value $splat.path -PassThru
            }
            Catch {
                Write-Warning "The stream $($splat.stream) was not found on $path"
            }
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close get-AuthorStreamJson

[玩转系统] PowerShell 和备用数据流的周五乐趣

该函数将为缺少流的文件显示警告,因此我在本示例中抑制警告消息。

最后,添加标签怎么样?

Function Add-TagStream {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,
        [parameter(Mandatory, HelpMessage = "Specify a set of tags")]
        [alias("tag")]
        [string[]]$Value,
        [Parameter(HelpMessage = "Specify the name of the Tags stream. The default is 'tags'.")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        if (-Not ($psboundparameters.containskey["Stream"])) {
            $psboundparameters.Add("Stream", "tags")
        }
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding Tags $($value -join ',') to stream $($psboundparameters['Stream']) in $Path"
        Set-Content @psboundparameters
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Add-Tagstream

使用此命令可以轻松添加标签。

dir *.csv,*.db | Add-TagStream -tag "data","company"

当然,我想要一种简单的方法来获取标签流。

Function Get-TagStream {
    [cmdletbinding()]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Specify a file path."
        )]
        [ValidateScript({ (Test-Path $_) -AND ((Get-Item $_).psprovider.name -eq 'FileSystem') })]
        [Alias("fullname")]
        [string[]]$Path,

        [Parameter(HelpMessage = "Specify the name of the tag stream. The default is 'tags'")]
        [string]$Stream
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        $splat = @{
            ErrorAction = "Stop"
        }

        if ($psboundparameters.containskey["Stream"]) {

            $splat.Add("Stream", $psboundparameters["Stream"])
        }
        else {
            $splat.Add("Stream", "tags")
        }
    } #begin

    Process {
        foreach ($item in $path) {
            $splat['Path'] = Convert-Path $item
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting tags stream $($splat['Stream']) from $item"
            Try {
                $info = Get-Content @splat
                [pscustomobject]@{
                    Path       = $splat.path
                    Tags = $info
                }
            }
            Catch {
                Write-Warning "The stream $($splat['Stream']) was not found on $path"
            }
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Get-TagStream

[玩转系统] PowerShell 和备用数据流的周五乐趣

使用此命令,可以轻松根据标签查找文件。

[玩转系统] PowerShell 和备用数据流的周五乐趣

如何使用备用数据流是没有止境的,欢迎您使用我的代码作为框架。我的功能应该被视为概念验证,而不是生产就绪。

局限性

但在你兴奋之前,交替数据流是有限制的。此功能仅在 Windows 和 NTFS 格式的驱动器上受支持。如果将包含备用数据流的文件从一个 NTFS 驱动器复制到另一个 NTFS 驱动器,则这些流也应该复制。但如果将文件复制到非 NTFS 驱动器,您将丢失流。

如果您备份或归档文件,您还可能会丢失备用数据流。然而,值得研究一下设置。我使用 WinRar,该应用程序有一个保存文件流的设置。

[玩转系统] PowerShell 和备用数据流的周五乐趣

最后,如果您与 OneDrive 或 DropBox 等云服务同步文件,预计会丢失备用数据流。我无法找到任何方法来配置这些服务以包含备用数据流。如果你知道一种方法,我很想听听。

概括

我希望您会发现这一点很有趣,并积极思考如何利用此功能。正如我希望您已经看到的那样,围绕备用数据流构建 PowerShell 工具并不那么困难。如果您发现 ADS 的用途,希望您告诉我。

顺便说一句,除了 PowerShell 之外,您还可以使用 SysInternals 中的 Streams.exe 实用程序来列出和删除备用数据流。

享受这个乐趣,并请使用非生产文件进行测试。享受。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯