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 @@ | @@ -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 @@ | @@ -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,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 | this is to be embeded. | 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