Commit eb8204436b7f815e43661e80208af5fc543e6cb7
1 parent
c9fdeb00
Exists in
master
staged.
Showing
6 changed files
with
346 additions
and
148 deletions
Show diff stats
... | ... | @@ -0,0 +1,148 @@ |
1 | +""" | |
2 | +This module implements a common base class of the steganographic | |
3 | +algorithms which embed data into JPEG files. | |
4 | +In order to run plugins inheriting from this class | |
5 | +you must build the rw_dct.so library which interfaces with libjpeg. | |
6 | +To do so, just run | |
7 | +% python setup.py build | |
8 | +in stegotool/util and copy the rw_dct.so | |
9 | +library (which can be found somewhere in the build-directory to | |
10 | +stegotool/util. | |
11 | + | |
12 | +""" | |
13 | +import numpy as np | |
14 | +import time | |
15 | +import os | |
16 | +import sys | |
17 | +import random | |
18 | +import re | |
19 | + | |
20 | +import mjsteg | |
21 | +import jpegObj | |
22 | +from common import * | |
23 | + | |
24 | + | |
25 | +class StegBase(object): | |
26 | + """ | |
27 | + This is the base class for some of the JPEG-algorithms that behave | |
28 | + similarly such as JSteg, OutGuess and F3. | |
29 | + """ | |
30 | + | |
31 | + def __init__(self): | |
32 | + """ | |
33 | + Constructor of the JPEGSteg class. | |
34 | + """ | |
35 | + self.t0 = None | |
36 | + self.cov_jpeg = None | |
37 | + self.cov_data = None | |
38 | + self.hid_data = None | |
39 | + | |
40 | + def _get_cov_data(self, img_path): | |
41 | + """ | |
42 | + Returns DCT coefficients of the cover image. | |
43 | + """ | |
44 | + self.cov_jpeg = jpegObj.Jpeg(img_path) | |
45 | + self.cov_data = self.cov_jpeg.getCoefBlocks() | |
46 | + return self.cov_data | |
47 | + | |
48 | + | |
49 | + def _get_hid_data(self, src_hidden): | |
50 | + """ | |
51 | + Returnsthe secret data as byte sequence. | |
52 | + """ | |
53 | + raw = [0, 0, 0, 0] + np.fromfile(src_hidden, np.uint8).tolist() | |
54 | + raw_size = len(raw) | |
55 | + for i in xrange(4): | |
56 | + raw[i] = raw_size % 256 | |
57 | + raw_size /= 256 | |
58 | + self.hid_data = np.array(raw) | |
59 | + | |
60 | + if np.size(self.hid_data) * 8 > np.size(self.cov_data): | |
61 | + raise Exception("Cover image is too small to embed data.Cannot fit %d bits in %d DCT coefficients" % ( | |
62 | + np.size(self.hid_data) * 8, np.size(self.cov_data))) | |
63 | + return self.hid_data | |
64 | + | |
65 | + | |
66 | + def _post_embed_actions(self, src_cover, src_hidden, tgt_stego): | |
67 | + """ | |
68 | + This function isn't named very accurately. It actually calls the | |
69 | + _raw_embed function in inherited classes. | |
70 | + """ | |
71 | + try: | |
72 | + cov_data = self._get_cov_data(src_cover) | |
73 | + hid_data = self._get_hid_data(src_hidden) | |
74 | + cov_data = self._raw_embed(cov_data, hid_data) | |
75 | + | |
76 | + self.cov_jpeg.setCoefBlocks(cov_data) | |
77 | + self.cov_jpeg.Jwrite(tgt_stego) | |
78 | + | |
79 | + size = os.path.getsize(tgt_stego) | |
80 | + size_embedded = np.size(hid_data) | |
81 | + | |
82 | + self._display_stats("embedded", size, size_embedded, | |
83 | + time.time() - self.t0) | |
84 | + | |
85 | + except TypeError as e: | |
86 | + raise e | |
87 | + except Exception: | |
88 | + raise Exception( | |
89 | + 'DCT coefficients exhausted. This usually means there are not enough DCT coefficients in the image in which algorithm can actually embed data. You should choose a larger image.') | |
90 | + | |
91 | + def _post_extract_actions(self, src_steg, tgt_hidden): | |
92 | + """ | |
93 | + This function isn't named very accurately. It actually calls the | |
94 | + _raw_extract function in inherited classes. | |
95 | + """ | |
96 | + try: | |
97 | + steg_data = self._get_cov_data(src_steg) | |
98 | + emb_size = os.path.getsize(src_steg) | |
99 | + | |
100 | + # recovering file size | |
101 | + header_size = 4 * 8 | |
102 | + size_data = self._raw_extract(steg_data, header_size) | |
103 | + size_data = bits2bytes(size_data) | |
104 | + size_hd = 0 | |
105 | + for i in xrange(4): | |
106 | + size_hd += size_data[i] * 256 ** i | |
107 | + | |
108 | + raw_size = size_hd * 8 | |
109 | + | |
110 | + if raw_size > np.size(steg_data): | |
111 | + raise Exception("Supposed secret data too large for stego image.") | |
112 | + | |
113 | + hid_data = self._raw_extract(steg_data, raw_size) | |
114 | + hid_data = bits2bytes(hid_data) | |
115 | + hid_data[4:].tofile(tgt_hidden) | |
116 | + | |
117 | + self._display_stats("extracted", emb_size, | |
118 | + np.size(hid_data), | |
119 | + time.time() - self.t0) | |
120 | + except: | |
121 | + raise Exception('DCT coefficients exhausted.The stego image is probably corrupted.') | |
122 | + | |
123 | + | |
124 | + def _looks_like_jpeg(self, path): | |
125 | + try: | |
126 | + with open(path, 'r') as f: | |
127 | + return f.read(2) == '\xff\xd8' | |
128 | + except IOError: | |
129 | + return False | |
130 | + | |
131 | + def _display_stats(self, verb, cov_size, emb_size, duration): | |
132 | + print( | |
133 | + "%dB %s in %.2fs (%.2f kBps), embedding ratio: %.4f" % | |
134 | + (emb_size, verb, duration, (emb_size / duration) / 1000., | |
135 | + float(emb_size) / cov_size)) | |
136 | + | |
137 | + # dummy functions to please pylint | |
138 | + def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
139 | + pass | |
140 | + | |
141 | + def _raw_extract(self, steg_data, num_bits): | |
142 | + pass | |
143 | + | |
144 | + def _dummy_embed_hook(self, cov_data, hid_data): | |
145 | + pass | |
146 | + | |
147 | + def _dummy_extract_hook(self, steg_data, num_bits): | |
148 | + pass | ... | ... |
... | ... | @@ -0,0 +1,165 @@ |
1 | +""" | |
2 | +<p> | |
3 | +This module implements an algorithm described by Andreas Westfeld in [1,2], | |
4 | +which detects if there was data embedded into an image using JSteg. | |
5 | +It uses the property that JSteg generates pairs of values in the | |
6 | +DCT-coefficients histogram, which can be detected by a \chi^2 test. | |
7 | +</p> | |
8 | + | |
9 | +<pre> | |
10 | +[1]: Andreas Westfeld, F5 - A Steganographic Algorithm High Capacity Despite | |
11 | +Better Steganalysis | |
12 | +[2]: Andreas Westfeld, Angriffe auf steganographische Systeme | |
13 | +</pre> | |
14 | +""" | |
15 | + | |
16 | +from collections import defaultdict | |
17 | +import os | |
18 | + | |
19 | +import Image | |
20 | +import numpy | |
21 | +from scipy.stats import chisquare | |
22 | +import matplotlib.pyplot as plt | |
23 | +import itertools as it | |
24 | + | |
25 | +from stegotool.util.plugins import describe_and_annotate | |
26 | +from stegotool.util.plugins import ImagePath, NewFilePath | |
27 | +from stegotool.util.JPEGSteg import JPEGSteg | |
28 | +from stegotool.util import rw_dct | |
29 | + | |
30 | +class ChiSquare(JPEGSteg): | |
31 | + """ | |
32 | + The module contains only one method, <b>detect</b>. | |
33 | + """ | |
34 | + | |
35 | + def __init__(self, ui, core): | |
36 | + self.ui = ui | |
37 | + self.core = core | |
38 | + | |
39 | + @describe_and_annotate((None, None), | |
40 | + ("Source image", ImagePath), | |
41 | + ("Target image", NewFilePath), | |
42 | + ("2nd Target image", NewFilePath)) | |
43 | + def detect(self, src, tgt, tgt2): | |
44 | + """ | |
45 | + <p> | |
46 | + Detect if there was data embedded in the <i>source image</i> image with | |
47 | + JSteg algorithm. | |
48 | + </p> | |
49 | + | |
50 | + <p> | |
51 | + Parameters: | |
52 | + <ol> | |
53 | + <li><pre>Source image</pre> Image which should be tested</li> | |
54 | + <li><pre>Target image</pre> Image which displays a graphic with the | |
55 | + embedding probability</li> | |
56 | + <li><pre>2nd Target image</pre> Image which displays the embedding | |
57 | + positions in the image</li> | |
58 | + </ol> | |
59 | + </p> | |
60 | + """ | |
61 | + # --------------------------- Input ----------------------------------- | |
62 | + # If src is from the image pool, test whether the image exists encoded | |
63 | + # on the file system. Otherwise we can not read DCT-coefficients. | |
64 | + if self.core.media_manager.is_media_key(src): | |
65 | + src = self.core.media_manager.get_file(src) | |
66 | + if hasattr(src, 'tmp_file'): | |
67 | + src = src.tmp_file | |
68 | + self.ui.display_error('Trying file: %s' % src) | |
69 | + else: | |
70 | + self.ui.display_error('Can not detect anything from \ | |
71 | + decoded images.') | |
72 | + return | |
73 | + # Test whether the file exists. | |
74 | + if not os.path.isfile(src): | |
75 | + self.ui.display_error('No such file.') | |
76 | + return | |
77 | + # Test if it is a JPEG file. | |
78 | + if not self._looks_like_jpeg(src): | |
79 | + self.ui.display_error('Input is probably not a JPEG file.') | |
80 | + return | |
81 | + | |
82 | + # ---------------------------- Algorithm ------------------------------ | |
83 | + # Build DCT-histogram in steps of \approx 1% of all coefficients and | |
84 | + # calculate the p-value at each step. | |
85 | + dct_data = rw_dct.read_dct_coefficients(src) | |
86 | + hist = defaultdict(int) | |
87 | + cnt = 0 | |
88 | + l = len(dct_data) | |
89 | + one_p = l / 100 | |
90 | + result = [] | |
91 | + for block in dct_data: | |
92 | + # update the histogram with one block of 64 coefficients | |
93 | + for c in block: | |
94 | + hist[c] += 1 | |
95 | + | |
96 | + cnt += 1 | |
97 | + if not cnt % one_p: | |
98 | + # calculate p-value | |
99 | + self.ui.set_progress(cnt * 100 / l) | |
100 | + | |
101 | + # ignore the pair (0, 1), since JSteg does not embed data there | |
102 | + hl = [hist[i] for i in range(-2048, 2049) if not i in (0, 1)] | |
103 | + k = len(hl) / 2 | |
104 | + observed = [] | |
105 | + expected = [] | |
106 | + # calculate observed and expected distribution | |
107 | + for i in range(k): | |
108 | + t = hl[2 * i] + hl[2 * i + 1] | |
109 | + if t > 3: | |
110 | + observed.append(hl[2 * i]) | |
111 | + expected.append(t / 2) | |
112 | + # calculate (\chi^2, p) | |
113 | + p = chisquare(numpy.array(observed), numpy.array(expected))[1] | |
114 | + result.append(p) | |
115 | + | |
116 | + # ----------------------------- Output -------------------------------- | |
117 | + # Graph displaying the embedding probabilities in relation to the | |
118 | + # sample size. | |
119 | + figure = plt.figure() | |
120 | + plot = figure.add_subplot(111) | |
121 | + plot.grid(True) | |
122 | + plot.plot(result, color='r', linewidth=2.0) | |
123 | + plt.axis([0, 100, 0, 1.1]) | |
124 | + plt.title('Embedding probability for different percentages \ | |
125 | +of the file capacity.') | |
126 | + plt.xlabel('% of file capacity') | |
127 | + plt.ylabel('Embedding probability') | |
128 | + | |
129 | + if self.core.media_manager.is_media_key(tgt): | |
130 | + img = figure_to_pil(figure) | |
131 | + self.core.media_manager.put_media(tgt, img) | |
132 | + else: | |
133 | + plt.savefig(tgt) | |
134 | + | |
135 | + # Image displaying the length and position of the embedded data | |
136 | + # within the image | |
137 | + img2 = Image.open(src) | |
138 | + img2.convert("RGB") | |
139 | + width, height = img2.size | |
140 | + | |
141 | + for i in range(100): | |
142 | + result[i] = max(result[i:]) | |
143 | + | |
144 | + cnt2 = 0 | |
145 | + for (top, left) in it.product(range(0, height, 8), range(0, width, 8)): | |
146 | + if not cnt2 % one_p: | |
147 | + r = result[cnt2 / one_p] | |
148 | + if r >= 0.5: | |
149 | + color = (255, int((1 - r) * 2 * 255), 0) | |
150 | + else: | |
151 | + color = (int(r * 2 * 255), 255, 0) | |
152 | + cnt2 += 1 | |
153 | + img2.paste(color, (left, top, min(left + 8, width), | |
154 | + min(top + 8, height))) | |
155 | + self.core.media_manager.put_media(tgt2, img2) | |
156 | + | |
157 | + def __str__(self): | |
158 | + return 'Chi-Square-Test' | |
159 | + | |
160 | + | |
161 | +def figure_to_pil(figure): | |
162 | + figure.canvas.draw() | |
163 | + return Image.fromstring('RGB', | |
164 | + figure.canvas.get_width_height(), | |
165 | + figure.canvas.tostring_rgb()) | ... | ... |
msteg/steganography/StegBase.py
... | ... | @@ -1,148 +0,0 @@ |
1 | -""" | |
2 | -This module implements a common base class of the steganographic | |
3 | -algorithms which embed data into JPEG files. | |
4 | -In order to run plugins inheriting from this class | |
5 | -you must build the rw_dct.so library which interfaces with libjpeg. | |
6 | -To do so, just run | |
7 | -% python setup.py build | |
8 | -in stegotool/util and copy the rw_dct.so | |
9 | -library (which can be found somewhere in the build-directory to | |
10 | -stegotool/util. | |
11 | - | |
12 | -""" | |
13 | -import numpy as np | |
14 | -import time | |
15 | -import os | |
16 | -import sys | |
17 | -import random | |
18 | -import re | |
19 | - | |
20 | -import mjsteg | |
21 | -import jpegObj | |
22 | -from common import * | |
23 | - | |
24 | - | |
25 | -class StegBase(object): | |
26 | - """ | |
27 | - This is the base class for some of the JPEG-algorithms that behave | |
28 | - similarly such as JSteg, OutGuess and F3. | |
29 | - """ | |
30 | - | |
31 | - def __init__(self): | |
32 | - """ | |
33 | - Constructor of the JPEGSteg class. | |
34 | - """ | |
35 | - self.t0 = None | |
36 | - self.cov_jpeg = None | |
37 | - self.cov_data = None | |
38 | - self.hid_data = None | |
39 | - | |
40 | - def _get_cov_data(self, img_path): | |
41 | - """ | |
42 | - Returns DCT coefficients of the cover image. | |
43 | - """ | |
44 | - self.cov_jpeg = jpegObj.Jpeg(img_path) | |
45 | - self.cov_data = self.cov_jpeg.getCoefBlocks() | |
46 | - return self.cov_data | |
47 | - | |
48 | - | |
49 | - def _get_hid_data(self, src_hidden): | |
50 | - """ | |
51 | - Returnsthe secret data as byte sequence. | |
52 | - """ | |
53 | - raw = [0, 0, 0, 0] + np.fromfile(src_hidden, np.uint8).tolist() | |
54 | - raw_size = len(raw) | |
55 | - for i in xrange(4): | |
56 | - raw[i] = raw_size % 256 | |
57 | - raw_size /= 256 | |
58 | - self.hid_data = np.array(raw) | |
59 | - | |
60 | - if np.size(self.hid_data) * 8 > np.size(self.cov_data): | |
61 | - raise Exception("Cover image is too small to embed data.Cannot fit %d bits in %d DCT coefficients" % ( | |
62 | - np.size(self.hid_data) * 8, np.size(self.cov_data))) | |
63 | - return self.hid_data | |
64 | - | |
65 | - | |
66 | - def _post_embed_actions(self, src_cover, src_hidden, tgt_stego): | |
67 | - """ | |
68 | - This function isn't named very accurately. It actually calls the | |
69 | - _raw_embed function in inherited classes. | |
70 | - """ | |
71 | - try: | |
72 | - cov_data = self._get_cov_data(src_cover) | |
73 | - hid_data = self._get_hid_data(src_hidden) | |
74 | - cov_data = self._raw_embed(cov_data, hid_data) | |
75 | - | |
76 | - self.cov_jpeg.setCoefBlocks(cov_data) | |
77 | - self.cov_jpeg.Jwrite(tgt_stego) | |
78 | - | |
79 | - size = os.path.getsize(tgt_stego) | |
80 | - size_embedded = np.size(hid_data) | |
81 | - | |
82 | - self._display_stats("embedded", size, size_embedded, | |
83 | - time.time() - self.t0) | |
84 | - | |
85 | - except TypeError as e: | |
86 | - raise e | |
87 | - except Exception: | |
88 | - raise Exception( | |
89 | - 'DCT coefficients exhausted. This usually means there are not enough DCT coefficients in the image in which algorithm can actually embed data. You should choose a larger image.') | |
90 | - | |
91 | - def _post_extract_actions(self, src_steg, tgt_hidden): | |
92 | - """ | |
93 | - This function isn't named very accurately. It actually calls the | |
94 | - _raw_extract function in inherited classes. | |
95 | - """ | |
96 | - try: | |
97 | - steg_data = self._get_cov_data(src_steg) | |
98 | - emb_size = os.path.getsize(src_steg) | |
99 | - | |
100 | - # recovering file size | |
101 | - header_size = 4 * 8 | |
102 | - size_data = self._raw_extract(steg_data, header_size) | |
103 | - size_data = bits2bytes(size_data) | |
104 | - size_hd = 0 | |
105 | - for i in xrange(4): | |
106 | - size_hd += size_data[i] * 256 ** i | |
107 | - | |
108 | - raw_size = size_hd * 8 | |
109 | - | |
110 | - if raw_size > np.size(steg_data): | |
111 | - raise Exception("Supposed secret data too large for stego image.") | |
112 | - | |
113 | - hid_data = self._raw_extract(steg_data, raw_size) | |
114 | - hid_data = bits2bytes(hid_data) | |
115 | - hid_data[4:].tofile(tgt_hidden) | |
116 | - | |
117 | - self._display_stats("extracted", emb_size, | |
118 | - np.size(hid_data), | |
119 | - time.time() - self.t0) | |
120 | - except: | |
121 | - raise Exception('DCT coefficients exhausted.The stego image is probably corrupted.') | |
122 | - | |
123 | - | |
124 | - def _looks_like_jpeg(self, path): | |
125 | - try: | |
126 | - with open(path, 'r') as f: | |
127 | - return f.read(2) == '\xff\xd8' | |
128 | - except IOError: | |
129 | - return False | |
130 | - | |
131 | - def _display_stats(self, verb, cov_size, emb_size, duration): | |
132 | - print( | |
133 | - "%dB %s in %.2fs (%.2f kBps), embedding ratio: %.4f" % | |
134 | - (emb_size, verb, duration, (emb_size / duration) / 1000., | |
135 | - float(emb_size) / cov_size)) | |
136 | - | |
137 | - # dummy functions to please pylint | |
138 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
139 | - pass | |
140 | - | |
141 | - def _raw_extract(self, steg_data, num_bits): | |
142 | - pass | |
143 | - | |
144 | - def _dummy_embed_hook(self, cov_data, hid_data): | |
145 | - pass | |
146 | - | |
147 | - def _dummy_extract_hook(self, steg_data, num_bits): | |
148 | - pass |
res/embeded
1 | 1 | this is to be embeded. |
2 | +//0216 | |
3 | +vim of clang - https://github.com/JBakamovic/yavide | |
4 | +# Usage overview | |
5 | +Category | Shortcut | Description | |
6 | +--------------------------------- | --------------------------------- | --------------------------------- | |
7 | +**Project management** | | | |
8 | + | `<Ctrl-s>n` | Create new project | |
9 | + | `<Ctrl-s>i` | Import project with already existing code base | |
10 | + | `<Ctrl-s>o` | Open project | |
11 | + | `<Ctrl-s>c` | Close project | |
12 | + | `<Ctrl-s>s` | Save project | |
13 | + | `<Ctrl-s>d` | Delete project | |
14 | +**Buffer management** | | | |
15 | + | `<Ctrl-c>` | Close current buffer | |
16 | + | `<Ctrl-s>` | Save current buffer | |
17 | + | `<Ctrl-Tab>` | Go to next buffer | |
18 | + | `<Ctrl-Shift-Tab>` | Go to previous buffer | |
19 | + | `<Ctrl-Down>` | Scroll buffer by one line (down) | |
20 | + | `<Ctrl-Up>` | Scroll buffer by one line (up) | |
21 | +**Buffer modes** | | | |
22 | + | `<ESC>` | Enter the `normal` mode | |
23 | + | `<a>` | Enter the `insert` mode (append after cursor) | |
24 | + | `<i>` | Enter the `insert` mode (insert before cursor) | |
25 | + | `<Shift-v>` | Enter the `visual` mode (line mode) | |
26 | + | `<v>` | Enter the `visual` mode (character mode) | |
27 | +**Buffer editing** | | | |
28 | + | `<Ctrl-a>` | Select all | |
29 | + | `<Ctrl-x>` | Cut | |
30 | + | `<Ctrl-c>` | Copy | |
31 | + | `<Ctrl-v>` | Paste | |
32 | + | `<Ctrl-z>` | Undo | |
33 | + | `<Ctrl-r>` | Redo | |
34 | + | `<Shift-s>` | Delete the whole line | ... | ... |
res/extracted
res/steged.jpg