本人并非专业人士,在部分专业名词的表述上可能会出现错误,敬请谅解
前段时间在 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()
结果如下
对于如何区分长(“-”)与短(“.”),我想到的方法是计算出所有信号的平均长度,大于平均长度的即为长(“-”)
# 计算平均频率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 1morse_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博客
来源地址:https://blog.csdn.net/AnKores/article/details/131138260