数字取证实战:从受损Garmin设备中恢复并重现语音导航指令

本文详述了对一台损坏的Garmin nuvi 56LM GPS设备进行芯片级数据提取与分析的过程,重点恢复了其语音日志文件,并开发了Python脚本利用espeak-ng库将X-SAMPA格式的语音指令字符串转换为可播放的WAV音频文件。

恢复并重现Garmin语音指令

等等,猴子,你刚才说的是Carmen还是Garmin?

我们有一台损坏的Garmin nuvi 56LM GPS设备,从中恢复了一个包含语音日志的文本文件。整个过程有些不同寻常,所以我们认为分享这个故事可能会很有趣。基于这项努力,猴子编写了一个Python3脚本(parse_garmin56LM.py),该脚本使用免费的espeak-ng库将Garmin 56LM语音日志文本转换为WAV文件,以便更好地识别地点名称。该脚本可在GitHub上获取。

特别感谢以下人员提供的帮助:

  • Rusolut的Sasha Sheremetov在数据恢复方面提供的建议
  • Berla的Ken Case在GPS数据日志方面提供的建议
  • Benjamin “BJ” Duncan分享了关于语音日志的发现
  • Katie Russ创建了她的实用网站

我们的故事始于一台损坏的Garmin nuvi 56LM,它"提供易于遵循、带有街道名称的语音逐向导航指示"。嗯,它曾经是这样的!维基百科显示它于2014年发布。

谷歌搜索找到了那个时期一篇有趣的论文——Alexandre Arbelet(2014年8月)的"Garmin车载导航仪取证方法与痕迹:一项探索性研究"。虽然它没有涵盖我们的型号,但它提到GPX文件是GPS轨迹日志的潜在来源。听起来很有希望,对吧?

不幸的是,我们的设备损坏严重,无法修复,因此芯片提取是我们唯一的选择。该芯片是一颗SanDisk 8 GB eMMC芯片。对芯片的多次读取产生了相同的哈希值,因此芯片似乎相当稳定。X-Ways、Autopsy、Oxygen Forensic Detective和FTK Imager都无法从转储文件中识别分区。Cellebrite Physical Analyzer的Garmin Legacy链也没有从转储文件中提取任何信息。不过转储中可见ASCII纯文本,所以并非完全没有希望。

Berla的Ken建议,Garmin通常使用包含GPX轨迹日志的FAT32分区。不幸的是,转储文件的第一个512字节扇区并未以MBR通常的55AA结尾,因此我们需要找到提取文件系统的方法。

Rusolut的Sasha建议使用R-studio从转储中恢复/提取文件系统。大获成功!提取出了两个分区——FAT16(128 MB)和FAT32(3.3 GB——比预期小一点。也许Garmin使用8 GB芯片是为了通用性/易于升级?)

在寻找带有时间戳的经纬度坐标时,发现了一些值得注意的文件… 注意:这是基于一台设备的内容,其他设备/型号可能以不同方式存储数据。

FAT16/.System/SQLite/RecentStops.db 包含一个带有缩放经纬度数字的"history"表。通过将这些原始数字乘以180/2^31,我们能够获得可绘制的经纬度坐标。

FAT16/.System/SQLite/pre.db “route_segment"表包含时间戳和经纬度路线信息(不确定这些路线是否实际行驶过)。“history"表包含起点经纬度、终点经纬度和开始时间。注意:Garmin时间戳测量的是自1989年12月31日(Garmin发布日期)以来的秒数——更多细节请参见此处。通过将631065600秒加到Garmin数字时间戳上,就得到了自1970年Unix纪元以来的秒数,这使得查找人类可读的时间更加容易(支持Unix纪元时间的工具比支持Garmin时间的工具多)。

FAT16/.System/Diag/EventLogs/*.TXT 包含可能带有时间戳的经纬度坐标。然而,该文件也包含非ASCII/二进制字节。

FAT16/.System/SQLite/quick_search_list.db FAT16/.System/SQLite/recent_searches.db FAT16/.System/Logs/searches.txt 包含潜在的时间戳搜索信息。

FAT16/.System/GPS/ARC.bin 包含ASCII经纬度字符串(搜索"GPS main”)

FAT16/.System/Trips/Current.trip 未知的文件格式,可能包含当前行程日志?

FAT32/Garmin/GarminDevice.xml 包含各种设置、版本和型号信息。它还提到了一个GPX目录,其中引用了.gpx文件(我们的设备中不存在这些文件)。Rusolut的Sasha证实他的测试设备有这些文件。我不确定为什么存在这种差异——也许是由于地区差异或用户设置?查找GPX日志的文件格式显示,它们是使用特定关键字/字段名来记录经纬度的XML文本文件。然而,在整个转储中搜索"trkpt"和"trkType"关键字没有找到任何GPX格式的数据。

到目前为止,所有信息都还算有趣… 然而,BJ也引起了我们对一个有趣痕迹的注意,这催生了这篇帖子…

FAT32/Voice/logs/vpm_log_all.log 这个文本文件似乎是按时间顺序记录GPS语音指令。由于我们缺乏测试设备,无法保证车辆正好在语音提示的位置,但它可能被用来表明用户在该区域或知晓该区域。

例如,语音日志包含如下行:

1
2
D[2019/06/12 07:55:03] {22ce6e88} [vpm_tts_parse.c:vpm_tts_parse:3770] Navigation phrase selected: Keep right $USR_TO_NEXT_ROAD. (229)
D[2019/06/12 07:55:03] {22ce6e88} [vpm_tts_log.c:vpm_tts_log_phonetics:277] Map Phonetics: "nju "IN|gl@nd *"haI|%we (MDB Lang: 23)

语音部分是指"Map Phonetics: “和”(MDB Lang: 23)“之间的字符串。例如:“nju “IN|gl@nd *“haI|%we” 另一个例子可能是: “wE|st@n *“mo|t@|%we”

行格式可以概括为:

1
D[YYYY/MM/DD HH:MM:SS] {4字节十六进制ID?进程/线程ID?} [vpm_tts_log.c:vpm_tts_log_phonetics:277] Map Phonetics: VOICE_STRING (MDB Lang: 23)

其中VOICE_STRING看起来是系统的本地化发音指南。

还有各种行包含字符串"Voice Language: Australian English-Karen (TTS)",这似乎指示用户听到的是哪种语音。快速谷歌搜索找到了Karen的声音,她还做过其他可识别的配音工作(例如Garmin GPS – Australian Karen, Navman GPS – Australian Karen, Apple iPhone 4s & 5 – Australian Voice of Siri)。

“好的,Karen”,肯定存在一个与字符串发音相关的系统,但当时猴子认为它可能是Garmin专有的。快进到几周前,猴子有了一个小突破。有一种发音系统叫做国际音标(IPA)。如果你读过维基百科或字典,你可能见过这些奇怪的发音符号,并讽刺地想"嗯,这很有帮助”。例如,维基百科的"Cooking banana"页面使用IPA来描述如何发音"plantain”。参见下图包含奇怪符号的高亮文本:

IPA发音示例(高亮文本)

然而,如果我们仅限于95个可打印的英文ASCII字符集,我们需要一种额外的方法来编码这些奇怪的IPA符号。有几种可用的方法,但为了我们的目的,我们将讨论范围限制在Kirshenbaum和X-SAMPA系统。

再次查看我们日志中的语音字符串——它们似乎使用的是X_SAMPA系统。例如,使用双引号表示重音,使用{符号等。

方便的是,Katie Russ创建了一个使用亚马逊Polly Speech API的网站,可以接收(X-SAMPA)IPA字符串并将其转换为声音。而且还可以配置语音/口音!你可以在这里找到她的网站:http://ipa-reader.xyz/

如果你有兴趣,尝试复制并粘贴以下文本:

1
"nju "IN|gl@nd *"haI|%we

到网站上,你应该能听到相应的"New England Highway"发音。非常酷!

IPA-Reader网站示例

这让猴子想到——对于一两个字符串,网站很好用,但从设备日志中复制粘贴数百个条目是不现实的。亚马逊Polly也不是免费的,这促使我们寻找免费的替代方案。

我们找到了一个名为espeak-ng的开源C库。虽然你可以从头编译/构建它,但Ubuntu也提供了可安装的debian包。容易多了!注意:我们尝试在Ubuntu 16.04上安装时遇到了问题(可能是因为它不再受支持),但在Ubuntu 20.04上没有问题。

这是Ubuntu 20.04的espeak-ng帮助页面。它允许用户输入(Kirshenbaum)IPA字符串,然后收听/录制相应的音频到WAV文件。

要在Ubuntu 18+上安装,输入:

1
sudo apt-get install espeak-ng

然后你可以像这样使用:

1
espeak-ng "[[Hello w3:ld]]"

注意:字符串用双引号括起来

它不如亚马逊Polly那么精致,有时会对一些字符串感到困惑,但它工作得相当好(如果你不介意一些类似斯蒂芬·霍金的声音的话)。 还要注意,它使用Kirshenbaum字符串作为输入,而不是X-SAMPA字符串(像语音日志中那样),因此需要进行一些转换。 注意:我们发现维基百科中列出的比较/转换图表并不能完全转换我们所有的数据,因此我们的脚本必须进行一些定制化的转换。这些转换对于其他用户来说,根据语言不同,可能听起来不正确。

以下是我们总结的转换过程:

  • 将输入字符串括在’[[‘和’]]‘字符之间,以便解释符号而不是拼写出来
  • 将’{‘字符替换为’a’
  • 将双引号"替换为单引号’以表示主重音
  • 将’%‘替换为’,‘以表示次重音
  • 将’A’替换为’a’

有关Kirshenbaum系统的更多细节,请参见此处

例如,语音日志字符串是:

1
"h{|m@nd *"{|v@n|ju

转换为:

1
[['ha|m@nd *'a|v@n|ju]]

要听到它发音为"Hammond Avenue”,输入:

1
espeak-ng "[['ha|m@nd *'a|v@n|ju]]"

注意:通过命令行输入时,使用双引号括起来。

要将其保存为WAV文件,你可以使用:

1
espeak-ng "[['ha|m@nd *'a|v@n|ju]]" -w output.WAV

有些转换后的字符串可能听起来不正确/不可识别,对于这些(希望是罕见的)情况,你可以将语音日志字符串输入ipa-reader.xyz网站来听发音短语。也可以使用-s参数调整espeak-ng的播放速度。

脚本

现在我们知道如何为一个语音字符串获取音频文件了,让我们尝试从整个语音日志中自动提取字符串。

输入文件:vpm_log_all.txt 输出文件:一个包含原始行文本和行号的HTML报告表格,用于输入espeak-ng的转换后文本文件,以及输出WAV文件的链接。

以下是概括的脚本逻辑:

1
2
3
4
5
6
7
8
打开/读取 vpm_log_all.txt
对于每一行:
    从行文本中提取语音字符串
    转换并将语音字符串写入 LINENUMBER.txt
    调用 "espeak-ng -s 100 -w LINENUMBER.WAV -f LINENUMBER.txt" 来生成WAV文件
    将 (LINENUMBER, 行文本, 转换后的语音文本, WAV文件名) 存储在列表中
读取列表
从列表打印HTML表格 ("日志行号", "日志行文本", "处理后的espeak-ng字符串" (链接到文本文件), "音频文件"链接)

以下是如何运行它——这将输出.WAV、.txt和Report.html文件到给定的"op"输出目录:

1
2
3
4
5
6
7
python3 parse_garmin56LM.py -f vpm_log_all.log -o op
parse_garmin56LM.py 2020-05-17 初始版本
目录 op 已创建
185.WAV = [[*'maks|,wEl 'strit]]
...
2225.WAV = [['ha|m@nd *'a|v@n|ju]]
已处理875个语音条目。退出...

以下是"op"输出目录的内容:

以下是"Report.html"的样子(时间戳已编辑):

从上方的截图可以看出,表格非常简单——点击链接可以查看输入文本文件或打开WAV音频文件。

注意:我们尝试从脚本中直接调用"espeak-ng"并传递转换后的语音字符串(而不是包含字符串的文本文件),但生成的WAV文件总是因未知原因被截断,即单词缺失。使用文本文件输入似乎避免了这个问题。

该脚本是用Python3在Ubuntu 20.04 LTS上编写/测试的,但我们只有一组测试数据,因此它可能需要一些调整。

最终思考

我们已经成功编写了一个脚本,可以从Garmin nuvi 56LM(2014)语音日志中提取IPA字符串并将其转换为WAV文件。

脚本的代码(参见"process_voicestring"函数)用于将语音日志字符串转换为espeak-ng输入字符串,可能需要根据用户数据/语言设置进行一些调整。

该脚本也可能适用于其他型号的Garmin GPS,但这尚未经过测试。

如果你在其他设备中看到/见过类似的语音日志,我们很乐意在评论区听到你的分享。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计