From 1a21f0e3c99d53a3f29ab271faa4dcbf174ea785 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 21 Nov 2023 12:21:22 +0900 Subject: [PATCH 1/2] Umarsh transport cards parser added --- applications/main/nfc/application.fam | 9 ++ .../main/nfc/plugins/supported_cards/umarsh.c | 131 ++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/umarsh.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index c49170d08..dcfde7c36 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -83,6 +83,15 @@ App( sources=["plugins/supported_cards/two_cities.c"], ) +App( + appid="umarsh_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="umarsh_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/umarsh.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c new file mode 100644 index 000000000..080b4bcbc --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -0,0 +1,131 @@ +/* + * Parser for Umarsh card (Russia). + * + * Copyright 2023 Leptoptilos + * Thanks https://github.com/krolchonok for the provided dumps and their analysis + * + * Note: All meaningful data is stored in sectors 0, 8 and 12, reading data + * from which is possible only with the B key. The key B for these sectors + * is unique for each card. To get it, you should use a nested attack. + * More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "core/core_defines.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include +#include +#include +#include + +#define TAG "Umarsh" + +static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + if(data->type != MfClassicType1k) break; + + const uint32_t ticket_sector = 8; + + const uint8_t ticket_sector_start_block_number = + mf_classic_get_first_block_num_of_sector(ticket_sector); + + //Validate specific for Umarsh ticket sector header + const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0]; + + uint32_t header = block_start_ptr[0] << 24 | block_start_ptr[1] << 16 | + block_start_ptr[2] << 8 | block_start_ptr[3]; + if(header != 0xFFFFFF7F && header != 0xFEFFFF7F && header != 0xE3FFFF7F) break; + + // Data parsing from block 1 + block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0]; + uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) | + (block_start_ptr[12] & 0x0f); + uint32_t card_number = (block_start_ptr[8] << 24 | block_start_ptr[9] << 16 | + block_start_ptr[10] << 8 | block_start_ptr[11]) & + 0x3FFFFFFF; + uint8_t refill_counter = (block_start_ptr[7]); + + if(card_number == 0) break; + + // Data parsing from block 2 + block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0]; + uint16_t expiry_date = (block_start_ptr[0] << 8 | block_start_ptr[1]); + uint32_t terminal_number = + (block_start_ptr[3] << 16 | block_start_ptr[4] << 8 | block_start_ptr[5]); + uint16_t last_refill_date = (block_start_ptr[6] << 8 | block_start_ptr[7]); + uint16_t balance = (block_start_ptr[8] << 8 | block_start_ptr[9]) & 0x7FFF; + + FuriHalRtcDateTime expiry_datetime; + expiry_datetime.year = 2000 + (expiry_date >> 9); + expiry_datetime.month = expiry_date >> 5 & 0x0F; + expiry_datetime.day = expiry_date & 0x1F; + + FuriHalRtcDateTime last_refill_datetime; + last_refill_datetime.year = 2000 + (last_refill_date >> 9); + last_refill_datetime.month = last_refill_date >> 5 & 0x0F; + last_refill_datetime.day = last_refill_date & 0x1F; + + furi_string_printf( + parsed_data, + "\e#Umarsh\nCard number: %lu\nRegion: %02u\nBalance: %u RUR\nTerminal number: %lu\nRefill counter: %u\nLast refill: %02u.%02u.%u\nExpires: %02u.%02u.%u", + card_number, + region_number, + balance, + terminal_number, + refill_counter, + last_refill_datetime.day, + last_refill_datetime.month, + last_refill_datetime.year, + expiry_datetime.day, + expiry_datetime.month, + expiry_datetime.year); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin umarsh_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = umarsh_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &umarsh_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* umarsh_plugin_ep() { + return &umarsh_plugin_descriptor; +} From b6ad07b47cc17c8e0c391d065454ad20cfc498fb Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 21 Nov 2023 13:18:18 +0900 Subject: [PATCH 2/2] Umarsh parser: kopecks support added --- applications/main/nfc/plugins/supported_cards/umarsh.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c index 080b4bcbc..5e29f5c03 100644 --- a/applications/main/nfc/plugins/supported_cards/umarsh.c +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -64,7 +64,7 @@ static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { // Data parsing from block 1 block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0]; uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) | - (block_start_ptr[12] & 0x0f); + (block_start_ptr[12] & 0x0F); uint32_t card_number = (block_start_ptr[8] << 24 | block_start_ptr[9] << 16 | block_start_ptr[10] << 8 | block_start_ptr[11]) & 0x3FFFFFFF; @@ -78,7 +78,8 @@ static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { uint32_t terminal_number = (block_start_ptr[3] << 16 | block_start_ptr[4] << 8 | block_start_ptr[5]); uint16_t last_refill_date = (block_start_ptr[6] << 8 | block_start_ptr[7]); - uint16_t balance = (block_start_ptr[8] << 8 | block_start_ptr[9]) & 0x7FFF; + uint16_t balance_rub = (block_start_ptr[8] << 8 | block_start_ptr[9]) & 0x7FFF; + uint8_t balance_kop = block_start_ptr[10] & 0x7F; FuriHalRtcDateTime expiry_datetime; expiry_datetime.year = 2000 + (expiry_date >> 9); @@ -92,10 +93,11 @@ static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_printf( parsed_data, - "\e#Umarsh\nCard number: %lu\nRegion: %02u\nBalance: %u RUR\nTerminal number: %lu\nRefill counter: %u\nLast refill: %02u.%02u.%u\nExpires: %02u.%02u.%u", + "\e#Umarsh\nCard number: %lu\nRegion: %02u\nBalance: %u.%u RUR\nTerminal number: %lu\nRefill counter: %u\nLast refill: %02u.%02u.%u\nExpires: %02u.%02u.%u", card_number, region_number, - balance, + balance_rub, + balance_kop, terminal_number, refill_counter, last_refill_datetime.day,