- 知识点:
typedef FrameHeader
{ //数字代表大小,单位bite
unsigned int sync:12; //同步信息
unsigned int version:2; //版本
unsigned int layer: 2; //层
unsigned int error protection:1; // CRC校验
unsigned int bitrate_index:4; //位率
unsigned int sampling_frequency:2; //采样频率
unsigned int padding:1; //帧长调节
unsigned int private:1; //保留字
unsigned int mode:2; //声道模式
unsigned int mode extension:2; //扩充模式
unsigned int copyright:1; // 版权
unsigned int original:1; //原版标志
unsigned int emphasis:2; //强调模式
}
- 帧长度是压缩时每一帧的长度,包括帧头的4个字节(32bit)。它将填充的空位也计算在内。
- padding的值会影响每一帧的长度(具体分析见下面的题目)
题目文件 提取码:fwyh
-----常规尝试略------
将解压后的
mp3
文件用 010Editor (winhex等) 打开分析详见图片
通过观察可知,private bit的数值的在0和1中变化,但多看一些 mf[] 发现,大部分是 0,应该不会有什么有效的信息,再看
copyright
也是在 0 和 1 发生改变,去查资料,copyright
代表着版权,应该是固定不变的值,所以copyright
中应该有flag或者重要的信息,先提取出copyright
具体脚本如下:
import re
n = 984486 # 起始位置(是padding的起始位置)
result = ''
files = open('2.mp3', 'rb') # 以二进制打开文件
# 提取
while n < 12658083: # 结束位置
files.seek(n, 0)
head = files.read(1) #读取 一个 字节(8位),见上图呀
# print(head,end='')
padding = '{:08b}'.format(ord(head))[-2] # 该字节的倒数第二位是padding的值
# print(padding) 检验padding 值提取是否有误
files.seek(n + 1, 0)
file_read_result = files.read(1)
result += '{:08b}'.format(ord(file_read_result))[-4]
# print(len(bin(ord(file_read_result))))
# result += bin(ord(file_read_result))[-4]
# n += 1045 if padding == "1" else 1044 # 高级写法
# 普通写法
if padding == "0":
n += 1044
else:
n += 1045
# print(result)
# 拼接
flag = ''
textArr = re.findall('.{' + str(8) + '}', result)
for i in textArr:
flag = flag + chr(int(i, 2)).strip('\n')
print(flag)
脚本分析:
第3行的 n 为起始位置(padding的起始位置)
- 通过 010Editor 可知,mp3文件的头帧起始位置是235984(用十进制表示)、由下面的两张图片可以的到第一帧的起始位置
- 由上面的大图可以知道,每一帧的大小是32bit(即4字节),
sync
、mpeq id
、layer id
、protection id
共占了16bit(即2字节),所以padding的起始位置是头帧的起始位置 加2,即235986- 第7行的 结束位置 (拉到最下面,方法同上)
第 19 ~ 22 行,增加的值取决于 帧长(大小,用十进制表示,如下图的蓝框),其大小还与
padding
的值有关
第 8 行:seek(offset,x) -> 移动文件读取的指针到指定位置
- offset:是偏移量,也就是要移动的字节数,如果是我负数,就是从后往前
- x:可选,默认为 0 ,【0表示从头开始,1表示从当前位置开始,2表示从文件尾开始】
第 9 行:read([size])读取文件内容
- size:可选,代表从文件读取的字节数,默认为
-1
,表示读取整个文件第 11 行:获取padding对应的值
- ord() -> 返回对应字符的十进制整数
- format(num) -> 将括号里的整数 num 转换为对应到进制
- {:08b} -> 域宽为 8 ,不足八位就 补 0,b代表是
二进制
- 第13行:将文件读取的指针向下移动一个字节(看第一张图可以知道,
copyright
位于每一帧(4字节)的第四个字节中的第 5 个bite【注意:从零开始计数】(倒数第四个同理)(该MP3中,具体情况具体分析)- 第26 ~ 30行,就是正则表达式库(re库)函数的用法了
链接 提取码:hack
题目描述:我拿出自己的私密音乐来和你分享,一起享受快乐吧
可以看着上面的题目来举一反三咯,思路同上,只是private bit的位置在第三个字节的最后一位(bit)
脚本:
import re
n = 235986
result = ''
number = 0
file = open('1.mp3', 'rb')
l = []
while n < 1369844:
file.seek(n, 0)
head = file.read(1)
# print(head,end='、')
padding = '{:08b}'.format(ord(head))[-2]
# l.append(padding)
# result += bin(ord(head))[-1]
result += '{:08b}'.format(ord(head))[-1]
if padding == "0":
n += 417
else:
n += 418
file.seek(n, 0)
# 拼接
flag = ''
textArr = re.findall('.{' + str(8) + '}', result)
# print(textArr)
# print(chr(int(textArr[0],2)))
for i in textArr:
flag = flag + chr(int(i, 2)).strip('\n')
print(flag)
# print(l)
"""
验证padding
l1= []
for i in range(len(l)):
if l[i] == '0':
l1.append(i)
print(l1)
"""
代码详解:(基本原理遇上一题基本相似,大差不差)
不同点:
第 14 行 没有seek(n+1,0)
- 原因:
private bit
和padding
在同一字节(在010Editor)中即可发现
才接触ctf不到一个月,这也是我的第一次写wp,多有不足,见谅。(抠了一天呢)
更多内容,见博客super,感谢来访、指正,共同进步
One comment
大佬666