-
Notifications
You must be signed in to change notification settings - Fork 6
/
stegowiper.sh
240 lines (208 loc) · 6.58 KB
/
stegowiper.sh
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/bin/bash
#
# stegoWiper v0.1 - Cleans stego information from input image file
#
# Usage: see help()
#
# Copyright (C) 2022 - Madrid Spain
# Dr. Manuel Urueña <muruenya@gmail.com>
# Dr. Alfonso Muñoz @mindcrypt
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
readonly EXIT_SUCCESS=0
readonly EXIT_FAILURE=1
# Prints error message into STDERR
#
# Arguments:
# Message to be printed
function err ()
{
echo "$@" 1>&2
}
# Prints log message into STDOUT
#
# Globals:
# verbose
# Arguments:
# Message to be printed
function log ()
{
if [[ "$verbose" -eq 1 ]]; then
echo "$@"
fi
}
# Prints usage message into STDERR
function show_usage()
{
local myself
myself=$(basename "$0")
err "Usage: ${myself} [-hvc <comment>] <input file> <output file>"
}
# Prints help message into STDOUT
function show_help()
{
local myself
myself=$(basename "$0")
echo "stegoWiper v0.1 - Cleans stego information from image files"
echo " (png, jpg, gif, bmp, svg)"
echo
echo "Usage: ${myself} [-hvc <comment>] <input file> <output file>"
echo
echo "Options:"
echo " -h Show this message and exit"
echo " -v Verbose mode"
echo " -c <comment> Add <comment> to output image file"
}
# Main function
function main()
{
# Parse options
verbose=0
comment=""
while getopts ":c:hv" opt; do
case "${opt}" in
c )
comment=${OPTARG}
;;
h )
show_help
exit "$EXIT_SUCCESS"
;;
v )
verbose=1
;;
\? )
err "Invalid option: -${OPTARG}"
show_usage
exit "$EXIT_FAILURE"
;;
: )
err "Invalid option: -${OPTARG} requires an argument"
show_usage
exit "$EXIT_FAILURE"
;;
esac
done
shift $((OPTIND-1))
# Parse arguments
if [[ $# -eq 0 ]]; then
show_help
exit "$EXIT_FAILURE"
elif [[ $# -ne 2 ]]; then
show_usage
exit "$EXIT_FAILURE"
fi
readonly input_file="$1"
if [[ ! -r "$input_file" ]]; then
err "Invalid argument: '$input_file' file cannot be read"
exit "$EXIT_FAILURE"
fi
readonly output_file="$2"
if [[ -f "$output_file" ]] && [[ ! -w "$output_file" ]]; then
err "Invalid argument: '$output_file' file cannot be written"
exit "$EXIT_FAILURE"
fi
if [[ "$input_file" == "$output_file" ]]; then
err "Invalid argument: Input and output files must be different"
exit "$EXIT_FAILURE"
fi
# Get MIME Type of input file
mime_type=$(exiftool -short3 -MIMEType "${input_file}" 2>&1) ; exit_code=$?
if [[ $exit_code -ne 0 ]]; then
error_msg=${mime_type}
err "Invalid argument: '$input_file' is not a valid image file: ${error_msg}"
exit "$EXIT_FAILURE"
elif [[ "${mime_type}" != image/* ]]; then
err "Invalid argument: '$input_file' is not an image file (${mime_type})"
exit "$EXIT_FAILURE"
fi
log "${input_file}: ${mime_type}"
# Identify input format version so output file has the same format as input
file_format=""
if [[ "${mime_type}" = "image/bmp" ]]; then
# BMP files do not have a comments section. But they can smuggle
# information after the End of File (EOF), in unused fields
# (e.g. Reserved1, Reserved2), in the gaps between sections and in the
# image itself employing LSB techniques.
#
# BMP reencoding cleans EOF, gaps and unused fields (e.g. Reserved).
bmp_version=$(exiftool -short3 -BMPVersion "${input_file}")
case "${bmp_version}" in
"OS/2 V1") file_format="BMP2:" ;;
"Windows V3") file_format="BMP3:" ;;
"Windows V5") file_format="BMP:" ;;
"Windows V4") file_format="BMP:" ;;
#TODO: BMPv4 files are converted to BMPv5 because ImageMagick
# does not support BMPv4 format
esac
#BMP files cannot include a Comment
comment=""
elif [[ "${mime_type}" = "image/gif" ]]; then
# GIF files can have a Comment, Text Data, and Application Data
# sections (which may include XMP metadata).
#
# IM's -strip does remove Comment and Application Data sections of
# GIF files
gif_version=$(exiftool -short3 -GIFVersion "${input_file}")
case "${gif_version}" in
#TODO: ImageMagick does not seem to write comments in GIF87
# images and exiftool only ou
"87a") file_format="GIF87:" ;;
"89a") file_format="GIF:" ;;
esac
#TODO: Try with a GIF with multiple frames
elif [[ "${mime_type}" = image/svg* ]]; then
file_format="SVG:"
fi
# Most image formats allow appending data after the End of File (EOF).
# Reencoding the image deletes that data.
#
#TODO: Deal with more image formats, like TIFF
# (e.g. clean PageName with exiftool)
log "Converting '${input_file}' into '${file_format}${output_file}'"
if [[ "${mime_type}" = image/svg* ]]; then
# ImageMagick does not properly support SVG files.
# Using 'librsvg2-bin' instead
rsvg-convert -f svg "${input_file}" -o "${output_file}"
else
convert "${input_file}" -strip -set comment "${comment}" \
-attenuate 0.1 +noise Gaussian \
"${file_format}${output_file}"
fi
if [[ "$comment" != "" ]]; then
log "Adding comment to '${output_file}': '${comment}'"
if [[ "${mime_type}" = "image/png" ]]; then
# ImageMagick is not able to add comments to PNG files,
# use exiftool instead
exiftool -quiet -comment="${comment}" \
-overwrite_original "${output_file}"
elif [[ "${mime_type}" = image/svg* ]]; then
echo '<!-- ' "${comment}" ' -->' >> "${output_file}"
fi
fi
if [[ "$verbose" -eq 1 ]]; then
input_info=$(mktemp)
exiftool "$input_file" > "$input_info"
output_info=$(mktemp)
exiftool "$output_file" > "$output_info"
echo "================================================================================"
diff --side-by-side "$input_info" "$output_info"
echo "================================================================================"
rm -f "$input_info" "$output_info"
fi
exit "$EXIT_SUCCESS"
}
main "$@"