Steganografia i jej analiza w Pythonie

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ę.

Steganografia Kodek Online

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.

Kalkulator Kodów Do Radia Samochodowego API i SDK

Kalkulator Kodów Do Radia samochodowego to usługa Web API umożliwiająca generowanie kodów odblokowujących radioodtwarzacze i nawigację w samochodach. Jest dostępna poprzez prosty interfejs online oraz API i wiele bibliotek SDK dla popularnych języków programowania, takich jak np. Python.

Przykład generowania kodu radiowego

Prościej się chyba nie da, obsługa wszelkich możliwych wyjątków:

#!/usr/bin/env python

###############################################################################
#
# Radio Code Calculator API - WebApi interface usage example
#
# In this example, we will demonstrate how to generate a code for a specific
# type of car radio.
#
# Version        : v1.00
# Language       : Python
# Author         : Bartosz Wójcik
# Project        : https://www.pelock.com/products/radio-code-calculator
# Homepage       : https://www.pelock.com
#
###############################################################################

#
# include Radio Code Calculator API module
#
from radio_code_calculator import *

#
# create Radio Code Calculator API class instance (we are using our activation key)
#
myRadioCodeCalculator = RadioCodeCalculator("ABCD-ABCD-ABCD-ABCD")

#
# generate radio code (using Web API)
#
error, result = myRadioCodeCalculator.calc(RadioModels.FORD_M_SERIES, "123456")

if error == RadioErrors.SUCCESS:
    print(f'Radio code is {result["code"]}')
elif error == RadioErrors.INVALID_RADIO_MODEL:
    print("Invalid radio model (not supported)")
elif error == RadioErrors.INVALID_SERIAL_LENGTH:
    print(f'Invalid serial number length (expected {result["serialMaxLen"]} characters)')
elif error == RadioErrors.INVALID_SERIAL_PATTERN:
    print(f'Invalid serial number regular expression pattern (expected {result["serialRegexPattern"]["python"]} regex pattern)')
elif error == RadioErrors.INVALID_SERIAL_NOT_SUPPORTED:
    print("This serial number is not supported")
elif error == RadioErrors.INVALID_EXTRA_LENGTH:
    print(f'Invalid extra data length (expected {result["extraMaxLen"]} characters)')
elif error == RadioErrors.INVALID_EXTRA_PATTERN:
    print(f'Invalid extra data regular expression pattern (expected {result["extraRegexPattern"]["python"]} regex pattern)')
elif error == RadioErrors.INVALID_INPUT:
    print("Invalid input data")
elif error == RadioErrors.INVALID_COMMAND:
    print("Invalid command sent to the Web API interface")
elif error == RadioErrors.INVALID_LICENSE:
    print("Invalid license key")
elif error == RadioErrors.ERROR_CONNECTION:
    print("Something unexpected happen while trying to login to the service.")
else:
    print(f'Unknown error {error}')

Dzięki temu rozwiązaniu możesz tworzyć aplikacje mobilne lub internetowe umożliwiające łatwe generowanie kodów radia i samemu zarabiać na generowaniu kodów radiowych. Przykładowej projekcje zarobków w zależności od cen na stronie projektu.

Wersja angielska – https://www.pelock.com/products/radio-code-calculator

PS. Nie wszystko jest jeszcze ukończone na 100%, obecnie jedynie wersja dla Pythona jest dostępna.

Szyfrowanie w Pythonie

Opublikowałem właśnie pomocniczy moduł usługi szyfrującej StringEncrypt dla Pythona 3.

Moduł znajdziecie w Pythonowym repozytorium PyPI:

https://pypi.org/project/stringencrypt/

Jego kody źródłowe opublikowałem również na GitHubie:

https://github.com/PELock/StringEncrypt-Python

Instalacja dla Pythona z wykorzystaniem menedżera paczek pip:

pip install stringencrypt

Jak to działa i co to właściwie robi?

W skrócie bierze dowolny string lub zawartość binarnego pliku, szyfruje to silnik polimorficzny i generuje kod w wybranym języku programowania (w tym wypadku w Pythonie) z kodem zawierającym zaszyfrowany string oraz kodem samego dekryptora:

Moduł Pythona dla AutoIt Obfuscatora

Wydałem właśnie moduł Pythona dla mojego AutoIt Obfuscatora, pozwalający zautomatyzować zabezpieczanie skryptów AutoIt bezpośrednio z poziomu Pythona 3.

https://pypi.org/project/autoitobfuscator/

Jeśli kogoś ciekawi jak zbudowane są moduły Pythona, to jego źródła znajdziecie na:

https://github.com/PELock/AutoIt-Obfuscator-Python

Wszystkie przykłady użycia na:

https://www.pelock.com/products/autoit-obfuscator/api

Prosty przykład wykorzystania domyślnych opcji:

#!/usr/bin/env python

###############################################################################
#
# AutoIt Obfuscator WebApi interface usage example.
#
# In this example we will obfuscate sample source with default options.
#
# Version        : v1.0
# Language       : Python
# Author         : Bartosz Wójcik
# Web page       : https://www.pelock.com
#
###############################################################################

#
# include AutoIt Obfuscator module
#
from autoitobfuscator import AutoItObfuscator

#
# if you don't want to use Python module, you can import directly from the file
#
#from pelock.autoitobfuscator import AutoItObfuscator

#
# create AutoIt Obfuscator class instance (we are using our activation key)
#
myAutoItObfuscator = AutoItObfuscator("ABCD-ABCD-ABCD-ABCD")

#
# source code in AutoIt v3 format
#
scriptSourceCode = 'ConsoleWrite("Hello World")'

#
# by default all options are enabled, both helper random numbers
# generation & obfuscation strategies, so we can just simply call:
#
result = myAutoItObfuscator.obfuscate_script_source(scriptSourceCode)

#
# it's also possible to pass script path instead of a string with the source e.g.
#
# result = myAutoItObfuscator.obfuscate_script_file("/path/to/script/source.au3")

#
# result[] array holds the obfuscation results as well as other information
#
# result["error"]         - error code
# result["output"]        - obfuscated code
# result["demo"]          - was it used in demo mode (invalid or empty activation key was used)
# result["credits_left"]  - usage credits left after this operation
# result["credits_total"] - total number of credits for this activation code
# result["expired"]       - if this was the last usage credit for the activation key it will be set to True
#
if result and "error" in result:

    # display obfuscated code
    if result["error"] == AutoItObfuscator.ERROR_SUCCESS:

        # format output code for HTML display
        print(result["output"])

    else:
        print(f'An error occurred, error code: {result["error"]}')

else:
    print("Something unexpected happen while trying to obfuscate the code.")