Прячемся в палитре

Discussion in 'Анонимность' started by FunOfGun, 7 Aug 2013.

  1. FunOfGun

    FunOfGun Elder - Старейшина

    Joined:
    5 Sep 2012
    Messages:
    388
    Likes Received:
    72
    Reputations:
    124
    Недавно нас окатило волной громких скандалов, разразившихся словно гром среди ясного неба, одним из самых громких среди них было раскрытие программы PRISM.
    Итак, давайте представим такую ситуацию: есть два человека, которые хотят обмениваться сообщениями с секретной информацией, есть люди, желающие заполучить эту информацию. Одним из самых разумных решений данной проблемы может стать обмен сообщениями через открытые источники с использованием стеганографии.
    Например, пользователь1 оставляет на форуме смищную гифку с милым котэ, в недрах которой скрывается какое-то тайное послание. Для человеческого глаза, скорей всего, будет незаметно легкое искажение цветов, которое вполне можно списать на низкое качество картинки, а вот пользователь2, обладающий специальной программой и ключем без проблем получит из картинки информацию.

    [Intro]

    Для начала давайте поговорим о теории.
    Стеганография, которой посвящена данная статья, это, по сути, искуство сокрытия информации от посторонних глаз.
    Существует множество разных способов спрятать данные на виду, мы же прибегнем к модифицированному LSB(Least Significant Bit, наименьший значащий бит, подробнее о нем можно почитать в Википедии, скажу лишь, что данные при таком подходе прячутся в последние значащие биты, таким образом очень слабо влияя на значения байтов): будем менять 4 наименее значащих бита. Да, это намного более значительные изменения, но как показывает практика, изменение в значении цвета на 15(из 255 возможных) пунктов легко пропустить, не имея оригинала, что бы сравнить.
    Вот кстати пример:
    [​IMG]
    [​IMG]
    одно из этих изображений оригинал, другое копия с небольшим посланием.

    [Кодим]

    Я буду писать на питоне, этот язык понятен почти всем, даже тем, кто его не знает. Код писался и тестировался под python3, как будет работать под второй веткой неизвестно.
    Для начала можно ознакомиться с описанием формата вот тут: http://www.onicos.com/staff/iz/formats/gif.html Там все очень кратко, но достаточно полезно для понимания кода. Суть нашего кода будет предельно простой: зашифровать сообщение(максимальная длинна 384 байта) и положить его в главную палитру файла.
    В формате используются битовые поля, но в питоне, как оказалось, нет стандартной из реализации, поэтому пришлось городить свой костыль:
    PHP:
    class BitArray:
        
    def __init__(selfval=Nonelength=0):
            
    bits=[]
            if 
    type(valis int:
                
                while(
    val!=0):
                    if 
    val&1:
                        
    bits+=[1]
                    else:
                        
    bits+=[0]
                    
    val>>=1
                    
            elif type
    (valis list:
                
    bits=list(val)

            if 
    bits==[]:
                
    bits=[0]
                
            if 
    length!=0:
                if 
    length>=len(bits):
                    
    bits=bits+[0]*(length-len(bits))
                else:
                    
    raise Exception("val too long")
            
            
    self._val=bits
        
    #индексатор, возвращающий объект BitArray
        
    def __getitem__(selfitem):
            return 
    self.__class__(self._val[item])
        
    #длинна в битах
        
    def __len__(self):
            return 
    len(self._val)
        
    #исключительно для отладки, наглядное представление объекта для интерактивного режима
        
    def __repr__(self):
            
    bits=self._val
            bits
    .reverse()
            return 
    str(self.__class__)+" Bits: "+"".join(str(x) for x in bits)+"; val: "+str(int(self))
        
    #приведение к int
        
    def __int__(self):
            
    res=0
            
    for i in range(len(self._val)):
                
    res|=self._val[i]<<i
            
    return res
        
        def __add__
    (selfba):
            
    self._val+=ba._val
            
    return self
    Поскольку писать универсальный массив не хотелось, в классе только необходимый для конкретно этого проекта функционал, ничего лишнего.
    Теперь давайте опишем такой же минималистичный класс для работы с gif-файлом:
    PHP:
    class Gif:
        
    #http://www.onicos.com/staff/iz/formats/gif.html
        
    def __init__(selfdata):
            
    self._header=data[:6]
            
    self._info=data[6:12]
            
            
    info=BitArray(self._info[4])
            
            if 
    int(info[0])!=1:
                
    raise Exception("This file haven't global pallet")

            
    tblsize=2**(1+int(info[5:]))
            
            
    pallet=data[12:12+tblsize*3]
            
    self._data=data[12+tblsize*3:]#все, с чем не умеем работать храним в неизменном виде
            
    self._pallet=pallet
            
        def putData
    (selfdata):
            
    data=list(data)
            
    maxlen=len(self._pallet)*.5 #максимальная длинна данных, которые вместит файл
            
    data=list(len(data).to_bytes(2,"little"))+data #добвляем в начало длинну данных
            
            
    if maxlen>=len(data):
                
    data+=[0]*int(maxlen-len(data)) #при необходимости дополняем нулями наше смообщение
            
    else:
                
    raise Exception("Can't insert {0} bytes of data in {1}".format(len(data),maxlen))
            
            
    rdata=[]
            for 
    d in data#разбиваем каждый байт на две части
                
    ba=BitArray(d)
                
    rdata+=[int(ba[:4]),int(ba[4:])]
            
    data=rdata

            self
    ._pallet=[(int(x) & 0b11110000) for x in list(self._pallet)] #очищаем младшие биты каждого байта в палитре
            
    self._pallet=[(self._pallet[i]|data[i]) for i in range(len(data))] #ложим данные в палитру

        
    def getData(self):
            
    data=[(self._pallet[i]&0xf) for i in range(len(self._pallet))] #удаляем ненужные нам биты
            
    data=[int(BitArray(data[i],4)+BitArray(data[i+1],4)) for i in range(0,len(data),2)] #складываем байты из двух частей
            
    l=int.from_bytes(data[:2],"little")
            
            return 
    data[2:2+l]

        
    def construct(self):
            
    #собираем файл из частей
            
    return self._header+self._info+bytes(self._pallet)+self._data
    Надеюсь, коментов в коде достаточно для его понимания, но если нет -- задавайте вопросы, на все отвечу.

    [Шифруемся]

    Поскольку одной из целей было создать код, не зависящий от сторонних библиотек, а в питоне нет ничего кроме хешфункций, было принято решение просто гаммировать данные хитрым хешем пароля:
    PHP:
     import hashlib

    #генератор гаммы для шифрования
    #не лучший вариант, но и не худший:)
    def GammaString(secret):
        if 
    type(secretis str:
            
    secret=secret.encode()

        
    md5=hashlib.md5()
        
    sha=hashlib.sha512()

        
    md5.update(secret)
        
    sha.update(secret)
        
        while 
    True:
            
    digest=md5.digest()+sha.digest()
            for 
    d in digest:
                yield 
    d
            md5
    .update(digest)
            
    sha.update(digest)

    #опять таки не лучший вариант
    def xor(datasecret):
        if 
    type(datais str:
            
    data=data.encode()

        
    gamma=GammaString(secret)
        return 
    bytes(d^next(gamma) for d in data)
    Коротко о сути работы GammaString: этот генератор сначала берет md5 и sha512 хэши пароля, после чего складывает их в один digest и посимвольно отдает в функцию гаммирования, когда этот digest заканчивается, берется его md5 и sha512 хеши и складываются в новый digest и все начинается с начала, таким образом мы получаем почти бесконечную довольно качественную гамму.


    [Интерфейс]
    Поскольку это просто заготовка, интерфейс будет консольным, за него отвечает следующий код:
    PHP:
    import chipher
    from giffile import Gif

    import sys

    usage
    ="""
    Using {0}:
        {0} <put|get> <-s"
    password"|-sf"pwdfile"> [<-d"data string"|-df"datafile">] <-g"gif container"> [<-o"out file name">]

    Parameters:
        put \t put data to container
        get \t get data from container

        -s"
    password" \t set chiping password
        -sf"
    pwdfile" \t using as pwd content of file

        -d"
    data string" \t set chiping text, must be ignored when get
        -df"
    datafile" \t using text of file as chiping text, must be ignored when get

        <-g"
    gif container"> \t set gif container
        <-o"
    out file name"> \t set output, if ignored put out to console
    """

    def main(argv):
        
    gif=""
        
    data=""
        
    secret=""
        
    out=""
        
    action="put"
      
        
    for arg in argv:
            if 
    arg.startswith("-s"):
                
    secret=arg[3:-1]
            
    elif arg.startswith("-d"):
                
    data=arg[3:-1]
            
    elif arg.startswith("-sf"):
                
    secret=open(arg[3:-1],"rb").read()
            
    elif arg.startswith("-df"):
                
    data=open(arg[3:-1],"rb").read()
            
    elif arg.startswith("-g"):
                
    gif=open(arg[3:-1],"rb").read()
            
    elif arg.startswith("-o"):
                
    out=arg[3:-1]
            
    elif arg=="put":
                
    action="put"
            
    elif arg=="get":
                
    action="get"

        
    if gif=="" or secret=="":
            print(
    usage)
            return

        
    gif=Gif(gif)
        
    res=b""
        
        
    if action=="put":
            if 
    data=="":
                print(
    usage)
                return
            
            
    data=chipher.xor(data,secret)
            
    gif.putData(data)
            
    res=gif.construct()
        
    elif action=="get":
            
    res=chipher.xor(gif.getData(),secret)

        if 
    out=="":
            try:
                print(
    res.decode())
            
    except:
                print(
    "Can't decode content as string, use -o parameter")
                print(
    res)
                return
        else:
            
    with open(out,"wb") as f:
                
    f.write(res)

    if 
    __name__=="main":
        
    main(sys.argv)
    Как видите, все весьма просто. Останавливаться на нем, я пожалуй не буду, код кривой, но вроде рабочий.

    [В заключение]

    Определить наличие в файле зашифрованных скрытых данных может быть весьма сложно, особенно не зная пароля. Попробуйте угадать в какой из картинок скрыто сообщение(просто) и какое(сложнее). Подсказка: там секретные данные.
     
    #1 FunOfGun, 7 Aug 2013
    Last edited: 8 Aug 2013
    Coost, Arturiy, arcane_weaver and 6 others like this.
  2. FunOfGun

    FunOfGun Elder - Старейшина

    Joined:
    5 Sep 2012
    Messages:
    388
    Likes Received:
    72
    Reputations:
    124
    в статье забыл указать что у меня код распихан по 3 файлам, если все скинуть в один -- ничего не получится, вот готовый код: http://yadi.sk/d/Cq4jEwKQ7iT5g
    запускать steg.py
     
    Arturiy likes this.
  3. Arturiy

    Arturiy New Member

    Joined:
    25 Sep 2015
    Messages:
    25
    Likes Received:
    1
    Reputations:
    1
    интересно, явные недостатки в таких граф.файлах-контейнерах имеются?
     
  4. FunOfGun

    FunOfGun Elder - Старейшина

    Joined:
    5 Sep 2012
    Messages:
    388
    Likes Received:
    72
    Reputations:
    124
    нууу, например если грузить на чужой сервер вполне может произойти перекодирование в другой формат с потерей информации. да и конкретно этот алгоритм не позволяет передавать даже средние по размеру сообщения.
     
  5. Arturiy

    Arturiy New Member

    Joined:
    25 Sep 2015
    Messages:
    25
    Likes Received:
    1
    Reputations:
    1
    хотите сказать что по факту способ не то что небезопасен, так ещё и не надёжен, может аукнутся потерей информации?
     
  6. KGB

    KGB Member

    Joined:
    6 Dec 2010
    Messages:
    129
    Likes Received:
    5
    Reputations:
    0
    А почему бы просто не использовать ассиметричное шифрование для передачи секретной информации?