完全复制 Certutil.exe 的 -decodehex 输出

问题描述

自从我第一次发布这个问题以来,我做了大量的研究,我想我也弄错了一些术语。

困境:我公司的信息安全团队已将 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,$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()

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

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

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

我已经寻找了 VB.net(我最熟悉的第二语言)解决方案,但它们在方法上与我发现的 PowerShell 方法相当,没有什么可以充分获取整个文件并将其放入相对轻松或准确地ASCII

更新:

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

现在。 . . .让我们希望我的 I.S.部门也不会标记这种方法并对此发表意见。 . .

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

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

调用

再次 - 我对你的感谢不够!如果我通过 VB.Net 获得使用同一个 crypt32.dll 库的工作方法,我会回来并将其作为答案发布,但您已经获奖了!

解决方法

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

应得的信用!我没有想出这个转换位,Sysadmins LV 的加密天才 Vadims Podāns 做到了。请在此处查看他的帖子: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,[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,$array,[ref]$pdwFlags)
        $array
    } else {
        Write-Warning $((New-Object ComponentModel.Win32Exception ([Runtime.InteropServices.Marshal]::GetLastWin32Error())).Message)
    }
}

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

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

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