繁体   English   中英

如何使用Elixir打包/解开十六进制字符串(高位优先)

[英]How to pack / unpack a hex string (high nibble first) with Elixir

我想知道如何在Elixir中使用十六进制字符串。 具体来说,我对将十六进制转换为ASCII感兴趣。

在Ruby中,此实现可能是:

["001C7F616A8B002128C1A33E8100"].pack('H*').gsub(/[^[:print:]]/, '.')

我如何用Elixir完成这项任务? 我试过了:

<<00, 01, C7, F6...>>

但这不是字符串十六进制的正确表示形式。 感谢您的时间和协助!

因此,我取得了一些进展,但是目前正在为递归方面做着努力。

到目前为止,这是我的解决方案:

defmodule ElixirNetworkTools do
  def decode(payload) do
    upper_payload = String.upcase payload
    case Base.decode16(upper_payload) do
      :error -> decode_with_nonprintable_characters(payload)
      {:ok, decoded_payload} -> decoded_payload
    end
    |> IO.write
  end

def decode_with_nonprintable_characters(payload) do
String.chunk(payload, ~r/\w{2}/)
|> Enum.each(fn(byte) ->
  case Base.decode16(byte) do
    :error -> '.'
    {:ok, decoded_payload} -> decoded_payload
      end
    end)
  end
end

这是解决该问题的另一种方法。 我们开始之前的几件事:

  • 您可以将case: :mixed传递给Base.decode16/2Base.decode16(string, case: :mixed) ,因此,您不需要大写。

  • 如果您要提出一个无效的字符串,不要打扰检查,只需直接调用decode16,因为它还会检查大小。

这意味着我们可以从以下内容开始:

decoded = Base.decode16!(string, case: :mixed)

现在,您需要替换不可打印的字符。 不要使用String.printable?/1因为它是关于UTF-8而不是ASCII的。 我们需要实现自己的功能,但是更有意义的是:提出或替换它们? 如果有人发送无效数据,似乎必须将其视为错误? 如果是这样的话:

def validate_ascii!(<<h, t::binary>>) when h <= 127 do
  validate_ascii!(t)
end

def validate_ascii!(<<>>) do
  true
end

def validate_ascii!(rest) do
  raise "invalid ascii on string starting at: #{rest}"
end

或者,您可以只删除last子句,它也会失败。

现在我们可以将其放在一起:

decoded = Base.decode16!(string, case: :mixed)
validate_ascii!(decoded)
decoded

编辑:如果您需要用点替换非ASCII:

def keep_ascii(<<h, t::binary>>, acc) when h <= 127 do
  keep_ascii(t, acc <> <<h>>)
end

def keep_ascii(<<_, t::binary>>, acc) do
  keep_ascii(t, acc <> ".")
end

def keep_ascii(<<>>, acc) do
  acc
end

解决方案最终如下,尽管如果有更清洁或更完善的解决方案,我会对了解它非常感兴趣。

defmodule ElixirNetworkTools do
  @doc """
  The decode function takes a hexadecimal payload, such as one generated
  by Snort, and returns the ASCII representation of the string.

  ## Example
  iex> ElixirNetworkTools.decode("436F6E74656E742D4C656E6774683A203132")
  {:ok, "Content-Length: 12"}
  """
  def decode(payload) do
    case _validate_length_of_snort(payload) do
      :error -> raise "Invalid length hex string. Must be even length. Exiting"
      _ -> nil
    end

    decoded = String.upcase(payload)
    |> _do_decode
    |> to_string

    {:ok, decoded}
  end

  @doc """
  Internal function used to manually process the hexadecimal payload,
  and builds a char list of the printable characters. If a character is
  not printable, we instead use periods.

  ## Example
  iex> ElixirNetworkTools._do_decode("436F6E74656E742D4C656E6774683A203132")
  ["Content-Length: 12"]
  """
  def _do_decode(payload) do
    Base.decode16!(payload)
    |> String.chunk(:printable)
    |> Enum.map(fn(chunk) ->
        case String.printable? chunk do
          true -> chunk
          _ -> "."
        end
    end)
  end

  @doc """
  Internal function used to validate the length of the hexadecimal payload.
  Hexadecimal strings should have an even number of characters.

  ## Example
  iex> ElixirNetworkTools._validate_length_of_snort("436F6E74656E742D4C656E6774683A203132")
  :ok
  """
  def _validate_length_of_snort(payload) do
    String.length(payload)
    |> rem(2)
    |> case do
      0 -> :ok
      _ -> :error
    end
  end
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM