准确复制 Certutil.exe 的 -decodehex 输出

Replicating Certutil.exe's -decodehex output exactly

自从我第一次 post 回答这个问题以来,我做了更多的研究,我想我也搞砸了一些术语。

困境:我公司的信息安全团队已将 certutil.exe 标记为在最近的网络钓鱼攻击后使用的潜在危险应用程序。这真的很糟糕,因为我们喜欢 certutil.exe 将 hex 文件快速准确地转换为 ascii 文件。这些 ascii 文件必须完全按照 certutil.exe -decodehex 执行的方式进行转换,以便由另一个程序解析以读取和解释由单独的内部程序生成的各种数据。

我有一个 PowerShell 脚本,它似乎非常准确地将 Hex 转换为 ASCII,但是,它似乎永远不会“完成”,好像 while 循环可能被搞砸了.此外,由于流的分解方式,结果文件中的换行符太多。这些文件通常每个 7 MB,大约 7-8 十亿 个字符长,大约 11 个换行符。

我在下面提到的脚本是对 in this link 作品的改编。我没有将数据流转换为 Hex 数据的 Hex 表示,而是将其转换为 ASCII.

$bufferSize = 65536
$ASCIIFile = "C:\FooBar.dat"
$stream = [System.IO.File]::OpenRead(
  "C:\FixedOutput.dat")
while ( $stream.Position -lt $stream.Length ) {
#BEGIN CALLOUT A
  $buffer = new-object Byte[] $bufferSize
  $bytesRead = $stream.Read($buffer, 0, $bufferSize)
#END CALLOUT A
  for ( $line = 0; $line -lt [Math]::Floor($bytesRead /
  16); $line++ ) {
  
$slice = $buffer[($line * 16)..(($line * 16) + 15)]

$bytes=[System.Text.Encoding]::ASCII.GetString($slice)
$asc = -join($bytes-split"(..)"|?{$_}|%{[char][convert]::ToByte($_,16)})
$asc | Write-Host >> $ASCIIFile -NoNewline

  }
#BEGIN CALLOUT B
  if ( $bytesRead % 16 -ne 0 ) {
    $slice = $buffer[($line * 16)..($bytesRead - 1)]
    $output = ""
    foreach ( $byte in $slice ) {
      $output=[System.Text.Encoding]::ASCII.GetString($byte)
    }
    $output | ADD-Content $Asciifile
#END CALLOUT B
  }
}


$stream.Close()

此外,除了之前可能重复的问答之外,我还改编了 中的 PowerShell 代码。这组代码的问题是,输出仍然需要 15 分钟左右,但输出与 certutil.exe -decodehex 不同,因此我们的内部程序无法解析信息!

此外,我可以直接复制并粘贴原始文件中的十六进制数据,将其粘贴到十六进制编辑器中,然后将输出另存为新文件以获得我需要的内容。

问题是,我们经常同时拥有 30 - 40 个这样的文件,我们需要一个闪电般快速的解决方案。 . .

我一直在寻找 VB.net(我第二熟悉的语言)解决方案,但它们在方法上与我找到的 PowerShell 方法相当,而且没有什么能充分利用整个文件和相对容易或准确地将其设置为 ASCII

更新:

除了重新格式化问题之外,我还测试了下面来自 TheMadTechnician 的非常详细的答案,这让我热泪盈眶。如果我能穿过硅片亲吻你,我可能会的。 教科书匹配。快如闪电。美丽。

现在。 . . .让我们希望我的I.S。部门也没有标记这种方法,也没有大声疾呼。 . .

我修改了 -join 语句,因为我在从 Batch 脚本中调用 PowerShell 之前连接文件,但这在 PowerShell 中会很好地工作还有。

最后,自从我们 I.S。部门限制 .ps1 脚本的使用,不久前,我发现了一个很棒的选项,可以将复杂命令嵌入为 Base64 字符串,然后使用 Start /MIN powershell -encodedcommand _insertEncodedCommandHere_

调用它

再次感谢您!如果我通过 VB.Net 获得使用相同 crypt32.dll 库的工作方法,我会回来并 post 也将其作为答案,但你已经中奖了!

如果您不介意使用 Crypt32.dll 库,您可以添加它的 CryptStringToBinary 方法以将十六进制转换为二进制,然后将数组转储到文件中。

归功于应得的功劳!我没有想出这个转换位,Sysadmins LV 的加密天才 Vadims Podāns 做到了。请在这里查看他的 post:https://www.sysadmins.lv/blog-en/convert-data-between-binary-hex-and-base64-in-powershell.aspx

所以,首先我们必须从 DLL 添加方法,我们可以通过定义签名来实现,然后使用 Add-Type 像这样:

$signature = @"
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptStringToBinary(
    string pszString,
    int cchString,
    int dwFlags,
    byte[] pbBinary,
    ref int pcbBinary,
    int pdwSkip,
    ref int pdwFlags
);
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32 -UsingNamespace "System.Text"

请注意,他在他的页面上添加了两种方法,但如果只是将十六进制转换为二进制,我们只需要一种方法。他还展示了如何将二进制转换为十六进制,但您可以在他的页面上阅读相关内容。

现在,你说你有几个文件需要合并,对吗?我们可以在内存中将它们拼接在一起并一次转换它们。您并没有真正说明它们的命名方式或排序方式,但我们假设它们具有顺序名称(HexFile.01、HexFile.02 等)。我敢肯定,您可以弄清楚如何以正确的顺序排列它们。让我们加载它们:

$RawHex = (Get-content HexFile.* -raw) -join ''

这很简单!所以现在我们添加了类型,并且我们在内存中重建了原始的十六进制格式文件,现在我们只需要将其转换为二进制并将其转储到文件中即可。为了进行转换,我将继续使用 Vadims 的函数,因为它工作得很好,稍微修改一下,以便它实际输出字节数组:

function Convert-HexToBinary ([string]$hex) {
    # decoding hashtable contains universal flags: Base64Any and HexAny. The function attempts to
    # get the correct input string format and then decode it
    $decoding = @{'Base64Header' = 0; 'Base64' = 1; 'HexRaw' = 12; 'Hex' = 4; 'HexAddr' = 10;
        'HexAscii' = 5; 'HexAddrAscii' = 11; 'Base64Any' = 6; 'HexAny' = 8}
    # initialize variables to receive resulting byte array size and actual input string format
    $pcbBinary = 0
    $pdwFlags = 0
    # call CryptStringToBinary to get resulting byte array size and actual input string format
    if ([PKI.Crypt32]::CryptStringToBinary($hex,$hex.Length,$decoding['HexAny'],$null,[ref]$pcbBinary,0,[ref]$pdwFlags)) {
        # create enough large byte array
        $array = New-Object byte[] -ArgumentList $pcbBinary
        # call the function again to write converted bytes to a byte array
        [void][PKI.Crypt32]::CryptStringToBinary($hex,$hex.Length,$decoding['HexAny'],$array,[ref]$pcbBinary,0,[ref]$pdwFlags)
        $array
    } else {
        Write-Warning $((New-Object ComponentModel.Win32Exception ([Runtime.InteropServices.Marshal]::GetLastWin32Error())).Message)
    }
}

现在该函数转储字节数组,我们将在变量中捕获它。一旦我们有了它,我们就可以使用 [io.file] class 将它直接写入文件。

$BinArr = Convert-HexToBinary $RawHex
[io.file]::WriteAllBytes("C:\Path\To\OutFile.bin",$BinArr)

在我自己的测试中,这与使用 certutil 的结果相同。无论哪种方式,我都得到相同的文件哈希值。