diff --git a/content/posts/2024/05/studying-a-communication-protocol/featured-background.jpg b/content/posts/2024/05/studying-a-communication-protocol/featured-background.jpg new file mode 100644 index 0000000..8d5a4ef Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/featured-background.jpg differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/1.jpg b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/1.jpg new file mode 100644 index 0000000..495040b Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/1.jpg differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/2.jpg b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/2.jpg new file mode 100644 index 0000000..ac513b1 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/2.jpg differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/3.jpg b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/3.jpg new file mode 100644 index 0000000..68ba6b2 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/01-client-setup/3.jpg differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/02-wireshark-working.png b/content/posts/2024/05/studying-a-communication-protocol/images/02-wireshark-working.png new file mode 100644 index 0000000..b71aa60 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/02-wireshark-working.png differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/03-wireshark-with-filter.png b/content/posts/2024/05/studying-a-communication-protocol/images/03-wireshark-with-filter.png new file mode 100644 index 0000000..dc2e158 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/03-wireshark-with-filter.png differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/04-test-name.png b/content/posts/2024/05/studying-a-communication-protocol/images/04-test-name.png new file mode 100644 index 0000000..c3373f1 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/04-test-name.png differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/images/05-wireshark-tcp-stream.png b/content/posts/2024/05/studying-a-communication-protocol/images/05-wireshark-tcp-stream.png new file mode 100644 index 0000000..bf3ff39 Binary files /dev/null and b/content/posts/2024/05/studying-a-communication-protocol/images/05-wireshark-tcp-stream.png differ diff --git a/content/posts/2024/05/studying-a-communication-protocol/index.it.md b/content/posts/2024/05/studying-a-communication-protocol/index.it.md new file mode 100644 index 0000000..51d2e3a --- /dev/null +++ b/content/posts/2024/05/studying-a-communication-protocol/index.it.md @@ -0,0 +1,358 @@ ++++ +title = "Studiare un protocollo di comunicazione" +summary = "Step 2: usare uno squalo per sniffare pacchetti" +date = "2024-05-01" + +tags = ["Reverse Engineering", "Lettore di Presenze", "TCP", "Sniffing", "Wireshark"] +categories = ["Progetti"] +series = ["Lettore di presenze"] +series_order = 2 ++++ + +Nell'articolo precedente abbiamo iniziato a studiare come funziona il client +del lettore di presenze, andando anche a decompilarne l'eseguibile. In questo +articolo vorrei cercare di comprendere il protocollo di comunicazione che il +client usa per interfacciarsi col lettore. + +I motivi per cui non ho fatto subito il reverse engineering del protocollo sono +sostanzialmente due: + +1. Se fossi riuscito a decompilare correttamente il codice dell'eseguibile + avrei potuto creare un client alternativo con molta più semplicità; + +2. A volte non si può eseguire (almeno in modo semplice) lo *sniffing* di una + comunicazione per via del + [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security). + +Purtroppo però la decompilazione dei DLL non è affatto facile perchè: + +> Non esiste un pulsante magico per tornare indietro, ne esiste uno per +> generare codice C di merda con nomi di variabili a caso, ma non è un bel +> bottone +> +> Traduzione di **fasterthanlime** nel video [How does the detour crate +> work?](https://youtube.com/watch?v=aLeMCUXFJwY&t=174s) + +Nel caso foste interessati, l'NSA ha sviluppato il suo decompilatore chiamato +[Ghidra](https://ghidra-sre.org/); vi consiglio di darci un occhiata. + +## Configurazione del client + +Nello scorso articolo abbiamo solo installato il client per Windows, ma non lo +abbiamo mai aperto. + +Dato che per intercettare la comunicazione dobbiamo avere un client che vada +effettivamente ad interrogare il lettore, riapro la mia macchina virtuale con +[Windows 10 AME](https://archive.org/details/windows10-ame-21h1-2021-08-09/) e +finisco la configurazione del client: + +{{< carousel images="images/01-client-setup/*" aspectRatio="16-9" +interval="1000" >}} + +Una volta finita la configurazione (e aver modificato qualche file di +configurazione a mano perchè il client comunque non vedeva il lettore nella +rete) abbiamo la possibilità di poter richiedere le presenze tramite la rete. + +Una volta aperto il client **come amministratore**, aver premuto il tasto per +scaricare i dati e aver aspettato **due lunghi minuti**, la bellezza di 3543 +presenze compaiono sullo schermo. + +Qualcosa mi puzza: perchè impiega due minuti a trasferire l'equivalente di un +file che pesa poco meno di 200kiB? + +Facendo un secondo due calcoli: + +{{< katex >}} +$$ +\frac{3543\ \textrm{righe}}{120\ \textrm{secondi}} \ \cdot\sim460\ \textrm{bit +per riga} = 13.26\ kib/s +$$ + +13kibps di throughput utile su una connessione da 100Mbps? ***Che schifo!*** + +Non voglio sapere quale disastro di programmazione aziendale italiana può aver +causato questo, ma ho come l'impressione che sto per scoprirlo... + +## *The quieter you become...* + +Come analizzatore di reti andrò ad installare ed utilizzare +[Wireshark](https://wireshark.org), uno strumento molto popolare per questo +tipo di operazioni. + +Una volta installato ed aver aggiunto il nostro utente al gruppo `wireshark` +possiamo avviarlo ed incominciare a *sniffare* tutti i pacchetti sulla nostra +interfaccia di rete. + +![Wireshark in funzionamento](images/02-wireshark-working.png "Ecco Wireshark +mentre ascolta tutti i pacchetti che circolano sulla mia rete") + +Se è la prima volta che usate uno strumento del genere, potrete accorgervi che +anche in una rete locale di piccole dimensioni circolano davvero tanti +pacchetti, troppi per essere analizzati uno ad uno. + +È proprio qui che entrano in nostro soccorso i filtri: se digitiamo nella barra +dei filtri la seguente stringa: + +``` +ip.addr == +``` + +Vedremmo solo i pacchetti che provengono *da* o sono diretti *verso* +l'indirizzo IP che abbiamo specificato. Possiamo anche decidere di filtrare il +traffico che passa attraverso una specifica porta TCP con: + +``` +ip.addr == && tcp.port == +``` + +Il mondo dei filtri in Wireshark è molto vasto, lascio il [link alla +documentazione ufficiale](https://wiki.wireshark.org/DisplayFilters) per gli +interessati. + +Una volta fatta partire la registrazione con i filtri corretti possiamo far +ripartire un'altra scansione completa delle presenze sul client ufficiale e +dovremmo vedere tutti i pacchetti che si scambiano client e dispositivo in +tempo reale. + +![Wireshark con il filtro per IP](images/03-wireshark-with-filter.png "I +pacchetti scambiati fra il client ed il dispositivo") + +Alla fine del processo abbiamo registrato la bellezza di 14423 pacchetti, che +trasportano 3543 presenze. *Il tutto diventa ancora più strano...* + +Dando un'occhiata veloce al traffico possiamo intuire un po' di cose: + +1. Nel layer di trasporto viene usato il protocollo TCP sulla porta `5005` +2. Non viene utilizzato + [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) per + crittografare i dati, *phew* +3. Ci sono almeno tre fasi: + * Una prima fase di inizializzazione; + * Una seconda fase di scambio di dati nella quale vengono inviati pochi + pacchetti ma molto grandi; + * Una terza fase, nella quale vengono inviati moltissimi pacchetti di + piccole dimensioni, dove si può intravedere di tanto in tanto il nome dei + dipendenti in formato ASCII. + +![Utente "test" nella finestrella ASCII](images/04-test-name.png "Ecco che +compare un nome familiare nella finestrella ASCII, in basso a destra") + +Per studiare più approfonditamente il protocollo avremmo bisogno del solo +contenuto dei pacchetti TCP. Qui ci aiuta Wireshark con una funzionalità molto +utile. + +Se infatti andiamo a selezionare un pacchetto della comunicazione TCP a cui +siamo interessati e premiamo il tasto destro, selezionando `Follow` > `TCP +Stream`, Wireshark aprirà in automatico il payload di tutti i pacchetti e ci +mostrerà solo il traffico di livello 7. + +Se andiamo a visualizzare i dati come `Raw`, Wireshark ci mostrerà i dati +scambiati in formato esadecimale, mostrando in rosso i messaggi inviati dal +client ed in blu le risposte date dal lettore di presenze. + +Adesso possiamo copiare le richieste nel nostro text editor di fiducia ed +incominciare a studiare il protocollo. + +![Lo stream TCP mostrato da Wireshark](images/05-wireshark-tcp-stream.png "Ecco +come appare lo scambio di messaggi quando si vanno ad aprire solo i pacchetti +TCP") + +## *Fuck around and find out* + +Ora non rimane che comprendere il protocollo di comunicazione che, +sfortunatamente, non è in un formato testuale come l'ASCII o l'UTF-8. + +Può sembrare complesso, ma mi ci è voluto solo un pomeriggio per trovare una +soluzione abbastanza completa per quello che devo fare. + +### Richieste + +Le richieste inviate dal client sono tutte lunghe 16 byte e hanno questa +struttura: + +```rexeg +^55aa([0-9a-f]{24})([0-9a-f]{4})$ +``` + +* I primi due byte sono sempre `55 aa` (`01010101 10101010` in binario); +* I 12 byte successivi specificano il comando del client, che d'ora in poi + chiamerò "payload"; +* Infine, ci sono due byte **little-endian** che specificano il numero del + pacchetto, iniziando da `00 00`. + +Ho notato anche che il server non verifica che gli ultimi due byte siano +inviati in modo sequenziale, quindi possono rimanere a `00 00` per tutto lo +scambio di messaggi. + +### Risposte + +Le risposte del server invece non hanno una lunghezza fissa e sono divise in +due parti, che d'ora in poi andrò a chiamare "header" ed "payload". L'header è +sempre presente ed è lungo 10 byte, mentre il payload può anche essere assente. + +Quando il payload è assente il messaggio si comporta come una specie di +`null`/`ACK`. + +```regex +^aa55([0-9a-f]{16})(?:55aa([0-9a-f]+))?$ +``` + +* I primi due byte sono sempre `aa 55` (`10101010 01010101` in binario); +* Gli 8 byte successivi sono l'header. Di solito sono `01 01 00 00 00 00 00 + 00`, ma possono cambiare; +* Se è presente un payload, il messaggio continua con `55 aa` (`01010101 + 10101010` in binario); +* I byte rimanenti rappresentano il payload. + +--- + +### Ping + +Se vogliamo eseguire un "ping" e verificare che il server risponda possiamo +inviare una richiesta col payload impostato a `01 80 00 00 00 00 00 00 00 00 00 +00`: + +``` +55aa0180000000000000000000000100 +aa550101000000000000 +``` + +Il server risponderà poi con un pacchetto senza payload con l'header impostato +a `01 01 00 00 00 00 00 00`. + +### Nome del dipendente + +Sapendo l'ID di un dipendente, è possibile richiedere al server il suo nome +tramite una richiesta con payload impostato a `01 c7 xx xx xx xx 00 00 00 00 14 +00`, dove `xx xx xx xx` è un intero a 32 bit **little-endian** che rappresenta +l'ID del dipendente. + +``` +55aa01c7xxxxxxxx0000000014000100 +aa55010100000000000055aaxxxxxxxxxxxxxxxxxxxx4c0000000000595a7c7c0000 +``` + +I primi 10 bit del payload sono il nome del dipendente, in caso il nome sia +più corto di 10 caratteri lo spazio rimanente sarà riempito con dei caratteri +terminatori `\0`. + +Questi messaggi compongono quasi la totailtà della terza fase che ho descritto +nel capitolo precedente, quella nella quale ci sono tanti piccoli messaggi. +Questo fa intuire che il client che prima di tutto fa il dump delle presenze in +modo quasi istantaneo , poi aspetta due minuti scaricando **per ogni presenza +rilevata** il nome del dipendente, anche se questo è già stato richiesto in +precedenza. Qualcuno insegni il concetto di +[memoizzazione](https://it.wikipedia.org/wiki/Memoizzazione) a questi +informatici... + +### Numero totale di presenze + +Per chiedere quante presenze sono registrate sul dispositivo bisogna effettuare +una richiesta con payload `01 b4 08 00 00 00 00 00 ff ff 00 00`: + +``` +55aa01b4080000000000ffff00000100 +aa550101xxxx00000000 +``` + +Dove `xx xx` sarà il numero delle presenze salvate rappesentato in un intero a +16 bit **little-endian**. + +65535 richieste massime sembrano un po' troppo poche, ma immagino che sarà un +problema del me del futuro. + +### Scaricamento di tutte le presenze + +La lista di tutte le presenze va scaricata a blocchi, continuando a chiedere al +server dei blocchi da 1024 byte (ovvero 85,333 presenze alla volta) finchè non +viene estratto il tutto. + +Per fare ciò dobbiamo prima di tutto richiedere il numero totale delle +presenze, poi dobbiamo inviare una richiesta con payload `01 a4 00 00 00 00 xx +xx 00 00 00 04`, dove `xx xx` è il numero delle presenze totali +**little-endian**. + +``` +55aa01a400000000xxxx000000040100 +aa55010100000000000055aa ... +``` + +Il server ci risponderà con un payload da 1026 byte, contenente le prime +registrazioni seguite da due byte a zero. + +Possiamo richiedere un altro blocco da 1026 byte inviando una richiesta con +payload `01 a4 00 00 00 00 00 00 xx xx 00 04`, dove `xx xx` è un intero +**little-endian** che parte da `01 00`: + +``` +55aa01a4000000000000010000040100 +aa55010100000000000055aa ... +``` + +Una volta finite le registrazioni il server incomincerà ad inviare dei byte di +padding impostati a `ff` per arrivare a 1026 byte di payload. + +### Struttura delle presenze + +Una volta ottenuti tutti i blocchi di registrazioni, possiamo andarli a +scomporre in singole registrazioni da 12 byte ciascuno. Non sono riuscito a +comprendere per cosa stessero tutti i singoli byte, ma quelli importanti sono +questi: + +```regex +..([26ae]).{5}([0-9a-f]{8})([0-9a-f]{8}) +``` + +* Del secondo byte ci interessano i due bit più significativi per capire se la + registrazione rappresenta un'entrata o un'uscita: + * Se sono `00` allora la registrazione è la prima entrata; + * Se sono `01` allora la registrazione è la prima uscita; + * Se sono `10` allora la registrazione è la seconda entrata; + * Se sono `11` allora la registrazione è la seconda uscita; +* I penultimi quattro byte rappresentano l'ID del dipendende (in + little-endian); +* Gli ultimi 4 byte rappresentano la data e l'ora della presenza (in + little-endian). + +All'inizio pensavo che la data fosse rappresentata come una UNIX Epoch, invece +a quanto pare ha questo formato (quando rappresentato come big-endian): + +* I primi 6 bit rappresentano i minuti; +* I 5 bit successivi rappresentano le ore; +* I 5 bit successivi rappresentano i giorni; +* I 4 bit successivi rappresentano i mesi; +* Infine, gli ultimi 12 bit rappresentano gli anni. + +--- + +Sospetto inoltre che nei primi quattro byte di ogni presenza siano presenti +anche: + +* I secondi; +* La modalità di registrazione (se con il PIN, con l'impronta o col badge); +* L'ID del registratore. + +Ma dato che non sono campi molto importanti ho deciso che per il momento li +lascerò perdere. + +## Provare col terminale + +Se volessimo provare la comunicazione senza dover scrivere alcun programma che +invia byte su un socket TCP, possiamo utilizzare un po' di utils di sistema +come `netcat` e `xxd`: + +```shell +# Se usate Bash o Zsh +function send_bytes { echo -n "$3" | xxd -r -p | timeout 1 nc "$1" "$2" | xxd; } + +# Oppure, se state usate Fish +function send_bytes -a ip porta dati + echo -n "$dati" | xxd -r -p | timeout 1 nc "$ip" "$porta" | xxd +end + +send_bytes 127.0.0.1 5005 55aa0180000000000000000000000100 +``` + +Provando qualche comando preso dagli esempi sopra posso confermare che tutto +sembra funzionare correttamente. Nel prossimo articolo vedremo come creare una +piccola libreria in Rust per ricavare i dati dal lettore. diff --git a/content/posts/2024/05/studying-a-communication-protocol/index.md b/content/posts/2024/05/studying-a-communication-protocol/index.md new file mode 100644 index 0000000..d1fa503 --- /dev/null +++ b/content/posts/2024/05/studying-a-communication-protocol/index.md @@ -0,0 +1,338 @@ ++++ +title = "Studying a communication protocol" +summary = "Step 2: Using a shark to sniff packets" +date = "2024-05-01" + +tags = ["Reverse Engineering", "Attendance Reader", "TCP", "Sniffing", "Wireshark"] +categories = ["Projects"] +series = ["Attendance Reader"] +series_order = 2 ++++ + +In the previous article, we started studying how the attendance reader client +works, we even attempted to decompile its executable. In this article, I'd like +to explore the communication protocol that the client uses to talk to the +reader. + +There are basically two reasons why I didn't immediately reverse-engineer the +protocol: + +1. If I could decompile the executable code, I could create an alternative + client much more easily; +2. Sometimes it's not possible (not easily, at least) to *sniff* a + communication 'cause of + [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security). + +However, decompiling DLLs is far from easy because: + +> There's no magic "go back" button, there's a "generate shitty C code with +> random-ass variable names" button, but that's not a very good button +> +> **fasterthanlime** in the [How does the detour crate +> work?](https://www.youtube.com/watch?v=aLeMCUXFJwY&t=174s) video + +If you're interested, the NSA has developed its own decompiler called +[Ghidra](https://ghidra-sre.org/), check it out. + +## Client configuration + +In the last article, we only installed the client for Windows but never opened +it. + +Since we need a client that can actually interact with the reader to intercept +the communication, I reopened my VM with [Windows 10 +AME](https://archive.org/details/windows10-ame-21h1-2021-08-09/) and finished +configuring the client: + +{{< carousel images="images/01-client-setup/*" aspectRatio="16-9" +interval="1000" >}} + +Once the configuration is completed (and after manually modifying some +configuration files because the client still couldn't see the reader on the +network), we can request the reader's data over the network. + +After opening the client **as an administrator**, pressing the button to +download data, and waiting **two minutes**, a total of 3543 attendances +appeared on the screen. + +Something's odd: why does it take two minutes to transfer the equivalent of a +file weighing just under 200 kiB? + +Doing some quick math: + +{{< katex >}} +$$ +\frac{3543\ \textrm{lines}}{120\ \textrm{seconds}} \ \cdot\sim460\ \textrm{bit +per row} = 13.26\ kib/s +$$ + +13 kibps of useful throughput on a 100 Mbps connection? ***This sucks!*** + +I don't want to know what disaster of italian corporate coding could have +caused this, but I have a feeling I’m about to find out... + +## *The quieter you become...* + +To analyze the network, I will use [Wireshark](https://wireshark.org), a very +popular tool for this type of operations. + +After installing it and adding our user to the `wireshark` group, we can run it +and begin to *sniff* all packets on our network interface. + +![Wireshark in operation](images/02-wireshark-working.png "Here's Wireshark +listening to all the packets circulating on my network.") + +If this is your first time using a tool like this, you might notice that even +in a small Local Area Network there are a lot of packets flying around — too +many to analyze individually. + +This is where filters come and save the day. If we type the following string +into the filter bar: + +``` +ip.addr == +``` + +We will see only packets that come *from* or are directed *to* the specified IP +address. We can also filter traffic that passes through a specific TCP port +with: + +``` +ip.addr == && tcp.port == +``` + +Filters in Wireshark are a vast argument; here's a [link to the official +documentation](https://wiki.wireshark.org/DisplayFilters) for those interested. + +Once we start recording with the correct filters, we can start another full +scan of attendances on the official client, and we should see the packet +exchange between the client and the device in real-time. + +![Wireshark with the IP filter](images/03-wireshark-with-filter.png "The +packets exchanged between the client and the device.") + +At the end of the process, we've recorded an astonishing 14,423 packets, +carrying 3,543 attendances. *Things just get stranger...* + +By taking a quick look at the traffic, we can deduce a few things: + +1. The transport layer uses the TCP protocol on port `5005`; +2. [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) is not used, + *phew*; +3. There are at least three phases: + * An initial setup phase; + * A second phase in which data is exchanged with a few but large packets; + * A third phase with many but small packets, where you can occasionally + observe employee names in ASCII. + +!["test" user in the ASCII box](images/04-test-name.png "A familiar name +appears in the ASCII box at the bottom right.") + +To study the protocol in more depth, we'd need only the content of the TCP +packets. This is where Wireshar comes in handy. + +If we select a packet from the TCP communication we're interested in and +right-click, selecting `Follow` > `TCP Stream`, Wireshark will automatically +open the payload of all packets and show only the level-7 traffic. + +If we view the data as `Raw`, Wireshark will display the exchanged data in +hexadecimal format, with messages sent by the client in red and responses from +the attendance reader in blue. + +Now we can copy the payloads into our preferred text editor and start to study +the protocol. + +![The TCP stream shown by Wireshark](images/05-wireshark-tcp-stream.png "This +is what the message exchange looks like when we open the TCP packets.") + +## Fuck around and find out + +Now we just need to understand the communication protocol, which, +unfortunately, isn't in a text-based format like ASCII or UTF-8. + +It may seem complex, but it only took me an afternoon to find a comprehensive +enough solution for what I need to do. + +### Requests + +Client requests are all 16 bytes long and have this structure: + +```regex +^55aa([0-9a-f]{24})([0-9a-f]{4})$ +``` + +* The first two bytes are always `55 aa` (`01010101 10101010` in binary); +* The next 12 bytes specify the client command. I will call them "payload" from + now on; +* Finally, there are two **little-endian** bytes indicating the packet number, + starting from `00 00`. + +I noticed that the server doesn't check if the last two bytes are sent +sequentially, so they can remain at `00 00` throughout the message exchange. + +### Responses + +Server responses do not have a fixed length and are divided into two parts, +which I will call "header" and "payload." The header is always present and is +10 bytes long, while the payload can be absent. + +When there's no payload, the message acts like a kind of `null`/`ACK`. + +```regex +^aa55([0-9a-f]{16})(?:55aa([0-9a-f]+))?$ +``` + +* The first two bytes are always `aa 55` (`10101010 01010101` in binary); +* The following eight bytes are the header. Usually, they are `01 01 00 00 00 + 00 00 00`, but they can change; +* If a payload is present, the message continues with `55 aa` (`01010101 + 10101010` in binary); +* The remaining bytes are the payload. + +--- + +### Ping + +If we want to perform a "ping" and check if the server responds, we can send a +request with the payload set to `01 80 00 00 00 00 00 00 00 00 00 00`: + +``` +55aa0180000000000000000000000100 +aa550101000000000000 +``` + +The server will then respond with a packet without a payload and the header set +to `01 01 00 00 00 00 00 00`. + +### Employee uame + +Knowing the ID of an employee, it's possible to ask the server for their name +by sending a request with a payload set to `01 c7 xx xx xx xx 00 00 00 00 14 +00`, where `xx xx xx xx` is a 32-bit **little-endian** integer representing the +employee ID. + +``` +55aa01c7xxxxxxxx0000000014000100 +aa55010100000000000055aaxxxxxxxxxxxxxxxxxxxx4c0000000000595a7c7c0000 +``` + +The first 10 bits of the payload contain the employee's name; if it's shorter +than 10 characters, the remaining space will be filled with null terminators +(`\0`). + +These messages comprise almost the entirety of the third phase I described in +the last chapter, the one with many but small messages. This suggests that the +client quickly dumps the attendance data, then spends two whole minutes +downloading the employee's name **for each attendance**, even if it's been +requested before. Someone should teach these developers the concept of +[memoization](https://en.wikipedia.org/wiki/Memoization)... + +### Total number of records + +To ask for the total number of attendances registered on the device, you need +to send a request with a payload of `01 b4 08 00 00 00 00 00 ff ff 00 00`: + +``` +55aa01b4080000000000ffff00000100 +aa550101xxxx00000000 +``` + +Where `xx xx` is the number of saved attendances represented as a 16-bit +**little-endian** integer. + +65535 maximum requests seem a bit too few, but I guess it's a future-me +problem. + +### Downloading all records + +The list of all attendances must be downloaded in blocks, continuing to request +1024-byte blocks from the server (approximately 85.333 attendances at a time) +until the entire list is extracted. + +To do this, we first have to request the total number of attendances, then send +a request with a payload of `01 a4 00 00 00 00 xx xx 00 00 00 04`, where `xx +xx` is the total number of attendances in **little-endian**. + +``` +55aa01a400000000xxxx000000040100 +aa55010100000000000055aa ... +``` + +The server will respond with a 1026-byte payload, containing the initial +records followed by two zero bytes. + +We can request another 1026-byte block by sending a request with a payload of +`01 a4 00 00 00 00 00 00 xx xx 00 04`, where `xx xx` is a **little-endian** +integer starting from `01 00`: + +``` +55aa01a4000000000000010000040100 +aa55010100000000000055aa ... +``` + +Once the records are finished, the server will start sending padding bytes set +to `ff` to reach 1026-byte. + +### Record structure + +Once we have all the registration blocks, we can break them down into +individual registrations, each one 12 bytes long. I wasn't able to +understand what all the bytes represent, but the important ones are: + +```regex +..([26ae]).{5}([0-9a-f]{8})([0-9a-f]{8}) +``` + +* The second byte's two most significant bits indicate if the registration + represents an entry or an exit: + * If it's `00`, it's the first entry; + * If it's `01`, it's the first exit; + * If it's `10`, it's the second entry; + * If it's `11`, it's the second exit; +* The second-to-last four bytes represent the employee ID (in + **little-endian**); +* The last four bytes represent the date and time of the attendance (in + **little-endian**). + +Initially I thought the date was represented as a UNIX Epoch, but it seems to +have this format when shown as big-endian: + +* The first 6 bits represent the minutes; +* The next 5 bits represent the hours; +* The next 5 bits represent the days; +* The next 4 bits represent the months; +* The last 12 bits represent the years. + +--- + +I suspect that the first four bytes of each attendance may contain: + +* The seconds; +* The recording method (if the employee checked-in with the PIN, fingerprint, + or the badge); +* The recorder ID. + +But since these aren't very important fields, I've decided to ignore them for +now. + +## Testing using the terminal + +If you want to test communication without writing any program that sends bytes +over a TCP socket, you can use some basic core utilities like `netcat` and +`xxd`: + +```shell +# If you're using Bash or Zsh +function send_bytes { echo -n "$3" | xxd -r -p | timeout 1 nc "$1" "$2" | xxd; } + +# If you're using Fish +function send_bytes -a ip port data + echo -n "$data" | xxd -r -p | timeout 1 nc "$ip" "$port" | xxd +end + +send_bytes 127.0.0.1 5005 55aa0180000000000000000000000100 +``` + +Trying some requests from the examples above, I can confirm everything seems to +work correctly. In the next article, we'll see how to create a small Rust +library to extract data from the reader.