2016-07-29 17:04:12 +08:00
//-----------------------------------------------------------------------------
2022-01-07 08:58:03 +08:00
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
2016-07-29 17:04:12 +08:00
//
2022-01-07 08:58:03 +08:00
// 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.
//
// See LICENSE.txt for the text of the license.
2016-07-29 17:04:12 +08:00
//-----------------------------------------------------------------------------
2017-07-15 03:22:03 +08:00
// Low frequency Jablotron tag commands
2017-07-30 15:17:48 +08:00
// Differential Biphase, RF/64, 64 bits long (complete)
2016-07-29 17:04:12 +08:00
//-----------------------------------------------------------------------------
2016-07-31 01:37:15 +08:00
2016-07-29 17:04:12 +08:00
# include "cmdlfjablotron.h"
2019-08-08 22:57:33 +08:00
# include <string.h>
# include <inttypes.h>
# include <stdlib.h>
# include <ctype.h>
# include "cmdparser.h" // command_t
# include "comms.h"
2020-11-29 19:49:09 +08:00
# include "commonutil.h" // ARRAYLEN
2019-08-08 22:57:33 +08:00
# include "ui.h"
# include "cmddata.h"
# include "cmdlf.h"
2020-11-29 19:49:09 +08:00
# include "protocols.h" // for T55xx config register definitions
# include "lfdemod.h" // parityTest
# include "cmdlft55xx.h" // verifywrite
# include "cliparser.h"
# include "cmdlfem4x05.h" // EM defines
2019-08-08 22:57:33 +08:00
2019-11-03 00:59:06 +08:00
# define JABLOTRON_ARR_LEN 64
2016-07-29 17:04:12 +08:00
static int CmdHelp ( const char * Cmd ) ;
2019-03-10 18:20:22 +08:00
static uint8_t jablontron_chksum ( uint8_t * bits ) {
2019-03-10 06:35:06 +08:00
uint8_t chksum = 0 ;
2019-03-10 07:00:59 +08:00
for ( int i = 16 ; i < 56 ; i + = 8 ) {
chksum + = bytebits_to_byte ( bits + i , 8 ) ;
2019-03-10 06:35:06 +08:00
}
chksum ^ = 0x3A ;
return chksum ;
2016-07-29 20:31:39 +08:00
}
2019-03-10 18:20:22 +08:00
static uint64_t getJablontronCardId ( uint64_t rawcode ) {
2019-03-10 06:35:06 +08:00
uint64_t id = 0 ;
2019-03-10 07:00:59 +08:00
uint8_t bytes [ ] = { 0 , 0 , 0 , 0 , 0 } ;
2019-03-10 06:35:06 +08:00
num_to_bytes ( rawcode , 5 , bytes ) ;
2020-05-04 07:18:26 +08:00
for ( int i = 0 ; i < 5 ; i + + ) {
id * = 100 ;
id + = NIBBLE_HIGH ( bytes [ i ] ) * 10 + NIBBLE_LOW ( bytes [ i ] ) ;
2019-03-10 06:35:06 +08:00
}
return id ;
2016-07-30 05:30:40 +08:00
}
2020-09-28 17:50:20 +08:00
int demodJablotron ( bool verbose ) {
( void ) verbose ; // unused so far
2019-03-10 06:35:06 +08:00
//Differential Biphase / di-phase (inverted biphase)
//get binary from ask wave
2020-09-28 17:50:20 +08:00
if ( ASKbiphaseDemod ( 0 , 64 , 1 , 0 , false ) ! = PM3_SUCCESS ) {
2019-03-10 06:35:06 +08:00
if ( g_debugMode ) PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron ASKbiphaseDemod failed " ) ;
2019-05-22 20:57:08 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2021-08-21 22:26:06 +08:00
size_t size = g_DemodBufferLen ;
int ans = detectJablotron ( g_DemodBuffer , & size ) ;
2019-03-10 07:00:59 +08:00
if ( ans < 0 ) {
if ( g_debugMode ) {
2019-03-10 06:35:06 +08:00
if ( ans = = - 1 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron too few bits found " ) ;
else if ( ans = = - 2 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron preamble not found " ) ;
else if ( ans = = - 3 )
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron size not correct: %zu " , size ) ;
2019-03-10 06:35:06 +08:00
else if ( ans = = - 5 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron checksum failed " ) ;
else
PrintAndLogEx ( DEBUG , " DEBUG: Error - Jablotron ans: %d " , ans ) ;
}
2019-05-22 20:57:08 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2021-08-21 22:26:06 +08:00
setDemodBuff ( g_DemodBuffer , JABLOTRON_ARR_LEN , ans ) ;
2019-03-10 07:00:59 +08:00
setClockGrid ( g_DemodClock , g_DemodStartIdx + ( ans * g_DemodClock ) ) ;
2019-03-10 06:35:06 +08:00
//got a good demod
2021-08-21 22:26:06 +08:00
uint32_t raw1 = bytebits_to_byte ( g_DemodBuffer , 32 ) ;
uint32_t raw2 = bytebits_to_byte ( g_DemodBuffer + 32 , 32 ) ;
2019-03-10 06:35:06 +08:00
2019-04-15 23:04:12 +08:00
// bytebits_to_byte - uint32_t
2021-08-21 22:26:06 +08:00
uint64_t rawid = ( ( uint64_t ) ( bytebits_to_byte ( g_DemodBuffer + 16 , 8 ) & 0xff ) < < 32 ) | bytebits_to_byte ( g_DemodBuffer + 24 , 32 ) ;
2019-03-10 06:35:06 +08:00
uint64_t id = getJablontronCardId ( rawid ) ;
2020-06-27 00:33:56 +08:00
PrintAndLogEx ( SUCCESS , " Jablotron - Card: " _GREEN_ ( " % " PRIx64 ) " , Raw: %08X%08X " , id , raw1 , raw2 ) ;
2019-03-10 06:35:06 +08:00
uint8_t chksum = raw2 & 0xFF ;
2021-08-21 22:26:06 +08:00
bool isok = ( chksum = = jablontron_chksum ( g_DemodBuffer ) ) ;
2019-11-03 00:59:06 +08:00
2022-02-25 00:38:01 +08:00
PrintAndLogEx ( DEBUG , " Checksum: %02X ( %s ) " , chksum , isok ? _GREEN_ ( " ok " ) : _RED_ ( " Fail " ) ) ;
2019-03-10 06:35:06 +08:00
id = DEC2BCD ( id ) ;
// Printed format: 1410-nn-nnnn-nnnn
2020-06-26 19:01:17 +08:00
PrintAndLogEx ( SUCCESS , " Printed: " _GREEN_ ( " 1410-%02X-%04X-%04X " ) ,
2019-03-10 07:00:59 +08:00
( uint8_t ) ( id > > 32 ) & 0xFF ,
( uint16_t ) ( id > > 16 ) & 0xFFFF ,
( uint16_t ) id & 0xFFFF
) ;
2019-05-22 20:57:08 +08:00
return PM3_SUCCESS ;
2016-07-29 17:04:12 +08:00
}
2020-09-28 17:50:20 +08:00
//see ASKDemod for what args are accepted
static int CmdJablotronDemod ( const char * Cmd ) {
2020-11-29 19:49:09 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf jablotron demod " ,
" Try to find Jablotron preamble, if found decode / descramble data " ,
" lf jablotron demod \n "
) ;
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
2020-09-28 17:50:20 +08:00
return demodJablotron ( true ) ;
}
2020-11-29 19:49:09 +08:00
static int CmdJablotronReader ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf jablotron reader " ,
" read a jablotron tag " ,
" lf jablotron reader -@ -> continuous reader mode "
) ;
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " @ " , NULL , " optional - continuous reader mode " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
bool cm = arg_get_lit ( ctx , 1 ) ;
CLIParserFree ( ctx ) ;
2020-12-01 18:26:54 +08:00
if ( cm ) {
PrintAndLogEx ( INFO , " Press " _GREEN_ ( " <Enter> " ) " to exit " ) ;
}
2020-11-29 19:49:09 +08:00
do {
lf_read ( false , 16000 ) ;
demodJablotron ( ! cm ) ;
} while ( cm & & ! kbd_enter_pressed ( ) ) ;
return PM3_SUCCESS ;
2016-07-29 17:04:12 +08:00
}
2019-04-12 08:07:11 +08:00
static int CmdJablotronClone ( const char * Cmd ) {
2016-07-29 17:04:12 +08:00
2020-11-29 19:49:09 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf jablotron clone " ,
2021-04-25 16:55:39 +08:00
" clone a Jablotron tag to a T55x7, Q5/T5555 or EM4305/4469 tag. \n "
" Tag must be on the antenna when issuing this command. " ,
2022-04-29 21:45:45 +08:00
" lf jablotron clone --cn 01b669 -> encode for T55x7 tag \n "
" lf jablotron clone --cn 01b669 --q5 -> encode for Q5/T5555 tag \n "
" lf jablotron clone --cn 01b669 --em -> encode for EM4305/4469 "
2020-11-29 19:49:09 +08:00
) ;
void * argtable [ ] = {
arg_param_begin ,
arg_str1 ( NULL , " cn " , " <hex> " , " Jablotron card ID - 5 bytes max " ) ,
arg_lit0 ( NULL , " q5 " , " optional - specify writing to Q5/T5555 tag " ) ,
arg_lit0 ( NULL , " em " , " optional - specify writing to EM4305/4469 tag " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
int raw_len = 0 ;
uint8_t raw [ 5 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 1 , raw , & raw_len ) ;
bool q5 = arg_get_lit ( ctx , 2 ) ;
bool em = arg_get_lit ( ctx , 3 ) ;
CLIParserFree ( ctx ) ;
if ( q5 & & em ) {
PrintAndLogEx ( FAILED , " Can't specify both Q5 and EM4305 at the same time " ) ;
return PM3_EINVARG ;
}
2019-03-10 06:35:06 +08:00
uint32_t blocks [ 3 ] = { T55x7_MODULATION_DIPHASE | T55x7_BITRATE_RF_64 | 2 < < T55x7_MAXBLOCK_SHIFT , 0 , 0 } ;
2020-11-29 19:49:09 +08:00
char cardtype [ 16 ] = { " T55x7 " } ;
// Q5
if ( q5 ) {
blocks [ 0 ] = T5555_FIXED | T5555_MODULATION_BIPHASE | T5555_INVERT_OUTPUT | T5555_SET_BITRATE ( 64 ) | 2 < < T5555_MAXBLOCK_SHIFT ;
snprintf ( cardtype , sizeof ( cardtype ) , " Q5/T5555 " ) ;
}
2016-07-29 17:04:12 +08:00
2020-11-29 19:49:09 +08:00
// EM4305
if ( em ) {
blocks [ 0 ] = EM4305_JABLOTRON_CONFIG_BLOCK ;
snprintf ( cardtype , sizeof ( cardtype ) , " EM4305/4469 " ) ;
}
2016-07-29 17:04:12 +08:00
2019-03-09 15:59:13 +08:00
2020-11-29 19:49:09 +08:00
uint64_t fullcode = bytes_to_num ( raw , raw_len ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
// clearing the topbit needed for the preambl detection.
if ( ( fullcode & 0x7FFFFFFFFF ) ! = fullcode ) {
fullcode & = 0x7FFFFFFFFF ;
PrintAndLogEx ( INFO , " Card Number Truncated to 39bits: % " PRIx64 , fullcode ) ;
}
2019-03-09 15:59:13 +08:00
2019-11-03 00:59:06 +08:00
uint8_t * bits = calloc ( JABLOTRON_ARR_LEN , sizeof ( uint8_t ) ) ;
if ( bits = = NULL ) {
2019-11-03 00:29:20 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
2019-11-03 00:59:06 +08:00
return PM3_EMALLOC ;
}
2019-11-03 00:29:20 +08:00
2019-05-22 20:57:08 +08:00
if ( getJablotronBits ( fullcode , bits ) ! = PM3_SUCCESS ) {
2019-07-14 06:35:18 +08:00
PrintAndLogEx ( ERR , " Error with tag bitstream generation. " ) ;
2019-05-22 20:57:08 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
blocks [ 1 ] = bytebits_to_byte ( bits , 32 ) ;
blocks [ 2 ] = bytebits_to_byte ( bits + 32 , 32 ) ;
2016-07-29 17:04:12 +08:00
2019-11-03 00:59:06 +08:00
free ( bits ) ;
2020-12-12 21:46:40 +08:00
uint64_t id = getJablontronCardId ( fullcode ) ;
2019-03-09 15:59:13 +08:00
2020-11-29 19:49:09 +08:00
PrintAndLogEx ( INFO , " Preparing to clone Jablotron to " _YELLOW_ ( " %s " ) " with FullCode: " _GREEN_ ( " % " PRIx64 ) " id: " _GREEN_ ( " % " PRIx64 ) , cardtype , fullcode , id ) ;
print_blocks ( blocks , ARRAYLEN ( blocks ) ) ;
2020-12-12 21:46:40 +08:00
2020-11-29 19:49:09 +08:00
int res ;
if ( em ) {
res = em4x05_clone_tag ( blocks , ARRAYLEN ( blocks ) , 0 , false ) ;
} else {
res = clone_t55xx_tag ( blocks , ARRAYLEN ( blocks ) ) ;
}
PrintAndLogEx ( SUCCESS , " Done " ) ;
PrintAndLogEx ( HINT , " Hint: try " _YELLOW_ ( " `lf jablotron reader` " ) " to verify " ) ;
return res ;
2016-07-29 17:04:12 +08:00
}
2019-04-12 08:07:11 +08:00
static int CmdJablotronSim ( const char * Cmd ) {
2020-12-01 19:18:07 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf jablotron sim " ,
" Enables simulation of jablotron card with specified card number. \n "
" Simulation runs until the button is pressed or another USB command is issued. " ,
" lf jablotron sim --cn 01b669 "
) ;
2016-07-29 17:04:12 +08:00
2020-12-01 19:18:07 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_str1 ( NULL , " cn " , " <hex> " , " Jablotron card ID - 5 bytes max " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
int raw_len = 0 ;
uint8_t raw [ 5 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 1 , raw , & raw_len ) ;
CLIParserFree ( ctx ) ;
2016-07-29 17:04:12 +08:00
2020-12-01 19:18:07 +08:00
uint64_t fullcode = bytes_to_num ( raw , raw_len ) ;
2016-07-29 20:31:39 +08:00
2019-03-10 06:35:06 +08:00
// clearing the topbit needed for the preambl detection.
if ( ( fullcode & 0x7FFFFFFFFF ) ! = fullcode ) {
fullcode & = 0x7FFFFFFFFF ;
PrintAndLogEx ( INFO , " Card Number Truncated to 39bits: % " PRIx64 , fullcode ) ;
}
2019-03-09 15:59:13 +08:00
2020-11-29 19:49:09 +08:00
PrintAndLogEx ( SUCCESS , " Simulating Jablotron - FullCode: " _YELLOW_ ( " % " PRIx64 ) , fullcode ) ;
2016-07-29 17:04:12 +08:00
2019-11-03 00:59:06 +08:00
uint8_t * bs = calloc ( JABLOTRON_ARR_LEN , sizeof ( uint8_t ) ) ;
if ( bs = = NULL ) {
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
return PM3_EMALLOC ;
}
2019-05-24 19:06:08 +08:00
getJablotronBits ( fullcode , bs ) ;
2019-11-03 00:59:06 +08:00
lf_asksim_t * payload = calloc ( 1 , sizeof ( lf_asksim_t ) + JABLOTRON_ARR_LEN ) ;
2019-05-24 19:06:08 +08:00
payload - > encoding = 2 ;
payload - > invert = 1 ;
payload - > separator = 0 ;
payload - > clock = 64 ;
2019-11-03 00:59:06 +08:00
memcpy ( payload - > data , bs , JABLOTRON_ARR_LEN ) ;
free ( bs ) ;
2019-05-24 19:06:08 +08:00
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-11-03 00:59:06 +08:00
SendCommandNG ( CMD_LF_ASK_SIMULATE , ( uint8_t * ) payload , sizeof ( lf_asksim_t ) + JABLOTRON_ARR_LEN ) ;
2019-05-24 19:06:08 +08:00
free ( payload ) ;
2019-05-09 06:08:59 +08:00
PacketResponseNG resp ;
2019-08-04 01:17:00 +08:00
WaitForResponse ( CMD_LF_ASK_SIMULATE , & resp ) ;
2019-05-24 19:06:08 +08:00
PrintAndLogEx ( INFO , " Done " ) ;
2019-05-10 02:20:54 +08:00
if ( resp . status ! = PM3_EOPABORTED )
2019-05-09 06:08:59 +08:00
return resp . status ;
return PM3_SUCCESS ;
2016-07-29 17:04:12 +08:00
}
static command_t CommandTable [ ] = {
2019-05-02 02:48:15 +08:00
{ " help " , CmdHelp , AlwaysAvailable , " This help " } ,
2021-04-24 20:39:27 +08:00
{ " demod " , CmdJablotronDemod , AlwaysAvailable , " demodulate an Jablotron tag from the GraphBuffer " } ,
{ " reader " , CmdJablotronReader , IfPm3Lf , " attempt to read and extract tag data " } ,
2020-08-28 17:27:53 +08:00
{ " clone " , CmdJablotronClone , IfPm3Lf , " clone jablotron tag to T55x7 or Q5/T5555 " } ,
2019-05-02 06:02:38 +08:00
{ " sim " , CmdJablotronSim , IfPm3Lf , " simulate jablotron tag " } ,
2019-05-02 02:48:15 +08:00
{ NULL , NULL , NULL , NULL }
2016-07-29 17:04:12 +08:00
} ;
2019-04-12 08:07:11 +08:00
static int CmdHelp ( const char * Cmd ) {
( void ) Cmd ; // Cmd is not used so far
CmdsHelp ( CommandTable ) ;
2019-05-22 20:57:08 +08:00
return PM3_SUCCESS ;
2016-07-29 17:04:12 +08:00
}
2019-04-12 08:07:11 +08:00
int CmdLFJablotron ( const char * Cmd ) {
clearCommandBuffer ( ) ;
2019-04-19 06:47:51 +08:00
return CmdsParse ( CommandTable , Cmd ) ;
2016-07-29 17:04:12 +08:00
}
2019-04-12 06:38:54 +08:00
int getJablotronBits ( uint64_t fullcode , uint8_t * bits ) {
//preamp
num_to_bytebits ( 0xFFFF , 16 , bits ) ;
//fullcode
num_to_bytebits ( fullcode , 40 , bits + 16 ) ;
//chksum byte
uint8_t chksum = jablontron_chksum ( bits ) ;
num_to_bytebits ( chksum , 8 , bits + 56 ) ;
2019-05-22 20:57:08 +08:00
return PM3_SUCCESS ;
2019-04-12 06:38:54 +08:00
}
// ASK/Diphase fc/64 (inverted Biphase)
// Note: this is not a demod, this is only a detection
// the parameter *bits needs to be demoded before call
// 0xFFFF preamble, 64bits
int detectJablotron ( uint8_t * bits , size_t * size ) {
2019-11-03 00:59:06 +08:00
if ( * size < JABLOTRON_ARR_LEN * 2 ) return - 1 ; //make sure buffer has enough data
2019-04-12 06:38:54 +08:00
size_t startIdx = 0 ;
uint8_t preamble [ ] = { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 } ;
if ( preambleSearch ( bits , preamble , sizeof ( preamble ) , size , & startIdx ) = = 0 )
return - 2 ; //preamble not found
2019-11-03 00:59:06 +08:00
if ( * size ! = JABLOTRON_ARR_LEN ) return - 3 ; // wrong demoded size
2019-04-12 06:38:54 +08:00
uint8_t checkchksum = jablontron_chksum ( bits + startIdx ) ;
uint8_t crc = bytebits_to_byte ( bits + startIdx + 56 , 8 ) ;
if ( checkchksum ! = crc ) return - 5 ;
return ( int ) startIdx ;
}