对常见文件文件头和隐写术做个归纳总结。

一、文件头文件尾

1、图片

  • JPEG

文件头:FF D8 FF
文件尾:FF D9

  • TGA

未压缩的前4字节 00 00 02 00

RLE压缩的前5字节 00 00 10 00 00

  • PNG

文件头:89 50 4E 47 0D 0A 1A 0A   
文件尾:AE 42 60 82

  • GIF

文件头:47 49 46 38 39(37) 61  
文件尾:00 3B

  • BMP

文件头:42 4D

文件头标识(2 bytes) 42(B) 4D(M)

  • TIFF (tif)

文件头:49 49 2A 00

  • ico

文件头:00 00 01 00

  • Adobe Photoshop (psd)

文件头:38 42 50 53

2、office文件

  • MS Word/Excel (xls.or.doc)

文件头:D0 CF 11 E0

  • MS ppt

文件头:50 4B 03 04

  • MS Access (mdb)

文件头:53 74 61 6E 64 61 72 64 20 4A

  • WordPerfect (wpd)

文件头:FF 57 50 43

  • Adobe Acrobat (pdf)

文件头:25 50 44 46 2D 31 2E

  • application/vnd.visio(vsd)

文件头:D0 CF 11 E0 A1 B1 1A E1

  • Email [thorough only] (eml)

文件头:44 65 6C 69 76 65 72 79 2D 64 61 74 65 3A

  • Outlook Express (dbx)

文件头:CF AD 12 FE C5 FD 74 6F

  • Outlook (pst)

文件头:21 42 44 4E

  • Rich Text Format (rtf)

文件头:7B 5C 72 74 66

  • txt 文件(txt)

文件头:Unicode:FE FF

Unicode big endian:FF FE

UTF-8:EF BB BF

ANSI编码是没有文件头的

3、压缩包文件

  • ZIP Archive (zip)

文件头:50 4B 03 04

文件尾:50 4B

  • RAR Archive (rar)

文件头:52 61 72 21 1A 07 00

文件尾:C4 3D 7B 00 40 07 00

  • LZIP Archive (lzip)

文件头:4C 5A 49 50

4、音频文件

  • Wave (wav)

文件头:57 41 56 45

52 49 46 46 xx xx xx xx 57 41 56 45 66 6D 74 20

  • audio(Audio)

文件头: 4D 54 68 64

  • audio/x-aac(aac)

文件头:FF F1(9)

  • 网易云(ape)

文件头:4D 41 43 20

5、视频文件

  • AVI (avi)

文件头:41 56 49 20

  • Real Audio (ram)

文件头:2E 72 61 FD

  • Real Media (rm)

文件头:2E 52 4D 46

  • MPEG (mpg)

文件头:00 00 01 BA(3)

  • Quicktime (mov)

文件头:6D 6F 6F 76

  • Windows Media (asf)

文件头:30 26 B2 75 8E 66 CF 11

  • MIDI (mid)

文件头:4D 54 68 64

6、代码文件

  • XML (xml)

文件头:3C 3F 78 6D 6C

  • HTML (html)

文件头:68 74 6D 6C 3E

  • Quicken (qdf)

文件头:AC 9E BD 8F

  • Windows Password (pwl)

文件头:E3 82 85 96

7、其他类型

  • windows证书文件(der)

文件头:30 82 03 C9

  • CAD (dwg)

文件头:41 43 31 30

  • Windows Shortcut (lnk)

文件头:4C 00 00 00

  • Windows reg(reg)

文件头:52 45 47 45 44 49 54 34

  • java类class

文件头:CA FE BA BE

  • PCAP

文件头:0A 0D 0D 0A

二、图片隐写

1、附加式的图片隐写

操作系统识别,从文件头标志,到文件的结束标志位
当系统识别到图片的结束标志位后,默认是不再继续识别的
所以可以在文件尾后面加东西

(1)附加字符串

最简单的是附加字符串

附加方法

  • winhex直接附加再保存

  • copy /b a.jpg+b.txt c.jpg,在a图片里加b的内容,得到c图片

识别方法

  • winhex直接看

  • notepad也可以看

  • linux的strings指令

  • liunx的exiftool(查看隐藏信息)

应用

  • 制作图片马,即把木马放到图片的最后

(2)隐藏压缩文件

可以把压缩文件藏在图片文件尾后
看起来还是图片

附加方法

  • winhex直接附加再保存

识别方法

  • 有些直接改扩展名就可以用
  • linux的binwalk指令参数-e
  • stegsolve分离
  • winhex复制压缩文件内容重新保存
  • liunx的foremost指令
  • liunx的outguess指令(outguess -k ‘密码’ -r 图片 保存的位置 )
  • liunx的steghide指令(steghide extract -sf 图片名称 -p 密码)

2、基于文件结构的图片隐写

主要是针对PNG图片

标准的PNG文件结构应包括:

  • PNG文件标志

  • PNG数据块:关键数据块和辅助数据块,其中正常的关键数据块有长度、数据块类型码、数据块数据和CRC这4种

(1)png图片文件头数据块(IHDR)

PNG图片的第一个数据块

一张PNG图片仅有一个IHDR数据块

包括了图片的宽,高,图像深度,颜色类型,压缩方法等信息

蓝色部分就是IHDR

可以修改高度值或宽度值对部分信息进行隐藏

  • 如果图片原本是800(宽)*600(高),然后图片的高度从600变成500

  • 这样下面800×100区域的信息就无法从图片中显示出来,我们可见的只有上方800*500的区域,这样就达成了图片隐写的目的

  • 同理可知图片的宽度也可以进行类似的修改以达到隐藏信息的目的

识别方法

  • 用winhex或者010Editor等编辑器打开图片

  • 修改长度或宽度值

  • 在修改文件后,需要利用CRC Calculator对CRC校验码进行重新计算赋值,以防图片被修改后,自身的CRC校验报错,导致图片不能正常打开

(2)IDAT 数据块

  • 存储实际的数据
  • 在数据流中可包含多个连续顺序的图像数据块
  • 写入一个多余的IDAT也不会多大影响肉眼对图片的观察

识别方法

  • 用pngcheck对图片进行检测pngcheck -v hidden.png

可能会出现一个size为0的异常块

提取内容的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/python2

from struct import unpack
from binascii import hexlify, unhexlify
import sys, zlib

# Returns [Position, Chunk Size, Chunk Type, Chunk Data, Chunk CRC]
def getChunk(buf, pos):
a = []
a.append(pos)
size = unpack('!I', buf[pos:pos+4])[0]
# Chunk Size
a.append(buf[pos:pos+4])
# Chunk Type
a.append(buf[pos+4:pos+8])
# Chunk Data
a.append(buf[pos+8:pos+8+size])
# Chunk CRC
a.append(buf[pos+8+size:pos+12+size])
return a

def printChunk(buf, pos):
print 'Pos : '+str(pos)+''
print 'Type: ' + str(buf[pos+4:pos+8])
size = unpack('!I', buf[pos:pos+4])[0]
print 'Size: ' + str(size)
#print 'Cont: ' + str(hexlify(buf[pos+8:pos+8+size]))
print 'CRC : ' + str(hexlify(buf[pos+size+8:pos+size+12]).upper())
print

if len(sys.argv)!=2:
print 'Usage: ./this Stegano_PNG'
sys.exit(2)

buf = open(sys.argv[1]).read()
pos=0

print "PNG Signature: " + str(unpack('cccccccc', buf[pos:pos+8]))
pos+=8

chunks = []
for i in range(3):
chunks.append(getChunk(buf, pos))
printChunk(buf, pos)
pos+=unpack('!I',chunks[i][1])[0]+12


decompressed = zlib.decompress(chunks[1][3])
# Decompressed data length = height x (width * 3 + 1)
print "Data length in PNG file : ", len(chunks[1][3])
print "Decompressed data length: ", len(decompressed)

height = unpack('!I',(chunks[0][3][4:8]))[0]
width = unpack('!I',(chunks[0][3][:4]))[0]
blocksize = width * 3 + 1
filterbits = ''
for i in range(0,len(decompressed),blocksize):
bit = unpack('2401c', decompressed[i:i+blocksize])[0]
if bit == '\x00': filterbits+='0'
elif bit == '\x01': filterbits+='1'
else:
print 'Bit is not 0 or 1... Default is 0 - MAGIC!'
sys.exit(3)

s = filterbits
endianess_filterbits = [filterbits[i:i+8][::-1] for i in xrange(0, len(filterbits), 8)]

flag = ''
for x in endianess_filterbits:
if x=='00000000': break
flag += unhexlify('%x' % int('0b'+str(x), 2))

print 'Flag: ' + flag

3、LSB隐写

LSB,最低有效位,英文是Least Significant Bit

  • 容量大、嵌入速度快、对载体图像质量影响小

  • 在PNG和BMP上可以实现

原理

  • 图片中的像素一般是由三种颜色组成,即三原色(红绿蓝),由这三种原色可以组成其他各种颜色

  • 在png图片的存储中,每个颜色占有8bit,即有256种颜色,一共包含256的三次方颜色,即16777216种颜色

  • 人类的眼睛可以区分约1,000万种不同的颜色,剩下无法区分的颜色就有6777216

  • LSB隐写就是修改了像素中的最低位,把一些信息隐藏起来

给个直观例子

这人眼看不出颜色区别,但最低位不一样

嵌入脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
from PIL import Image
import math

class LSB:
def __init__(self):
self.im=None

def load_bmp(self,bmp_file):
self.im=Image.open(bmp_file)
self.w,self.h=self.im.size
self.available_info_len=self.w*self.h # 不是绝对可靠的
print ("Load>> 可嵌入",self.available_info_len,"bits的信息")

def write(self,info):
"""先嵌入信息的长度,然后嵌入信息"""
info=self._set_info_len(info)
info_len=len(info)
info_index=0
im_index=0
while True:
if info_index>=info_len:
break
data=info[info_index]
x,y=self._get_xy(im_index)
self._write(x,y,data)
info_index+=1
im_index+=1

def save(self,filename):
self.im.save(filename)

def read(self):
"""先读出信息的长度,然后读出信息"""
_len,im_index=self._get_info_len()
info=[]
for i in range(im_index,im_index+_len):
x,y=self._get_xy(i)
data=self._read(x,y)
info.append(data)
return info
#===============================================================#
def _get_xy(self,l):
return l%self.w,int(l/self.w)

def _set_info_len(self,info):
l=int(math.log(self.available_info_len,2))+1
info_len=[0]*l
_len=len(info)
info_len[-len(bin(_len))+2:]=[int(i) for i in bin(_len)[2:]]
return info_len+info

def _get_info_len(self):
l=int(math.log(self.w*self.h,2))+1
len_list=[]
for i in range(l):
x,y=self._get_xy(i)
_d=self._read(x,y)
len_list.append(str(_d))
_len=''.join(len_list)
_len=int(_len,2)
return _len,l

def _write(self,x,y,data):
origin=self.im.getpixel((x,y))
lower_bit=origin%2
if lower_bit==data:
pass
elif (lower_bit,data) == (0,1):
self.im.putpixel((x,y),origin+1)
elif (lower_bit,data) == (1,0):
self.im.putpixel((x,y),origin-1)

def _read(self,x,y):
data=self.im.getpixel((x,y))
return data%2

if __name__=="__main__":
lsb=LSB()
# 写
lsb.load_bmp('test.bmp')
info1=[0,1,0,1,1,0,1,0]
lsb.write(info1)
lsb.save('lsb.bmp')
# 读
lsb.load_bmp('lsb.bmp')
info2=lsb.read()
print (info2)

识别方法

  • stegsolve,调通道
  • zsteg,神一样的工具

提取脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from PIL import Image

im = Image.open("extracted.bmp")
pix = im.load()
width, height = im.size

extracted_bits = []
for y in range(height):
for x in range(width):
r, g, b = pix[(x,y)]
extracted_bits.append(r & 1)
extracted_bits.append(g & 1)
extracted_bits.append(b & 1)

extracted_byte_bits = [extracted_bits[i:i+8] for i in range(0, len(extracted_bits), 8)]
with open("extracted2.bmp", "wb") as out:
for byte_bits in extracted_byte_bits:
byte_str = ''.join(str(x) for x in byte_bits)
byte = chr(int(byte_str, 2))
out.write(byte)

4、基于DCT域的JPG图片隐写

JPEG图像格式使用离散余弦变换(Discrete Cosine Transform,DCT)函数来压缩图像

  • 通过识别每个8×8像素块中相邻像素中的重复像素来减少显示图像所需的位数

  • 使用近似估算法降低其冗余度

  • 有损压缩(Loss Compression)技术

  • 常见的隐写方法有JSteg、JPHide、Outguess、F5

Jsteg隐写

  • 将秘密信息嵌入在量化后的DCT系数的LSB上

  • 原始值为-1,0,+1的DCT系数除外

  • 量化后的DCT系数中有负数

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import math
import cv2
import numpy as np

def dct(m):
m = np.float32(m)/255.0
return cv2.dct(m)*255

class Jsteg:
def __init__(self):
self.sequence_after_dct=None

def set_sequence_after_dct(self,sequence_after_dct):
self.sequence_after_dct=sequence_after_dct
self.available_info_len=len([i for i in self.sequence_after_dct if i not in (-1,1,0)]) # 不是绝对可靠的
print ("Load>> 可嵌入",self.available_info_len,'bits')

def get_sequence_after_dct(self):
return self.sequence_after_dct

def write(self,info):
"""先嵌入信息的长度,然后嵌入信息"""
info=self._set_info_len(info)
info_len=len(info)
info_index=0
im_index=0
while True:
if info_index>=info_len:
break
data=info[info_index]
if self._write(im_index,data):
info_index+=1
im_index+=1


def read(self):
"""先读出信息的长度,然后读出信息"""
_len,sequence_index=self._get_info_len()
info=[]
info_index=0

while True:
if info_index>=_len:
break
data=self._read(sequence_index)
if data!=None:
info.append(data)
info_index+=1
sequence_index+=1

return info

#===============================================================#

def _set_info_len(self,info):
l=int(math.log(self.available_info_len,2))+1
info_len=[0]*l
_len=len(info)
info_len[-len(bin(_len))+2:]=[int(i) for i in bin(_len)[2:]]
return info_len+info

def _get_info_len(self):
l=int(math.log(self.available_info_len,2))+1
len_list=[]
_l_index=0
_seq_index=0
while True:
if _l_index>=l:
break
_d=self._read(_seq_index)
if _d!=None:
len_list.append(str(_d))
_l_index+=1
_seq_index+=1
_len=''.join(len_list)
_len=int(_len,2)
return _len,_seq_index

def _write(self,index,data):
origin=self.sequence_after_dct[index]
if origin in (-1,1,0):
return False

lower_bit=origin%2
if lower_bit==data:
pass
elif origin>0:
if (lower_bit,data) == (0,1):
self.sequence_after_dct[index]=origin+1
elif (lower_bit,data) == (1,0):
self.sequence_after_dct[index]=origin-1
elif origin<0:
if (lower_bit,data) == (0,1):
self.sequence_after_dct[index]=origin-1
elif (lower_bit,data) == (1,0):
self.sequence_after_dct[index]=origin+1

return True

def _read(self,index):
if self.sequence_after_dct[index] not in (-1,1,0):
return self.sequence_after_dct[index]%2
else:
return None

if __name__=="__main__":
jsteg=Jsteg()
# 写
sequence_after_dct=[-1,0,1]*100+[i for i in range(-7,500)]
jsteg.set_sequence_after_dct(sequence_after_dct)
info1=[0,1,0,1,1,0,1,0]
jsteg.write(info1)
sequence_after_dct2=jsteg.get_sequence_after_dct()
# 读
jsteg.set_sequence_after_dct(sequence_after_dct2)
info2=jsteg.read()
print (info2)

Outgusee算法

  • 针对Jsteg算法的缺陷提出的一种方法

  • 嵌入过程不修改ECT系数值为0,1的DCT系数

  • 利用为随机数发生器产生间隔以决定下一个要嵌入的DCT系数的位置

  • 纠正过程消除对效应的出现

识别方法

  • Stegdetect:检测到通过JSteg、JPHide、OutGuess、Invisible Secrets、F5、appendX和Camouflage等这些隐写工具隐藏的信息

  • JPHS:针对JPHide

  • Outguess:针对OutGuess

5、数字水印隐写

数字水印(digital watermark)

  • 在数字化的数据内容中嵌入不明显的记号

  • 被嵌入的记号通常是不可见或不可察的

  • 可以通过计算操作检测或者提取

盲水印

  • 对图像进行傅里叶变换,起始是一个二维离散傅里叶变换,图像的频率是指图像灰度变换的强烈程度

  • 将二维图像由空间域变为频域后,图像上的每个点的值都变成了复数,也就是所谓的复频域,通过复数的实部和虚部,可以计算出幅值和相位,计算幅值即对复数取模值,将取模值后的矩阵显示出来,即为其频谱图

  • 对模值再取对数,在在0~255的范围内进行归一化,这样才能够准确的反映到图像上,发现数据之间的差别,区分高频和低频分量

识别方法:

6、图片容差隐写

容差

  • 在选取颜色时所设置的选取范围

  • 容差越大,选取的范围也越大

  • 其数值是在0-255之间

容差比较的隐写

  • 若是有两张图片,则对两张图片的每一个像素点进行对比,设置一个容差的阈值α,超出这个阈值的像素点RGB值设置为(255,255,255),若是没超过阈值,则设置该像素点的RGB值为(0,0,0)。因此,通过调整不同的α值,可以使对比生成的图片呈现不同的画面。比如两张图完全一样,设置阈值α为任何值,最后得到的对比图都只会是全黑。若两张图每一个像素点都不同,阈值α设置为1,则对比图将是全白。如果将隐藏信息附加到某些像素点上,这时调整阈值α即可看到隐藏信息。

  • 如果是一张图片,则根据每一像素点周围像素的值进行判断,同样设置一个阈值,若当前像素点超过周围像素点的均值,或者其它的某种规则,则将该像素点RGB值置为(255,255,255),反之则不进行处理,或者设置为全0.这样也可以获得隐藏的信息。

识别方法

  • beyond compare比较图片

7、打乱进制

比如把整个二进制都逆序
得到一堆乱码

识别方法

  • winhex,看文件头尾是不是互换且逆序

反序脚本

1
2
3
4
5
6
7
8
9
import os
f = open('1',"rb")#二进制形式打开
f = f.read()[::-1]
for i in f:
ans = str(hex(i))[2:][::-1]
if len(ans) == 1:
ans = ans + '0'
print(ans,end='')
#实现反序输出一个文件

翻转脚本

1
2
3
4
5
6
7
8
9
f1=open('task_flag.jpg','rb').read()
f1_len=len(f1)
f2=open('tt.jpg', 'ab')
i=0
while i<f1_len:
f2.write(f1[i:i+4][::-1])
i=i+4
f2.close()
#实现镜像翻转图片

8、GIF的组合

gif每帧是某个图的一部分
提取每帧再拼接

工具

  • stegsolve

9、YCrCb隐写

gif每帧是某个图的一部分
提取每帧再拼接

工具

  • stegsolve

颜色翻转代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from cv2 import cv2 as cv
#需要打开图片的路径,可以是绝对路径或者相对路径,路径中不能出现中文
#读取的彩色图片,是按照 GBR 的形式,数据格式在 0~255
#根据提示不止RGB,opencv中有多种色彩空间,包括 RGB、HSI、HSL、HSV、HSB、YCrCb、CIE XYZ、CIE Lab8种
'''
使用函数cv2.imread(filepath,flags)读入一副图片
filepath:要读入图片的完整路径
flags:读入图片的标志
cv2.IMREAD_COLOR:默认参数,读入一副彩色图片,忽略alpha通道
cv2.IMREAD_GRAYSCALE:读入灰度图片
cv2.IMREAD_UNCHANGED:顾名思义,读入完整图片,包括alpha通道

'''
imag=cv.imread('G:/Desktop/211119619784cbdb9fb.png')
'''
用法: cv2.cvtColor(src, code[, dst[, dstCn]])
参数:
src:它是要更改其色彩空间的图像。
code:它是色彩空间转换代码。
dst:它是与src图像大小和深度相同的输出图像。它是一个可选参数。
dstCn:它是目标图像中的频道数。如果参数为0,则通道数自动从src和代码得出。它是一个可选参数。
'''
src=cv.cvtColor(imag,cv.COLOR_BGR2YCrCb)
Y,Cr,Cb=cv.split(src)
'''
使用函数cv2.imwrite(file,img,num)保存一个图像。
第一个参数是要保存的文件名,第二个参数是要保存的图像。
可选的第三个参数,它针对特定的格式:对于JPEG,其表示的是图像的质量,
用0 - 100的整数表示,默认95;对于png ,第三个参数表示的是压缩级别,默认为3
'''
cv.imwrite('G:/Desktop/Y.png',(Y%2)*255)
cv.imwrite('G:/Desktop/Cr.png',(Cr%2)*255)
cv.imwrite('G:/Desktop/Cb.png',(Cb%2)*255)

三、音频隐写

简单提一下

  • 频谱图藏信息
  • 高低位二进制
  • 波形藏摩斯密码
  • MP3Stego
  • 音频中也有LSB

1、摩斯电码

考察点

  • WAV音频文件隐写术
  • Python基础
  • 密码学

工具

  • Audacity

莫斯电码解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# -*- coding:utf-8 -*-
s = input("input the cipher_text Enclose with quotes:")
codebook = {
'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':"-----",
'.':".━.━.━",
'?':"..--..",
'!':"-.-.--",
'(':"-.--.",
'@':".--.-.",
':':"---...",
'=':"-...-",
'-':"-....-",
')':"-.--.-",
'+':".-.-.",
',':"--..--",
'\'':".----.",
'_':"..--.-",
'$':"...-..-",
';':"-.-.-.",
'/':"-..-.",
'\"':".-..-.",
}
clear = ""
cipher = ""

while 1:
ss = s.split(" ");
for c in ss:
for k in codebook.keys():
if codebook[k] == c:
cipher+=k
print(cipher)
break;

除了使用本地脚本,还有很多在线网站可以解密

2、频谱图

考察点

  • WAV 音频文件隐写术
  • Audacity 的基操

工具

  • Audacity

识别方法

  • 这里使用工具 Audacity 去打开 wav文件。切换到频谱图去观察:

  • 从频谱图可以直接看到 flag:

3、波形图

考察点

  • WAV 音频文件隐写术
  • 简单密码学

工具

  • Audacity

识别方法

  • 用 Audacity 打开文件后,放大波形谱。

以高为 1 低为 0,转换得到 01 字符串:

1
110011011011001100001110011111110111010111011000010101110101010110011011101011101110110111011110011111101

一共105位,额,不符合8位一个字符,符合7位,于是在每个7位之前加个0,得到:

1
2
3
01100110,01101100,01100001,01100111,01111011,01010111,00110000
01010111,00101010,01100110,01110101,01101110,01101110,01111001,
01111101

然后二进制转十进制,然后再转为 ASCII,得到 flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1100110	102
1101100 108
1100001 97
1100111 103
1111011 123
1010111 87
0110000 48
1010111 87
0101010 42
1100110 102
1110101 117
1101110 110
1101110 110
1111001 121
1111101 125

flag{W0W*funny}

感觉这题的难点不是从频谱中读取二进制数据。

个人觉得从读出的二进制数据想到 8 位一组补上 0 ,这一步还是比较难的,得保持数据的敏感性才可以

4、MP3 隐写

概览

ISCC-2016:Music Never Sleep

考察点

  • mp3 音频文件隐写

工具

在压缩过程中,MP3Stego 会将信息隐藏在 MP3 文件中。数据首先被压缩、加密,然后隐藏在 MP3 比特流中。

基本介绍和用法如下:

1
2
encode -E hidden_text.txt -P pass svega.wav svega_stego.mp3
decode -X -P pass svega_stego.mp3

听音频无异常猜测使用隐写软件隐藏数据,搜索 mp3 里面的关键字符串:

得到密码后使用 Mp3Stego解密:

1
decode.exe -X ISCC2016.mp3 -P bfsiscc2016

得到文件 iscc2016.mp3.txt,其内容是:

1
Flag is SkYzWEk0M1JOWlNHWTJTRktKUkdJTVpXRzVSV0U2REdHTVpHT1pZPQ== ???

base64 && base32 后得到 flag:

none

1
flag: IwtsqndljERbd367cbxf32gg

5、SilentEye

考察点

  • SilentEye 工具的了解使用

工具

SilentEye 是一个跨平台的应用程序设计,可以轻松地使用隐写术,在这种情况下,可以将消息隐藏到图片或声音中。它提供了一个很好的界面,并通过使用插件系统轻松集成了新的隐写算法和加密过程。

Writeup

直接使用 slienteye 这个工具即可 :

6、综合

工具

Writeup

解压 mp3,首先使用 Mp3Stego解密 mp3 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
D:\soft\MP3Stego_1_1_18\MP3Stego
λ Decode.exe -X C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3
MP3StegoEncoder 1.1.17
See README file for copyright info
Input file = 'C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3' output file = 'C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3.pcm'
Will attempt to extract hidden information. Output: C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3.txt
Enter a passphrase:
Confirm your passphrase:
the bit stream file C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3 is a BINARY file
HDR: s=FFF, id=1, l=3, ep=off, br=9, sf=0, pd=1, pr=0, m=0, js=0, c=0, o=0, e=0
alg.=MPEG-1, layer=III, tot bitrate=128, sfrq=44.1
mode=stereo, sblim=32, jsbd=32, ch=2
[Frame 1265]Avg slots/frame = 417.631; b/smp = 2.90; br = 127.899 kbps
Decoding of "C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3" is finished
The decoded PCM output file name is "C:\Users\CTF\Desktop\CTF\misc\hong_Ksfw02V.mp3.pcm"

得到一个 txt 文件内容是:

1
http://weibo.com/u/5280474214?from=feed&loc=avatar&is_all=1      hint:这是一个文件哦,呦呦呦,切克闹

访问得到一张二维码:

读取二维码得到内容为:

1
03F30D0AC252BA576300000000000000000100000040000000730D0000006400008400005A00006401005328020000006300000000030000000F000000430000007363000000640100640200640300640400640500640600640400640700640800640300640400640900640300640900640A00670F007D0000640B007D0100781E007C0000445D16007D02007C01007400007C0200830100377D0100714000577C0100474864000053280C0000004E697A00000069680000006969000000695F0000006966000000697500000069730000006961000000696E0000006967000000740000000028010000007403000000636872280300000074030000007374727404000000666C6167740100000069280000000028000000007304000000622E70795203000000010000007326000000000103010301030103010301030103010301030103010301030103010301090106010D0114024E280100000052030000002800000000280000000028000000007304000000622E707974080000003C6D6F64756C653E010000007300000000

将二进制内容保存文件为gg,然后用xxd命令生成一个 hex 文件:

用 file 命令查看下是一个 python 2.7的编译文件,手动改为 hex.pyc然后在线反编译一下文件得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
# encoding: utf-8

def flag():
str = [
122,
104,
105,
95,
102,
117,
95,
115,
97,
105,
95,
110,
105,
110,
103]
flag = ''
for i in str:
flag += chr(i)

print flag

代码最后加上flag()调用 flag 函数输出 flag:

7、mp3文件private bit隐写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import re
import binascii
n = 235986
result = ''
fina = ''
number = 0
list = [0,1,26,50,75,99,124,148,173,197,222,246,271,295,320,344,369,393,418]
file = open('cb1bc8789569146483da7bd65ee4a063.mp3','rb')
while n < 1369844 :
file.seek(n,0)
if number in list:
n += 417
else:
n += 418
file_read_result = file.read(1)
read_content = bin(ord(file_read_result))[-1]
result = result + read_content
number += 1
#print result

fina = ''
textArr = re.findall('.{'+str(8)+'}', result)
# textArr.append(result[(len(textArr)*8):])
for i in textArr:
fina = fina + chr(int(i,2)).strip('\n')
print fina

四、电子文档隐写

1、隐藏文字

利用隐藏文本功能进行隐写

首先打开,flag.doc
打开flag.doc,能看到的文字内容只有Flag in here。,我们就可以猜测,flag是被隐藏起来了

开启隐藏文字显示功能,查看flag是否被隐写

word文档在此处

wps在菜单栏中,找到文件,移动鼠标到工具一栏,选择选项功能。

在弹出来的菜单栏中,找到隐藏文字功能,选择使其打上对勾。

点击确定,回到文字编辑界面就能看到flag了。

word文档的xml转换

我们可以将word文档转换成xml格式,当然反过来我们也可以将xml转换成word文档,这导致了我们如果重新打包为word文档的过程中,有可能被隐藏进其他数据。

首先,找到文件并打开文件查看

尝试分离文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+bash-4.3$ file file.docx 
file.docx: Zip archive data, at least v2.0 to extract
+bash-4.3$ 7z x file.docx -oout

7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=utf8,Utf16=on,HugeFiles=on,8 CPUs)

Processing archive: file.docx

Extracting word/numbering.xml
Extracting word/settings.xml
Extracting word/fontTable.xml
Extracting word/styles.xml
Extracting word/document.xml
Extracting word/_rels/document.xml.rels
Extracting _rels/.rels
Extracting [Content_Types].xml
Extracting flag.txt

Everything is Ok

我们会发现又flag.txt的文件被打包在file.docx中,直接用7z等压缩包工具打开file.docx

打开,flag.txt文件,就能看到flag了。

2、文件隐藏

PDF隐写中,我们最常用,也是最熟知的工具就是wbStego4open,这是可以把文件隐藏到BMP,TXT,HTM和PDF文件中的工具,当然,这里我们只用他来最为以PDF为载体进行隐写的工具。

在工具目录中找到 wbStego4open,使用工具载入文档,

Step 1 是文件介绍

Step 2 中,我们选择Decode,

Step 3 我们选择目标文件

Step 4 输入加密密码,这里我是空密码,直接跳过

Step 5 为保存文件为 flag.txt

最后打开保存后的文件,flag.txt,就能得到flag了。

总结

CTF MISC 这块还是建议大家靠兴趣去刷题,因为学这玩意出来不一定可以找到工作,如果不是真正喜欢这个,不建议大家去做专职的 MISC 手,CTF 竞赛中二进制是主力,Web 次之。就业方面 Web 就业机会要多于二进制,MISC、密码学这里的方向就业岗位真的不多,所以还是建议 Web 手或者二进制手兼职刷一些 MISC 题目,不要主次颠倒了。