ethical.blue Magazine

// Cybersecurity clarified.

Dekodowanie AZTEC z dowodu rejestracyjnego pojazdu (C++)

25.05.2022   Dawid Farbaniec
...
W dowodzie rejestracyjnym pojazdu można zauważyć kod dwuwymiarowy. Jest to kod Aztec 2D i jak się można domyślić, w założeniach miał usprawnić odczyt danych z tego dokumentu. Mało by mnie to interesowało, gdyby nie fakt, że sposób kodowania w nim danych - do niedawna - znany był tylko wąskiej grupie ludzi. Taka się wydaje powszechna rzecz ten kod i takie ograniczenia dostępu do informacji. Zgodnie z moją pamięcią temat został podjęty pierwszy raz na wykop.pl (podziękowania!), dalej temat poruszały kolejne witryny (np. zaufanatrzeciastrona.pl), osoby etc.

Czytaj więcej tutaj:
https://zaufanatrzeciastrona.pl/post/historia-o-dowodach-rejestracyjnych-dekoderze-aztec-i-pewnym-monopolu/ [access: 2022-05-23]

C++ implementation by ethical.blue

/*

* C++ implementation of NRV2E decompression algorithm
* which was used in this project to decode
* Aztec 2D from Polish Vehicle Registration Documents
* - by https://ethical.blue 2019 (last edit: October 2020)
*
* Based on original UCL library written by:
* Markus F.X.J. Oberhumer <[email protected]>
* http://www.oberhumer.com/opensource/ucl/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*/

#include <fstream>
#include <vector>

const signed START_OFFSET { 4 };
signed ilen { START_OFFSET };
unsigned current_bit { 0 };
std::byte current_byte { 0 };
std::vector<std::byte> src { 0 };

static unsigned get_bit()
{
    if (ilen >= src.size())
        throw std::invalid_argument("Przesunięcie jest poza zakresem.");

    if (current_bit == 0)
    {
        current_byte = src[ilen++];
        current_bit = 8;
    }

    return (static_cast<unsigned>(current_byte) >> --current_bit) & 1;
}

static std::vector<std::byte> decompress_NRV2E(const std::vector<std::byte>& source_data)
{
    src = source_data;

    unsigned dest_size { static_cast<unsigned>(src[0]) bitor
                         static_cast<unsigned>(src[1]) << 8u bitor
                         static_cast<unsigned>(src[2]) << 16u bitor
                         static_cast<unsigned>(src[3]) << 24u };
    
    std::vector<std::byte> dst(dest_size);

    unsigned olen { 0 };
    unsigned last_m_off { 1 };

    while (ilen < src.size())
    {
        unsigned m_off { 0 };
        unsigned m_len { 0 };

        while (get_bit() == 1)
        {
            dst[olen++] = src[ilen++];
        }

        m_off = 1;
        while (true)
        {
            m_off = m_off * 2 + get_bit();
            if (get_bit() == 1)
                break;
            m_off = (m_off - 1) * 2 + get_bit();
        }

        if (m_off == 2)
        {
            m_off = last_m_off;
            m_len = get_bit();
        }
        else
        {
            m_off = (m_off - 3) * 256 + static_cast<unsigned>(src[ilen++]);
            if (m_off == std::numeric_limits<unsigned>().max())
                break;
            m_len = (m_off ^ std::numeric_limits<unsigned>().max()) & 1;
            m_off >>= 1;
            last_m_off = ++m_off;
        }
        if (m_len > 0)
            m_len = 1 + get_bit();
        else if (get_bit() == 1)
            m_len = 3 + get_bit();
        else
        {
            m_len++;
            do
            {
                m_len = m_len * 2 + get_bit();
            } while (get_bit() == 0);
            m_len += 3;
        }
        m_len += m_off > 0x500 ? 1 : 0;

        unsigned m_pos { 0 };
        m_pos = olen - m_off;

        dst[olen++] = dst[m_pos++];
        do dst[olen++] = dst[m_pos++]; while (--m_len > 0);
    }
    return dst;
}

static std::vector<std::byte> base64_decode(const std::vector<std::byte>& in) {

    std::vector<std::byte> out { 0 };

    std::vector<int> T(256, -1);
    for (int i = 0; i < 64; i++)
        T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

    signed val { 0 };
    signed valb { -8 };
    for (auto b : in)
    {
        if (T[static_cast<unsigned>(b)] == -1) break;
        val = (val << 6) + T[static_cast<unsigned>(b)];
        valb += 6;
        if (valb >= 0)
        {
            out.push_back(static_cast<std::byte>((val >> valb) & 0xFF));
            valb -= 8;
        }
    }
    return out;
}

int main()
{
    std::string test1 { "BgQAANtYAAJDAPkxAHwAQXIw7zcGNN4ANiox+w81HrUGOP8eUABSAEUA+1oAWQBEDv9OAFQAIABN3wAuClMAvlQPV/eKUhq9Wg5X7k58UtcWSVq9TF5J79pBZ+5PAEsG12bTSm5GVQBM/ntSAEH7L1dj+0MAS1vvMvovewo3Ut4wDi39HjEAN6Pbl0FNe3YgPt5Q3kv3IlSevVnX1z9FMmuCShL2WgBaG9umKADvSAApJnx75k+itwZMAEx9X0rvbkSOTXtOOF/DRy0WOW53fPYLFoMzLr0xAi3DGnevLQOCfJ/vQZ5TcBZrN0oa9k4AfA82Q4QaDzj3q8deN6sN7zIE/1x8lbMnQdwBQi5ZT86jL2tqNAr2MwAw34xSH+uPSVPYFxZThBMzON8AMJM5wQA3MwRcMX7bNcET2jInwyedE01HZ4dlM94qKy0DL38fNgAqeBszSxOvNIeKfHM7fCLxNQAwVkMtdzl7Xiw/YMyrFzxQACBWw+Hza7c3C93/NWuHg1OWRquPQ5KP02K9IBZT4QZC9oNZU7aXFiOX83U4ADJFC7ADhrNVCyOW8w9qMbEnZhdHbHxjdjIT7E4DW0M3OQuGaxYmCSSSSSr/" };

    if (test1.length() % 2 == 1)
        test1[test1.length() - 1] = u8'\0';

    std::vector<std::byte> text_bytes { 0 };

    for (char c : test1)
        text_bytes.push_back(static_cast<std::byte>(c));

    std::vector<std::byte> decoded = base64_decode(text_bytes);
    std::vector<std::byte> decompressed = decompress_NRV2E(decoded);
    std::u16string plain_data { 0 };
    plain_data.assign(reinterpret_cast<std::u16string::const_pointer>(&decompressed[0]), decompressed.size() / sizeof(std::u16string::value_type));

    /* Zapisz rozkodowane dane do pliku tekstowego
       (zmień ścieżkę według swojego systemu) */
    std::basic_ofstream<char16_t> outfile("C:\Users\x\Desktop\Nowy folder\file1.txt", std::ios_base::binary);
    outfile.write(plain_data.c_str(), plain_data.length());
    outfile.close();

    std::string visit { "https://ethical.blue" };

    return EXIT_SUCCESS;
}

Wykaz literatury

https://zaufanatrzeciastrona.pl/post/historia-o-dowodach-rejestracyjnych-dekoderze-aztec-i-pewnym-monopolu/ [access: 2022-05-23]