diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a05c4d1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +## v1.0.0 +* First public release (December 18, 2022) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fd54084 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.14) +project(nfc-srix-programmer C) +set(CMAKE_C_STANDARD 99) + +# Use PkgConfig to find libnfc +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBNFC REQUIRED libnfc) +include_directories(${LIBNFC_INCLUDE_DIRS}) +link_directories(${LIBNFC_LIBRARY_DIRS}) +add_definitions(${LIBNFC_CFLAGS_OTHER}) + + +# main +add_executable(nfc-srix-programmer main.c logging.c nfc_utils.c) +target_link_libraries(nfc-srix-programmer ${LIBNFC_LIBRARIES}) + +# Read EEPROM content +add_executable(read_eeprom_content read_eeprom_content.c logging.c nfc_utils.c) +target_link_libraries(read_eeprom_content ${LIBNFC_LIBRARIES}) + +# Read Tag info +add_executable(read_tag_info read_tag_info.c logging.c nfc_utils.c) +target_link_libraries(read_tag_info ${LIBNFC_LIBRARIES}) + +# Write EEPROM to a file +add_executable(write_eeprom_to_file write_eeprom_to_file.c logging.c nfc_utils.c) +target_link_libraries(write_eeprom_to_file ${LIBNFC_LIBRARIES}) + +# Read EEPROM from a file +add_executable(read_eeprom_file read_eeprom_file.c logging.c nfc_utils.c) +target_link_libraries(read_eeprom_file ${LIBNFC_LIBRARIES}) + +# modify_block +add_executable(modify_block modify_block.c logging.c nfc_utils.c) +target_link_libraries(modify_block ${LIBNFC_LIBRARIES}) + +# Write EEPROM file to NFC tag +add_executable(write_to_tag write_to_tag.c logging.c nfc_utils.c) +target_link_libraries(write_to_tag ${LIBNFC_LIBRARIES}) + +# reset OTP +add_executable(otp_reset otp_reset.c logging.c nfc_utils.c) +target_link_libraries(otp_reset ${LIBNFC_LIBRARIES}) + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1c9819e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019-2020 Giacomo Ferretti + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0649dad --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# NFC SRIX Programmer + +An CLI NFC application for reading, writing, analyzing, NFC ST SRI512 and SRIX4K tags. + +## Prerequisites + +* [libnfc](https://github.com/nfc-tools/libnfc) + +## Build + +You can use the provided `build.sh` or follow these simple steps: + +```bash +mkdir build +cd build +cmake .. +make +``` + +## Features + +* Read EEPROM content +* Read NFC Tag information (such UID, Serial number, System bloks etc...) +* Write EEPROM to a file +* Read EEPROM from a file +* Modify block manually +* Write EEPROM file to NFC tag +* Reset OTP bits + +## Screenshots + +Main Menu +Read EEPROM Content +Read EEPROM Content + +## Config file + +* `tag_type=x4k;` [x4k|512] Select SRIX4K or SRI512 tag type [default: x4k] +* `print_columns=1;` [1|2] erint on one or two columns [default: 1] +* `verbose=off;` [on|off] Enable verbose - print debugging data [default: off] +* `skip_confirmation=off;` [on|off] Skip All confirmation input [default: off] + +## Supported tags + +* `SRI512` - ISO14443B-2 ST SRx Tag IC 13.56MHz with 2 binary counters, 5 OTP blocks and anti-collision with 512-bit EEPROM in 16 Bloks +* `SRIX4` - ISO14443B-2 ST SRx or ISO14443B-3 ST SRxTag IC 13.56MHz with 2 binary counters, 5 OTP blocks and anti-collision with 4096-bit EEPROM in 128 Blocks + +## Note on writing tags! + +Compliant ST SRx tags have some blocks that, once changed,cannot be changed back to their original value.Example Counters Blocks 5 and 6. +Before writing a tag, make sure you're aware of this. +# nfc-srix-programmer diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..e0d93a4 --- /dev/null +++ b/build.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Create build folder +mkdir build + +# Copy config file +cp ./config build/config + +cd build + +# Create commands folder +mkdir commands + +TEMP_DIR=$(mktemp -d build.XXXXXXXX) +cd "${TEMP_DIR}" + +# Build +cmake ../../ +make + +# Copy executables +mv nfc-srix-programmer ../ +mv read_eeprom_content ../commands/ +mv read_tag_info ../commands/ +mv write_eeprom_to_file ../commands/ +mv read_eeprom_file ../commands/ +mv modify_block ../commands/ +mv write_to_tag ../commands/ +mv otp_reset ../commands/ + +# Cleanup +cd ../ +rm -fr "${TEMP_DIR}" diff --git a/config b/config new file mode 100644 index 0000000..645d0ed --- /dev/null +++ b/config @@ -0,0 +1,4 @@ +tag_type=x4k; +print_columns=1; +verbose=off; +skip_confirmation=off; diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..3e98508 --- /dev/null +++ b/logging.c @@ -0,0 +1,85 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "logging.h" + +bool verbose_status = false; +int verbosity_level = 0; + +void set_verbose(bool setting) { + verbose_status = setting; +} + +void set_verbosity(int level) { + verbosity_level = level; +} + +int lverbose(const char * restrict format, ...) { + if (!verbose_status) { + return 0; + } + + va_list args; + va_start(args, format); + int ret = vprintf(format, args); + va_end(args); + + return ret; +} + +int lverbose_lvl(int level, const char * restrict format, ...) { + if (level < verbosity_level) { + return 0; + } + + va_list args; + va_start(args, format); + int ret = vprintf(format, args); + va_end(args); + + return ret; +} + +int lerror(const char * restrict format, ...) { + fprintf(stderr, BOLD); + fprintf(stderr, RED); + fprintf(stderr, "ERROR: "); + fprintf(stderr, RESET); + + va_list args; + va_start(args, format); + int ret = vfprintf(stderr, format, args); + va_end(args); + + return ret; +} + +int lwarning(const char * restrict format, ...) { + fprintf(stderr, BOLD); + fprintf(stderr, YELLOW); + fprintf(stderr, "WARNING: "); + fprintf(stderr, RESET); + + va_list args; + va_start(args, format); + int ret = vfprintf(stderr, format, args); + va_end(args); + + return ret; +} \ No newline at end of file diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..9ffb27c --- /dev/null +++ b/logging.h @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOGGING_H +#define LOGGING_H + +// Foreground +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define NOTE "\033[36m" +#define BLUE "\e[34m" + +// Styles +#define RESET "\033[0m" +#define BOLD "\033[1m" +#define DIM "\033[2m" + +extern bool verbose_status; +extern int verbosity_level; +void set_verbose(bool); +void set_verbosity(int); +int lverbose(const char * restrict, ...); +int lverbose_lvl(int, const char * restrict, ...); +int lerror(const char * restrict, ...); +int lwarning(const char * restrict, ...); + +#endif // LOGGING_H diff --git a/main.c b/main.c new file mode 100644 index 0000000..13ba91a --- /dev/null +++ b/main.c @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + +int main() { + + int choice = 0; + + while(true){ + + printf("\n\n[NFC SRIX Programmer]\n\n"); + + printf(GREEN "1) " RESET "Search for NFC devices\n" ); + printf(GREEN "2) " RESET "Read EEPROM content\n" ); + printf(GREEN "3) " RESET "Read NFC Tag information\n" ); + printf(GREEN "4) " RESET "Write EEPROM to a file\n" ); + printf(GREEN "5) " RESET "Read EEPROM from a file\n" ); + printf(GREEN "6) " RESET "Modify block manually\n" ); + printf(GREEN "7) " RESET "Write EEPROM file to NFC tag\n" ); + printf(GREEN "8) " RESET "Reset OTP Blocks\n" ); + printf(GREEN "9) " RESET "Modify config file\n" ); + printf(GREEN "0) " RESET "Exit\n" ); + + printf(YELLOW "\n>>> Choose an option: " RESET); + scanf("%d", &choice); + + switch (choice){ + case 1: system("nfc-list"); break; + case 2: system("./commands/read_eeprom_content"); break; + case 3: system("./commands/read_tag_info"); break; + case 4: system("./commands/write_eeprom_to_file"); break; + case 5: system("./commands/read_eeprom_file"); break; + case 6: system("./commands/modify_block"); break; + case 7: system("./commands/write_to_tag"); break; + case 8: system("./commands/otp_reset"); break; + case 9: system("nano ./config"); break; + case 0: exit(0); + } + + } + + return 0; +} \ No newline at end of file diff --git a/modify_block.c b/modify_block.c new file mode 100644 index 0000000..9657fd2 --- /dev/null +++ b/modify_block.c @@ -0,0 +1,252 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + + +int main(int argc, char *argv[], char *envp[]) { + + + // Default options + set_verbose(false); + bool skip_confirmation = false; + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + + // skip_confirmation + if (strcmp(setting[3].value, "on") == 0) { + skip_confirmation = true; + } + + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + + + + // Ask for block adress + + printf(YELLOW ">>> Enter Block address [ex 0A]:" RESET); + + unsigned int block_addr; + scanf("%x", &block_addr); + + /* + if (!block_addr > 2) { + lerror("Invalid block address %d. Exiting...\n", 1); + exit(0); + } + + */ + + + // Read target blocks + printf("Reading Block...\n"); + + uint8_t block_bytes[4] = {}; + uint8_t block_bytes_read = nfc_srix_read_block(reader, block_bytes, block_addr); + + // Check for errors + if (block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", 1); + lverbose("Received %d bytes instead of 4.\n", block_bytes_read); + close_nfc(context, reader); + exit(1); + } + + + // print block + printf("[%02X] %02X %02X %02X %02X" DIM " --- %s\n" RESET, block_addr, block_bytes[0], block_bytes[1], block_bytes[2], block_bytes[3], srix_get_block_type(block_addr)); + + + + // Ask for new value for the block + + printf(YELLOW ">>> Enter hexadecimal value without Space or \"0x\": " RESET); + + unsigned int block_new_value; + scanf("%8x", &block_new_value); + if (!block_new_value) { + lerror("Invalid hexadecimal value %d. Exiting...\n", 1); + exit(0); + } + + + // info + //printf(BOLD NOTE "Note on writing tags!\n" RESET); + lwarning("Note on writing tags!\n"); + printf("Compliant ST SRx tags have some blocks that, once changed, \ncannot be changed back to their original value. \nExample Counters Blocks 5 and 6. \nBefore writing a tag, make sure you're aware of this.\n"); + + + printf(YELLOW ">>> This action is irreversible. Are you sure? [Y/N]: " RESET); + + char c = 'n'; + scanf(" %c", &c); + if (c != 'Y' && c != 'y') { + printf("\nExiting...\n"); + exit(0); + } + + + // Write Block + nfc_write_block(reader, block_new_value, block_addr); + + + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file diff --git a/nfc_utils.c b/nfc_utils.c new file mode 100644 index 0000000..3add66b --- /dev/null +++ b/nfc_utils.c @@ -0,0 +1,129 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "nfc_utils.h" +#include "logging.h" + +const nfc_modulation nmISO14443B = { + .nmt = NMT_ISO14443B, + .nbr = NBR_106, +}; + +const nfc_modulation nmISO14443B2SR = { + .nmt = NMT_ISO14443B2SR, + .nbr = NBR_106, +}; + +void log_command_sent(const uint8_t *command, size_t num_bytes) { + if (verbosity_level < 2) { + return; + } + + printf("TX >> "); + for (unsigned int i = 0; i < num_bytes; i++) { + printf("%02X ", command[i]); + } + printf("\n"); +} + +void log_command_received(const uint8_t *command, size_t num_bytes) { + if (verbosity_level < 2) { + return; + } + + if (num_bytes > MAX_RESPONSE_LEN) { + return; + } + + printf("RX << "); + for (unsigned int i = 0; i < num_bytes; i++) { + printf("%02X ", command[i]); + } + printf("\n"); +} + +size_t nfc_transceive_bytes(nfc_device *reader, const uint8_t *tx_data, size_t tx_size, uint8_t *rx_data) { + log_command_sent(tx_data, tx_size); + + size_t rx_size = nfc_initiator_transceive_bytes(reader, tx_data, tx_size, rx_data, sizeof(rx_data), 0); + + if (rx_data != NULL) { + log_command_received(rx_data, rx_size); + } + + return rx_size; +} + +size_t nfc_srix_get_uid(nfc_device *reader, uint8_t *rx_data) { + uint8_t cmd[1] = {SR_GET_UID_COMMAND}; + return nfc_transceive_bytes(reader, cmd, sizeof(cmd), rx_data); +} + +size_t nfc_srix_read_block(nfc_device *reader, uint8_t *rx_data, uint8_t block) { + uint8_t cmd[2] = {SR_READ_BLOCK_COMMAND}; + cmd[1] = block; + return nfc_transceive_bytes(reader, cmd, sizeof(cmd), rx_data); +} + +size_t nfc_srix_write_block(nfc_device *reader, uint8_t *rx_data, uint8_t block, const uint8_t *data) { + uint8_t cmd[6] = {SR_WRITE_BLOCK_COMMAND}; + cmd[1] = block; + cmd[2] = data[0]; + cmd[3] = data[1]; + cmd[4] = data[2]; + cmd[5] = data[3]; + return nfc_transceive_bytes(reader, cmd, sizeof(cmd), rx_data); +} + +void nfc_write_block(nfc_device *pnd, uint32_t block, uint8_t block_num) { + uint8_t bytes[4] = {}; + bytes[0] = block >> 24u; + bytes[1] = block >> 16u; + bytes[2] = block >> 8u; + bytes[3] = block >> 0u; + + printf("Writing block %02X... ", block_num); + nfc_srix_write_block(pnd, NULL, block_num, bytes); + printf("Done!\n"); +} + +void nfc_write_block_bytes(nfc_device *pnd, uint8_t *block, uint8_t block_num) { + printf("Writing block %02X... ", block_num); + nfc_srix_write_block(pnd, NULL, block_num, block); + printf("Done!\n"); +} + +char *srix_get_block_type(uint8_t block_num) { + if (block_num < 5) { + return "Resettable OTP bits"; + } else if (block_num < 7) { + return "Count down counter"; + } else if (block_num < 16) { + return "Lockable EEPROM"; + } else { + return "EEPROM"; + } +} + +uint32_t eeprom_bytes_to_block(uint8_t *dump, uint8_t block) { + return (dump[(block*4)] << 24u) + (dump[(block*4)+1] << 16u) + (dump[(block*4)+2] << 8u) + dump[(block*4)+3]; +} + +void close_nfc(nfc_context *context, nfc_device *reader) { + if (reader != NULL) nfc_close(reader); + if (context != NULL) nfc_exit(context); +} \ No newline at end of file diff --git a/nfc_utils.h b/nfc_utils.h new file mode 100644 index 0000000..b73f548 --- /dev/null +++ b/nfc_utils.h @@ -0,0 +1,53 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NFC_SRIX_UTILS_H__ +#define __NFC_SRIX_UTILS_H__ + +/* Macros */ +#define MAX_DEVICE_COUNT 16 +#define MAX_TARGET_COUNT 1 +#define MAX_RESPONSE_LEN 10 +#define SRIX4K_EEPROM_SIZE 512 +#define SRIX4K_EEPROM_BLOCKS 128 +#define SRI512_EEPROM_SIZE 64 +#define SRI512_EEPROM_BLOCKS 16 +#define SR_GET_UID_COMMAND 0x0B +#define SR_READ_BLOCK_COMMAND 0x08 +#define SR_WRITE_BLOCK_COMMAND 0x09 + +/* Constants */ +extern const nfc_modulation nmISO14443B; +extern const nfc_modulation nmISO14443B2SR; + +/* Logging */ +void log_command_sent(const uint8_t *command, size_t num_bytes); +void log_command_received(const uint8_t *command, size_t num_bytes); + +/* Commands */ +size_t nfc_transceive_bytes(nfc_device *reader, const uint8_t *tx_data, size_t tx_size, uint8_t *rx_data); +size_t nfc_srix_get_uid(nfc_device *reader, uint8_t *rx_data); +size_t nfc_srix_read_block(nfc_device *reader, uint8_t *rx_data, uint8_t block); +size_t nfc_srix_write_block(nfc_device *reader, uint8_t *rx_data, uint8_t block, const uint8_t *data); +void nfc_write_block(nfc_device *pnd, uint32_t block, uint8_t block_num); +void nfc_write_block_bytes(nfc_device *pnd, uint8_t *block, uint8_t block_num); + +/* Utilities */ +char *srix_get_block_type(uint8_t block_num); +uint32_t eeprom_bytes_to_block(uint8_t *dump, uint8_t block); +void close_nfc(nfc_context *context, nfc_device *reader); + +#endif // __NFC_SRIX_UTILS_H__ diff --git a/otp_reset.c b/otp_reset.c new file mode 100644 index 0000000..fc1f432 --- /dev/null +++ b/otp_reset.c @@ -0,0 +1,249 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + +int main(int argc, char *argv[], char *envp[]) { + + // Default options + bool skip_confirmation = false; + set_verbose(false); + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + // skip_confirmation + if (strcmp(setting[3].value, "on") == 0) { + skip_confirmation = true; + } + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + // Read necessary blocks + uint32_t otp_blocks[6] = {}; + printf("Reading OTP blocks...\n"); + for (uint8_t i = 0; i < 6; i++) { + // Skip block 0x05 + if (i == 5) i++; + + uint8_t block_bytes[4] = {}; + uint8_t block_bytes_read = nfc_srix_read_block(reader, block_bytes, i); + + // Check for errors + if (block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", i); + lverbose("Received %d bytes instead of 4.\n", block_bytes_read); + close_nfc(context, reader); + exit(1); + } + + otp_blocks[i] = block_bytes[0] << 24u | block_bytes[1] << 16u | block_bytes[2] << 8u | block_bytes[3]; + + //printf("%08X\n", otp_blocks[i]); + printf("[%02X] %08X \n", i, otp_blocks[i]); + } + + // Check if already reset + bool otp_already_reset = true; + for (uint8_t i = 0; i < 5; i++) { + if (otp_blocks[i] != 0xFFFFFFFF) otp_already_reset = false; + } + + if (otp_already_reset) { + printf("OTP area already reset.\n"); + close_nfc(context, reader); + exit(0); + } + + uint32_t block_6 = (otp_blocks[5] << 24u) | (otp_blocks[5] << 16u) | (otp_blocks[5] << 8u) | otp_blocks[5]; + printf("OTP resets available: %u\n", block_6 >> 21u); + + block_6 -= (1u << 21u); + printf("OTP resets remaining after this operation: %u\n", block_6 >> 21u); + + // Reverse + block_6 = (otp_blocks[5] << 24u) | (otp_blocks[5] << 16u) | (otp_blocks[5] << 8u) | otp_blocks[5]; + + // Show differences + printf("[%02X] %08X -> FFFFFFFF\n", 0x00, otp_blocks[0]); + printf("[%02X] %08X -> FFFFFFFF\n", 0x01, otp_blocks[1]); + printf("[%02X] %08X -> FFFFFFFF\n", 0x02, otp_blocks[2]); + printf("[%02X] %08X -> FFFFFFFF\n", 0x03, otp_blocks[3]); + printf("[%02X] %08X -> FFFFFFFF\n", 0x04, otp_blocks[4]); + printf("[%02X] %08X -> %08X\n", 0x06, otp_blocks[5], block_6); + + // Ask for confirmation + if (!skip_confirmation) { + printf(YELLOW ">>> This action is irreversible. Are you sure? [Y/N]: " RESET); + char c = 'n'; + scanf(" %c", &c); + if (c != 'Y' && c != 'y') { + printf("\nExiting...\n"); + exit(0); + } + } + + // Write Block 06 first to trigger an Auto erase cycle + nfc_write_block(reader, block_6, 0x06); + nfc_write_block(reader, 0xFFFFFFFF, 0x00); + nfc_write_block(reader, 0xFFFFFFFF, 0x01); + nfc_write_block(reader, 0xFFFFFFFF, 0x02); + nfc_write_block(reader, 0xFFFFFFFF, 0x03); + nfc_write_block(reader, 0xFFFFFFFF, 0x04); + + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file diff --git a/read_eeprom_content.c b/read_eeprom_content.c new file mode 100644 index 0000000..e3e914a --- /dev/null +++ b/read_eeprom_content.c @@ -0,0 +1,209 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + +int main(int argc, char *argv[], char *envp[]) { + + // Default options + bool skip_confirmation = false; + uint32_t eeprom_size = SRIX4K_EEPROM_SIZE; + uint32_t eeprom_blocks_amount = SRIX4K_EEPROM_BLOCKS; + set_verbose(false); + + + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + //check tag type + if (strcmp(setting[0].value, "512") == 0) { + eeprom_size = SRI512_EEPROM_SIZE; + eeprom_blocks_amount = SRI512_EEPROM_BLOCKS; + } + + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + + // Read EEPROM + uint8_t *eeprom_bytes = malloc(sizeof(uint8_t) * eeprom_size); + lverbose("Reading %d blocks...\n", eeprom_blocks_amount); + for (int i = 0; i < eeprom_blocks_amount; i++) { + uint8_t *current_block = eeprom_bytes + (i * 4); + uint8_t block_bytes_read = nfc_srix_read_block(reader, current_block, i); + + // Check for errors + if (block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", i); + lverbose("Received %d bytes instead of 4.\n", block_bytes_read); + close_nfc(context, reader); + exit(1); + } + + printf("[%02X] ", i); + printf("%02X %02X %02X %02X ", current_block[0], current_block[1], current_block[2], current_block[3]); + printf(DIM); + printf("--- %s\n", srix_get_block_type(i)); + printf(RESET); + } + + + + + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file diff --git a/read_eeprom_file.c b/read_eeprom_file.c new file mode 100644 index 0000000..f1bea09 --- /dev/null +++ b/read_eeprom_file.c @@ -0,0 +1,157 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + +int main(int argc, char *argv[], char *envp[]) { + + // Default options + int print_columns = 1; + uint32_t eeprom_size = SRIX4K_EEPROM_SIZE; + uint32_t eeprom_blocks_amount = SRIX4K_EEPROM_BLOCKS; + set_verbose(false); + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + if (strcmp(setting[0].value, "512") == 0) { + eeprom_size = SRI512_EEPROM_SIZE; + eeprom_blocks_amount = SRI512_EEPROM_BLOCKS; + } + + print_columns = (int) strtol(setting[1].value, NULL, 10); + + // Check columns + if (print_columns != 1 && print_columns != 2) { + lwarning("Invalid number of columns. Input is %d, but must be either 1 or 2.\nUsing default value.\n", print_columns); + print_columns = 1; + } + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + + + + // Start for read dump file + char file_path[100]; + uint8_t *eeprom_bytes = malloc(sizeof(uint8_t) * eeprom_size); + + printf(YELLOW "\n>>> Enter file name: " RESET); + scanf("%100s", file_path); + + + // Open file + lverbose("Reading \"%s\"...\n", file_path); + FILE *fp = fopen(file_path, "rb"); + if (fp == NULL) { + lerror("Cannot open \"%s\". Exiting...\n", file_path); + exit(1); + } + + // Check file size + int fd = fileno(fp); + struct stat file_stat; + if (fstat(fd, &file_stat) < 0) { + lerror("Error doing fstat. Exiting...\n"); + exit(1); + } + if (file_stat.st_size < eeprom_size) { + lerror("File wrong size, expected %llu but read %llu. Exiting...\n", eeprom_size, file_stat.st_size); + exit(1); + } + + // Read file + fseek(fp, 0, SEEK_SET); + if (fread(eeprom_bytes, eeprom_size, 1, fp) != 1) { + lerror("Error encountered while reading file. Exiting...\n"); + exit(1); + } + fclose(fp); + + if (print_columns == 1) { // Single column print + for (int i = 0; i < eeprom_blocks_amount; i++) { + uint8_t *block = eeprom_bytes + (i * 4); + printf("[%02X] %02X %02X %02X %02X" DIM " --- %s\n" RESET, i, block[0], block[1], block[2], block[3], srix_get_block_type(i)); + } + } else { // Double column print + for (int i = 0; i < eeprom_blocks_amount; i += 2) { + uint8_t *block_1 = eeprom_bytes + (i * 4); + uint8_t *block_2 = eeprom_bytes + ((i+1) * 4); + printf(DIM "%19s --- " RESET "[%02X] %02X %02X %02X %02X %02X %02X %02X %02X [%02X]" DIM " --- %s\n" RESET, + srix_get_block_type(i), i, block_1[0], block_1[1], block_1[2], block_1[3], block_2[0], block_2[1], block_2[2], block_2[3], i+1, srix_get_block_type(i+1)); + } + } + + return 0; +} \ No newline at end of file diff --git a/read_tag_info.c b/read_tag_info.c new file mode 100644 index 0000000..a635466 --- /dev/null +++ b/read_tag_info.c @@ -0,0 +1,285 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + + +int main(int argc, char *argv[], char *envp[]) { + + + // Default options + char *output_path = NULL; + uint32_t eeprom_size = SRIX4K_EEPROM_SIZE; + uint32_t eeprom_blocks_amount = SRIX4K_EEPROM_BLOCKS; + set_verbose(false); + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + //check tag type + if (strcmp(setting[0].value, "512") == 0) { + eeprom_size = SRI512_EEPROM_SIZE; + eeprom_blocks_amount = SRI512_EEPROM_BLOCKS; + } + + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + // Read UID + uint8_t uid_rx_bytes[MAX_RESPONSE_LEN] = {}; + uint8_t uid_bytes_read = nfc_srix_get_uid(reader, uid_rx_bytes); + + // Check for errors + if (uid_bytes_read != 8) { + lerror("Error while reading UID. Exiting...\n"); + lverbose("Received %d bytes instead of 8.\n", uid_bytes_read); + close_nfc(context, reader); + exit(1); + } + + // Convert to uint64 + uint64_t uid = (uint64_t) uid_rx_bytes[0] | (uint64_t) uid_rx_bytes[1] << 8u |(uint64_t) uid_rx_bytes[2] << 16u | (uint64_t) uid_rx_bytes[3] << 24u |(uint64_t) uid_rx_bytes[4] << 32u |(uint64_t) uid_rx_bytes[5] << 40u |(uint64_t) uid_rx_bytes[6] << 48u | (uint64_t) uid_rx_bytes[7] << 56u; + uint64_t uid_fix_reding = (uint64_t) uid_rx_bytes[7] | (uint64_t) uid_rx_bytes[6] << 8u |(uint64_t) uid_rx_bytes[5] << 16u | (uint64_t) uid_rx_bytes[4] << 24u |(uint64_t) uid_rx_bytes[3] << 32u |(uint64_t) uid_rx_bytes[2] << 40u |(uint64_t) uid_rx_bytes[1] << 48u | (uint64_t) uid_rx_bytes[0] << 56u; + + // Print UID + + printf("UID: %016" PRIX64 "\n", uid_fix_reding); + + // Convert uint64 to binary char array + char uid_binary[65] = {}; + for (unsigned int i = 0; i < sizeof(uid); i++) { + uint8_t tmp = (uid >> (sizeof(uid) - 1 - i) * 8u) & 0xFFu; + sprintf(uid_binary + i * 8 + 0, "%c", tmp & 0x80u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 1, "%c", tmp & 0x40u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 2, "%c", tmp & 0x20u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 3, "%c", tmp & 0x10u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 4, "%c", tmp & 0x08u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 5, "%c", tmp & 0x04u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 6, "%c", tmp & 0x02u ? '1' : '0'); + sprintf(uid_binary + i * 8 + 7, "%c", tmp & 0x01u ? '1' : '0'); + } + + printf("├── Prefix: %02" PRIX64 "\n", uid >> 56u); + printf("├── IC manufacturer code: %02" PRIX64, (uid >> 48u) & 0xFFu); + switch ((uid >> 48u) & 0xFFu) { + case 0x02: + printf(" (STMicroelectronics)\n"); + break; + default: + printf(" (unknown)\n"); + } + + // Print 6bit IC code + char ic_code[7] = {}; + memcpy(ic_code, uid_binary + 16, 6); + printf("├── IC code: %s [%" PRIu64 "]\n", ic_code, (uid >> 42u) & 0x7u); + + + // Print 42bit unique serial number + char unique_serial_number[43] = {}; + memcpy(unique_serial_number, uid_binary + 22, 42); + + // Print UID Binary + printf("├── 42bit UID Binary: %s\n", unique_serial_number); + + printf("└── Serial number: %" PRIu64 "\n" , uid & 0x3FFFFFFFFFFu); + + + + // Print System blocks + uint8_t system_block_bytes[4] = {}; + uint8_t system_block_bytes_read = nfc_srix_read_block(reader, system_block_bytes, 0xFF); + + // Check for errors + if (system_block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", 0xFF); + lverbose("Received %d bytes instead of 4.\n", system_block_bytes_read); + close_nfc(context, reader); + exit(1); + } + + uint32_t system_block = system_block_bytes[3] << 24u | system_block_bytes[2] << 16u | system_block_bytes[1] << 8u | system_block_bytes[0]; + + printf("\nSystem block: %02X %02X %02X %02X\n", system_block_bytes[3], system_block_bytes[2], system_block_bytes[1], system_block_bytes[0]); + printf("├── CHIP_ID: %02X\n", system_block_bytes[0]); + printf("├── ST reserved: %02X%02X\n", system_block_bytes[1], system_block_bytes[2]); + printf("└── OTP_Lock_Reg:\n"); + for (uint8_t i = 24; i < 32; i++) { + if (i == 31) { + printf(" └── b%d = %d - ", i, (system_block >> i) & 1u); + } else { + printf(" ├── b%d = %d - ", i, (system_block >> i) & 1u); + } + + if (i == 24) { + printf("Block 07 and 08 are "); + } else { + printf("Block %02X is ", i - 16); + } + + if (((system_block >> i) & 1u) == 0) { + printf(RED); + printf("LOCKED\n"); + printf(RESET); + } else { + printf(GREEN); + printf("unlocked\n"); + printf(RESET); + } + } + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file diff --git a/screenshots/main.png b/screenshots/main.png new file mode 100644 index 0000000..987da91 Binary files /dev/null and b/screenshots/main.png differ diff --git a/screenshots/read_eeprom_content.png b/screenshots/read_eeprom_content.png new file mode 100644 index 0000000..85f5c3e Binary files /dev/null and b/screenshots/read_eeprom_content.png differ diff --git a/screenshots/read_tag_info.png b/screenshots/read_tag_info.png new file mode 100644 index 0000000..46dc1cc Binary files /dev/null and b/screenshots/read_tag_info.png differ diff --git a/write_eeprom_to_file.c b/write_eeprom_to_file.c new file mode 100644 index 0000000..080cb58 --- /dev/null +++ b/write_eeprom_to_file.c @@ -0,0 +1,245 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + +int main(int argc, char *argv[], char *envp[]) { + + // Default options + bool skip_confirmation = false; + uint32_t eeprom_size = SRIX4K_EEPROM_SIZE; + uint32_t eeprom_blocks_amount = SRIX4K_EEPROM_BLOCKS; + set_verbose(false); + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + //check tag type + if (strcmp(setting[0].value, "512") == 0) { + eeprom_size = SRI512_EEPROM_SIZE; + eeprom_blocks_amount = SRI512_EEPROM_BLOCKS; + } + + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + // skip_confirmation + if (strcmp(setting[3].value, "on") == 0) { + skip_confirmation = true; + } + + + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + + + // ask for file path + char output_path[100]; + + printf(YELLOW "\n>>> Enter file name: " RESET); + scanf("%100s", output_path); + + // Check if file already exists + FILE *file = fopen(output_path, "r"); + if (file) { + fclose(file); + + // Ask for confirmation + if (!skip_confirmation) { + printf("\"%s\" already exists.\n", output_path); + printf(YELLOW "\n>>> Do you want to overwrite it? [Y/N]:" RESET); + char c = 'n'; + scanf(" %c", &c); + if (c != 'Y' && c != 'y') { + printf("Exiting...\n"); + exit(0); + } + } + } + + + // Read EEPROM + uint8_t *eeprom_bytes = malloc(sizeof(uint8_t) * eeprom_size); + lverbose("Reading %d blocks...\n", eeprom_blocks_amount); + for (int i = 0; i < eeprom_blocks_amount; i++) { + uint8_t *current_block = eeprom_bytes + (i * 4); + uint8_t block_bytes_read = nfc_srix_read_block(reader, current_block, i); + + // Check for errors + if (block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", i); + lverbose("Received %d bytes instead of 4.\n", block_bytes_read); + close_nfc(context, reader); + exit(1); + } + + printf("[%02X] ", i); + printf("%02X %02X %02X %02X ", current_block[0], current_block[1], current_block[2], current_block[3]); + printf(DIM); + printf("--- %s\n", srix_get_block_type(i)); + printf(RESET); + } + + + // export dump to file + FILE *fp = fopen(output_path, "w"); + fwrite(eeprom_bytes, eeprom_size, 1, fp); + fclose(fp); + + printf("Written dump to \"%s\".\n", output_path); + + + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file diff --git a/write_to_tag.c b/write_to_tag.c new file mode 100644 index 0000000..39b219c --- /dev/null +++ b/write_to_tag.c @@ -0,0 +1,292 @@ +/* + * Copyright 2022 Hassan ABBAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" +#include "nfc_utils.h" + +int main(int argc, char *argv[], char *envp[]) { + + // Default options + bool skip_confirmation = false; + uint32_t eeprom_size = SRIX4K_EEPROM_SIZE; + uint32_t eeprom_blocks_amount = SRIX4K_EEPROM_BLOCKS; + set_verbose(false); + + + // read config + typedef struct {char key[20];char value[10];} settings; + + // file pointer variable for accessing the file + FILE *file_config; + + // attempt to open config in read mode to read the file contents + file_config = fopen("config", "r"); + + // if the file failed to open, exit with an error message and status + if (file_config == NULL){ + lerror("Cannot open config file \"%s\". Exiting...\n", file_config); + exit(1); + } + + // array of structs for storing the settings data from the file + settings setting[100]; + + // read will be used to ensure each line/record is read correctly + int read = 0; + + int records = 0; + + // read all records from the file and store them into the settings array + do { + read = fscanf(file_config,"%20[^=]=%6[^;];\n", setting[records].key, setting[records].value); + + if (read == 2) records++; + + if (read != 2 && !feof(file_config)){ + lerror("Config file format incorrect. Exiting...\n"); + exit(1); + } + + // if there was an error reading from the file exit with an error message + // and status + if (ferror(file_config)){ + lerror("Error reading Config file. Exiting...\n"); + exit(1); + return 1; + } + + } while (!feof(file_config)); + + fclose(file_config); + + + // Parse options + + //check tag type + if (strcmp(setting[0].value, "512") == 0) { + eeprom_size = SRI512_EEPROM_SIZE; + eeprom_blocks_amount = SRI512_EEPROM_BLOCKS; + } + + + // check verbose + if (strcmp(setting[2].value, "on") == 0) { + set_verbose(true); + } + + // skip_confirmation + if (strcmp(setting[3].value, "on") == 0) { + skip_confirmation = true; + } + + + // Initialize NFC + nfc_context *context = NULL; + nfc_device *reader = NULL; + nfc_init(&context); + if (context == NULL) { + lerror("Unable to init libnfc. Exiting...\n"); + exit(1); + } + + // Display libnfc version + lverbose("libnfc version: %s\n", nfc_version()); + + // Search for readers + lverbose("Searching for readers... "); + nfc_connstring connstrings[MAX_DEVICE_COUNT] = {}; + size_t num_readers = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); + lverbose("found %zu.\n", num_readers); + + // Check if no readers are available + if (num_readers == 0) { + lerror("No readers available. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Print out readers + for (unsigned int i = 0; i < num_readers; i++) { + if (i == num_readers - 1) { + lverbose("└── "); + } else { + lverbose("├── "); + } + lverbose("[%d] %s\n", i, connstrings[i]); + } + lverbose("Opening %s...\n", connstrings[0]); + + // Open first reader + reader = nfc_open(context, connstrings[0]); + if (reader == NULL) { + lerror("Unable to open NFC device. Exiting...\n"); + close_nfc(context, reader); + exit(1); + } + + // Set opened NFC device to initiator mode + if (nfc_initiator_init(reader) < 0) { + lerror("nfc_initiator_init => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + + lverbose("NFC reader: %s\n", nfc_device_get_name(reader)); + + nfc_target target_key[MAX_TARGET_COUNT]; + + /* + * This is a known bug from libnfc. + * To read ISO14443B2SR you have to initiate first ISO14443B to configure internal registers. + * + * https://github.com/nfc-tools/libnfc/issues/436#issuecomment-326686914 + */ + lverbose("Searching for ISO14443B targets... found %d.\n", nfc_initiator_list_passive_targets(reader, nmISO14443B, target_key, MAX_TARGET_COUNT)); + + lverbose("Searching for ISO14443B2SR targets..."); + int ISO14443B2SR_targets = nfc_initiator_list_passive_targets(reader, nmISO14443B2SR, target_key, MAX_TARGET_COUNT); + lverbose(" found %d.\n", ISO14443B2SR_targets); + + // Check for tags + if (ISO14443B2SR_targets == 0) { + printf("Waiting for tag...\n"); + + // Infinite select for tag + if (nfc_initiator_select_passive_target(reader, nmISO14443B2SR, NULL, 0, target_key) <= 0) { + lerror("nfc_initiator_select_passive_target => %s\n", nfc_strerror(reader)); + close_nfc(context, reader); + exit(1); + } + } + + + char file_path[100]; + uint8_t *dump_bytes = malloc(sizeof(uint8_t) * eeprom_size); + + printf(YELLOW "\n>>> Enter file name: " RESET); + scanf("%100s", file_path); + + + // Open file + lverbose("Reading \"%s\"...\n", file_path); + FILE *fp = fopen(file_path, "rb"); + if (fp == NULL) { + lerror("Cannot open \"%s\". Exiting...\n", file_path); + exit(1); + } + + // Check file size + int fd = fileno(fp); + struct stat file_stat; + if (fstat(fd, &file_stat) < 0) { + lerror("Error doing fstat. Exiting...\n"); + exit(1); + } + if (file_stat.st_size < eeprom_size) { + lerror("File wrong size, expected %llu but read %llu. Exiting...\n", eeprom_size, file_stat.st_size); + exit(1); + } + + // Read file + fseek(fp, 0, SEEK_SET); + if (fread(dump_bytes, eeprom_size, 1, fp) != 1) { + lerror("Error encountered while reading file. Exiting...\n"); + exit(1); + } + fclose(fp); + + // Read EEPROM + uint8_t *eeprom_bytes = malloc(sizeof(uint8_t) * eeprom_size); + lverbose("Reading %d blocks...\n", eeprom_blocks_amount); + for (int i = 0; i < eeprom_blocks_amount; i++) { + uint8_t *current_block = eeprom_bytes + (i * 4); + uint8_t block_bytes_read = nfc_srix_read_block(reader, current_block, i); + + // Check for errors + if (block_bytes_read != 4) { + lerror("Error while reading block %d. Exiting...\n", i); + lverbose("Received %d bytes instead of 4.\n", block_bytes_read); + close_nfc(context, reader); + exit(1); + } + } + + // Preview write + bool is_equal = true; + for (uint8_t i = 7; i < eeprom_blocks_amount; i++) { + uint32_t dump_block = dump_bytes[(i*4)+0] << 24u | dump_bytes[(i*4)+1] << 16u | dump_bytes[(i*4)+2] << 8u | dump_bytes[(i*4)+3]; + uint32_t eeprom_block = eeprom_bytes[(i*4)+0] << 24u | eeprom_bytes[(i*4)+1] << 16u | eeprom_bytes[(i*4)+2] << 8u | eeprom_bytes[(i*4)+3]; + + if (dump_block != eeprom_block) { + is_equal = false; + printf("[%02X] %08X -> %08X\n", i, dump_block, eeprom_block); + } + } + + if (!is_equal) { + // Ask for confirmation + if (!skip_confirmation) { + printf(YELLOW ">>> This action is irreversible. Are you sure? [Y/N]: " RESET); + char c = 'n'; + scanf(" %c", &c); + if (c != 'Y' && c != 'y') { + printf("Exiting...\n"); + exit(0); + } + } + + // Ask for OTP area + bool write_otp_area = true; + if (!skip_confirmation) { + printf(YELLOW ">>> Writing to OTP area do you want to continue? [Y/N]: " RESET); + char c = 'n'; + scanf(" %c", &c); + if (c != 'Y' && c != 'y') { + write_otp_area = false; + } + } + + + + for (uint8_t i = 0; i < eeprom_blocks_amount; i++) { + // Skip critical sectors + if (!write_otp_area) { + continue; + } + + uint32_t dump_block = dump_bytes[(i*4)+0] << 24u | dump_bytes[(i*4)+1] << 16u | dump_bytes[(i*4)+2] << 8u | dump_bytes[(i*4)+3]; + uint32_t eeprom_block = eeprom_bytes[(i*4)+0] << 24u | eeprom_bytes[(i*4)+1] << 16u | eeprom_bytes[(i*4)+2] << 8u | eeprom_bytes[(i*4)+3]; + + if (dump_block != eeprom_block) { + nfc_write_block(reader, dump_block, i); + } + } + } else { + printf("This dump is already written to this NFC tag.\n"); + } + + // Close NFC + close_nfc(context, reader); + + return 0; +} \ No newline at end of file