• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Стеганографируем через LSB (Least Significant Bit) и EasyBMP на С++

salsa20

(L2) cache
Пользователь
Регистрация
03.05.2019
Сообщения
498
Реакции
110
Гарант сделки
1
Это чисто авторская статья и код. Думаю в криптерах пригодится, кто понимает назначение.
Коротко берем одну фотку и зашиваем в нее текст, при расшифровке получаем зайца (текст). В тексте может тот же шеллкод или другие данные.

Так как EasyBMP пришлось ручками допилить, прикреплю код в архиве. Там есть cmake.

Шифрование текста в изображении: понятное объяснение​


Представьте, что изображение - это огромная книга, каждая страница которой состоит из миллионов букв. А текст, который нужно зашифровать - это секретное послание, которое мы хотим спрятать в этой книге. Процесс шифрования можно представить как тайного писаря, который умело вплетает буквы послания в текст книги, заменяя некоторые символы на свои зашифрованные версии.

Основные компоненты​


В данном коде мы видим три основных компонента:

1. Стратегия шифрования (EncryptionStrategy): Это абстрактный класс, определяющий методы для шифрования и расшифровки. В нашем случае используется конкретная реализация LSBEncryptionStrategy, которая использует метод LSB (Least Significant Bit) для шифрования.

2. Контекст шифрования (EncryptionContext): Это класс, который управляет процессом шифрования, принимая стратегию шифрования и данные для шифрования/расшифровки.

3. Вспомогательные функции: Такие как BitToByte и ByteToBit, которые помогают преобразовывать биты в байты и наоборот, необходимые для процесса шифрования.

Процесс шифрования​


Давайте проследим, как наш тайный писарь шифрует текст в изображении:

1. Сначала он проверяет, не был ли текст уже зашифрован в этом изображении (IsEncrypted). Если да, то процесс прерывается.

2. Затем писарь помечает первый пиксель изображения специальной меткой (EmbedEncryptionMarker), чтобы указать, что изображение зашифровано.

3. После этого он записывает размер текста в первые несколько пикселей (WriteCountText), чтобы при расшифровке знать, сколько символов нужно извлечь.

4. Далее идет самый интересный процесс - EmbedTextInImage. Здесь писарь берет каждый байт текста и преобразует его в биты (0 и 1). Затем он берет пиксели изображения и заменяет несколько младших бит цветовых компонентов (красный, зеленый, синий) на биты из текста. Это позволяет почти незаметно вплести текст в изображение.

5. Наконец, зашифрованное изображение сохраняется (SaveEncryptedImage).

Процесс расшифровки​


Для расшифровки текста из изображения наш писарь выполняет обратный процесс:

1. Сначала он проверяет, было ли изображение зашифровано (IsEncrypted).

2. Затем он извлекает размер зашифрованного текста из первых нескольких пикселей (ReadCountText).

3. После этого он проходит по всем пикселям изображения и извлекает младшие биты цветовых компонентов, формируя из них байты расшифрованного текста.

4. Наконец, расшифрованный текст записывается в файл.

Вот так, шаг за шагом, наш тайный писарь умело манипулирует пикселями изображения, превращая его в хранилище для секретных посланий. А мы, читатели, теперь можем лучше понять этот процесс благодаря метафорам и визуализации.

Сам код

C++:
#include "../EasyBMP.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include <algorithm>
#include <vector>

// Constants
const int EncryptionPercentSize = 1;
const int EncryptionTextSize = 3;
const int EncryptionTextMaxSize = 1024 * 50; // 50 KB

ebmpBYTE BitToByte(const std::vector<bool>& src);
std::vector<bool> ByteToBit(ebmpBYTE src);

class EncryptionStrategy
{
public:
    virtual void Encrypt(BMP& src, const std::string& fileText) = 0;
    virtual void Decrypt(BMP& src) = 0;
};

class LSBEncryptionStrategy : public EncryptionStrategy
{
public:
    void Encrypt(BMP& src, const std::string& fileText) override;
    void Decrypt(BMP& src) override;

private:
    bool IsEncrypted(BMP& src);
    void WriteCountText(int count, BMP& src);
    int ReadCountText(BMP& src);

    std::vector<ebmpBYTE> ReadTextFile(const std::string& fileText);
    bool ValidateTextSize(int countText, BMP& src);
    void EmbedEncryptionMarker(BMP& src);
    RGBApixel EmbedBitsInColor(const RGBApixel& color, const std::vector<bool>& bits);
    void EmbedTextInImage(BMP& src, const std::vector<ebmpBYTE>& bList);
    void SaveEncryptedImage(BMP& src);
};

class EncryptionContext
{
public:
    void SetEncryptionStrategy(EncryptionStrategy* strategy)
    {
        m_strategy = strategy;
    }

    void Encrypt(BMP& src, const std::string& fileText)
    {
        m_strategy->Encrypt(src, fileText);
    }

    void Decrypt(BMP& src)
    {
        m_strategy->Decrypt(src);
    }

private:
    EncryptionStrategy* m_strategy;
};

ebmpBYTE BitToByte(const std::vector<bool>& src)
{
    ebmpBYTE num = 0;
    for (int i = 0; i < src.size(); i++)
    {
        if (src[i])
        {
            num |= 1 << i;
        }
    }
    return num;
}

std::vector<bool> ByteToBit(ebmpBYTE src)
{
    std::vector<bool> bitArray(8);
    for (int i = 0; i < 8; i++)
    {
        bitArray[i] = (src >> i) & 1;
    }
    return bitArray;
}

bool LSBEncryptionStrategy::IsEncrypted(BMP& src)
{
    RGBApixel color = src.GetPixel(0, 0);
    std::vector<bool> colorArray = ByteToBit(color.Red);
    std::vector<bool> messageArray = ByteToBit(color.Red);

    messageArray[0] = colorArray[0];
    messageArray[1] = colorArray[1];

    colorArray = ByteToBit(color.Green);
    messageArray[2] = colorArray[0];
    messageArray[3] = colorArray[1];
    messageArray[4] = colorArray[2];

    colorArray = ByteToBit(color.Blue);
    messageArray[5] = colorArray[0];
    messageArray[6] = colorArray[1];
    messageArray[7] = colorArray[2];

    ebmpBYTE rez[1];
    rez[0] = BitToByte(messageArray);
    std::string m(reinterpret_cast<char*>(rez), 1);

    return m == "/";
}

void LSBEncryptionStrategy::WriteCountText(int count, BMP& src)
{
    std::vector<ebmpBYTE> countBytes;
    for (int i = 0; i < EncryptionTextSize + 1; i++)
    {
        countBytes.push_back(static_cast<ebmpBYTE>((count >> (i * 8)) & 0xFF));
    }

    int bytesPerPixel = 3;
    int requiredPixels = (countBytes.size() + bytesPerPixel - 1) / bytesPerPixel;

    for (int i = 0; i < requiredPixels && i < src.TellHeight() - 1; i++)
    {
        int offset = i * bytesPerPixel;
        int remainingBytes = countBytes.size() - offset;
        int bytesToWrite = std::min(bytesPerPixel, remainingBytes);

        ebmpBYTE pixelBytes[3] = { 0 };
        for (int j = 0; j < bytesToWrite; j++)
        {
            pixelBytes[j] = countBytes[offset + j];
        }

        RGBApixel pixelColor(pixelBytes[0], pixelBytes[1], pixelBytes[2]);
        src.SetPixel(0, i + 1, pixelColor);
    }
}

int LSBEncryptionStrategy::ReadCountText(BMP& src)
{
    std::vector<ebmpBYTE> countBytes;
    int bytesPerPixel = 3;

    for (int i = 1; i <= EncryptionTextSize; i++)
    {
        RGBApixel pixelColor = src.GetPixel(0, i);
        countBytes.push_back(pixelColor.Red);
        countBytes.push_back(pixelColor.Green);
        countBytes.push_back(pixelColor.Blue);
    }

    int count = 0;
    for (int i = 0; i < EncryptionTextSize; i++)
    {
        count |= static_cast<int>(countBytes[i]) << (i * 8);
    }

    return count;
}

void LSBEncryptionStrategy::Encrypt(BMP& src, const std::string& fileText)
{
    std::vector<ebmpBYTE> bList = ReadTextFile(fileText);
    int countText = bList.size();

    if (!ValidateTextSize(countText, src))
    {
        return;
    }

    if (IsEncrypted(src))
    {
        std::cout << "The file is already encrypted." << std::endl;
        return;
    }

    EmbedEncryptionMarker(src);
    WriteCountText(countText, src);
    EmbedTextInImage(src, bList);

    SaveEncryptedImage(src);
}

std::vector<ebmpBYTE> LSBEncryptionStrategy::ReadTextFile(const std::string& fileText)
{
    std::ifstream rText(fileText, std::ios::binary);
    return std::vector<ebmpBYTE>((std::istreambuf_iterator<char>(rText)),
        std::istreambuf_iterator<char>());
}

bool LSBEncryptionStrategy::ValidateTextSize(int countText, BMP& src)
{
    if (countText > EncryptionTextMaxSize - EncryptionPercentSize - EncryptionTextSize)
    {
        std::cout << "The text size exceeds the maximum allowed size for this algorithm. Please reduce the size." << std::endl;
        return false;
    }

    if (countText * 8 > (src.TellWidth() - EncryptionTextSize - 1) * src.TellHeight())
    {
        std::cout << "The selected image is too small to accommodate the selected text." << std::endl;
        return false;
    }

    return true;
}

void LSBEncryptionStrategy::EmbedEncryptionMarker(BMP& src)
{
    ebmpBYTE symbol[1] = { '/' };
    std::vector<bool> arrBeginSymbol = ByteToBit(symbol[0]);
    RGBApixel curColor = src.GetPixel(0, 0);

    RGBApixel nColor = EmbedBitsInColor(curColor, arrBeginSymbol);
    src.SetPixel(0, 0, nColor);
}

RGBApixel LSBEncryptionStrategy::EmbedBitsInColor(const RGBApixel& color, const std::vector<bool>& bits)
{
    std::vector<bool> tempArray = ByteToBit(color.Red);
    tempArray[0] = bits[0];
    tempArray[1] = bits[1];
    ebmpBYTE nR = BitToByte(tempArray);

    tempArray = ByteToBit(color.Green);
    tempArray[0] = bits[2];
    tempArray[1] = bits[3];
    tempArray[2] = bits[4];
    ebmpBYTE nG = BitToByte(tempArray);

    tempArray = ByteToBit(color.Blue);
    tempArray[0] = bits[5];
    tempArray[1] = bits[6];
    tempArray[2] = bits[7];
    ebmpBYTE nB = BitToByte(tempArray);

    return RGBApixel(nR, nG, nB);
}

void LSBEncryptionStrategy::EmbedTextInImage(BMP& src, const std::vector<ebmpBYTE>& bList)
{
    int index = 0;
    for (int i = EncryptionTextSize + 1; i < src.TellWidth() && index < bList.size(); i++)
    {
        for (int j = 0; j < src.TellHeight() && index < bList.size(); j++)
        {
            RGBApixel pixelColor = src.GetPixel(i, j);
            std::vector<bool> messageArray = ByteToBit(bList[index]);

            RGBApixel newColor = EmbedBitsInColor(pixelColor, messageArray);
            src.SetPixel(i, j, newColor);
            index++;
        }
    }
}

void LSBEncryptionStrategy::SaveEncryptedImage(BMP& src)
{
    std::string encryptedFilename = "C:\\Users\\WORKER\\Downloads\\Wallpaper1_with_text.bmp";
    src.WriteToFile(encryptedFilename.c_str());
}

void LSBEncryptionStrategy::Decrypt(BMP& src)
{
    if (!IsEncrypted(src))
    {
        std::cout << "The file does not contain encrypted information." << std::endl;
        return;
    }

    int countSymbol = ReadCountText(src);
    std::vector<ebmpBYTE> message(countSymbol);
    int index = 0;
    bool st = false;

    for (int i = EncryptionTextSize + 1; i < src.TellWidth(); i++)
    {
        for (int j = 0; j < src.TellHeight(); j++)
        {
            RGBApixel pixelColor = src.GetPixel(i, j);
            if (index == countSymbol)
            {
                st = true;
                break;
            }

            std::vector<bool> colorArray = ByteToBit(pixelColor.Red);
            std::vector<bool> messageArray = ByteToBit(pixelColor.Red);

            messageArray[0] = colorArray[0];
            messageArray[1] = colorArray[1];

            colorArray = ByteToBit(pixelColor.Green);
            messageArray[2] = colorArray[0];
            messageArray[3] = colorArray[1];
            messageArray[4] = colorArray[2];

            colorArray = ByteToBit(pixelColor.Blue);
            messageArray[5] = colorArray[0];
            messageArray[6] = colorArray[1];
            messageArray[7] = colorArray[2];

            message[index] = BitToByte(messageArray);
            index++;
        }

        if (st)
        {
            break;
        }
    }

    std::string strMessage(message.begin(), message.end());

    std::string sFileText = "C:\\Users\\WORKER\\Downloads\\te2st.txt";
    std::ofstream wFile(sFileText, std::ios::binary);
    wFile << strMessage;

    std::cout << "The text has been written to the file." << std::endl;
}

int main()
{
    std::string filePic = "C:\\Users\\WORKER\\Downloads\\Wallpaper1_src.bmp";
    std::string fileText = "C:\\Users\\WORKER\\Downloads\\test.txt";

    BMP bPic;
    bPic.ReadFromFile(filePic.c_str());

    EncryptionContext context{};
    LSBEncryptionStrategy strategy;
    context.SetEncryptionStrategy(&strategy);
    context.Encrypt(bPic, fileText);
    context.Decrypt(bPic);

    return 0;
}
 

Вложения

  • EasyBMP.zip
    197 КБ · Просмотры: 12
Пожалуйста, обратите внимание, что пользователь заблокирован
Метод интересный и прикольный, больше возможностей. Как один из вариантов пойдет но по итогу при запуске и уже в памяти это будет таким же текстом или массивом информации в расшифрованным виде
 
очень давно делал реализацию крипта с стеганографией и размытием пейлоада по ресурсам на практике очень хорошо себя показывало и энтропия в космос не улетала

ps не большой сниппет

C++:
DWORD round_size(DWORD dwSize)
{

    int rc = (int)sqrt((double)dwSize);
    while((DWORD)(rc*rc) < dwSize)
        rc++;

    return rc;
}
BYTE* bufferAsBitmap(BYTE* pBuffer,DWORD dwSize,DWORD& dwOutSize,DWORD&  dwPadding)
{
    unsigned char file[14] = {
        'B','M', // magic
        0,0,0,0, // size in bytes
        0,0, // app data
        0,0, // app data
        40+14,0,0,0 // start of data offset
        };

    unsigned char info[40] = {
        40,0,0,0, // info hd size
        0,0,0,0, // width
        0,0,0,0, // heigth
        1,0, // number color planes
        24,0, // bits per pixel
        0,0,0,0, // compression is none
        0,0,0,0, // image bits size
        0x16,0x1B,0,0, // horz resoluition in pixel / m//0x13,0x0B,0,0,
        0x11,0x2B,0,0, // vert resolutions (0x03C3 = 96 dpi, 0x0B13 = 72 dpi)
        0,0,0,0, // #colors in pallete
        0,0,0,0, // #important colors
        };

    int realSize = (int)(round_size((int)dwSize/3));
    int w=realSize;
    int h=realSize;


    int padSize  = (4-w%4)%4;
    int sizeData = w*h*3 + h*padSize;
    int sizeAll  = sizeData + sizeof(file) + sizeof(info);

    BYTE* pNewBuffer = (BYTE*)VirtualAlloc(NULL,sizeAll,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
    memset(pNewBuffer,0,sizeAll);

    BYTE* pTempBuffer = (BYTE*)VirtualAlloc(NULL,sizeAll,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
    memset(pTempBuffer,0,sizeAll);
    memcpy(pTempBuffer,pBuffer,dwSize);

    file[ 2] = (unsigned char)( sizeAll    );
    file[ 3] = (unsigned char)( sizeAll>> 8);
    file[ 4] = (unsigned char)( sizeAll>>16);
    file[ 5] = (unsigned char)( sizeAll>>24);

    info[ 4] = (unsigned char)( w   );
    info[ 5] = (unsigned char)( w>> 8);
    info[ 6] = (unsigned char)( w>>16);
    info[ 7] = (unsigned char)( w>>24);

    info[ 8] = (unsigned char)( h    );
    info[ 9] = (unsigned char)( h>> 8);
    info[10] = (unsigned char)( h>>16);
    info[11] = (unsigned char)( h>>24);

    info[24] = (unsigned char)( sizeData    );
    info[25] = (unsigned char)( sizeData>> 8);
    info[26] = (unsigned char)( sizeData>>16);
    info[27] = (unsigned char)( sizeData>>24);

    int fOffset = 0;

    memcpy(pNewBuffer,file,sizeof(file));
    fOffset += sizeof(file);
    memcpy(pNewBuffer+fOffset,info,sizeof(info));
    fOffset += sizeof(info);

    unsigned char pad[3] = {0,0,0};

    int offset = 0;

    file[ 6] = (padSize >> 8)&0x00FF;
    file[ 7] = padSize&0x00FF;

    for ( int y=0; y<h; y++ )
    {
        for ( int x=0; x<w; x++ )
        {
            long value1 = pTempBuffer[y*realSize + x + offset];
            long value2 = pTempBuffer[y*realSize + x + offset + 1];
            long value3 = pTempBuffer[y*realSize + x + offset + 2];

            unsigned char pixel[3];
            pixel[0] = value1;
            pixel[1] = value2;
            pixel[2] = value3;


            memcpy(pNewBuffer+fOffset,pixel,3);
            fOffset+=3;

            offset+=2;
        }
        memcpy(pNewBuffer+fOffset,pad,padSize);
        fOffset+=padSize;
    }

    dwPadding = padSize;

    VirtualFree(pTempBuffer,0,MEM_RELEASE);

    dwOutSize = sizeAll;


    return pNewBuffer+14;
}
DWORD AddPayload(HANDLE hRes,BYTE* pBuffer,DWORD dwSize,DWORD dwChunkSize)
{
    DWORD dwChunkNum = dwSize/dwChunkSize;
    if(dwSize%dwChunkSize)
        dwChunkNum++;

    BYTE* pFinal = (BYTE*)VirtualAlloc(NULL,dwChunkSize*dwChunkNum,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
    memset(pFinal,0,dwChunkSize*dwChunkNum);
    memcpy(pFinal,pBuffer,dwSize);

    DWORD dwOffset = 0;
    for(int i =0;i<dwChunkNum;i++)
    {
        BYTE* pTemp = (BYTE*)VirtualAlloc(NULL,dwChunkSize,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
        memcpy(pTemp,pFinal+dwOffset,dwChunkSize);
        dwOffset+=dwChunkSize;

        DWORD dwOut = 0;
        DWORD dwOutSize = 0;
        DWORD dwPadding = 0;

        BYTE* pFinalTemp = bufferAsBitmap(pTemp,dwChunkSize,dwOutSize,dwPadding);

        if(!UpdateResourceA(hRes,RT_BITMAP,MAKEINTRESOURCE(i+1),1024,pFinalTemp,dwOutSize))
            printf("Error update resource: %d",GetLastError());


        VirtualFree(pFinalTemp,0,MEM_RELEASE);
        VirtualFree(pTemp,0,MEM_RELEASE);
    }

    VirtualFree(pFinal,0,MEM_RELEASE);
    return dwChunkNum;
}
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх