首页 > 解决方案 > 准确复制 Certutil.exe 的 -decodehex 输出

问题描述

自从我第一次发布这个问题以来,我已经做了很多研究,我想我也搞砸了一些术语。

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

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

我提到的脚本在下面,是对这个链接中呈现的作品的改编。我没有将数据流转换为数据的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()

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

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

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

我一直在寻找VB.net(我最熟悉的第二语言)解决方案,但它们在方法上与PowerShell我找到的方法相当,没有什么能充分获取整个文件并ASCII相对容易或准确地放置它。

更新:

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

现在。. . . 让我们希望我的 IS 部门也不要标记这种方法并对此发出噪音。. .

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

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

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

标签: vb.netpowershellbatch-filecertutil

解决方案


如果您不介意使用 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,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]该类将其直接写入文件。

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

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


推荐阅读