Zgłosił się do mnie klient z projektem, którego celem było dojście do tego, czy w danym obrazku nie znajdują się ukryte informacje zakodowane poprzez steganografię.
Poniżej prezentuję skrypt w Pythonie, który wyciąga piksele w trybach horyzontalnych (czyli piksel po pikselu na wysokość obrazka) i wertykalnych z grafiki:
- najmniej znaczące bity w składowych R
- najmniej znaczące bity w składowych G
- najmniej znaczące bity w składowych B
- najmniej znaczące bity w składowych ALPHA
- kombinowane najmniej znaczące bity w RGB
- kombinowane najmniej znaczące bity w BGR
- kombinowane najmniej znaczące bity w RGBA
- kombinowane najmniej znaczące bity w ABGR
Są to najczęściej stosowane metody używane przez narzędzia do steganografii do ukrywania rozbitych danych w najmniej znaczących bitach składowych kolorów RGB.
from PIL import Image from bitarray import bitarray def dump(filename, content): f = open(filename, 'wb') f.write(content) f.close() def stego(filename, filename_prefix): im = Image.open(filename) pixels = im.load() width, height = im.size # # RED, GREEN, BLUE least significant bits # byte1, byte2, byte3, byte4, bits = 0, 0, 0, 0, 0 out1, out2, out3, out4 = bytearray(), bytearray(), bytearray(), bytearray() bit1, bit2, bit3, bit4 = bitarray(), bitarray(), bitarray(), bitarray() all1, all2, all3, all4 = bitarray(), bitarray(), bitarray(), bitarray() rgb, rgba = bitarray(), bitarray() xx = width yy = height horizontal = True if filename_prefix == "vertical": xx = height yy = width horizontal = False for x in range(xx): for y in range(yy): if horizontal is True: pixel = pixels[x, y] else: pixel = pixels[y, x] # separate R G B bit1.append(pixel[0] & 1) bit2.append(pixel[1] & 1) bit3.append(pixel[2] & 1) bit4.append(pixel[3] & 1) # combined RGB all1.append(pixel[0] & 1) all2.append(pixel[1] & 1) all3.append(pixel[2] & 1) all4.append(pixel[3] & 1) # RGB rgb.append(pixel[0] & 1) rgb.append(pixel[1] & 1) rgb.append(pixel[2] & 1) # RGBA rgba.append(pixel[0] & 1) rgba.append(pixel[1] & 1) rgba.append(pixel[2] & 1) rgba.append(pixel[3] & 1) # compose 8 bits if bits < 8: byte1 = (byte1 << 1) | (pixel[0] & 1) byte2 = (byte2 << 1) | (pixel[1] & 1) byte3 = (byte3 << 1) | (pixel[2] & 1) byte4 = (byte4 << 1) | (pixel[3] & 1) bits = bits + 1 else: bits = 0 out1.append(byte1 & 0xFF) out2.append(byte2 & 0xFF) out3.append(byte3 & 0xFF) out4.append(byte4 & 0xFF) dump(filename_prefix + "_1_RED.bin", out1) dump(filename_prefix + "_1_GREEN.bin", out2) dump(filename_prefix + "_1_BLUE.bin", out3) dump(filename_prefix + "_1_ALPHA.bin", out4) dump(filename_prefix + "_2_RED_BITS.bin", bit1) dump(filename_prefix + "_2_GREEN_BITS.bin", bit2) dump(filename_prefix + "_2_BLUE_BITS.bin", bit3) dump(filename_prefix + "_2_ALPHA_BITS.bin", bit4) bit1.reverse() bit2.reverse() bit3.reverse() bit4.reverse() dump(filename_prefix + "_3_RED_REV_BITS.bin", bit1) dump(filename_prefix + "_3_GREEN_REV_BITS.bin", bit2) dump(filename_prefix + "_3_BLUE_REV_BITS.bin", bit3) dump(filename_prefix + "_3_ALPHA_REV_BITS.bin", bit4) dump(filename_prefix + "_4_RED_BITS_RGB.bin", all1) dump(filename_prefix + "_4_GREEN_BITS_RGB.bin", all2) dump(filename_prefix + "_4_BLUE_BITS_RGB.bin", all3) dump(filename_prefix + "_4_ALPHA_BITS_RGB.bin", all4) all1.reverse() all2.reverse() all3.reverse() all4.reverse() dump(filename_prefix + "_5_RED_BITS_BGR.bin", all1) dump(filename_prefix + "_5_GREEN_BITS_BGR.bin", all2) dump(filename_prefix + "_5_BLUE_BITS_BGR.bin", all3) dump(filename_prefix + "_5_ALPHA_BITS_BGR.bin", all4) dump(filename_prefix + "_6_RGB_ALL_BITS.bin", rgb) dump(filename_prefix + "_6_RGBA_ALL_BITS.bin", rgba) rgb.reverse() rgba.reverse() dump(filename_prefix + "_6_RGB_ALL_REV_BITS.bin", rgb) dump(filename_prefix + "_6_RGBA_ALL_REV_BITS.bin", rgba) if __name__ == '__main__': stego('IMG1.PNG', 'horizontal') stego('IMG1.PNG', 'vertical' )
Może komuś się kiedyś przyda.
Literówki: stenografia != steganografia 😉
Faktycznie, chociaż jakie to ma znacznie hehe jeden ciul i tak 99.9% ludzi nie wie o co chodzi 😀