From 66803e442d3cedbb5c128e486904720759c12ec1 Mon Sep 17 00:00:00 2001
From: Kevin-Nakamoto <root@YKURIHASHI2-NB.na.ff-americas.com>
Date: Wed, 22 Jan 2020 13:55:37 -0500
Subject: [PATCH] Support LTO-CM read block and add CM-type into hf lto info
 command.

---
 client/cmdhflist.c |   4 +-
 client/cmdhflto.c  | 197 ++++++++++++++++++++++++++++++++++-----------
 client/cmdhflto.h  |   3 +-
 3 files changed, 152 insertions(+), 52 deletions(-)

diff --git a/client/cmdhflist.c b/client/cmdhflist.c
index 2fde8492c..b35e4d622 100644
--- a/client/cmdhflist.c
+++ b/client/cmdhflist.c
@@ -1119,7 +1119,7 @@ void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
             snprintf(exp, size, "READWORD");
             break;
         case (LTO_READBLOCK & 0xF0):
-            snprintf(exp, size, "READBLOCK");
+            snprintf(exp, size, "READBLOCK(%d)", cmd[1]);
             break;
         case LTO_READBLOCK_CONT:
             snprintf(exp, size, "READBLOCK CONT");
@@ -1128,7 +1128,7 @@ void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
             snprintf(exp, size, "WRITEWORD");
             break;
         case (LTO_WRITEBLOCK & 0xF0):
-            snprintf(exp, size, "WRITEBLOCK");
+            snprintf(exp, size, "WRITEBLOCK(%d)", cmd[1]);
             break;
         case LTO_HALT:
             snprintf(exp, size, "HALT");
diff --git a/client/cmdhflto.c b/client/cmdhflto.c
index 39e9cf30b..e82fee381 100644
--- a/client/cmdhflto.c
+++ b/client/cmdhflto.c
@@ -33,6 +33,18 @@ static int usage_lto_info(void) {
     return PM3_SUCCESS;
 }
 
+static int usage_lto_rdbl(void) {
+    PrintAndLogEx(NORMAL, "Usage:  hf lto rdbl [h] s <start block> e <end block>");
+    PrintAndLogEx(NORMAL, "Options:");
+    PrintAndLogEx(NORMAL, "           h     this help");
+    PrintAndLogEx(NORMAL, "           s     start block in decimal >= 0");
+    PrintAndLogEx(NORMAL, "           e     end block in decimal <= 254");
+    PrintAndLogEx(NORMAL, "");
+    PrintAndLogEx(NORMAL, "Examples:");
+    PrintAndLogEx(NORMAL, "           hf lto rdbl s 0 e 254 - Read data block from 0 to 254");
+    return PM3_SUCCESS;
+}
+
 static void lto_switch_off_field(void) {
     SendCommandMIX(CMD_HF_ISO14443A_READER, 0, 0, 0, NULL, 0);
 }
@@ -42,15 +54,22 @@ static void lto_switch_on_field(void) {
 }
 
 // send a raw LTO-CM command, returns the length of the response (0 in case of error)
-static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16_t *response_len, bool addcrc, bool verbose) {
+static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16_t *response_len, bool addcrc, bool is7bits, bool verbose) {
 
     uint64_t arg0 = ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS;
-    uint32_t arg1 = (len == 1) ? (7 << 16) : 0;
-    arg1 |= len;
+    uint32_t arg1;
 
     if (addcrc) {
         arg0 |= ISO14A_APPEND_CRC;
     }
+   
+    if (is7bits) { 
+        arg1 = 7 << 16;
+    } else {
+        arg1 = 0;
+    }
+
+    arg1 |= len;
 
     SendCommandOLD(CMD_HF_ISO14443A_READER, arg0, arg1, 0, cmd, len);
     PacketResponseNG resp;
@@ -62,7 +81,6 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16
 
     if (resp.oldarg[0] == *response_len) {
         *response_len = resp.oldarg[0];
-
         if (*response_len > 0) {
             memcpy(response, resp.data.asBytes, *response_len);
         }
@@ -70,40 +88,36 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16
         if (verbose) PrintAndLogEx(WARNING, "Wrong response length (%d != %" PRIu64 ")", *response_len, resp.oldarg[0]);
         return PM3_ESOFT;
     }
+
     return PM3_SUCCESS;
 }
 
 // select a LTO-CM tag. Send WUPA and RID.
-static int lto_select(uint8_t *id_response, uint8_t id_len, bool verbose) {
+static int lto_select(uint8_t *id_response, uint8_t id_len, uint8_t *type_response, bool verbose) {
     // Todo: implement anticollision
 
     uint8_t resp[] = {0, 0};
     uint16_t resp_len;
     uint8_t wupa_cmd[] = {LTO_REQ_STANDARD};
-    uint8_t select_cmd[] = {LTO_SELECT, 0x20};
-    uint8_t select_1_cmd[] = {LTO_SELECT, 0x70, 0, 0, 0, 0, 0};
-
-    lto_switch_on_field();
+    uint8_t select_sn_cmd[] = {LTO_SELECT, 0x20};
+    uint8_t select_cmd[] = {LTO_SELECT, 0x70, 0, 0, 0, 0, 0};
 
     resp_len = 2;
-    int status = lto_send_cmd_raw(wupa_cmd, sizeof(wupa_cmd), resp, &resp_len, false, verbose);
+    int status = lto_send_cmd_raw(wupa_cmd, sizeof(wupa_cmd), type_response, &resp_len, false, true, verbose);
     if (status == PM3_ETIMEOUT || status == PM3_ESOFT) {
-        lto_switch_off_field();
         return PM3_ESOFT; // WUPA failed
     }
 
     resp_len = id_len;
-    status = lto_send_cmd_raw(select_cmd, sizeof(select_cmd), id_response, &resp_len, false, verbose);
+    status = lto_send_cmd_raw(select_sn_cmd, sizeof(select_sn_cmd), id_response, &resp_len, false, false, verbose);
     if (status == PM3_ETIMEOUT || status == PM3_ESOFT) {
-        lto_switch_off_field();
-        return PM3_EWRONGANSVER; // SELECT failed
+        return PM3_EWRONGANSVER; // REQUEST SERIAL NUMBER failed
     }
 
-    memcpy(select_1_cmd + 2, id_response, sizeof(select_1_cmd) - 2);
+    memcpy(select_cmd + 2, id_response, sizeof(select_cmd) - 2);
     resp_len = 1;
-    status = lto_send_cmd_raw(select_1_cmd, sizeof(select_1_cmd), resp, &resp_len, true, verbose);
+    status = lto_send_cmd_raw(select_cmd, sizeof(select_cmd), resp, &resp_len, true, false, verbose);
     if (status == PM3_ETIMEOUT || status == PM3_ESOFT || resp[0] != 0x0A) {
-        lto_switch_off_field();
         return PM3_EWRONGANSVER; // SELECT failed
     }
 
@@ -132,63 +146,148 @@ static int CmdHfLTOInfo(const char *Cmd) {
         return PM3_EINVARG;
     }
 
-    return infoLTO(false);
+    return infoLTO(true);
 }
 
 int infoLTO(bool verbose) {
 
     clearCommandBuffer();
 
+    lto_switch_on_field();
+
     uint8_t serial_number[5];
     uint8_t serial_len = sizeof(serial_number);
-    int ret_val = lto_select(serial_number, serial_len, verbose);
+    uint8_t type_info[2];
+    
+    int ret_val = lto_select(serial_number, serial_len, type_info, verbose);
     lto_switch_off_field();
 
     if (ret_val == PM3_SUCCESS) {
         PrintAndLogEx(NORMAL, "");
-        PrintAndLogEx(SUCCESS, " UID : " _YELLOW_("%s"), sprint_hex_inrow(serial_number, sizeof(serial_number)));
-        PrintAndLogEx(SUCCESS, "TYPE : ");
-        // todo:  add printing of all configuration
-    } else {
-        if (verbose) PrintAndLogEx(WARNING, "LTO-CM card select failed");
+        PrintAndLogEx(SUCCESS, "TYPE INFO: " _YELLOW_("%s"), sprint_hex_inrow(type_info, sizeof(type_info))); 
+        PrintAndLogEx(SUCCESS, "UID: " _YELLOW_("%s"), sprint_hex_inrow(serial_number, sizeof(serial_number)));
     }
 
-    /* read block:
-
-        SendCommandNG(CMD_HF_THINFILM_READ, NULL, 0);
-        PacketResponseNG resp;
-        if (!WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500)) {
-            PrintAndLogEx(WARNING, "timeout while waiting for reply.");
-            return PM3_ETIMEOUT;
-        }
-
-        if (resp.status == PM3_SUCCESS) {
-            if (resp.length == 16 || resp.length == 32)  {
-                print_barcode(resp.data.asBytes, resp.length, verbose);
-            } else {
-                if (verbose)
-                    PrintAndLogEx(WARNING, "Response is wrong length. (%d)", resp.length);
-
-                return PM3_ESOFT;
-            }
-        }
-
-        return resp.status;
-    */
     return ret_val;
 }
 
 static int CmdHfLTOList(const char *Cmd) {
     (void)Cmd; // Cmd is not used so far
-    CmdTraceList("14a");
-//    CmdTraceList("lto");
+    CmdTraceList("lto");
     return PM3_SUCCESS;
 }
 
+static int lto_rdbl(uint8_t blk, uint8_t *block_responce, uint8_t *block_cnt_responce, bool verbose) {
+
+    uint16_t resp_len;
+    uint8_t rdbl_cmd[] = {0x30, blk};
+    uint8_t rdbl_cnt_cmd[] ={0x80};
+
+    resp_len = 18;
+    int status = lto_send_cmd_raw(rdbl_cmd, sizeof(rdbl_cmd), block_responce, &resp_len, true, false, verbose);
+    if (status == PM3_ETIMEOUT || status == PM3_ESOFT ) {
+        return PM3_EWRONGANSVER; // READ BLOCK failed
+    }
+
+    resp_len = 18;
+    status = lto_send_cmd_raw(rdbl_cnt_cmd, sizeof(rdbl_cnt_cmd), block_cnt_responce, &resp_len, false, false, verbose);
+    if (status == PM3_ETIMEOUT || status == PM3_ESOFT ) {
+        return PM3_EWRONGANSVER; // READ BLOCK CONTINUE failed
+    }
+
+    return PM3_SUCCESS;
+}
+
+int rdblLTO(uint8_t st_blk, uint8_t end_blk, bool verbose) {
+
+    clearCommandBuffer();
+
+    lto_switch_on_field();
+
+    uint8_t serial_number[5];
+    uint8_t serial_len = sizeof(serial_number);
+    uint8_t type_info[2];
+    int ret_val = lto_select(serial_number, serial_len, type_info, verbose);
+
+    if (ret_val != PM3_SUCCESS) {
+        lto_switch_off_field();
+        return ret_val;
+    }
+
+    uint8_t block_data_d00_d15[18];
+    uint8_t block_data_d16_d31[18];
+    uint8_t block_data[32];
+
+    for(uint8_t i = st_blk; i < end_blk + 1; i++) {
+       
+        ret_val = lto_rdbl(i, block_data_d00_d15,  block_data_d16_d31, verbose);
+
+        if (ret_val == PM3_SUCCESS) {
+            //Remove CRCs
+            for (int t = 0; t < 16; t++) {
+                block_data[t] = block_data_d00_d15[t];
+                block_data[t + 16] = block_data_d16_d31[t];
+            }
+            
+            PrintAndLogEx(SUCCESS, "BLK%03d: " _YELLOW_("%s"), i, sprint_hex_inrow(block_data, sizeof(block_data)));
+        } else {
+            lto_switch_off_field();
+            return ret_val; 
+        }
+    }
+
+    lto_switch_off_field();
+    return ret_val;
+}
+
+static int CmdHfLTOReadBlock(const char *Cmd) {
+
+    uint8_t cmdp = 0;
+    bool errors = false;
+    uint8_t st_blk = 0;
+    uint8_t end_blk = 254;
+
+    while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
+        switch (tolower(param_getchar(Cmd, cmdp))) {
+            case 'h':
+                return usage_lto_rdbl();
+            case 's':
+                st_blk = param_get8(Cmd, cmdp+1);
+                if ( end_blk < st_blk ) {
+                    errors = true;
+                    break;
+                }
+                cmdp += 2;
+                break;
+
+            case 'e':
+                end_blk = param_get8(Cmd, cmdp+1);
+                if ( end_blk < st_blk ) { 
+                    errors = true;
+                    break;                                                                                                                                                                                                                                   }
+                cmdp += 2;
+                break;
+
+            default:
+                PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
+                errors = true;
+                break;
+        }
+    }
+
+    //Validations
+    if (errors) {
+        usage_lto_rdbl();
+        return PM3_EINVARG;
+    }
+
+    return rdblLTO(st_blk, end_blk, true);
+}
+
 static command_t CommandTable[] = {
     {"help",    CmdHelp,             AlwaysAvailable, "This help"},
     {"info",    CmdHfLTOInfo,        IfPm3Iso14443a, "Tag information"},
-//    {"rdbl",    CmdHfLTOReadBlock,   IfPm3Iso14443a, "Read block"},
+    {"rdbl",    CmdHfLTOReadBlock,   IfPm3Iso14443a, "Read block"},
 //    {"wrbl",    CmdHfLTOWriteBlock,  IfPm3Iso14443a, "Write block"},
 //    {"sim",     CmdHfLTOSim,         IfPm3Iso14443a, "<uid> Simulate LTO-CM tag"},
     {"list",    CmdHfLTOList,        AlwaysAvailable, "List LTO-CM history"},
diff --git a/client/cmdhflto.h b/client/cmdhflto.h
index 52c22da0d..d54eb75f1 100644
--- a/client/cmdhflto.h
+++ b/client/cmdhflto.h
@@ -14,7 +14,8 @@
 #include "common.h"
 
 int infoLTO(bool verbose);
-
+int rdblLTO(uint8_t st_blk, uint8_t end_blk, bool verbose);
 int CmdHFLTO(const char *Cmd);
 
 #endif
+