Online: 0x032 (50)
haker.iиfø  — Etyczny hacking_
Spreading knowledge like a virus.

Dekodowanie kodu Aztec 2D z dowodu rejestracyjnego

   Dawid Farbaniec    2290 słów

0x01. Kod Aztec 2D z dowodu rejestracyjnego

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 i pozdrowienia!), dalej temat poruszały kolejne witryny (np. zaufanatrzeciastrona.pl), osoby etc.

Kod Aztec 2D na dowodzie rejestracyjnym
Rysunek 1.1. Przykładowy kod Aztec 2D na dowodzie rejestracyjnym

0x02. Metoda kodowania danych w dowodzie rej. pojazdu

Na rysunku 2.1. poniżej przedstawiono schemat poszczególnych kroków jakie należy podjąć, aby móc odczytać dane zeskanowane z kodu Aztec 2D dowodu rejestracyjnego pojazdu.

Dekodowanie kodu Aztec 2D z dowodu rejestracyjnego
Rysunek 2.1. Metoda dekodowania znaku Aztec 2D z dowodu rejestracyjnego

Postaram się poniżej rozwinąć trochę opis poszczególnych etapów:

  • Skanowanie: Aby zeskanować kod Aztec 2D z polskiego dowodu rejestracyjnego pojazdu można użyć gotowych skanerów kodów Aztec/2D. Po zeskanowaniu otrzymamy ciąg znaków.
  • Dekodowanie Base64: otrzymany ciąg znaków jest kodowany poprzez algorytm Base64. Dodatkowo można się spotkać z dodanym jednym dodatkowym znakiem na końcu. Należy to wziąć pod uwagę i pominąć ten znak, gdy zauważymy, że funkcja Base64 nie działa poprawnie.
  • Kompresja NRV2E: Na tym etapie dane są skompresowane i należy je zdekompresować algorytmem NRV2E, którego implementację można znaleźć m.in. w darmowej bibliotece UCL autorstwa Markus F.X.J. Oberhumer (http://www.oberhumer.com/opensource/ucl/). Funkcja ucl_nrv2e_decompress_8().
  • Dane czyste (plain): Po tych wszystkich etapach otrzymujemy dane tekstowe z kodowaniem UTF16-LE, gdzie poszczególne informacje są oddzielone znakiem pionowej kreski |, której kod to 0x7C.

0x03. Dekodowanie Aztec 2D z dowodu rej. pojazdu — kody źródłowe


3.1. C#.NET — kod źródłowy (License)


VehicleDocumentAztecDecoder.cs

/*  * C#.NET implementation of NRV2E decompression algorithm  * - by http://haker.info A.D. 2019  * 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. */ using System; using System.Text; namespace FreeAztecVehicleDocDecoder { public class VehicleDocumentAztecDecoder { private const int START_OFFSET = 4; private byte[] src; private int ilen = START_OFFSET; private int currentByte; private int currentBit; private byte[] dst; public string Decode(string text) { byte[] decoded = Base64Decode(text); byte[] decompressed = DecompressNRV2E(decoded); return Encoding.Unicode.GetString(decompressed); } private byte[] DecompressNRV2E(byte[] sourceData) { src = sourceData; uint olen = 0, last_m_off = 1; dst = new byte[BitConverter.ToInt32(src, 0)]; while (ilen < src.Length) { uint m_off, m_len; while (GetBit() == 1) { dst[olen++] = src[ilen++]; } m_off = 1; while (true) { m_off = m_off * 2 + GetBit(); if (GetBit() == 1) break; m_off = (m_off - 1) * 2 + GetBit(); } if (m_off == 2) { m_off = last_m_off; m_len = GetBit(); } else { m_off = (m_off - 3) * 256 + src[ilen++]; if (m_off == 0xffffffff) break; m_len = (m_off ^ 0xffffffff) & 1; m_off >>= 1; last_m_off = ++m_off; } if (m_len > 0) m_len = (uint)1 + GetBit(); else if (GetBit() == 1) m_len = (uint)3 + GetBit(); else { m_len++; do { m_len = m_len * 2 + GetBit(); } while (GetBit() == 0); m_len += 3; } m_len += (uint)(m_off > 0x500 ? 1 : 0); uint m_pos; m_pos = olen - m_off; dst[olen++] = dst[m_pos++]; do dst[olen++] = dst[m_pos++]; while (--m_len > 0); } return dst; } private byte GetBit() { if (ilen >= src.Length) throw new Exception("Przesunięcie jest poza zakresem."); if (currentBit == 0) { currentByte = src[ilen++]; currentBit = 8; } return (byte)(((uint)currentByte >> --currentBit) & 1); } private byte[] Base64Decode(string textToDecode) { if (string.IsNullOrWhiteSpace(textToDecode)) return new byte[0]; if (textToDecode.Length % 2 == 1) { textToDecode = textToDecode.Substring(0, textToDecode.Length - 1); } return Convert.FromBase64String(textToDecode); } } }

Program.cs

/*  * C#.NET decoder of Aztec 2D used on Polish Vehicle Registration Documents  * - by http://haker.info A.D. 2019  * 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. */ using System; using System.Collections.Generic; using System.Text; namespace FreeAztecVehicleDocDecoder { class Program { static void Main(string[] args) { //Przykładowy ciąg Aztec 2D string test1 = "BgQAANtYAAJDAPkxAHwAQXIw7zcGNN4ANiox+w81HrUGOP8eUABSAEUA+1oAWQBEDv9OAFQAIABN3wAuClMAvlQPV/eKUhq9Wg5X7k58UtcWSVq9TF5J79pBZ+5PAEsG12bTSm5GVQBM/ntSAEH7L1dj+0MAS1vvMvovewo3Ut4wDi39HjEAN6Pbl0FNe3YgPt5Q3kv3IlSevVnX1z9FMmuCShL2WgBaG9umKADvSAApJnx75k+itwZMAEx9X0rvbkSOTXtOOF/DRy0WOW53fPYLFoMzLr0xAi3DGnevLQOCfJ/vQZ5TcBZrN0oa9k4AfA82Q4QaDzj3q8deN6sN7zIE/1x8lbMnQdwBQi5ZT86jL2tqNAr2MwAw34xSH+uPSVPYFxZThBMzON8AMJM5wQA3MwRcMX7bNcET2jInwyedE01HZ4dlM94qKy0DL38fNgAqeBszSxOvNIeKfHM7fCLxNQAwVkMtdzl7Xiw/YMyrFzxQACBWw+Hza7c3C93/NWuHg1OWRquPQ5KP02K9IBZT4QZC9oNZU7aXFiOX83U4ADJFC7ADhrNVCyOW8w9qMbEnZhdHbHxjdjIT7E4DW0M3OQuGaxYmCSSSSSr/"; VehicleDocumentAztecDecoder dec = new VehicleDocumentAztecDecoder(); string result = dec.Decode(test1); Console.OutputEncoding = Encoding.Unicode; //Wyświetl rozkodowane dane na konsoli tekstowej Console.WriteLine(result); #if DEBUG Console.ReadLine(); #endif } } }

3.2. C++ — kod źródłowy (License)


/* * C++ implementation of NRV2E decompression algorithm * which was used in this project to decode * Aztec 2D from Polish Vehicle Registration Documents * - by http://haker.info A.D. 2019 * * 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 <iostream> #include <string> #include <vector> #include <fstream> const int START_OFFSET = 4; std::vector<unsigned char> src; int ilen = START_OFFSET; int currentByte; int currentBit; static unsigned char GetBit() { if (ilen >= src.size()) throw std::invalid_argument("Przesunięcie jest poza zakresem."); if (currentBit == 0) { currentByte = src[ilen++]; currentBit = 8; } return (unsigned char)(((unsigned int)currentByte >> --currentBit) & 1); } static std::vector<unsigned char> DecompressNRV2E(std::vector<unsigned char> sourceData) { src = sourceData; int destSize = src[0] | (int)src[1] << 8 | (int)src[2] << 16 | (int)src[3] << 24; std::vector<unsigned char> dst(destSize); unsigned int olen = 0, last_m_off = 1; while (ilen < src.size()) { unsigned int m_off, m_len; while (GetBit() == 1) { dst[olen++] = src[ilen++]; } m_off = 1; while (true) { m_off = m_off * 2 + GetBit(); if (GetBit() == 1) break; m_off = (m_off - 1) * 2 + GetBit(); } if (m_off == 2) { m_off = last_m_off; m_len = GetBit(); } else { m_off = (m_off - 3) * 256 + src[ilen++]; if (m_off == 0xffffffff) break; m_len = (m_off ^ 0xffffffff) & 1; m_off >>= 1; last_m_off = ++m_off; } if (m_len > 0) m_len = (unsigned int)1 + GetBit(); else if (GetBit() == 1) m_len = (unsigned int)3 + GetBit(); else { m_len++; do { m_len = m_len * 2 + GetBit(); } while (GetBit() == 0); m_len += 3; } m_len += (unsigned int)(m_off > 0x500 ? 1 : 0); unsigned int m_pos; m_pos = olen - m_off; dst[olen++] = dst[m_pos++]; do dst[olen++] = dst[m_pos++]; while (--m_len > 0); } return dst; } static std::string base64_decode(const std::string& in) { std::string out; std::vector<int> T(256, -1); for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; int val = 0, valb = -8; for (unsigned char c : in) { if (T[c] == -1) break; val = (val << 6) + T[c]; valb += 6; if (valb >= 0) { out.push_back(char((val >> valb) & 0xFF)); valb -= 8; } } return out; } int main(int argc, char* argv[]) { 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] = '\0'; } std::string decoded = base64_decode(test1); std::vector<unsigned char> decodedVec = std::vector<unsigned char>(decoded.begin(), decoded.end()); std::vector<unsigned char> decompressed = DecompressNRV2E(decodedVec); std::string plainData(decompressed.begin(), decompressed.end()); //zapisz rozkodowane dane do pliku tekstowego //(zmień ścieżkę według swojego systemu) std::ofstream outfile("D:\\0001\\file1.txt", std::ofstream::binary); outfile.write(plainData.c_str(), plainData.length()); outfile.close(); return EXIT_SUCCESS; }

03 stycznia 2020 r. — poprawiono kodowanie polskich znaków w przykładzie dla Visual C++


Rysunek 3.1. Przykład dla Visual C++ zapisuje rozkodowane dane do pliku tekstowego


0x04. Opis poszczególnych pól z danymi

Przykładowe rozkodowane dane w stanie surowym przedstawiono na rysunku 4.1 poniżej.

Przykładowe rozkodowane dane
Rysunek 4.1. Przykład rozkodowania kodu Aztec 2D z dow. rej. - dane surowe

W przypadku pisania własnego oprogramowania bazującego na powyższym kodzie, na pewno będzie przydatny opis poszczególnych pól.

Pozycja (indeks) Opis
0Wersja protokołu
1Seria i numer dowodu
2Kod terytorialny urzędu rejestrującego
3Organ wydający - Nazwa
4Organ wydający - Gmina
5Organ wydający - Ulica i nr
6Organ wydający - Kod i miejscowość
7Numer rejestracyjny pojazdu
8Marka
9Typ homologacji
10Wariant homologacji
11Wersja homologacji
12Model
13Numer VIN
14Data wydania dowodu rejestracyjnego
15Okres ważności dowodu
16Nazwa właściciela
17Imiona
18Nazwisko
19?
20PESEL
21Kod pocztowy
22Gmina
23Miejscowość
24Ulica
25Nr domu
26Nr mieszkania
27Nazwa właściciela
28Imiona
29Nazwisko
30?
31PESEL
32Kod pocztowy
33Gmina
34Miejscowość
35Ulica
36Nr domu
37Nr mieszkania
38Maksymalna masa całkowita [kg]
39Maksymalna masa całkowita pojazdu [kg]
40Maksymalna masa całkowita zespołu pojazdów [kg]
41Masa własna
42Kategoria pojazdu
43Numer świadectwa homologacji typu pojazdu
44Liczba osi
45Maksymalna masa całkowita przyczepy z hamulcem
46Maksymalna mas całkowita przyczepy bez hamulca
47Stosunek mocy do masy (w KW/kg)
48Pojemność silnika [cm3]
49Moc silnika [kW]
50Rodzaj paliwa
51Data pierwszej rejestracji pojazdu
52Liczba miejsc siedzących
53Liczba miejsc stojących
54Rodzaj pojazdu
55Przeznaczenie
56Rok produkcji
57Dopuszczalna ładowność
58Największy dopuszczalny nacisk osi
59Numer karty pojazdu
60Kod ITS
61?
62?
63?
64?
65?

0x05. Podsumowanie

Dzięki za przeczytanie. Jeśli są jakieś pytania lub sugestie dot. tego wpisu to proszę pisać.

Dodatki

Przykładowe dane w poszczególnych etapach

Poniżej przedstawiono przykładowe dane w poszczególnych etapach.

string text = "BgQAANtYAAJDAPkxAHwAQXIw7zcGNN4ANiox+w81HrUGOP8eUABSAEUA+1oAWQBEDv9OAFQAIABN3wAuClMAvlQPV/eKUhq9Wg5X7k58UtcWSVq9TF5J79pBZ+5PAEsG12bTSm5GVQBM/ntSAEH7L1dj+0MAS1vvMvovewo3Ut4wDi39HjEAN6Pbl0FNe3YgPt5Q3kv3IlSevVnX1z9FMmuCShL2WgBaG9umKADvSAApJnx75k+itwZMAEx9X0rvbkSOTXtOOF/DRy0WOW53fPYLFoMzLr0xAi3DGnevLQOCfJ/vQZ5TcBZrN0oa9k4AfA82Q4QaDzj3q8deN6sN7zIE/1x8lbMnQdwBQi5ZT86jL2tqNAr2MwAw34xSH+uPSVPYFxZThBMzON8AMJM5wQA3MwRcMX7bNcET2jInwyedE01HZ4dlM94qKy0DL38fNgAqeBszSxOvNIeKfHM7fCLxNQAwVkMtdzl7Xiw/YMyrFzxQACBWw+Hza7c3C93/NWuHg1OWRquPQ5KP02K9IBZT4QZC9oNZU7aXFiOX83U4ADJFC7ADhrNVCyOW8w9qMbEnZhdHbHxjdjIT7E4DW0M3OQuGaxYmCSSSSSr/";

Po dekodowaniu Base64 przykładowe dane prezentują się następująco:

/* byte[] decoded = Base64Decode(text); */ 0x06 0x04 0x00 0x00 0xDB 0x58 0x00 0x02 0x43 0x00 0xF9 0x31 0x00 0x7C 0x00 0x41 0x72 0x30 0xEF 0x37 0x06 0x34 0xDE 0x00 0x36 0x2A 0x31 0xFB 0x0F 0x35 0x1E 0xB5 0x06 0x38 0xFF 0x1E 0x50 0x00 0x52 0x00 0x45 0x00 0xFB 0x5A 0x00 0x59 0x00 0x44 0x0E 0xFF 0x4E 0x00 0x54 0x00 0x20 0x00 0x4D 0xDF 0x00 0x2E 0x0A 0x53 0x00 0xBE 0x54 0x0F 0x57 0xF7 0x8A 0x52 0x1A 0xBD 0x5A 0x0E 0x57 0xEE 0x4E 0x7C 0x52 0xD7 0x16 0x49 0x5A 0xBD 0x4C 0x5E 0x49 0xEF 0xDA 0x41 0x67 0xEE 0x4F 0x00 0x4B 0x06 0xD7 0x66 0xD3 0x4A 0x6E 0x46 0x55 0x00 0x4C 0xFE 0x7B 0x52 0x00 0x41 0xFB 0x2F 0x57 0x63 0xFB 0x43 0x00 0x4B 0x5B 0xEF 0x32 0xFA 0x2F 0x7B 0x0A 0x37 0x52 0xDE 0x30 0x0E 0x2D 0xFD 0x1E 0x31 0x00 0x37 0xA3 0xDB 0x97 0x41 0x4D 0x7B 0x76 0x20 0x3E 0xDE 0x50 0xDE 0x4B 0xF7 0x22 0x54 0x9E 0xBD 0x59 0xD7 0xD7 0x3F 0x45 0x32 0x6B 0x82 0x4A 0x12 0xF6 0x5A 0x00 0x5A 0x1B 0xDB 0xA6 0x28 0x00 0xEF 0x48 0x00 0x29 0x26 0x7C 0x7B 0xE6 0x4F 0xA2 0xB7 0x06 0x4C 0x00 0x4C 0x7D 0x5F 0x4A 0xEF 0x6E 0x44 0x8E 0x4D 0x7B 0x4E 0x38 0x5F 0xC3 0x47 0x2D 0x16 0x39 0x6E 0x77 0x7C 0xF6 0x0B 0x16 0x83 0x33 0x2E 0xBD 0x31 0x02 0x2D 0xC3 0x1A 0x77 0xAF 0x2D 0x03 0x82 0x7C 0x9F 0xEF 0x41 0x9E 0x53 0x70 0x16 0x6B 0x37 0x4A 0x1A 0xF6 0x4E 0x00 0x7C 0x0F 0x36 0x43 0x84 0x1A 0x0F 0x38 0xF7 0xAB 0xC7 0x5E 0x37 0xAB 0x0D 0xEF 0x32 0x04 0xFF 0x5C 0x7C 0x95 0xB3 0x27 0x41 0xDC 0x01 0x42 0x2E 0x59 0x4F 0xCE 0xA3 0x2F 0x6B 0x6A 0x34 0x0A 0xF6 0x33 0x00 0x30 0xDF 0x8C 0x52 0x1F 0xEB 0x8F 0x49 0x53 0xD8 0x17 0x16 0x53 0x84 0x13 0x33 0x38 0xDF 0x00 0x30 0x93 0x39 0xC1 0x00 0x37 0x33 0x04 0x5C 0x31 0x7E 0xDB 0x35 0xC1 0x13 0xDA 0x32 0x27 0xC3 0x27 0x9D 0x13 0x4D 0x47 0x67 0x87 0x65 0x33 0xDE 0x2A 0x2B 0x2D 0x03 0x2F 0x7F 0x1F 0x36 0x00 0x2A 0x78 0x1B 0x33 0x4B 0x13 0xAF 0x34 0x87 0x8A 0x7C 0x73 0x3B 0x7C 0x22 0xF1 0x35 0x00 0x30 0x56 0x43 0x2D 0x77 0x39 0x7B 0x5E 0x2C 0x3F 0x60 0xCC 0xAB 0x17 0x3C 0x50 0x00 0x20 0x56 0xC3 0xE1 0xF3 0x6B 0xB7 0x37 0x0B 0xDD 0xFF 0x35 0x6B 0x87 0x83 0x53 0x96 0x46 0xAB 0x8F 0x43 0x92 0x8F 0xD3 0x62 0xBD 0x20 0x16 0x53 0xE1 0x06 0x42 0xF6 0x83 0x59 0x53 0xB6 0x97 0x16 0x23 0x97 0xF3 0x75 0x38 0x00 0x32 0x45 0x0B 0xB0 0x03 0x86 0xB3 0x55 0x0B 0x23 0x96 0xF3 0x0F 0x6A 0x31 0xB1 0x27 0x66 0x17 0x47 0x6C 0x7C 0x63 0x76 0x32 0x13 0xEC 0x4E 0x03 0x5B 0x43 0x37 0x39 0x0B 0x86 0x6B 0x16 0x26 0x09 0x24 0x92 0x49 0x2A 0xFF

Po dekompresji NRV2E przykładowe dane prezentują się następująco:

/* byte[] decompressed = DecompressNRV2E(decoded); */ 0x58 0x00 0x58 0x00 0x43 0x00 0x31 0x00 0x7C 0x00 0x41 0x00 0x41 0x00 0x41 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x37 0x00 0x30 0x00 0x34 0x00 0x36 0x00 0x7C 0x00 0x31 0x00 0x34 0x00 0x36 0x00 0x35 0x00 0x30 0x00 0x35 0x00 0x38 0x00 0x7C 0x00 0x50 0x00 0x52 0x00 0x45 0x00 0x5A 0x00 0x59 0x00 0x44 0x00 0x45 0x00 0x4E 0x00 0x54 0x00 0x20 0x00 0x4D 0x00 0x2E 0x00 0x20 0x00 0x53 0x00 0x54 0x00 0x2E 0x00 0x20 0x00 0x57 0x00 0x41 0x00 0x52 0x00 0x53 0x00 0x5A 0x00 0x41 0x00 0x57 0x00 0x59 0x00 0x7C 0x00 0x44 0x00 0x5A 0x00 0x49 0x00 0x45 0x00 0x4C 0x00 0x4E 0x00 0x49 0x00 0x43 0x00 0x41 0x00 0x20 0x00 0x4D 0x00 0x4F 0x00 0x4B 0x00 0x4F 0x00 0x54 0x00 0xD3 0x00 0x57 0x00 0x7C 0x00 0x55 0x00 0x4C 0x00 0x2E 0x00 0x20 0x00 0x52 0x00 0x41 0x00 0x4B 0x00 0x4F 0x00 0x57 0x00 0x49 0x00 0x45 0x00 0x43 0x00 0x4B 0x00 0x41 0x00 0x20 0x00 0x32 0x00 0x35 0x00 0x2F 0x00 0x32 0x00 0x37 0x00 0x7C 0x00 0x30 0x00 0x32 0x00 0x2D 0x00 0x35 0x00 0x31 0x00 0x37 0x00 0x20 0x00 0x57 0x00 0x41 0x00 0x52 0x00 0x53 0x00 0x5A 0x00 0x41 0x00 0x57 0x00 0x41 0x00 0x7C 0x00 0x44 0x00 0x4D 0x00 0x49 0x00 0x20 0x00 0x31 0x00 0x50 0x00 0x4E 0x00 0x4B 0x00 0x7C 0x00 0x54 0x00 0x4F 0x00 0x59 0x00 0x4F 0x00 0x54 0x00 0x41 0x00 0x7C 0x00 0x45 0x00 0x31 0x00 0x32 0x00 0x4A 0x00 0x7C 0x00 0x5A 0x00 0x5A 0x00 0x45 0x00 0x31 0x00 0x32 0x00 0x30 0x00 0x28 0x00 0x48 0x00 0x29 0x00 0x7C 0x00 0x7C 0x00 0x43 0x00 0x4F 0x00 0x52 0x00 0x4F 0x00 0x4C 0x00 0x4C 0x00 0x41 0x00 0x7C 0x00 0x4A 0x00 0x54 0x00 0x44 0x00 0x4B 0x00 0x4D 0x00 0x32 0x00 0x38 0x00 0x45 0x00 0x31 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x38 0x00 0x39 0x00 0x31 0x00 0x32 0x00 0x30 0x00 0x7C 0x00 0x32 0x00 0x30 0x00 0x31 0x00 0x33 0x00 0x2D 0x00 0x31 0x00 0x31 0x00 0x2D 0x00 0x30 0x00 0x36 0x00 0x7C 0x00 0x2D 0x00 0x2D 0x00 0x2D 0x00 0x7C 0x00 0x4B 0x00 0x4F 0x00 0x57 0x00 0x41 0x00 0x4C 0x00 0x53 0x00 0x4B 0x00 0x49 0x00 0x20 0x00 0x4A 0x00 0x41 0x00 0x4E 0x00 0x7C 0x00 0x4A 0x00 0x41 0x00 0x4E 0x00 0x7C 0x00 0x4B 0x00 0x4F 0x00 0x57 0x00 0x41 0x00 0x4C 0x00 0x53 0x00 0x4B 0x00 0x49 0x00 0x7C 0x00 0x7C 0x00 0x38 0x00 0x32 0x00 0x30 0x00 0x39 0x00 0x31 0x00 0x37 0x00 0x31 0x00 0x31 0x00 0x30 0x00 0x32 0x00 0x32 0x00 0x7C 0x00 0x30 0x00 0x32 0x00 0x2D 0x00 0x35 0x00 0x31 0x00 0x37 0x00 0x7C 0x00 0x57 0x00 0x41 0x00 0x52 0x00 0x53 0x00 0x5A 0x00 0x41 0x00 0x57 0x00 0x41 0x00 0x7C 0x00 0x7C 0x00 0x57 0x00 0x41 0x00 0x41 0x01 0x42 0x00 0x52 0x00 0x5A 0x00 0x59 0x00 0x53 0x00 0x4B 0x00 0x41 0x00 0x7C 0x00 0x32 0x00 0x34 0x00 0x7C 0x00 0x33 0x00 0x30 0x00 0x7C 0x00 0x4B 0x00 0x4F 0x00 0x57 0x00 0x41 0x00 0x4C 0x00 0x53 0x00 0x4B 0x00 0x41 0x00 0x20 0x00 0x4D 0x00 0x41 0x00 0x52 0x00 0x49 0x00 0x41 0x00 0x7C 0x00 0x4D 0x00 0x41 0x00 0x52 0x00 0x49 0x00 0x41 0x00 0x7C 0x00 0x4B 0x00 0x4F 0x00 0x57 0x00 0x41 0x00 0x4C 0x00 0x53 0x00 0x4B 0x00 0x41 0x00 0x7C 0x00 0x7C 0x00 0x38 0x00 0x38 0x00 0x30 0x00 0x33 0x00 0x30 0x00 0x39 0x00 0x37 0x00 0x31 0x00 0x30 0x00 0x32 0x00 0x32 0x00 0x7C 0x00 0x30 0x00 0x32 0x00 0x2D 0x00 0x35 0x00 0x31 0x00 0x37 0x00 0x7C 0x00 0x57 0x00 0x41 0x00 0x52 0x00 0x53 0x00 0x5A 0x00 0x41 0x00 0x57 0x00 0x41 0x00 0x7C 0x00 0x7C 0x00 0x57 0x00 0x41 0x00 0x41 0x01 0x42 0x00 0x52 0x00 0x5A 0x00 0x59 0x00 0x53 0x00 0x4B 0x00 0x41 0x00 0x7C 0x00 0x32 0x00 0x34 0x00 0x7C 0x00 0x33 0x00 0x30 0x00 0x7C 0x00 0x31 0x00 0x36 0x00 0x35 0x00 0x35 0x00 0x7C 0x00 0x31 0x00 0x36 0x00 0x35 0x00 0x35 0x00 0x7C 0x00 0x32 0x00 0x36 0x00 0x35 0x00 0x35 0x00 0x7C 0x00 0x31 0x00 0x32 0x00 0x30 0x00 0x35 0x00 0x7C 0x00 0x4D 0x00 0x31 0x00 0x7C 0x00 0x65 0x00 0x31 0x00 0x31 0x00 0x2A 0x00 0x32 0x00 0x30 0x00 0x30 0x00 0x31 0x00 0x2F 0x00 0x31 0x00 0x31 0x00 0x36 0x00 0x2A 0x00 0x30 0x00 0x31 0x00 0x38 0x00 0x30 0x00 0x2A 0x00 0x30 0x00 0x34 0x00 0x7C 0x00 0x32 0x00 0x7C 0x00 0x31 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x7C 0x00 0x34 0x00 0x35 0x00 0x30 0x00 0x7C 0x00 0x2D 0x00 0x2D 0x00 0x2D 0x00 0x7C 0x00 0x31 0x00 0x33 0x00 0x39 0x00 0x38 0x00 0x2C 0x00 0x30 0x00 0x30 0x00 0x7C 0x00 0x37 0x00 0x31 0x00 0x2C 0x00 0x30 0x00 0x30 0x00 0x7C 0x00 0x50 0x00 0x20 0x00 0x7C 0x00 0x32 0x00 0x30 0x00 0x30 0x00 0x35 0x00 0x2D 0x00 0x30 0x00 0x37 0x00 0x2D 0x00 0x30 0x00 0x31 0x00 0x7C 0x00 0x35 0x00 0x7C 0x00 0x2D 0x00 0x2D 0x00 0x2D 0x00 0x7C 0x00 0x53 0x00 0x41 0x00 0x4D 0x00 0x4F 0x00 0x43 0x00 0x48 0x00 0xD3 0x00 0x44 0x00 0x20 0x00 0x4F 0x00 0x53 0x00 0x4F 0x00 0x42 0x00 0x4F 0x00 0x57 0x00 0x59 0x00 0x7C 0x00 0x2D 0x00 0x2D 0x00 0x2D 0x00 0x7C 0x00 0x32 0x00 0x30 0x00 0x30 0x00 0x35 0x00 0x7C 0x00 0x2D 0x00 0x2D 0x00 0x2D 0x00 0x7C 0x00 0x38 0x00 0x2C 0x00 0x38 0x00 0x32 0x00 0x7C 0x00 0x41 0x00 0x41 0x00 0x41 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x7C 0x00 0x30 0x00 0x32 0x00 0x36 0x00 0x35 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x38 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x31 0x00 0x35 0x00 0x38 0x00 0x7C 0x00 0x30 0x00 0x33 0x00 0x7C 0x00 0x30 0x00 0x32 0x00 0x7C 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x7C 0x00 0x32 0x00 0x30 0x00 0x30 0x00 0x30 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x4E 0x00 0x7C 0x00 0x30 0x00 0x30 0x00 0x39 0x00 0x30 0x00 0x30 0x00 0x32 0x00 0x30 0x00 0x30 0x00 0x31 0x00 0x7C 0x00

Dawid Farbaniec


Tagi:  reverse-engineering  visual-cpp  c-sharp 

Komentarze czytających

Czytelnik napisał:
Rewelacja, super robota. Mam pytanie, jak wykonać to w drugą stronę tzn. aby otrzymać kod Aztec obrazkowy z posiadanych danych.
 rok temu (13 lutego 2019 godz. 18:55)
Dawid (haker.info) napisał:
Ja próbowałbym wykonać poszczególne kroki dekodowania w odwrotnej kolejności :)

1. Zapisać dane tekstem w kodowaniu UTF-16LE oddzielone znakiem pionowej linii | (0x7C) w odpowiedniej kolejności
2. Skompresować algorytmem NRV2E (wariant 8-bitowy)
3. Zakodować za pomocą algorytmu Base64
4. No i o ten ostatni krok pewnie jest pytanie. Trzeba znaleźć coś co wygeneruje Aztec 2D. Nie pomogę w wyborze, bo nigdy takich bibliotek nie szukałem. Ale powinno być coś takiego, gdyż ten cały Aztec 2D to dość popularny barcode.

Dla wielu programistów problemem było to, że do niedawna algorytm nie był jawny dla wszystkich. Nie było wiadomo, że to kompresja NRV2E w wariancie 8-bitowym. Teraz wiadomo czego należy użyć i pozostaje tylko odpowiednie zaprogramowanie tego.

Pozdrawiam!
 rok temu (13 lutego 2019 godz. 20:02)
Czytelnik napisał:
No właśnie z kompresją algorytmem NRV2E mam problem.
 rok temu (13 lutego 2019 godz. 20:24)
Dawid (haker.info) napisał:
Włączyłem wyszukiwanie w źródłach biblioteki UCL i oprócz dekompresji są tam też funkcje kompresujące np. ucl_nrv2e_99_compress (nazwa pliku n2_99.ch. Trzeba eksperymentować. Ja bibliotekę UCL używałem tylko na potrzeby tego artykułu. I to tylko funkcję dekompresującą.
 rok temu (13 lutego 2019 godz. 21:03)
Krzysiek napisał:
Wszystko super opisane. Mam pytanie dotyczące implementacji w C++.
Jak rozwiązać problem polskich znaków.
Zamiast litery Ł otrzymuje dwuznak A? (ten znak zapytania jest w takim jakby kwadracie).
Czy mógłbyś mi napisać w jaki sposób wyświetlić te polskie znaki?
 rok temu (26 lipca 2019 godz. 13:42)
Dawid (haker.info) napisał:
Myślę, że w napisie (zmiennej) znaki są poprawne, a jedynie konsola tekstowa Windows je źle wyświetla.
Dodaj na początku funkcji głównej main wywołanie: setlocale(LC_ALL, "Polish");
Powinno to wyglądać:
//(...)
int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "Polish");
    //(...)
Dziękuję za zainteresowanie artykułem :)
 rok temu (26 lipca 2019 godz. 23:05)
Krzysiek napisał:
Po dodaniu setlocale(LC_ALL, "Polish"); litera Ó wyświetla się poprawnie ale litera Ł w dalszym ciągu wyświetla się błędnie (nawet przy zapisie do pliku).
Czy dysponujesz może jakimś opisem algorytmu NRV2E?
 rok temu (27 lipca 2019 godz. 13:15)
Dawid (haker.info) napisał:
Uruchomiłem teraz jeszcze raz kod źródłowy w C# i tam polskie znaki diakrytyczne wyświetlają się poprawnie. Coś widocznie jest nie tak z kodowaniem znaków w przykładzie dla C++. Nie znalazłem dokumentów, które by opisywały algorytm NRV2E. Przeglądałem tylko jego oryginalną implementację jako kod z biblioteki UCL.
 rok temu (28 lipca 2019 godz. 01:20)
Jacek napisał:
"Aby zeskanować kod Aztec 2D z polskiego dowodu rejestracyjnego pojazdu można użyć gotowych skanerów kodów Aztec/2D. Po zeskanowaniu otrzymamy ciąg znaków."

znasz jakąś bibliotekę która pozwala np w xamarin zeskanować taki kod ? Oczywiście nie pytam o bibliotekę od peloka.
 rok temu (18 września 2019 godz. 14:53)
Dawid (haker.info) napisał:
Jest dużo profesjonalnych bibliotek, ale płatnych. Warto pomyśleć czy nie kupić profesjonalnego SDK, który odczyta kod obrazkowy (zaoszczędzi to czas na własne rozpoznawanie obrazów), a resztą aplikacji zająć się samodzielnie skoro algorytm jest już jawny.
 rok temu (19 września 2019 godz. 19:48)
Klausvonbrown napisał:
Hej! Czy masz może w zanadrzu wersje dekodera dla php lub javascript aby dekodować to na stronie www (html) ? Probowałem przekształcić kod dex4er z github'a, ale nie najlepiej mi to wychodzi :(
 ponad 11 miesięcy temu (03 listopada 2019 godz. 12:17)
Dawid (haker.info) napisał:
Będę miał na uwadze rozbudowanie tego artykułu o kolejne implementacje dla różnych języków programowania. Jednak nie mogę obiecać kiedy dokładnie.

Na ten czas można stworzyć witrynę w ASP.NET MVC i użyć implementacji w C#, która wykona się po stronie serwera.

Natomiast dla JavaScript to może Bridge.NET (https://bridge.net) — nie używałem, ale zgodnie z opisem na stronie projektu:
Open Source C# to JavaScript Compiler and Frameworks.
Run your C# Apps on the web.
 ponad 11 miesięcy temu (04 listopada 2019 godz. 14:17)
Łukasz :) napisał:
Też chętnie przygarnąłbym dekoder AZTEC dla PHP
 ponad 10 miesięcy temu (18 grudnia 2019 godz. 12:21)
jas napisał:
Super sprawa z tym algorytmem. Zupełnie nie rozumiem dlaczego było to okryte taką tajemnicą skoro zawiera jawne informacje wydrukowane w dowodzie. Gratuluję rozwiązania problemu i ułatwienia życia innym :)
 ponad miesiąc temu (03 września 2020 godz. 18:41)
Fisper napisał:
Cześć Dawid, dobra robota!
Obecnie staram się "przetłumaczyć" to co masz na Pythona z pomocą:
https://gist.github.com/herrcore/b935bd64d0af4e5761ba39c29a16b3f0
Możesz pokazać jak wygląda nasz string po każdym z kroków?
 przedwczoraj o 12:42 (19 października 2020 godz. 12:42)
Dawid (haker.info) napisał:
Niestety nie znam języka Python. Jeśli będą chętni, to mogę opisać krok po kroku jak postawić WebAPI w C# na hostingu i uzyskiwać sobie dostęp do dekodowania z dowolnego języka programowania.

Zauważyłem jednak pewien szczegół. W podanym linku (UCL NRV2B Decompression Library - Full Python) jest odmiana funkcji o nazwie NRV2B (NRV2B), natomiast ja stosowałem NRV2E (NRV2E).
 przedwczoraj o 13:51 (19 października 2020 godz. 13:51)



Wszystkie treści umieszczone na tej witrynie są chronione prawem autorskim. Surowo zabronione jest kopiowanie i rozpowszechnianie zawartości tej witryny bez zgody autora. Wszelkie opublikowane tutaj treści (w tym kody źródłowe i inne) służą wyłącznie celom informacyjnym oraz edukacyjnym. Właściciele tej witryny nie ponoszą odpowiedzialności za ewentualne niezgodne z prawem wykorzystanie zasobów dostępnych w witrynie. Użytkownik tej witryny oświadcza, że z zamieszczonych tutaj danych korzysta na własną odpowiedzialność. Wszelkie znaki towarowe i nazwy zastrzeżone zostały użyte jedynie w celach informacyjnych i należą wyłącznie do ich prawnych właścicieli. Korzystając z zasobów witryny haker.info oświadczasz, że akceptujesz powyższe warunki oraz politykę prywatności.