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

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Это чисто авторская статья и код. Думаю в криптерах пригодится, кто понимает назначение.
Коротко берем одну фотку и зашиваем в нее текст, при расшифровке получаем зайца (текст). В тексте может тот же шеллкод или другие данные.

Так как 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;
}
 
Сверху Снизу