ruby script to hide a string in an image
require 'rmagick' | |
require 'chunky_png' | |
# Simple least significant bit steganography. Hides a message in the | |
# least significant bit of the red channel of the image. | |
# | |
# Requires ImageMagick, Ruby 2.5, rmagick gem, chunky_png gem | |
# | |
# To encode a message: | |
# ruby lsb.rb encode image_file message encdoded_filename | |
# | |
# To decode a message: | |
# ruby isb.rb decode filename | |
class LSB | |
include ChunkyPNG | |
def initialize(filename:) | |
@filename = convert_to_png(filename) | |
@img = Image.from_file(@filename) | |
end | |
def encode(message:, stego_filename:) | |
message = message.force_encoding('UTF-8') | |
unless message.ascii_only? | |
puts 'Please only include ascii characters in the message.' | |
return 1 | |
end | |
binary_message = message.unpack('B*')[0].split(//).map(&:to_i) | |
if img.area < binary_message.size | |
puts "Message requires #{binary_message.size} pixels to encode and the "\ | |
"image contains only #{img.area} pixels." | |
return 1 | |
end | |
binary_message += [0] * (img.area - binary_message.size) | |
img.height.times do |y| | |
img.width.times do |x| | |
img[x, y] = encode_pixel(img[x, y], | |
binary_message[y * img.width + x]) | |
end | |
end | |
img.save(stego_filename) | |
return 0 | |
end | |
def decode | |
message = [] | |
img.height.times do |y| | |
img.width.times do |x| | |
message << (Color.r(img[x, y]) % 2) | |
end | |
end | |
message.each_slice(8).map { |s| s.map(&:to_s).join('') } | |
.map { |s| [s].pack('B*') }.join('') | |
end | |
private | |
attr_reader :img, :filename | |
def convert_to_png(file) | |
return file if file.split('.')[1] == 'png' | |
img = Magick::Image.read(file)[0] | |
img.format = 'png' | |
png_name = file.split('.')[0] + '.png' | |
img.write(png_name) | |
png_name | |
end | |
def encode_pixel(pixel, bit) | |
return pixel if (Color.r(pixel) + bit) % 2 == 0 | |
Color.rgba(Color.r(pixel) + 1, Color.g(pixel), | |
Color.b(pixel), Color.a(pixel)) | |
end | |
end | |
mode, filename = ARGV | |
lsb = LSB.new(filename: filename) | |
if mode == 'encode' | |
result = lsb.encode(message: ARGV[2].dup, | |
stego_filename: ARGV[3]) | |
if result == 0 | |
puts 'Success!' | |
else | |
puts 'Failed to encode.' | |
end | |
elsif mode == 'decode' | |
puts lsb.decode | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment