尝试在Python中解压/解码专有数据文件
简而言之 - 我在尝试破解一个专有数据库文件时发现,Wordpad能够神奇地将一些数据解码成可读的格式。我想在Python中实现这种解码,但现在连Wordpad的效果也无法重复了。
准备好来点脑筋急转弯吗?
我正在解决一个有点奇怪的问题。我有一个数据文件,这是一个科学仪器(Mettler DSC / STARe软件)的数据库,我想从实验中获取样本信息。经过我的研究,这个文件似乎包含了关于实验运行的明文、未加密的信息,以及一些数据。它是一个.t00文件,大小超过40MB(基本上存储了所有的运行数据),而我对编码了解不多(只知道它看起来是任意的,不是文本文件)。我可以在Wordpad中打开这个文件,看到我想要的信息(样本名称、时间戳、实验参数),这些信息被实验运行数据包围(如预期的那样,这些数据看起来像一堆乱码,例如¶+ú@”‹ø@ðßö@¨...)。我基本上是运气好,能够理解一些内容,现在我想复制这个过程。
我可以用基本的文件处理方式在Python中读取这个文件,并使用正则表达式来获取一些我想要的信息。'r'和'rb'的区别似乎没有帮助。
def textOpenLines(filename,mode='rb'):
with open(filename, mode) as content_file:
return [line for line in content_file]
我能够从这个列表中搜索相关字符串,并获取样本名称。但是,从在Wordpad中查看文件时,我发现样本名称出现了两次,第二次后面跟着时间戳(例如'Dibenzoylperoxid 120 C 03.05.1994 14:24:30')。在Python中,我找不到这个字符串,甚至连时间戳单独也找不到。当我查看应该出现的那一行时,我看到一堆随机字节。在记事本中打开的结果看起来和Python的输出一样。
我怀疑这是编码问题。我尝试以Unicode格式读取文件,也尝试读取一些行的片段,但我还是破解不了。我感到很困惑。
有没有什么想法可以让我正确读取这个文件,使其解码正常?Wordpad能正确显示(不过现在再打开时,看起来像记事本的输出)。
谢谢!!
编辑:
- 我不知道是谁改了标题,但当然在Python/记事本中“看起来像随机字节”。这主要是数据。
- 这个文件不是为了作为文本文件而设计的。我算是运气好,才能在Wordpad中打开。
- 文件没有损坏。DSC仪器程序可以正常读取它。只是它是专有的,所以我不知道它是怎么工作的。
- 我尝试过使用'r'、'rb'和'U'标志。
- 我尝试使用codecs.open,使用utf8、16和32,但出现了UnicodeDecodeError:'utf8'编解码器无法解码位置49的字节0xdf:无效的续字节。我认为它没有BOM,因为我觉得它不是为了人类可读。
前32个字节(f.read(32))读取为:
'\x10 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00'
我对BOM了解不多,但从维基百科的页面来看,这看起来不像任何有效的UTF标记。
文件开始时,在Wordpad中第一次神奇解码的样子是这样的: 121 22Dibenzoylperoxid 120 C 03.05.1994 14:24:30 1 0 4096 ESTimeAI–@£®@nôÂ@49Õ@kÉå@FÞò@`sþ@N5A2A®"A"A—¥A¿ÝA¡zA"ÓAÿãAÐÅAäHA‚œAÑÌAŸäA¤ÆAE–AFNATöAÐ|AõAº^A(ÄAèAýqA¹AÖûAº8A¬uAK«AgÜAüAÞAo4A>N AfAB
文件开始时,在记事本、Python和现在的Wordpad中打开的样子是: (空字节x00...)(x00...)eß(x00...)NvN(x00...)等等。
2 个回答
这是FutureMe。
你可能在使用Wordpad的时候运气不错。我不太确定,因为那些数据早就没了,但我猜Wordpad努力尝试把文件解码成UTF-8(或者可能是UTF-16或CP1252)。之所以看起来有效,是因为在大多数二进制协议中,字符串通常是用UTF-8编码的,所以对于ASCII字符集,它们在转储中看起来是一样的。不过,其他的内容都是以二进制形式编码的。
你用open(fn, 'rb')
的想法是对的,但你应该一次性读取整个数据块,而不是用readlines
,因为它会尝试根据\n
来分割。由于数据库文件并不是以\n
为分隔符的,这样做就不行了。
更好的方法是对字节进行直方图分析,尝试推测字段或行的分隔符是否存在。可以寻找TLV(类型-长度-值)编码字段。因为你知道样本名称的列表,可以用这些起始字符串来找到数据块中的切片点,从而确定字段大小的规律性。
另外,买点比特币。
你的文件并不是由ASCII字符组成的,但打开它的应用程序却把它当成ASCII字符来处理。就像你用记事本打开一个.jpg图片一样,你会看到一堆二进制数据和一些可以被人眼识别的ASCII字符。
这就是为什么你无法直接搜索你的时间戳的原因,比如说。
下面是一个代码示例,来说明这个问题。在你的二进制文件中,有以下字节:
\x44\x69\x62\x65\x6e\x7a\x6f\x79\x6c\x70\x65\x72\x6f\x78\x69\x64\x20\x31\
x32\x30\x20\x43\x20\x30\x33\x2e\x30\x35\x2e\x31\x39\x39\x34\x20\x31\x34\x3a\x32\
x34\x3a\x33\x30
如果你在像记事本这样的文本编辑器中打开它,显示的内容会是:
Dibenzoylperoxid 120 C 03.05.1994 14:24:30
这里有一段Python代码:
>>> c='\x44\x69\x62\x65\x6e\x7a\x6f\x79\x6c\x70\x65\x72\x6f\x78\x69\x64\x20\x31\
x32\x30\x20\x43\x20\x30\x33\x2e\x30\x35\x2e\x31\x39\x39\x34\x20\x31\x34\x3a\x32\
x34\x3a\x33\x30'
>>> print c
Dibenzoylperoxid 120 C 03.05.1994 14:24:30
这些字节是以十六进制格式表示的,这就是你不能用纯文本搜索它的原因。
原因在于,这个二进制文件遵循一种非常特定的结构(协议、规范),这样读取它的程序才能正确解析。如果以jpeg图片为例,你会发现图片的前两个字节和最后两个字节总是相同的(具体取决于使用的格式)——FF D8
是jpeg的前两个字节,而FF D9
是jpeg的最后两个字节,用来标识它是jpeg格式。图像编辑程序会知道从这里开始解析这个二进制数据为jpeg格式,并且会“遍历”文件内部的结构来渲染图像。这里有一个资源链接,可以帮助你根据“签名”或“头部”来识别文件——你的文件的前两个字节10 00
没有出现在那个数据库中,所以你很可能在处理一种专有格式,因此在网上很难找到相关的规范。这时候,逆向工程就派上用场了。
我建议你用十六进制编辑器打开你的文件——它会给你提供十六进制输出和ASCII输出,这样你就可以开始分析文件格式。我个人使用的Hackman Hexeditor可以在这里找到(它是免费的,并且功能很多)。
但现在,为了给你提供一些有用的东西来搜索你感兴趣的数据,这里有一个快速的方法,可以在开始搜索之前将你的查询转换为二进制。
import struct
#binary_data = open("your_binary_file.bin","rb").read()
#your binary data would show up as a big string like this one when you .read()
binary_data = '\x44\x69\x62\x65\x6e\x7a\x6f\x79\x6c\x70\x65\x72\x6f\x78\x69\x64\x20\x31\
x32\x30\x20\x43\x20\x30\x33\x2e\x30\x35\x2e\x31\x39\x39\x34\x20\x31\x34\x3a\x32\
x34\x3a\x33\x30'
def search(text):
#convert the text to binary first
s = ""
for c in text:
s+=struct.pack("b", ord(c))
results = binary_data.find(s)
if results == -1:
print "no results found"
else:
print "the string [%s] is found at position %s in the binary data"%(text, results)
search("Dibenzoylperoxid")
search("03.05.1994")
上述脚本的结果是:
the string [Dibenzoylperoxid] is found at position 0 in the binary data
the string [03.05.1994] is found at position 25 in the binary data
这应该能帮助你入门。