使用 Python 从音频中提取摩斯密码

本人并非专业人士,在部分专业名词的表述上可能会出现错误,敬请谅解

前段时间在 BugKu 做题时碰到了这么一题 铁子,来一道 – Bugku CTF
(WriteUp 可见同学的博客 【BugKu】铁子,来一道_s1ameseL的博客-CSDN博客)
在解题过程中有一步,需要从音频中提取出摩斯密码。看了同学写的 WriteUp 后,我突发奇想,决定写一个自动从音频中提取摩斯密码的程序,于是就有了这么个工具

Github地址: https://github.com/CrystalMoling/MorseAudioDecoder

编写过程

1.载入音频

在这里使用的是 python 的 wave

# 加载音频
audio = wave.open(sys.argv[1], 'rb')

# 读音频信息
params = audio.getparams()
print(params)
n_channels, _, sample_rate, n_frames = params[:4]

# 读频谱信息
str_wave_data = audio.readframes(n_frames)
audio.close()

# 将频谱信息转为数组
wave_data = np.frombuffer(str_wave_data, dtype=np.short).T

2.提取数据

计算出横轴的时间轴后,使用 pylab 库绘制频谱图像

time = np.arange(0, n_frames) * (1.0 / sample_rate)
pylab.plot(time, wave_data)
pylab.show()

结果如下
pylab 绘制的频谱图片
对于如何区分长(“-”)与短(“.”),我想到的方法是计算出所有信号的平均长度,大于平均长度的即为长(“-”)

# 计算平均频率
wave_avg = int(sum([abs(x / 10) for x in wave_data]) / len(wave_data))

在此处有个小插曲,由于音频的波形呈现正弦型,所以在带有信息的区域也会出现频率值为0的情况,最终生成的数据也无法转换为摩斯密码
正弦型的波形
后来在 Python 波形处理_Rone-X的博客-CSDN博客 这篇博客中发现可以取一段区域内的平均值
比较代码如下,使用了 tqdm 库显示绘制进度

# 绘制摩斯图像
morse_block_sum = 0  # 待划分的数据
morse_block_length = 0  # 待划分的数据长度
morse_arr = []
time_arr = []
pbar = tqdm(wave_data, desc="Drawing Morse Image")
for i in pbar:
    # 高于平均值记为 1 ,反之为 0
    if abs(i) > wave_avg:
        morse_block_sum += 1
    else:
        morse_block_sum += 0
    morse_block_length += 1
    # 将数据按照指定长度划分
    if morse_block_length == 100:
        # 计算划分块的平均值
        if math.sqrt(morse_block_sum / 100) > 0.5:
            morse_arr.append(1)
        else:
            morse_arr.append(0)
        # 横坐标
        time_arr.append(len(time_arr))
        morse_block_length = 0
        morse_block_sum = 0

最后生成的图像如下
最终生成的图像
接着取出 0 位和 1 位的长度信息

# 摩斯电码 按信号长度存储
morse_type = []
morse_len = []
# 摩斯电码长度     0  1
morse_obj_sum = [0, 0]
morse_obj_len = [0, 0]
for i in morse_arr:
    if len(morse_type) == 0 or morse_type[len(morse_type) - 1] != i:
        morse_obj_len[i] += 1
        morse_obj_sum[i] += 1
        morse_type.append(i)
        morse_len.append(1)
    else:
        morse_obj_sum[i] += 1
        morse_len[len(morse_type) - 1] += 1

# 计算信息与空位的平均长度
morse_block_avg = morse_obj_sum[1] / morse_obj_len[1]
morse_blank_avg = morse_obj_sum[0] / morse_obj_len[0]

与平均长度比较

# 转换为摩斯电码
morse_result = ""
for i in range(len(morse_type)):
    if morse_type[i] == 1:
        # 大于平均长度为"-"
        if morse_len[i] > morse_block_avg:
            morse_result += "-"
        # 小于平均长度即为"."
        elif morse_len[i] < morse_block_avg:
            morse_result += "."
    # 大于平均空位长度的为分割
    elif morse_type[i] == 0:
        if morse_len[i] > morse_blank_avg:
            morse_result += "/"

3.解码数据

使用如下字典解码

morse_dict = {
    '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F',
    '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L',
    '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R',
    '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X',
    '-.--': 'Y', '--..': 'Z',

    '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5',
    '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-----': '0',

    '.-.-.-': '.', '---...': ':', '--..--': ',', '-.-.-.': ';', '..--..': '?',
    '-...-': '=', '.----.': '\'', '-..-.': '/', '-.-.--': '!', '-....-': '-',
    '..--.-': '_', '.-..-.': '"', '-.--.': '(', '-.--.-': ')', '...-..-': '$',
    '.--.-.': '@'
}
# 摩斯电码解码
morse_array = morse_result.split("/")
plain_text = ""
for morse in morse_array:
    plain_text += morse_dict[morse]

plain_text 变量中即为解码后的数据

验证

运行结果

参考:
利用python自动解析摩斯电码音频文件_如何从音频中提取摩斯密码_Rabbit_Gray的博客-CSDN博客
使用Python绘制语音信号的波形图_python画信号图_进击的小杨人的博客-CSDN博客
Python 波形处理_Rone-X的博客-CSDN博客

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年11月7日
下一篇 2023年11月7日

相关推荐