2020-12-04 07:14:18 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
2020-12-09 21:48:10 +08:00
// modified marshmellow
// modified Iceman, 2020
2020-12-04 07:14:18 +08:00
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x commands
//-----------------------------------------------------------------------------
# include "cmdlfem410x.h"
# include "cmdlfem4x50.h"
# include <stdio.h>
# include <string.h>
# include <inttypes.h>
# include <ctype.h>
# include <stdlib.h>
# include "fileutils.h"
# include "cmdparser.h" // command_t
# include "comms.h"
# include "commonutil.h"
# include "common.h"
# include "util_posix.h"
# include "protocols.h"
# include "ui.h"
# include "proxgui.h"
# include "graph.h"
# include "cmddata.h"
# include "cmdlf.h"
# include "lfdemod.h"
# include "generator.h"
# include "cliparser.h"
# include "cmdhw.h"
static uint64_t g_em410xid = 0 ;
static int CmdHelp ( const char * Cmd ) ;
/* Read the ID of an EM410x tag.
* Format :
* 1111 1111 1 < - - standard non - repeatable header
* XXXX [ row parity bit ] < - - 10 rows of 5 bits for our 40 bit tag ID
* . . . .
* CCCC < - - each bit here is parity for the 10 bits above in corresponding column
* 0 < - - stop bit , end of tag
*/
// Construct the graph for emulating an EM410X tag
2021-05-15 13:59:43 +08:00
static void em410x_construct_emul_graph ( uint8_t * uid , uint8_t clock , uint8_t gap ) {
2020-12-04 07:14:18 +08:00
2020-12-09 21:48:10 +08:00
// clear our graph
2020-12-04 07:14:18 +08:00
ClearGraph ( true ) ;
2020-12-09 21:48:10 +08:00
// write 16 zero bit sledge
2021-05-15 13:59:43 +08:00
for ( uint8_t i = 0 ; i < gap ; i + + )
2020-12-04 07:14:18 +08:00
AppendGraph ( false , clock , 0 ) ;
2020-12-09 21:48:10 +08:00
// write 9 start bits
for ( uint8_t i = 0 ; i < 9 ; i + + )
2020-12-04 07:14:18 +08:00
AppendGraph ( false , clock , 1 ) ;
2020-12-09 21:48:10 +08:00
uint8_t bs [ 8 ] , parity [ 8 ] ;
memset ( parity , 0 , sizeof ( parity ) ) ;
for ( uint8_t i = 0 ; i < 5 ; i + + ) {
for ( uint8_t j = 0 ; j < 8 ; j + + ) {
bs [ j ] = ( uid [ i ] > > ( 7 - j ) & 1 ) ;
}
PrintAndLogEx ( DEBUG , " uid[%d] 0x%02x (%s) " , i , uid [ i ] , sprint_bin ( bs , 4 ) ) ;
for ( uint8_t j = 0 ; j < 2 ; j + + ) {
// append each bit
AppendGraph ( false , clock , bs [ 0 + ( 4 * j ) ] ) ;
AppendGraph ( false , clock , bs [ 1 + ( 4 * j ) ] ) ;
AppendGraph ( false , clock , bs [ 2 + ( 4 * j ) ] ) ;
AppendGraph ( false , clock , bs [ 3 + ( 4 * j ) ] ) ;
// append parity bit
AppendGraph ( false , clock , bs [ 0 + ( 4 * j ) ] ^ bs [ 1 + ( 4 * j ) ] ^ bs [ 2 + ( 4 * j ) ] ^ bs [ 3 + ( 4 * j ) ] ) ;
// keep track of column parity
parity [ 0 ] ^ = bs [ 0 + ( 4 * j ) ] ;
parity [ 1 ] ^ = bs [ 1 + ( 4 * j ) ] ;
parity [ 2 ] ^ = bs [ 2 + ( 4 * j ) ] ;
parity [ 3 ] ^ = bs [ 3 + ( 4 * j ) ] ;
}
2020-12-04 07:14:18 +08:00
}
2020-12-09 21:48:10 +08:00
// parity columns
2020-12-04 07:14:18 +08:00
AppendGraph ( false , clock , parity [ 0 ] ) ;
AppendGraph ( false , clock , parity [ 1 ] ) ;
AppendGraph ( false , clock , parity [ 2 ] ) ;
AppendGraph ( false , clock , parity [ 3 ] ) ;
2020-12-09 21:48:10 +08:00
// stop bit
2020-12-04 07:14:18 +08:00
AppendGraph ( true , clock , 0 ) ;
}
//print 64 bit EM410x ID in multiple formats
2021-08-04 05:22:19 +08:00
void printEM410x ( uint32_t hi , uint64_t id , bool verbose , int type ) {
2020-12-04 07:14:18 +08:00
if ( ! id & & ! hi ) return ;
2020-12-09 21:48:10 +08:00
if ( verbose = = false ) {
2021-08-04 05:22:19 +08:00
if ( type & 0x1 ) { // Short ID
2020-12-12 21:46:40 +08:00
PrintAndLogEx ( SUCCESS , " EM 410x ID " _GREEN_ ( " %010 " PRIX64 ) , id ) ;
2020-12-09 21:48:10 +08:00
}
2021-08-04 05:22:19 +08:00
if ( type & 0x2 ) { // Long ID
PrintAndLogEx ( SUCCESS , " EM 410x XL ID " _GREEN_ ( " %06X%016 " PRIX64 ) , hi , id ) ;
}
if ( type & 0x4 ) { // Short Extended ID
uint64_t data = ( id < < 20 ) > > 20 ;
// Convert back to Short ID
id = ( ( uint64_t ) hi < < 16 ) | ( id > > 48 ) ;
if ( ( data & 0xFFFFFFFF ) = = 0 ) {
PrintAndLogEx ( SUCCESS , " EM 410x ID " _GREEN_ ( " %010 " PRIX64 ) " Electra " _GREEN_ ( " %03 " PRIu64 ) , id , data > > 32 ) ;
} else {
PrintAndLogEx ( SUCCESS , " EM 410x ID " _GREEN_ ( " %010 " PRIX64 ) " on 128b frame with data " _GREEN_ ( " %011 " PRIX64 ) , id , data ) ;
}
}
2020-12-09 21:48:10 +08:00
return ;
}
2021-08-04 05:22:19 +08:00
if ( type & 0x2 ) { // Long ID
2020-12-04 07:14:18 +08:00
//output 88 bit em id
2021-08-04 05:22:19 +08:00
PrintAndLogEx ( SUCCESS , " EM 410x XL ID " _GREEN_ ( " %06X%016 " PRIX64 ) " ( RF/%d ) " , hi , id , g_DemodClock ) ;
}
if ( type & 0x4 ) { // Short Extended ID
PrintAndLogEx ( SUCCESS , " EM 410x Short ID found on a 128b frame " ) ;
uint64_t data = ( id < < 20 ) > > 20 ;
PrintAndLogEx ( SUCCESS , " Data after ID: " _GREEN_ ( " %011 " PRIX64 ) , data ) ;
if ( ( data & 0xFFFFFFFF ) = = 0 ) {
PrintAndLogEx ( SUCCESS , " Possibly an Electra (RO), 0x " _GREEN_ ( " %03 " PRIX64 ) " = " _GREEN_ ( " %03 " PRIu64 ) , data > > 32 , data > > 32 ) ;
}
PrintAndLogEx ( SUCCESS , " Short ID details: " ) ;
// Convert back to Short ID
id = ( ( uint64_t ) hi < < 16 ) | ( id > > 48 ) ;
}
if ( type & ( 0x4 | 0x1 ) ) { // Short Extended or Short ID
2020-12-04 07:14:18 +08:00
//output 40 bit em id
2021-08-04 05:22:19 +08:00
uint64_t n = 1 ;
uint64_t id2lo = 0 ;
uint8_t m , i ;
for ( m = 5 ; m > 0 ; m - - ) {
for ( i = 0 ; i < 8 ; i + + ) {
id2lo = ( id2lo < < 1LL ) | ( ( id & ( n < < ( i + ( ( m - 1 ) * 8 ) ) ) ) > > ( i + ( ( m - 1 ) * 8 ) ) ) ;
}
}
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " EM 410x ID " _GREEN_ ( " %010 " PRIX64 ) , id ) ;
PrintAndLogEx ( SUCCESS , " EM410x ( RF/%d ) " , g_DemodClock ) ;
PrintAndLogEx ( INFO , " -------- " _CYAN_ ( " Possible de-scramble patterns " ) " --------- " ) ;
PrintAndLogEx ( SUCCESS , " Unique TAG ID : %010 " PRIX64 , id2lo ) ;
PrintAndLogEx ( INFO , " HoneyWell IdentKey " ) ;
PrintAndLogEx ( SUCCESS , " DEZ 8 : %08 " PRIu64 , id & 0xFFFFFF ) ;
PrintAndLogEx ( SUCCESS , " DEZ 10 : %010 " PRIu64 , id & 0xFFFFFFFF ) ;
PrintAndLogEx ( SUCCESS , " DEZ 5.5 : %05 " PRIu64 " .%05 " PRIu64 , ( id > > 16LL ) & 0xFFFF , ( id & 0xFFFF ) ) ;
PrintAndLogEx ( SUCCESS , " DEZ 3.5A : %03 " PRIu64 " .%05 " PRIu64 , ( id > > 32ll ) , ( id & 0xFFFF ) ) ;
PrintAndLogEx ( SUCCESS , " DEZ 3.5B : %03 " PRIu64 " .%05 " PRIu64 , ( id & 0xFF000000 ) > > 24 , ( id & 0xFFFF ) ) ;
PrintAndLogEx ( SUCCESS , " DEZ 3.5C : %03 " PRIu64 " .%05 " PRIu64 , ( id & 0xFF0000 ) > > 16 , ( id & 0xFFFF ) ) ;
PrintAndLogEx ( SUCCESS , " DEZ 14/IK2 : %014 " PRIu64 , id ) ;
PrintAndLogEx ( SUCCESS , " DEZ 15/IK3 : %015 " PRIu64 , id2lo ) ;
PrintAndLogEx ( SUCCESS , " DEZ 20/ZK : %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 " %02 " PRIu64 ,
2020-12-04 07:14:18 +08:00
( id2lo & 0xf000000000 ) > > 36 ,
( id2lo & 0x0f00000000 ) > > 32 ,
( id2lo & 0x00f0000000 ) > > 28 ,
( id2lo & 0x000f000000 ) > > 24 ,
( id2lo & 0x0000f00000 ) > > 20 ,
( id2lo & 0x00000f0000 ) > > 16 ,
( id2lo & 0x000000f000 ) > > 12 ,
( id2lo & 0x0000000f00 ) > > 8 ,
( id2lo & 0x00000000f0 ) > > 4 ,
( id2lo & 0x000000000f )
) ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( INFO , " " ) ;
2020-12-04 07:14:18 +08:00
uint64_t paxton = ( ( ( id > > 32 ) < < 24 ) | ( id & 0xffffff ) ) + 0x143e00 ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " Other : %05 " PRIu64 " _%03 " PRIu64 " _%08 " PRIu64 , ( id & 0xFFFF ) , ( ( id > > 16LL ) & 0xFF ) , ( id & 0xFFFFFF ) ) ;
PrintAndLogEx ( SUCCESS , " Pattern Paxton : % " PRIu64 " [0x% " PRIX64 " ] " , paxton , paxton ) ;
2020-12-04 07:14:18 +08:00
uint32_t p1id = ( id & 0xFFFFFF ) ;
uint8_t arr [ 32 ] = { 0x00 } ;
int j = 23 ;
for ( int k = 0 ; k < 24 ; + + k , - - j ) {
arr [ k ] = ( p1id > > k ) & 1 ;
}
uint32_t p1 = 0 ;
p1 | = arr [ 23 ] < < 21 ;
p1 | = arr [ 22 ] < < 23 ;
p1 | = arr [ 21 ] < < 20 ;
p1 | = arr [ 20 ] < < 22 ;
p1 | = arr [ 19 ] < < 18 ;
p1 | = arr [ 18 ] < < 16 ;
p1 | = arr [ 17 ] < < 19 ;
p1 | = arr [ 16 ] < < 17 ;
p1 | = arr [ 15 ] < < 13 ;
p1 | = arr [ 14 ] < < 15 ;
p1 | = arr [ 13 ] < < 12 ;
p1 | = arr [ 12 ] < < 14 ;
p1 | = arr [ 11 ] < < 6 ;
p1 | = arr [ 10 ] < < 2 ;
p1 | = arr [ 9 ] < < 7 ;
p1 | = arr [ 8 ] < < 1 ;
p1 | = arr [ 7 ] < < 0 ;
p1 | = arr [ 6 ] < < 8 ;
p1 | = arr [ 5 ] < < 11 ;
p1 | = arr [ 4 ] < < 3 ;
p1 | = arr [ 3 ] < < 10 ;
p1 | = arr [ 2 ] < < 4 ;
p1 | = arr [ 1 ] < < 5 ;
p1 | = arr [ 0 ] < < 9 ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " Pattern 1 : %d [0x%X] " , p1 , p1 ) ;
2020-12-04 07:14:18 +08:00
uint16_t sebury1 = id & 0xFFFF ;
uint8_t sebury2 = ( id > > 16 ) & 0x7F ;
uint32_t sebury3 = id & 0x7FFFFF ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " Pattern Sebury : %d %d %d [0x%X 0x%X 0x%X] " , sebury1 , sebury2 , sebury3 , sebury1 , sebury2 , sebury3 ) ;
PrintAndLogEx ( INFO , " ------------------------------------------------ " ) ;
2020-12-04 07:14:18 +08:00
}
}
/* Read the ID of an EM410x tag.
* Format :
* 1111 1111 1 < - - standard non - repeatable header
* XXXX [ row parity bit ] < - - 10 rows of 5 bits for our 40 bit tag ID
* . . . .
* CCCC < - - each bit here is parity for the 10 bits above in corresponding column
* 0 < - - stop bit , end of tag
*/
int AskEm410xDecode ( bool verbose , uint32_t * hi , uint64_t * lo ) {
size_t idx = 0 ;
uint8_t bits [ 512 ] = { 0 } ;
size_t size = sizeof ( bits ) ;
if ( ! getDemodBuff ( bits , & size ) ) {
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x problem during copy from ASK demod " ) ;
return PM3_ESOFT ;
}
int ans = Em410xDecode ( bits , & size , & idx , hi , lo ) ;
if ( ans < 0 ) {
if ( ans = = - 2 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x not enough samples after demod " ) ;
else if ( ans = = - 4 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x preamble not found " ) ;
else if ( ans = = - 5 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x Size not correct: %zu " , size ) ;
else if ( ans = = - 6 )
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x parity failed " ) ;
return PM3_ESOFT ;
}
if ( ! lo & & ! hi ) {
PrintAndLogEx ( DEBUG , " DEBUG: Error - Em410x decoded to all zeros " ) ;
return PM3_ESOFT ;
}
//set GraphBuffer for clone or sim command
setDemodBuff ( DemodBuffer , ( size = = 40 ) ? 64 : 128 , idx + 1 ) ;
setClockGrid ( g_DemodClock , g_DemodStartIdx + ( ( idx + 1 ) * g_DemodClock ) ) ;
PrintAndLogEx ( DEBUG , " DEBUG: Em410x idx: %zu, Len: %zu, Printing Demod Buffer: " , idx , size ) ;
if ( g_debugMode ) {
printDemodBuff ( 0 , false , false , true ) ;
}
2021-08-04 05:22:19 +08:00
printEM410x ( * hi , * lo , verbose , ans ) ;
2020-12-09 21:48:10 +08:00
g_em410xid = * lo ;
2020-12-04 07:14:18 +08:00
return PM3_SUCCESS ;
}
int AskEm410xDemod ( int clk , int invert , int maxErr , size_t maxLen , bool amplify , uint32_t * hi , uint64_t * lo , bool verbose ) {
bool st = true ;
// em410x simulation etc uses 0/1 as signal data. This must be converted in order to demod it back again
if ( isGraphBitstream ( ) ) {
convertGraphFromBitstream ( ) ;
}
if ( ASKDemod_ext ( clk , invert , maxErr , maxLen , amplify , false , false , 1 , & st ) ! = PM3_SUCCESS )
return PM3_ESOFT ;
return AskEm410xDecode ( verbose , hi , lo ) ;
}
// this read loops on device side.
// uses the demod in lfops.c
static int CmdEM410xWatch ( const char * Cmd ) {
2020-12-09 21:48:10 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x watch " ,
" Enables Electro Marine (EM) compatible reader mode printing details of scanned tags. \n "
" Run until the button is pressed or another USB command is issued. " ,
" lf em 410x watch "
) ;
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
2020-12-04 07:14:18 +08:00
2021-02-03 07:14:40 +08:00
PrintAndLogEx ( SUCCESS , " Watching for EM410x cards - place tag on Proxmark3 antenna " ) ;
2020-12-04 07:14:18 +08:00
clearCommandBuffer ( ) ;
SendCommandNG ( CMD_LF_EM410X_WATCH , NULL , 0 ) ;
2021-02-03 00:55:54 +08:00
return lfsim_wait_check ( CMD_LF_EM410X_WATCH ) ;
2020-12-04 07:14:18 +08:00
}
//by marshmellow
//takes 3 arguments - clock, invert and maxErr as integers
//attempts to demodulate ask while decoding manchester
//prints binary found and saves in graphbuffer for further commands
int demodEM410x ( bool verbose ) {
( void ) verbose ; // unused so far
uint32_t hi = 0 ;
uint64_t lo = 0 ;
return AskEm410xDemod ( 0 , 0 , 100 , 0 , false , & hi , & lo , true ) ;
}
static int CmdEM410xDemod ( const char * Cmd ) {
2020-12-09 21:48:10 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x demod " ,
" Try to find EM 410x preamble, if found decode / descramble data " ,
" lf em 410x demod -> demod an EM410x Tag ID from GraphBuffer \n "
" lf em 410x demod --clk 32 -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/32 \n "
" lf em 410x demod --clk 32 -i -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/32 and inverting data \n "
" lf em 410x demod -i -> demod an EM410x Tag ID from GraphBuffer while inverting data \n "
" lf em 410x demod --clk 64 -i --err 0 -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/64 and inverting data and allowing 0 demod errors "
) ;
void * argtable [ ] = {
arg_param_begin ,
2021-02-02 23:34:45 +08:00
arg_u64_0 ( NULL , " clk " , " <dec> " , " clock (default autodetect) " ) ,
arg_u64_0 ( NULL , " err " , " <dec> " , " maximum allowed errors (default 100) " ) ,
arg_u64_0 ( NULL , " len " , " <dec> " , " maximum length " ) ,
arg_lit0 ( " i " , " invert " , " invert output " ) ,
arg_lit0 ( " a " , " amp " , " amplify signal " ) ,
2020-12-09 21:48:10 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
int clk = arg_get_u32_def ( ctx , 1 , 0 ) ;
int max_err = arg_get_u32_def ( ctx , 2 , 100 ) ;
size_t max_len = arg_get_u32_def ( ctx , 3 , 0 ) ;
bool invert = arg_get_lit ( ctx , 4 ) ;
bool amplify = arg_get_lit ( ctx , 5 ) ;
CLIParserFree ( ctx ) ;
2020-12-04 07:14:18 +08:00
uint32_t hi = 0 ;
uint64_t lo = 0 ;
2020-12-09 21:48:10 +08:00
if ( AskEm410xDemod ( clk , invert , max_err , max_len , amplify , & hi , & lo , true ) ! = PM3_SUCCESS )
2020-12-04 07:14:18 +08:00
return PM3_ESOFT ;
return PM3_SUCCESS ;
}
// this read is the "normal" read, which download lf signal and tries to demod here.
2020-12-09 21:48:10 +08:00
static int CmdEM410xReader ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x reader " ,
" read EM 410x tag " ,
" lf em 410x reader -> reader \n "
" lf em 410x reader -@ -> continuous reader mode \n "
" lf em 410x reader --clk 32 -> reader using a clock of RF/32 \n "
" lf em 410x reader --clk 32 -i -> reader using a clock of RF/32 and inverting data \n "
" lf em 410x reader -i -> reader while inverting data \n "
" lf em 410x reader --clk 64 -i --err 0 -> reader using a clock of RF/64 and inverting data and allowing 0 demod errors "
) ;
void * argtable [ ] = {
arg_param_begin ,
2021-02-02 23:34:45 +08:00
arg_u64_0 ( NULL , " clk " , " <dec> " , " clock (default autodetect) " ) ,
arg_u64_0 ( NULL , " err " , " <dec> " , " maximum allowed errors (default 100) " ) ,
arg_u64_0 ( NULL , " len " , " <dec> " , " maximum length " ) ,
arg_lit0 ( " i " , " invert " , " invert output " ) ,
arg_lit0 ( " a " , " amp " , " amplify signal " ) ,
arg_lit0 ( " b " , NULL , " break on first found " ) ,
arg_lit0 ( " @ " , NULL , " continuous reader mode " ) ,
2020-12-09 21:48:10 +08:00
arg_lit0 ( " v " , " verbose " , " verbose output " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
int clk = arg_get_u32_def ( ctx , 1 , 0 ) ;
int max_err = arg_get_u32_def ( ctx , 2 , 100 ) ;
size_t max_len = arg_get_u32_def ( ctx , 3 , 0 ) ;
bool invert = arg_get_lit ( ctx , 4 ) ;
bool amplify = arg_get_lit ( ctx , 5 ) ;
2021-02-02 23:34:45 +08:00
bool break_first = arg_get_lit ( ctx , 6 ) ;
bool cm = arg_get_lit ( ctx , 7 ) ;
bool verbose = arg_get_lit ( ctx , 8 ) ;
2020-12-09 21:48:10 +08:00
CLIParserFree ( ctx ) ;
if ( cm ) {
PrintAndLogEx ( INFO , " Press " _GREEN_ ( " <Enter> " ) " to exit " ) ;
}
2020-12-04 07:14:18 +08:00
2020-12-09 21:48:10 +08:00
do {
uint32_t hi = 0 ;
uint64_t lo = 0 ;
lf_read ( false , 12288 ) ;
AskEm410xDemod ( clk , invert , max_err , max_len , amplify , & hi , & lo , verbose ) ;
2021-02-02 23:34:45 +08:00
if ( break_first & & g_em410xid ! = 0 ) {
break ;
}
2020-12-09 21:48:10 +08:00
} while ( cm & & ! kbd_enter_pressed ( ) ) ;
return PM3_SUCCESS ;
2020-12-04 07:14:18 +08:00
}
// emulate an EM410X tag
static int CmdEM410xSim ( const char * Cmd ) {
2020-12-12 21:46:40 +08:00
2020-12-09 21:48:10 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x sim " ,
" Enables simulation of EM 410x card. \n "
" Simulation runs until the button is pressed or another USB command is issued. " ,
" lf em 410x sim --id 0F0368568B \n "
2021-05-15 13:59:43 +08:00
" lf em 410x sim --id 0F0368568B --clk 32 \n "
" lf em 410x sim --id 0F0368568B --gap 0 "
2020-12-09 21:48:10 +08:00
) ;
void * argtable [ ] = {
arg_param_begin ,
2021-02-02 23:34:45 +08:00
arg_u64_0 ( NULL , " clk " , " <dec> " , " <32|64> clock (default 64) " ) ,
arg_str1 ( NULL , " id " , " <hex> " , " ID number (5 hex bytes) " ) ,
2021-05-15 13:59:43 +08:00
arg_u64_0 ( NULL , " gap " , " <dec> " , " gap (0's) between ID repeats (default 20) " ) ,
2020-12-09 21:48:10 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
// clock is 64 in EM410x tags
int clk = arg_get_u32_def ( ctx , 1 , 64 ) ;
int uid_len = 0 ;
2021-05-19 05:56:11 +08:00
int gap = arg_get_u32_def ( ctx , 3 , 20 ) ;
2020-12-09 21:48:10 +08:00
uint8_t uid [ 5 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 2 , uid , & uid_len ) ;
CLIParserFree ( ctx ) ;
if ( uid_len ! = 5 ) {
PrintAndLogEx ( FAILED , " UID must include 5 hex bytes (%u) " , uid_len ) ;
2020-12-04 07:14:18 +08:00
return PM3_EINVARG ;
}
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " Starting simulating UID " _YELLOW_ ( " %s " ) " clock: " _YELLOW_ ( " %d " ) , sprint_hex_inrow ( uid , sizeof ( uid ) ) , clk ) ;
2021-05-15 13:59:43 +08:00
em410x_construct_emul_graph ( uid , clk , gap ) ;
2021-02-02 23:34:45 +08:00
CmdLFSim ( " " ) ;
2020-12-04 07:14:18 +08:00
return PM3_SUCCESS ;
}
2020-12-12 21:46:40 +08:00
static int CmdEM410xBrute ( const char * Cmd ) {
2020-12-09 21:48:10 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x brute " ,
" bruteforcing by emulating EM 410x tag " ,
" lf em 410x brute -f ids.txt \n "
" lf em 410x brute -f ids.txt --clk 32 \n "
" lf em 410x brute -f ids.txt --delay 3000 \n "
" lf em 410x brute -f ids.txt --delay 3000 --clk 32 \n "
) ;
void * argtable [ ] = {
arg_param_begin ,
2021-02-02 23:34:45 +08:00
arg_u64_0 ( NULL , " clk " , " <dec> " , " <32|64> clock (default 64) " ) ,
arg_u64_0 ( NULL , " delay " , " <dec> " , " pause delay in milliseconds between UIDs simulation (default 1000ms) " ) ,
2020-12-09 21:48:10 +08:00
arg_str1 ( " f " , " file " , " <hex> " , " file with UIDs in HEX format, one per line " ) ,
2021-05-15 13:59:43 +08:00
arg_u64_0 ( NULL , " gap " , " <dec> " , " gap (0's) between ID repeats (default 20) " ) ,
2020-12-09 21:48:10 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
2020-12-12 21:46:40 +08:00
// clock default 64 in EM410x
2020-12-09 21:48:10 +08:00
uint32_t clk = arg_get_u32_def ( ctx , 1 , 64 ) ;
2021-05-15 13:59:43 +08:00
int gap = arg_get_u32_def ( ctx , 4 , 20 ) ;
2020-12-12 21:46:40 +08:00
// default pause time: 1 second
2020-12-09 21:48:10 +08:00
uint32_t delay = arg_get_u32_def ( ctx , 2 , 1000 ) ;
int fnlen = 0 ;
2020-12-04 07:14:18 +08:00
char filename [ FILE_PATH_SIZE ] = { 0 } ;
2020-12-09 21:48:10 +08:00
CLIParamStrToBuf ( arg_get_str ( ctx , 3 ) , ( uint8_t * ) filename , FILE_PATH_SIZE , & fnlen ) ;
CLIParserFree ( ctx ) ;
2020-12-04 07:14:18 +08:00
2020-12-09 21:48:10 +08:00
if ( fnlen = = 0 ) {
2020-12-04 07:14:18 +08:00
PrintAndLogEx ( ERR , " Error: Please specify a filename " ) ;
return PM3_EINVARG ;
}
2020-12-12 21:46:40 +08:00
2020-12-09 21:48:10 +08:00
uint32_t uidcnt = 0 ;
uint8_t stUidBlock = 20 ;
uint8_t * p = NULL ;
uint8_t uid [ 5 ] = { 0x00 } ;
2020-12-04 07:14:18 +08:00
2020-12-09 21:48:10 +08:00
// open file
FILE * f = NULL ;
2020-12-04 07:14:18 +08:00
if ( ( f = fopen ( filename , " r " ) ) = = NULL ) {
PrintAndLogEx ( ERR , " Error: Could not open UIDs file [ " _YELLOW_ ( " %s " ) " ] " , filename ) ;
return PM3_EFILE ;
}
2020-12-09 21:48:10 +08:00
// allocate mem for file contents
uint8_t * uidblock = calloc ( stUidBlock , 5 ) ;
if ( uidblock = = NULL ) {
2020-12-04 07:14:18 +08:00
fclose ( f ) ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( ERR , " Error: can't allocate memory " ) ;
return PM3_EMALLOC ;
2020-12-04 07:14:18 +08:00
}
2020-12-09 21:48:10 +08:00
// read file into memory
char buf [ 11 ] ;
2020-12-04 07:14:18 +08:00
while ( fgets ( buf , sizeof ( buf ) , f ) ) {
if ( strlen ( buf ) < 10 | | buf [ 9 ] = = ' \n ' ) continue ;
while ( fgetc ( f ) ! = ' \n ' & & ! feof ( f ) ) ; //goto next line
//The line start with # is comment, skip
if ( buf [ 0 ] = = ' # ' ) continue ;
if ( param_gethex ( buf , 0 , uid , 10 ) ) {
PrintAndLogEx ( FAILED , " UIDs must include 10 HEX symbols " ) ;
2020-12-09 21:48:10 +08:00
free ( uidblock ) ;
2020-12-04 07:14:18 +08:00
fclose ( f ) ;
return PM3_ESOFT ;
}
buf [ 10 ] = 0 ;
if ( stUidBlock - uidcnt < 2 ) {
2020-12-09 21:48:10 +08:00
p = realloc ( uidblock , 5 * ( stUidBlock + = 10 ) ) ;
2020-12-04 07:14:18 +08:00
if ( ! p ) {
PrintAndLogEx ( WARNING , " Cannot allocate memory for UIDs " ) ;
2020-12-09 21:48:10 +08:00
free ( uidblock ) ;
2020-12-04 07:14:18 +08:00
fclose ( f ) ;
return PM3_ESOFT ;
}
2020-12-09 21:48:10 +08:00
uidblock = p ;
2020-12-04 07:14:18 +08:00
}
2020-12-09 21:48:10 +08:00
memset ( uidblock + 5 * uidcnt , 0 , 5 ) ;
num_to_bytes ( strtoll ( buf , NULL , 16 ) , 5 , uidblock + 5 * uidcnt ) ;
2020-12-04 07:14:18 +08:00
uidcnt + + ;
memset ( buf , 0 , sizeof ( buf ) ) ;
}
fclose ( f ) ;
if ( uidcnt = = 0 ) {
PrintAndLogEx ( FAILED , " No UIDs found in file " ) ;
2020-12-09 21:48:10 +08:00
free ( uidblock ) ;
2020-12-04 07:14:18 +08:00
return PM3_ESOFT ;
}
PrintAndLogEx ( SUCCESS , " Loaded " _YELLOW_ ( " %d " ) " UIDs from " _YELLOW_ ( " %s " ) " , pause delay: " _YELLOW_ ( " %d " ) " ms " , uidcnt , filename , delay ) ;
// loop
2020-12-09 21:48:10 +08:00
uint8_t testuid [ 5 ] ;
2020-12-04 07:14:18 +08:00
for ( uint32_t c = 0 ; c < uidcnt ; + + c ) {
if ( kbd_enter_pressed ( ) ) {
2021-02-02 23:34:45 +08:00
SendCommandNG ( CMD_BREAK_LOOP , NULL , 0 ) ;
2021-05-03 15:31:51 +08:00
PrintAndLogEx ( WARNING , " aborted via keyboard! \n " ) ;
2020-12-09 21:48:10 +08:00
free ( uidblock ) ;
2020-12-04 07:14:18 +08:00
return PM3_EOPABORTED ;
}
2020-12-09 21:48:10 +08:00
memcpy ( testuid , uidblock + 5 * c , 5 ) ;
2021-02-03 07:14:40 +08:00
PrintAndLogEx ( INFO , " Bruteforce %d / %u: simulating UID " _YELLOW_ ( " %s " )
2020-12-12 21:46:40 +08:00
, c + 1
, uidcnt
, sprint_hex_inrow ( testuid , sizeof ( testuid ) )
) ;
2020-12-04 07:14:18 +08:00
2021-05-15 13:59:43 +08:00
em410x_construct_emul_graph ( testuid , clk , gap ) ;
2021-02-05 05:36:08 +08:00
lfsim_upload_gb ( ) ;
struct p {
uint16_t len ;
uint16_t gap ;
} PACKED payload ;
payload . len = GraphTraceLen ;
payload . gap = 0 ;
clearCommandBuffer ( ) ;
2021-02-09 23:56:55 +08:00
SendCommandNG ( CMD_LF_SIMULATE , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2021-02-05 05:36:08 +08:00
PacketResponseNG resp ;
if ( WaitForResponseTimeout ( CMD_LF_SIMULATE , & resp , delay ) ) {
if ( resp . status = = PM3_EOPABORTED ) {
PrintAndLogEx ( INFO , " Button pressed, user aborted " ) ;
break ;
}
}
2020-12-04 07:14:18 +08:00
}
2020-12-09 21:48:10 +08:00
free ( uidblock ) ;
2020-12-04 07:14:18 +08:00
return PM3_SUCCESS ;
}
//currently only supports manchester modulations
2020-12-09 21:48:10 +08:00
static int CmdEM410xSpoof ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x spoof " ,
" Watch 'nd Spoof, activates reader \n "
" Waits until a EM 410x tag gets presented then Proxmark3 starts simulating the found UID " ,
" lf em 410x spoof "
) ;
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
2020-12-04 07:14:18 +08:00
// loops if the captured ID was in XL-format.
2021-02-02 23:34:45 +08:00
g_em410xid = 0 ;
CmdEM410xReader ( " -b@ " ) ;
PrintAndLogEx ( SUCCESS , " Replaying captured ID " _YELLOW_ ( " %010 " PRIx64 ) , g_em410xid ) ;
CmdLFSim ( " " ) ;
2020-12-04 07:14:18 +08:00
return PM3_SUCCESS ;
}
static int CmdEM410xClone ( const char * Cmd ) {
2020-12-09 21:48:10 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " lf em 410x clone " ,
" Writes EM410x ID to a T55x7 or Q5/T5555 tag " ,
2021-02-02 23:34:45 +08:00
" lf em 410x clone --id 0F0368568B -> write id to T55x7 tag \n "
" lf em 410x clone --id 0F0368568B --q5 -> write id to Q5/T5555 tag "
2020-12-09 21:48:10 +08:00
) ;
void * argtable [ ] = {
arg_param_begin ,
2021-02-02 23:34:45 +08:00
arg_u64_0 ( NULL , " clk " , " <dec> " , " <16|32|40|64> clock (default 64) " ) ,
arg_str1 ( NULL , " id " , " <hex> " , " ID number (5 hex bytes) " ) ,
arg_lit0 ( NULL , " q5 " , " specify writing to Q5/T5555 tag " ) ,
2020-12-09 21:48:10 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
2020-12-12 21:46:40 +08:00
// clock default 64 in EM410x
2020-12-09 21:48:10 +08:00
uint32_t clk = arg_get_u32_def ( ctx , 1 , 64 ) ;
int uid_len = 0 ;
uint8_t uid [ 5 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 2 , uid , & uid_len ) ;
bool q5 = arg_get_lit ( ctx , 3 ) ;
CLIParserFree ( ctx ) ;
uint64_t id = bytes_to_num ( uid , uid_len ) ;
2020-12-04 07:14:18 +08:00
2020-12-09 21:48:10 +08:00
// Allowed clock rates: 16, 32, 40 and 64
if ( ( clk ! = 16 ) & & ( clk ! = 32 ) & & ( clk ! = 64 ) & & ( clk ! = 40 ) ) {
PrintAndLogEx ( FAILED , " supported clock rates are " _YELLOW_ ( " 16, 32, 40, 64 " ) " got " _RED_ ( " %d " ) " \n " , clk ) ;
2020-12-04 07:14:18 +08:00
return PM3_EINVARG ;
}
2020-12-09 21:48:10 +08:00
char cardtype [ 16 ] = { " T55x7 " } ;
if ( q5 ) {
snprintf ( cardtype , sizeof ( cardtype ) , " Q5/T5555 " ) ;
2020-12-04 07:14:18 +08:00
}
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( SUCCESS , " Preparing to clone EM4102 to " _YELLOW_ ( " %s " ) " tag with ID " _GREEN_ ( " %010 " PRIX64 ) " (RF/%d) " , cardtype , id , clk ) ;
2020-12-04 07:14:18 +08:00
// NOTE: We really should pass the clock in as a separate argument, but to
// provide for backwards-compatibility for older firmware, and to avoid
// having to add another argument to CMD_LF_EM410X_WRITE, we just store
// the clock rate in bits 8-15 of the card value
struct {
uint8_t card ;
uint8_t clock ;
uint32_t high ;
uint32_t low ;
} PACKED params ;
2020-12-09 21:48:10 +08:00
params . card = ( q5 ) ? 0 : 1 ;
params . clock = clk ;
2020-12-04 07:14:18 +08:00
params . high = ( uint32_t ) ( id > > 32 ) ;
params . low = ( uint32_t ) id ;
clearCommandBuffer ( ) ;
SendCommandNG ( CMD_LF_EM410X_WRITE , ( uint8_t * ) & params , sizeof ( params ) ) ;
PacketResponseNG resp ;
WaitForResponse ( CMD_LF_EM410X_WRITE , & resp ) ;
switch ( resp . status ) {
case PM3_SUCCESS : {
PrintAndLogEx ( SUCCESS , " Done " ) ;
2020-12-09 21:48:10 +08:00
PrintAndLogEx ( HINT , " Hint: try " _YELLOW_ ( " `lf em 410x reader` " ) " to verify " ) ;
2020-12-04 07:14:18 +08:00
break ;
}
default : {
PrintAndLogEx ( WARNING , " Something went wrong " ) ;
break ;
}
}
return resp . status ;
}
static command_t CommandTable [ ] = {
2020-12-09 21:48:10 +08:00
{ " help " , CmdHelp , AlwaysAvailable , " This help " } ,
//{"demod", CmdEMdemodASK, IfPm3Lf, "Extract ID from EM410x tag on antenna)"},
{ " demod " , CmdEM410xDemod , AlwaysAvailable , " demodulate a EM410x tag from the GraphBuffer " } ,
{ " reader " , CmdEM410xReader , IfPm3Lf , " attempt to read and extract tag data " } ,
{ " sim " , CmdEM410xSim , IfPm3Lf , " simulate EM410x tag " } ,
{ " brute " , CmdEM410xBrute , IfPm3Lf , " reader bruteforce attack by simulating EM410x tags " } ,
{ " watch " , CmdEM410xWatch , IfPm3Lf , " watches for EM410x 125/134 kHz tags (option 'h' for 134) " } ,
{ " spoof " , CmdEM410xSpoof , IfPm3Lf , " watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134) " } ,
{ " clone " , CmdEM410xClone , IfPm3Lf , " write EM410x UID to T55x7 or Q5/T5555 tag " } ,
2020-12-04 07:14:18 +08:00
{ NULL , NULL , NULL , NULL }
} ;
static int CmdHelp ( const char * Cmd ) {
( void ) Cmd ; // Cmd is not used so far
CmdsHelp ( CommandTable ) ;
return PM3_SUCCESS ;
}
int CmdLFEM410X ( const char * Cmd ) {
clearCommandBuffer ( ) ;
return CmdsParse ( CommandTable , Cmd ) ;
}