Win10 又带着Bug来了!部分 FLAC 格式音乐文件损坏,已发布紧急更新修复

发布者:Editor
发布于:2021-06-02 16:05

他来了,他来了,他又带着bug走来了!


5月底,微软发布的一份最新文档,称修复了一个令众多用户苦恼的问题。Win10 文件管理器中存在一个 Bug,可使部分 FLAC 格式音频文件损坏。(好家伙,无损变全损,这我可受不住)


微软称,在 Win10 2004 及以上版本中,如果使用文件管理器修改 FLAC 音频文件的元数据,比如标题、艺术家或者其他音频元数据,将使 FLAC 文件损坏,无法播放。


部分 FLAC 文件开头会包含 ID3 帧头,其中包含歌手、标题、专辑名称、年代、风格等信息。




而在 Win10 2004 及以上版本中,文件资源管理器会忽略 ID3 帧头,因为它默认判定 FLAC 文件使用 4 字节 fLaC 开头。因此当用户使用文件资源管理器修改 FLAC 文件的元数据时,就会把 ID3 帧头覆盖掉,从而破坏原有的 FLAC 文件结构,使音乐播放器无法正常识别。


该bug影响多个Windows 10版本(Home、Pro、Enterprise、Education、Pro Education和Pro for工作站)和版本2004和版本20H2。


微软已针对该 Bug 发布了紧急修复更新,在 KB5003214 更新中,微软确认已修复该问题,大家安装即可。


除了KB 5003214更新,微软还提供了一个PowerShell脚本,可用于修复以前损坏的FLAC音乐文件。


要修复FLAC文件,请按照以下步骤操作:


1. 打开notepad,并输入下方内容


    # Copyright 2021 Microsoft
    # This script will repair a FLAC file that has been corrupted by Media Foundation in reference to KB5003430.
    # Refer to KB5003430 for further information
    param(
    [parameter(Mandatory=$true,
    HelpMessage="The path to the FLAC file that has been corrupted by Media Foundation",
    ValueFromRemainingArguments=$true)]
    [ValidateScript({ -not [String]::IsNullOrEmpty($_) -and (Test-Path $_) })]
    [String]$File
    )
    # We need to back up the current file incase we have any errors
    $FileDirectory = Split-Path -Resolve $File
    $Filename = Split-Path -Leaf -Resolve $File
    $FullPath = Join-Path -Resolve $FileDirectory $Filename
    $Filename = [String]::Format("Backup_{0:yyyyMMdd_hhmmss}_{1}", [DateTime]::Now, $Filename)
    $BackupLocation = Join-Path $FileDirectory $Filename
    Write-Output "Microsoft FLAC Repair Tool. This tool will repair a FLAC audio file that was corrupted when editing its details."
    Write-Output "Affected File: $FullPath"
    Write-Output "A backup of the file will be made: $BackupLocation"
    Write-Output "Do you wish to continue?"
    $choice=$host.ui.PromptForChoice("Fixing FLAC Script", "Do you wish to continue", ('&Yes', '&No'), 1)
    function ParseStreamInfoMetadataBlock([System.IO.FileStream]$stream)
    {
    $blockType = $stream.ReadByte()
    $lastBlock = ($blockType -shr 7) -ne 0
    $blockType = $blockType -band 0x7F
    if ($blockType -ne 0)
    {
    return $false
    }
    $blockSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
    if ($blockSize -lt 34)
    {
    return $false
    }
    $minAudioBlockSize = ($stream.ReadByte() -shl 8) -bor $stream.ReadByte()
    $maxAudioBlockSize = ($stream.ReadByte() -shl 8) -bor $stream.ReadByte()
    if ($minAudioBlockSize -lt 16 -or $maxAudioBlockSize -lt 16)
    {
    return $false
    }
    $minFrameSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
    $maxFrameSize = (($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
    $sampleInfo = (($stream.ReadByte() -shl 24) -bor ($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
    $sampleRate = $sampleInfo -shr 12
    $channelCount = (($sampleInfo -shr 9) -band 0x7) + 1
    $bitsPerSample = (($sampleInfo -shr 4) -band 0x1F) + 1
    [UInt64]$sampleCount = (($stream.ReadByte() -shl 24) -bor ($stream.ReadByte() -shl 16) -bor ($stream.ReadByte() -shl 8) -bor $stream.ReadByte())
    $sampleCount = (([UInt64]$sampleInfo -band 0xF) -shl 32) -bor $sampleCount
    $MD5HashBytes = New-Object byte[] 16
    $stream.Read($MD5HashBytes, 0, $MD5HashBytes.Length)
    $MD5Hash = [Guid]($MD5HashBytes)
    if ($sampleRate -eq 0)
    {
    return $false
    }
    # Passing these checks means that we likely have a stream info header and can rebuild the file
    Write-Output "File Stream Information"
    Write-Output "Sample Rate: $sampleRate"
    Write-Output "Audio Channels: $channelCount"
    Write-Output "Sample Depth: $bitsPerSample"
    Write-Output "MD5 Audio Sample Hash: $MD5Hash"
    return $true
    }
    if ($choice -eq 0)
    {
    Copy-Item $FullPath -Destination $BackupLocation -Force
    $stream = [System.IO.File]::Open($FullPath, [System.IO.FileMode]::Open)
    $stream.Seek(4, [System.IO.SeekOrigin]::Begin)
    while ($stream.ReadByte() -eq 0) {}
    # We now need to figure out where a valid FLAC metadata frame begins
    # We are likely pointing to the last byte of the size member so we'll seek back 4 bytes and retry
    $flacDataStartPosition = $stream.Position - 4
    $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
    while (-not(ParseStreamInfoMetadataBlock($stream)))
    {
    $flacDataStartPosition = $flacDataStartPosition + 1
    $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
    }
    # Insert the start code
    $stream.Seek($flacDataStartPosition, [System.IO.SeekOrigin]::Begin)
    if (Test-Path "$FullPath.tmp")
    {
    Remove-Item "$FullPath.tmp"
    }
    $fixedStream = [System.IO.File]::Open("$FullPath.tmp", [System.IO.FileMode]::CreateNew)
    [byte[]]$startCode = [char[]]('f', 'L', 'a', 'C');
    $fixedStream.Write($startCode, 0, $startCode.Length)
    $stream.CopyTo($fixedStream)
    $stream.Close()
    $fixedStream.Close()
    Move-Item -Force "$FullPath.tmp" $FullPath
    }



    2. 点击保存,将文件保存为 FixFlacFiles.ps1,保存类型设置为(*.txt)

    3. 在Windows资源管理器中找到保存的PowerShell脚本,然后单击「使用PowerShell运行」



    4. 在弹出的对话框中,输入不可播放的fLAC文件的文件名,然后点击「enter」





    公众号ID:ikanxue

    官方微博:看雪安全

    商务合作:wsc@kanxue.com




    声明:该文观点仅代表作者本人,转载请注明来自看雪