-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStegaPic.py
408 lines (357 loc) · 17.2 KB
/
StegaPic.py
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# Import modules for tool designe and style
from colorama import Style, Fore
import random
import os
import time
# Import module for work with image and pixels
from PIL import Image
# Function to convert a message into a binary string
def message_to_binary(message):
return ''.join(format(ord(char), '08b') for char in message)
# Function to convert a binary into a messsage string
def binary_to_message(binary):
message = ''.join(chr(int(binary[i:i+8], 2)) for i in range(0, len(binary), 8))
return message
# Function to convert file data to binary string
def file_to_binary(file_path):
with open(file_path, 'rb') as file:
file_data = file.read()
return ''.join(format(byte, '08b') for byte in file_data)
def binary_to_file(binary_data, output_file_path):
binary_bytes = [binary_data[i:i+8] for i in range(0, len(binary_data), 8)]
file_data = bytes([int(byte, 2) for byte in binary_bytes])
# Write binary data to output file
with open(output_file_path, 'wb') as file:
file.write(file_data)
# Function for hide message in image
def hide_message():
print(line)
print(Fore.LIGHTMAGENTA_EX + "[*] HIDE MESSAGE/TEXT"+ Style.RESET_ALL + Fore.LIGHTRED_EX + "\t\t<Selected>" + Style.RESET_ALL )
# Get Image path from user
print("\nPlease type or paste the image path below.")
image_path = input(Fore.GREEN + Style.BRIGHT + "[+] Image Path -> " + Style.RESET_ALL)
if os.path.exists(image_path):
img = Image.open(image_path)
img = img.convert("RGB") # Ensure the image is in RGB format
print("Image Selected...")
print(line)
else:
print(Fore.RED + Style.BRIGHT + "Sorry... Something went wrong!\nPlease check the image path and try again." + Style.RESET_ALL)
print(line)
exit_from_tool()
# Print image information
print(Fore.RED + Style.BRIGHT + "[ALERT] Image Information\n" + Style.RESET_ALL)
width, height = img.size
total_pixels = width * height
total_bits = total_pixels * 3
total_bytes = total_bits / 8
total_kb = total_bytes / 1024
print(f"Image Size: {width}x{height}\nTotal Pixels: {total_pixels}\nBased on the image size, you can hide up to "+Fore.LIGHTBLUE_EX + str(total_kb) +"KB" + Style.RESET_ALL + " data.\nApproximately "+Fore.LIGHTBLUE_EX + str(total_bytes) + Style.RESET_ALL + " characters.")
print(line)
# Get password as a delimiter at the end of the message
print("Please insert a password for security reason.\nIf you don't want to use a password, simply press Enter.")
password = input(Fore.GREEN + Style.BRIGHT + "[+] Password (Optional) -> " + Style.RESET_ALL)
# In case user cant insert password we can chose default delimiter at the end of the message
if password.strip() == '':
password = '$$' # use Default pass
else:
print("Password Inserted...")
print(line)
# Get message from user
print("Please type or paste message/text hare that you want to hide on image.\nExample: (This is a secrect message)")
message = input(Fore.GREEN + Style.BRIGHT + "[+] Message Box ->" + Style.RESET_ALL)
if message.strip() == '':
print(Fore.RED + Style.BRIGHT + "Sorry... Looks like you not inserted message!\nPlease insert a message and try again." + Style.RESET_ALL)
print(line)
exit_from_tool()
else:
print("Message Inserted...")
print(line)
identifier = "TEXT"
# Create binary message
binary_message = message_to_binary(identifier) + message_to_binary(message) + message_to_binary(password)
# Ensure the binary message can fit into the image
max_bits = img.size[0] * img.size[1] * 3 # 3 bits per pixel (R, G, B)
if len(binary_message) > max_bits:
raise ValueError(Fore.LIGHTRED_EX + "Message is too long to fit in the image.\nPlease insert high resolution image or short message.",line)
# Access pixel data
pixels = img.load()
binary_message_idx = 0
# Modify the LSB pixels in the image
for i in range(img.size[0]): # Loop through width
for j in range(img.size[1]): # Loop through height
pixel = list(pixels[i, j]) # Get the pixel (tuple of RGB)
for k in range(3): # Modify R, G, B channels
if binary_message_idx < len(binary_message):
# Modify the least significant bit of the pixel's channel
pixel[k] = pixel[k] & ~1 | int(binary_message[binary_message_idx])
binary_message_idx += 1
# Update the pixel with modified RGB values
pixels[i, j] = tuple(pixel)
# Break out if we've embedded the entire message
if binary_message_idx >= len(binary_message):
break
if binary_message_idx >= len(binary_message):
break
# Save the modified image
print("Please type the name of the output image.\nExamples: \nWith path: (path/image.jpg)\nWithout path: (image.jpg)")
# print(Fore.LIGHTRED_EX + "Note: If you are not using Windows, you need to specify the name of the output image." + Style.RESET_ALL)
output_image_name = input(Fore.GREEN + Style.BRIGHT + "[+] Output Image name (Optional) ->" + Style.RESET_ALL)
if output_image_name.strip() == '':
output_image_name = '/'.join(image_path.split('/')[:-1]) + "/Output_of_StegaPic_" + image_path.split('/')[-1]
else:
print("Output Image Name Inserted...")
try:
# Show message to task ix completed
img.save(output_image_name)
print(Fore.LIGHTCYAN_EX + "\nMessage hidden successfully in the image!" + Style.RESET_ALL)
print(Fore.LIGHTBLUE_EX + "Output image Location : " + Style.RESET_ALL + output_image_name)
except:
print(Fore.RED + Style.BRIGHT + "\nSorry... Something went wrong!\nPlease Enter output image path and try again." + Style.RESET_ALL)
print(line,"\n")
# Function for hide file in image
def hide_file():
print(line)
print(Fore.LIGHTMAGENTA_EX + "[*] HIDE FILE(Not Recomded)"+ Style.RESET_ALL + Fore.LIGHTRED_EX + "\t<Selected>" + Style.RESET_ALL )
# Get Image path from user
print("\nPlease type or paste the image path below.")
image_path = input(Fore.GREEN + Style.BRIGHT + "[+] Image Path -> " + Style.RESET_ALL)
if os.path.exists(image_path):
img = Image.open(image_path)
img = img.convert("RGB") # Ensure the image is in RGB format
print("Image Selected...")
print(line)
else:
print(Fore.RED + Style.BRIGHT + "Sorry... Something went wrong!\nPlease check the image path and try again." + Style.RESET_ALL)
print(line)
exit_from_tool()
# Print image information
print(Fore.RED + Style.BRIGHT + "[ALERT] Image Information\n" + Style.RESET_ALL)
width, height = img.size
total_pixels = width * height
total_bits = total_pixels * 3
total_bytes = total_bits / 8
total_kb = total_bytes / 1024
print(f"Image Size: {width}x{height}\nTotal Pixels: {total_pixels}\nBased on the image size, you can hide up to "+Fore.LIGHTBLUE_EX + str(total_kb) +"KB" + Style.RESET_ALL + " data on the image.\n")
print(line)
# Get password as a delimiter at the end of the message
print("Please insert a password for security reason.\nIf you don't want to use a password, simply press Enter.")
password = input(Fore.GREEN + Style.BRIGHT + "[+] Password (Optional) -> " + Style.RESET_ALL)
# In case user cant insert password we can chose default delimiter at the end of the message
if password.strip() == '':
password = '$$' # use Default pass
else:
print("Password Inserted...")
print(line)
# Get message from user
print("Please type or paste path of file that you want to hide on image.\nExample: (path/msg.txt)")
file_path = input(Fore.GREEN + Style.BRIGHT + "[+] File path ->" + Style.RESET_ALL)
if os.path.exists(file_path):
binary_file = file_to_binary(file_path)
print("file Selected...")
print(line)
print(Fore.RED + Style.BRIGHT + "[ALERT] File Information\n" + Style.RESET_ALL)
print("File Size: " + str(os.path.getsize(file_path)),"Image able to contains data: "+str(total_kb)+"KB\n")
identifier = 'FILE'
# Create binary message
binary_message = message_to_binary(identifier) + binary_file + message_to_binary(password)
# Ensure the binary message can fit into the image
max_bits = img.size[0] * img.size[1] * 3 # 3 bits per pixel (R, G, B)
if len(binary_message) > max_bits:
raise ValueError(Fore.LIGHTRED_EX + "File size is too big to fit in the image.\nPlease insert high resolution image or short message.",line)
else:
print(Fore.RED + Style.BRIGHT + "Sorry... Something went wrong!\nPlease check the file path and try again." + Style.RESET_ALL)
print(line)
exit_from_tool()
# Access pixel data
pixels = img.load()
binary_message_idx = 0
# Modify the LSB pixels in the image
for i in range(img.size[0]): # Loop through width
for j in range(img.size[1]): # Loop through height
pixel = list(pixels[i, j]) # Get the pixel (tuple of RGB)
for k in range(3): # Modify R, G, B channels
if binary_message_idx < len(binary_message):
# Modify the least significant bit of the pixel's channel
pixel[k] = pixel[k] & ~1 | int(binary_message[binary_message_idx])
binary_message_idx += 1
# Update the pixel with modified RGB values
pixels[i, j] = tuple(pixel)
# Break out if we've embedded the entire message
if binary_message_idx >= len(binary_message):
break
if binary_message_idx >= len(binary_message):
break
# Save the modified image
print("Please type the name of the output image.\nExamples: \nWith path: (path/image.jpg)\nWithout path: (image.jpg)")
# print(Fore.LIGHTRED_EX + "Note: If you are not using Windows, you need to specify the name of the output image." + Style.RESET_ALL)
output_image_name = input(Fore.GREEN + Style.BRIGHT + "[+] Output Image name (Optional) ->" + Style.RESET_ALL)
if output_image_name.strip() == '':
output_image_name = '/'.join(image_path.split('/')[:-1]) + "/Output_of_StegaPic_" + image_path.split('/')[-1]
else:
print("Output Image Name Inserted...")
# Show message to task ix completed
print(Fore.LIGHTCYAN_EX + "\nFile hidden successfully in the image!" + Style.RESET_ALL)
print(Fore.LIGHTBLUE_EX + "Output image Location : " + Style.RESET_ALL + output_image_name)
img.save(output_image_name)
print(line,"\n")
# Function can Hide data on image
def hide_data():
print(line)
print(Fore.LIGHTMAGENTA_EX + "[*] HIDE DATA"+ Style.RESET_ALL + Fore.LIGHTRED_EX + "\t\t\t<Selected>" + Style.RESET_ALL )
print(Fore.LIGHTYELLOW_EX + "\nChoose an option from the following:" + Style.RESET_ALL)
print("\t[1] HIDE MESSAGE/TEXT\n\t[2] HIDE FILE(Not Recomded)\n\t[0] QUITE & EXIT")
try:
message_or_file = int(input(Fore.GREEN + Style.BRIGHT + "[+] Chose an Option ->" + Style.RESET_ALL))
if message_or_file == 1:
hide_message()
elif message_or_file == 2:
hide_file()
elif message_or_file == 0:
exit_from_tool()
else:
print(Fore.RED + Style.BRIGHT + "\nInvalid option. Please choose 1 or 2.\r" + Style.RESET_ALL)
hide_data()
except:
print(Fore.RED + Style.BRIGHT + "\nInvalid option. Please choose 1 or 2.\r" + Style.RESET_ALL)
hide_data()
# Function can Extract hidden data from image
def extract_data():
print(line)
print(Fore.LIGHTMAGENTA_EX + "[*] EXTRACT DATA" + Style.RESET_ALL + Fore.LIGHTRED_EX + "\t\t<Selected>" + Style.RESET_ALL)
# Get Image path from user
print("\nPlease type or paste the image path that contains the secret message.")
image_path = input(Fore.GREEN + Style.BRIGHT + "[+] Image Path -> " + Style.RESET_ALL)
# Check if image path exists
if os.path.exists(image_path):
try:
img = Image.open(image_path)
img = img.convert("RGB") # Ensure the image is in RGB format
print("Image Selected...")
print(line)
except Exception as e:
print(Fore.RED + "Failed to load the image. Please check the file format." + Style.RESET_ALL)
print(f"Error: {e}")
return
else:
print(Fore.RED + Style.BRIGHT + "Sorry... Something went wrong!\nPlease check the image path and try again." + Style.RESET_ALL)
print(line)
return
# Get Password or delimiter from user if it is set
print("Please insert a password if the file is secured with a password.\nIf no password is used, simply press Enter.")
password = input(Fore.GREEN + Style.BRIGHT + "[+] Password (Optional) -> " + Style.RESET_ALL)
# Use default delimiter if password not provided
if password.strip() == '':
password = '$$' # use Default delimiter
else:
print("Password Inserted...")
# Convert password into binary format
binary_pass = message_to_binary(password)
# Access pixel data
pixels = img.load()
# Collect binary message
binary_message_or_file_more = ''
start = 0
for i in range(img.size[0]):
load_list = ["-", "\\", "|", "/"]
if start < 4 :
print(f"\r[{load_list[start]}] Please wait! Depending on Image size it takes some time.", end="")
start += 1
else:
start = 0
for j in range(img.size[1]):
pixel = list(pixels[i, j])
for k in range(3):
binary_message_or_file_more += str(pixel[k] & 1)
# Find the delimiter (end of message) and slice it off
if binary_pass in binary_message_or_file_more:
binary_message_or_file = binary_message_or_file_more.split(binary_pass)[0]
identifier_binary = binary_message_or_file[:32]
identifier = binary_to_message(identifier_binary)
if identifier == "TEXT":
message_binary = binary_message_or_file[32:] # The rest is the message
# Convert binary back to message
hidden_message = binary_to_message(message_binary)
print(Fore.LIGHTCYAN_EX + "\n\nYour hidden message:" + Style.RESET_ALL)
print(hidden_message)
elif identifier == "FILE":
message_binary = binary_message_or_file[32:] # The rest is the message
# Save the File
print(Fore.LIGHTRED_EX + "\n\nThe image contains a hidden file." + Style.RESET_ALL + "\nPlease type the name of the output file.\nExamples: \nWith path: (path/image.txt)\nWithout path: (image.txt)\nBy default, it will provide a .txt file.")
output_file_name = input(Fore.GREEN + Style.BRIGHT + "[+] Output File name (Optional) ->" + Style.RESET_ALL)
if output_file_name.strip() == '':
img_name = image_path.split('/')[-1]
output_file_name = '/'.join(image_path.split('/')[:-1]) + "/" + img_name.split('.')[0] + ".txt"
print(output_file_name)
else:
print("Output File Name Inserted...")
# Convert binary back to message file
hidden_message = binary_to_file(message_binary, output_file_name)
print(Fore.LIGHTCYAN_EX+f"\nHidden file extracted and saved as {output_file_name}"+Style.RESET_ALL)
else:
print(Fore.RED + "\n\nSorry... something went wrong! \nIt looks like the image can't store any secret data.\nPlease insert a correct image and try again." + Style.RESET_ALL)
else:
print(Fore.RED + "\n\nSorry... something went wrong! \nThe correct password is required for the image.\nPlease enter the correct password and try again." + Style.RESET_ALL)
print(line,"\n")
# Chose Option Hide and Extract data from image
def hide_extract():
print(line)
print(Fore.LIGHTYELLOW_EX + "Choose an option from the following:" + Style.RESET_ALL)
print("\t[1] HIDE DATA\n\t[2] EXTRACT DATA\n\t[0] QUITE & EXIT")
try:
hide_and_extract = int(input(Fore.GREEN + Style.BRIGHT + "[+] Chose an Option ->" + Style.RESET_ALL))
if hide_and_extract == 1:
hide_data()
elif hide_and_extract == 2:
extract_data()
elif hide_and_extract == 0:
exit_from_tool()
else:
print(Fore.RED + Style.BRIGHT + "\nInvalid option. Please choose 1 or 2.\r" + Style.RESET_ALL)
hide_extract()
except:
print(Fore.RED + Style.BRIGHT + "\nInvalid option. Please choose 1 or 2.\r" + Style.RESET_ALL)
hide_extract()
# Exit from tool function
def exit_from_tool():
# Simple Exit Loading Bar
start = 0
end = 60
load = (Fore.GREEN + Style.BRIGHT + '='+ Style.RESET_ALL)
unload = (Fore.RED + '_'+ Style.RESET_ALL)
print(Fore.RED + Style.BRIGHT + "\nPlease wait while we are exiting the tool" + Style.RESET_ALL)
while start <= 60:
print("\r"+load*start+">"+unload*end,end="")
print("\r\t\t\t"+load*4,start*2-20,"\r",end="")
time.sleep(0.01)
start += 1
end -= 1
print("\n\n")
os._exit(0)
# Start
# Colors for designe banner of the tool
colors = [
Fore.RED, # Red
Fore.BLUE, # Blue
Fore.MAGENTA, # Magenta
Fore.CYAN, # Cyan
]
# Chose a Random color for banner
random_color = random.choice(colors)
# For designe
line = "_"*61
# Tool Name
print(random_color + Style.BRIGHT + """
______ ______ _
/ _____) _ (_____ (_)
( (____ _| |_ _____ ____ _____ _____) ) ____
(____ (_ _) ___ |/ _ (____ || ____/ |/ ___)
_____) )| |_| ____( (_| / ___ || | | ( (___
(______/ |___)_____)<___ |_____||_| |_|<____)
__| |""" + Style.RESET_ALL + " -" + Fore.GREEN + Style.BRIGHT + 'sh1vam.03'+ Style.RESET_ALL+ random_color + """
(____|
""" + Style.RESET_ALL)
print(line)
print(Fore.CYAN + Style.NORMAL + "Welcome to StegaPic – Your Ultimate Image Steganography Tool!\nEasily hide and extract messages or files in images.\nSecure and simple to use!" + Style.RESET_ALL)
# call Hide_Extract function to chose one option
hide_extract()