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 😀