使用正则表达式将其中嵌入了未知数量的十六进制字符串的字符串转换为字符串

Convert strings with an unknown number of hex strings embedded in them to strings using regex

所以我有一个字符串列表(来自 Snort 规则的内容),我正在尝试将它们的十六进制部分转换为 UTF-8/ASCII,以便我可以通过 netcat 发送内容。

我现在使用的方法适用于具有单个十六进制字符(即 3A)的字符串,但当存在一系列十六进制字符(即 3A 4B 00 FF)时会中断

我目前的解决方案是:

import re
import codecs

def convert_hex(match):
  string = match.group(1)
  string = string.replace(" ", "")
  decode_hex = codecs.getdecoder("hex_codec")
  try:
    result = decode_hex(string)[0]
  except:
    result = bytes.fromhex((lambda s: ("%s%s00" * (len(s)//2)) % tuple(s))(string)).decode('utf-16-le')
  return result.decode("utf-8")


strings = ['|0A|Referer|3A| res|3A|/C|3A|', 'RemoteNC Control Password|3A|', '/bbs/search.asp', 'User-Agent|3A| Mozilla/4.0 |28|compatible|3B| MSIE 5.0|3B| Windows NT 5.0|29|']

converted_strings = []

for string in strings:
    for i in range(len(string)):
        string = re.sub(r"\|(.{2})\|", convert_hex, string)
    converted_strings.append(string)

对于 strings 中的字符串,这有效,但对于像这样的字符串:

|08 00 00 00 27 C7 CC 6B C2 FD 13 0E|

它坏了。

我尝试将正则表达式更改为:

re.sub(r"\|.*([A-Fa-f0-9]{2}).*\|")

但这只会转换最后一个十六进制。

我需要此解决方案来处理 Hello|3A|World|3A 00 FF|Hello|3A 00|World

等字符串

我知道这是正则表达式的问题,但我不确定具体是什么。

如有任何帮助,我们将不胜感激。

看起来子串要么总是十六进制,即 (?:[A-Fa-f0-9]{2}\s)+[A-Fa-f0-9]{2},要么在 | 个符号之间根本不是十六进制?

这个有效:

for string in strings:
    for i in range(len(string)):
        string = re.sub(r"(?<=\|)((?:[A-Fa-f0-9]{2}\s)*[A-Fa-f0-9]{2})(?=\|)", convert_hex, string)
    converted_strings.append(string)

(捕获组 1 的额外括号 - 您可以省略一对括号并更改您的函数以改为作用于 group(0)

但它在您的示例 |08 00 00 00 27 C7 CC 6B C2 FD 13 0E| 上中断,因为它似乎不是有效的 UTF-8 编码。产生的错误:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc7 in position 5: invalid continuation byte

然而,有效的 UTF-8 编码 multi-byte 字符串如 '|74 65 73 74 20 f0 9f 98 80|' 工作正常:

import re
import codecs

def convert_hex(match):
  string = match.group(1)
  string = string.replace(" ", "")
  decode_hex = codecs.getdecoder("hex_codec")
  try:
    result = decode_hex(string)[0]
  except:
    result = bytes.fromhex((lambda s: ("%s%s00" * (len(s)//2)) % tuple(s))(string)).decode('utf-16-le')
  return result.decode("utf-8")


strings = ['|74 65 73 74 20 f0 9f 98 80|']

converted_strings = []

for string in strings:
    for i in range(len(string)):
        string = re.sub(r"(?<=\|)((?:[A-Fa-f0-9]{2}\s)*[A-Fa-f0-9]{2})(?=\|)", convert_hex, string)
    converted_strings.append(string)

print(converted_strings)

结果:

['|test |']

如果您真的不需要数据的可打印表示,您可以将函数 return 设为 bytes 对象,并且只将该函数应用于匹配的部分 - 而不是构造一个新字符串。

根据@Selcuk 的说法,byte-strings 的结果可能更有意义 - 这适用于所有三种类型的输入:

import re
import codecs

def convert_hex(match):
  string = match.group(1)
  string = string.replace(b" ", b"")
  decode_hex = codecs.getdecoder("hex_codec")
  try:
    result = decode_hex(string)[0]
  except:
    result = bytes.fromhex((lambda s: ("%s%s00" * (len(s)//2)) % tuple(s))(string)).decode('utf-16-le')
  return result


strings = ['|0A|Referer|3A| res|3A|/C|3A|', '|74 65 73 74 20 f0 9f 98 80|', '|08 00 00 00 27 C7 CC 6B C2 FD 13 0E|']

converted_strings = []

for string in strings:
    string = re.sub(rb"(?<=\|)((?:[A-Fa-f0-9]{2}\s)*[A-Fa-f0-9]{2})(?=\|)", convert_hex, string.encode())
    converted_strings.append(string)

print(converted_strings)

结果:

[b'|\n|Referer|:| res|:|/C|:|', b'|test \xf0\x9f\x98\x80|', b"|\x08\x00\x00\x00'\xc7\xcck\xc2\xfd\x13\x0e|"]

没有编码问题,因为没有选择编码。 (请注意,我并没有尝试对 convert_hex 进行过多更改 - 您可能需要查看其中的一些编码杂耍,我只是让它为 bytes 工作)