From 6200fbc55e6c1eb336ac9e5da6ce4ea41adea17b Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:25:46 +1100 Subject: [PATCH 001/150] T55xx Guide --- doc/T5577_Guide.md | 694 +++++++++++++++++++++++++++++++++++++++ doc/t55xx_block0.png | Bin 0 -> 26953 bytes doc/t55xx_clock0_cfg.png | Bin 0 -> 62447 bytes doc/t55xx_mem_map.png | Bin 0 -> 44034 bytes 4 files changed, 694 insertions(+) create mode 100644 doc/T5577_Guide.md create mode 100644 doc/t55xx_block0.png create mode 100644 doc/t55xx_clock0_cfg.png create mode 100644 doc/t55xx_mem_map.png diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md new file mode 100644 index 000000000..cdffadd20 --- /dev/null +++ b/doc/T5577_Guide.md @@ -0,0 +1,694 @@ +# T5577 Introduction Guide + +### Based on RRG proxmark3 fork. + +### Ver.1 8 Sep 2019 + +| Contents | +| ----------------------------------------------------------------------------------- | +| [Part 1](#part-1) | +| [Introduction](#introduction) | +| [T5577 Overview](#t5577-overview) | +| [What data is on my T5577](#what-data-is-on-my-t5577) | +| [Read and Write Blocks of Data](#read-and-write-blocks-of-data) | +| [Exercise 1](#exercise-1) | +| [How do I use a password](#how-do-i-use-a-password) | +| | +| [Part 2 – Configuration Blocks](#part-2-configuration-blocks) | +| [The configuration Block – Block 0 Page 0](#the-configuration-block-block-0-page-0) | +| [Exercise 2](#exercise-2) | +| [The configuration Block – Block 3 Page 1](#the-configuration-block-block-3-page-1) | + +# Part 1 + +## Introduction + +The T5577 is a generic LF (Low Frequency) RFID card the is used in the +125 Khz frequency space. It is a good card to use to learn about RFID and +learn how to use the proxmark3. + +It is highly recommend that when learning about RFID that learning how +to read the data sheets be near the top of the list. It can be very hard +as the data sheet will hold the information you need, but you don’t yet +know what it means. As such, I will attempt to point to sections of the +data sheet and would highly advise that you look at the data sheet as +you go. Overtime the data sheet may change, as a result things may not +always be reference correctly. + +As at writing this guide, the data sheet can be found at : + + + +This guide is not a how do I clone document. It is meant to help people +learn how to use the T5577 and in the process learn about rfid and the +proxmark3. + +Throughout this guide I will give examples. It is recommended that you +try these as we go. To do so, have a blank T5577 card that you can use +for this purpose. + +## T5577 Overview + +The T5577 is a chip that can hold data and a configuration (Section +4.12). + +In the diagram below, all white blocks can hold data. Some can be used +for a second purpose, such as the ‘password’ and ‘traceability data’. +The ‘Configuration Data’ and ‘Analog front end option setup’ will tell +the chip how to behave. + +![](./t55xx_mem_map.png) + + + +## What data is on my T5577 + +Let’s have a look and see what a card might look in the proxmark3 +software. Since we can change the configuration of how the T5577 will +output data, the proxmark3 software need to work out how to interpreted +the data it receives, we do this with the following command. + +It should be noted that the T5577 has many clones. As such the default +setup of each card may be different. If the tractability data is +present, then this will vary based on the batch of cards. + +Always run this command when you place a t5577 on the proxmark3. In all +examples shown, it will be assumed you have run the detect command. +``` +[usb] pm3 --> lf t55xx detect +``` +You should see a results simular to the following: +``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x000880E0 + Downlink Mode : default/fixed bit length +``` +Now that the proxmark3 has detected a T55x7 chip, and found some +information about it, we should be able to see all the data on the chip. +``` +[usb] pm3 --> lf t55xx dump +``` +Your results should look similar to the following: +``` +[+] Reading Page 0: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] Reading Page 1: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H +[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +``` +I will cover the meaning of this data as we go, but for now, lets keep +it simple. + +## Read and Write Blocks of Data + +The basic function of using the proxmark3 with rfid cards is to read and +write data. This reading and writing must be done in the correct way +needed for the chip (and its configuration). Lucky for us, the +developers have done a great job and gave us commands. What we need to +know is that with the T5577 data is read/written one complete block at a +time. Each block holds 32 bits of data (hence the binary output shown) + +Since we know that the card has data and configuration blocks, lets say +away from those while we learn how to read and write. I suggest you +follow along and perform each command and check the results as we go. + +We can store our own data in blocks 1-7 (remember that block 7 will be +needed if we want to set a password). + +(Don’t forget to run the detect command: lf t55xx detect, and ensure you +can see the card) + +1) Check what is stored in block 1. The following command can be read + as, run a low frequency (lf) command for the T55xx chip (t55xx) and + read block (b) number 1. + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + ``` + Note: Depending on the history of your card your data may vary, but + should match the dump data. + +2) Write some new data into block 1 on the card. + + We use the d option to supply the data ‘12345678’ + ``` + [usb] pm3 --> lf t55xx write b 1 d 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x12345678 + ``` +3) Now, lets check if the data was written. + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx + ``` +4) The data is written in Hexadecimal. A single hex digit holds 4 bits + of data. So to store 32 bits in a block we need to supply 8 hex + digits (8 \* 4 = 32). If you are familiar with hex and binary do a + little bit of home work to learn. The following is a quick start. + + | Hex | Binary | Decimal | + |:---:|:------:|:-------:| + | 0 | 0000 | 0 | + | 1 | 0001 | 1 | + | 2 | 0010 | 2 | + | 3 | 0011 | 3 | + | 4 | 0100 | 4 | + | 5 | 0101 | 5 | + | 6 | 0110 | 6 | + | 7 | 0111 | 7 | + | 8 | 1000 | 8 | + | 9 | 1001 | 9 | + | A | 1010 | 10 | + | B | 1011 | 11 | + | C | 1100 | 12 | + | D | 1101 | 13 | + | E | 1110 | 14 | + | F | 1111 | 15 | + + To use all the bits we supply the data in Hex format and it will + always be 8 hex digits. + + Lets try and write 89ABCDEF + ``` + [usb] pm3 --> lf t55xx write b 1 d 89abcdef + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x89ABCDEF + ``` + and check + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... + ``` + +### Exercise 1 + +Using the read and write commands you have learnt see if you can make +the lf t55 dump command show the following data for blocks 1-7 (Page 0). +Do not write to block 0 or try and change the data on page 1. +``` +[usb] pm3 --> lf t55 dump +``` +result: +``` +[+] Reading Page 0: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... +[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] Reading Page 1: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H +[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +``` + +Practice reading and writing to blocks 1 to 7 until you are happy you +can do it and get the results you wanted (i.e. the data you want stored +is written to the block you want it stored in). + +## How do I use a password + +This can be a little tricky for beginners. +***If you forget your password you will lose access to your card***. + +To tell the T5577 to use a password we have to change the data in the +configuration block (0). To help learn this and make it as simple as I +can, please read and follow exactly. If your results DON’T match 100% as +required, please do not proceed. + +1) Lets start with a known card state and wipe the card. This will set + a default configuration to block 0 and set all the data in blocks + 1-7 to a default. + ``` + [usb] pm3 --> lf t55xx wipe + ``` + Result: + ``` + [=] Begin wiping T55x7 tag + + [=] Default configation block 000880E0 + [=] Writing page 0 block: 00 data: 0x000880E0 + [=] Writing page 0 block: 01 data: 0x00000000 + [=] Writing page 0 block: 02 data: 0x00000000 + [=] Writing page 0 block: 03 data: 0x00000000 + [=] Writing page 0 block: 04 data: 0x00000000 + [=] Writing page 0 block: 05 data: 0x00000000 + [=] Writing page 0 block: 06 data: 0x00000000 + [=] Writing page 0 block: 07 data: 0x00000000 + ``` + +2) Check that the card is in the desired state. + ``` + [usb] pm3 --> lf t55xx detect + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x000880E0 + Downlink Mode : default/fixed bit length + ``` + + If block 0 does not hold the hex data **0x00088040 resolve this + first before proceeding.** + +3) Set the password we want to use. For this example lets use the + password : ***12345678*** + + The password is saved in block 7 of page 0. + ``` + [usb] pm3 --> lf t55xx write b 7 d 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 07 data: 0x12345678 + ``` + +4) Lets verify both block 0 and block 7 + ``` + [usb] pm3 --> lf t55xx dump + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... + [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx + [+] Reading Page 1: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... + [+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H + [+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. + [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + ``` + ***Important : If block 0 and block 7 don’t match exactly, do not continue.*** + +5) Now we have a known configuration block and a known password of + 12345678, we are ready to tell the card to use the password. + + To do this the datasheet tells us we need to set the 28th + bit “PWD”. Check your datasheet and see the entire table (remember + the data sheet is your friend). + + ![](./t55xx_block0.png) + + We will cover other things in the configuration later. But the key + note here is we ONLY want to change bit 28 and nothing else. + + Current Block 0 : ***00088040*** + New Block 0 : ***00088050*** + + To understand what happened to get from 00088040 to 00088050 we need + to look at the binary data. + + While this can be confusing it is important to understand this as we + do more advanced things. + + Bit Location (28) + 000000000011111111112222222 ***2*** 2233 + 123456789012345678901234567 ***8*** 9012 + + | Hex Data | Binary Data | + |:--------:|:---------------------------------------| + | 00088040 | 000000000000100010000000010***0***0000 | + | 00088050 | 000000000000100010000000010***1***0000 | + + + + See how in the above we change the bit in location 28 from a 0 to 1 + 0 = No Password, 1 = Use Password + + Note how we did NOT change any other part of the configuration, only bit 28. + + To re-cap. + We put the card into a known configuration Block 0 : 00088040 + We set the a known password Block 7 : 12345678 + We altered the config data to tell the T5577 to use the password. + New Block 0 : 00088050 + + If you have completed all steps and have the exact same results, we are + ready to apply the new configuration. + ``` + [usb] pm3 --> lf t55xx write b 0 d 00088050 + ``` + result: + ``` + [=] Writing page 0 block: 00 data: 0x00088050 + ``` + +6) Lets check what happens when the password is set. + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + [!] Could not detect modulation automatically. Try setting it manually with 'lf t55xx config' + ``` + Note how the lf t55 detect no longer seems to work\! + + In this case, this is due to needing a password to read/write to the + card. + + Lets try again, but this time supply the password. We use the option + p followed by the password. + ``` + [usb] pm3 --> lf t55 detect p 12345678 + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00088050 + Downlink Mode : default/fixed bit length + ``` + +7) Write a block of data with a password + ``` + [usb] pm3 --> lf t55xx write b 1 d 1234abcd p 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x1234ABCD pwd: 0x12345678 + ``` + +8) Read a block of data with a password + + ***\*\*\*\* Important \*\*\*\**** + + ***Reading a T5577 block with a password when a password is not + enabled can result in locking the card. Please only use read with a + password when it is known that a password is in use.*** + + The proxmark3 has a safety check\! + ``` + [usb] pm3 --> lf t55xx read b 1 p 12345678 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [!] Safety check: Could not detect if PWD bit is set in config block. Exits. + ``` + + Note that the proxmark3 did not read the block, the safty kicked in + and wants us to confirm by supply the override option ‘o’. + + Lets try again with the ‘o’ option as we know the password is set. + ``` + [usb] pm3 --> lf t55xx read b 1 p 12345678 o + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [=] Safety check overridden - proceeding despite risk + [+] 01 | 1234ABCD | 00010010001101001010101111001101 | .4.. + ``` + This time, we can see the data we wrote to block 1 is found with the + read command. + +9) Remove the need to supply the password. + + To do this we need to clear Bit 28 (set to 0) in the config. We have + this from above. + + Remember if we don’t know the config and write this config to the + card, it will over write all other settings. This can recoved the + card, but will lose any settings you may want. So it’s a good idea + to read the config, and set bit 28 to 0, rather than just overwrite + the config and change the way the card works. + + In our examples we know what it should be : 00088040 + ``` + [usb] pm3 --> lf t55xx write b 0 d 00088040 p 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 00 data: 0x00088040 pwd: 0x12345678 + ``` + Now check if we can detect without a password + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00088040 + Downlink Mode : default/fixed bit length + ``` + Yes we can and we can see Block 0 is the correct config 00088040 + +# Part 2 – Configuration Blocks + +One of the things a lot of people have trouble with or miss, is that the +T5577 has two different and separate communications protocols, each with +their own sub-protocols. + + - Card to Reader + - Reader to Card + +In Card to Reader, the T5577 will encode its data using the settings +from Block 0 in Page 0. It will use this in both default read mode +(where is sends out the blocks from 1 to x on power up), as well as when +it responds to commands. + +In the Read To Card, the T5577 will encode the data using the settings +from Block 3 Page 1. If the command is not encoded correctly it will +ignore the command and revert back to default read mode. + +## The configuration Block – Block 0 Page 0 + +For this configuration the settings chosen will be for the purpose of +the card when used in production. E.G. If you want the card to act like +an EM4100, then we need to choose the settings that work like the +EM4100; same goes for others like HID. I am not going to cover these +here, rather use an example. Others have collect these and posted on the +forum. + +To get started lets look back at the tech sheet. + +![](./t55xx_clock0_cfg.png) + +The non-password protect EM4100 could have a block 0 config of 00148040, +so what does it mean. + +To decode this config, we need to look at it in binary +00000000000101001000000001000000. Note that it had 32 bits and the +config block 0 is 32 bits. Now we can break it down. + +| Bits | Purpose | Value | +| ------- | ---------------------- | ----------- | +| 0000 | Master Key | Nothing Set | +| 0000000 | Not used in Basic Mode | | +| 101 | Data Bit Rate | RF/64 | +| 0 | Not used in Basic Mode | | +| 01000 | Modulation | Manchester | +| 00 | PSKCF | RF/2 | +| 0 | AOR | Not Set | +| 0 | Not used in Basic Mode | | +| 010 | Max Block | 2 | +| 0 | Password | Not Set | +| 0 | ST Sequence Terminator | Not Set | +| 00 | Not used in Basic Mode | | +| 0 | Init Delay | Not Set | + +To get more detail on each item, read through the data sheet. + +Lets see how the proxmark3 can help us learn. We will assume the T5577 +is in the same state from Part 1, where we can write to the card with no +password set (if not, review and get you card back to this state). + +1) Lets turn you T5577 into an EM4100 with ID 1122334455 + ``` + [usb] pm3 --> lf em 410x_write 1122334455 1 + ``` + result: + ``` + [+] Writing T55x7 tag with UID 0x1122334455 (clock rate: 64) + #db# Started writing T55x7 tag ... + #db# Clock rate: 64 + #db# Tag T55x7 written with 0xff8c65298c94a940 + ``` + +2) Check this has work. + ``` + [usb] pm3 --> lf search + ``` + result: + ``` + [=] NOTE: some demods output possible binary + [=] if it finds something that looks like a tag + [=] False Positives ARE possible + [=] + [=] Checking for known tags... + + [+] EM410x pattern found + + EM TAG ID : 1122334455 + + Possible de-scramble patterns + + Unique TAG ID : 8844CC22AA + HoneyWell IdentKey { + DEZ 8 : 03359829 + DEZ 10 : 0573785173 + DEZ 5.5 : 08755.17493 + DEZ 3.5A : 017.17493 + DEZ 3.5B : 034.17493 + DEZ 3.5C : 051.17493 + DEZ 14/IK2 : 00073588229205 + DEZ 15/IK3 : 000585269781162 + DEZ 20/ZK : 08080404121202021010 + } + Other : 17493_051_03359829 + Pattern Paxton : 289899093 [0x11478255] + Pattern 1 : 5931804 [0x5A831C] + Pattern Sebury : 17493 51 3359829 [0x4455 0x33 0x334455] + + [+] Valid EM410x ID found! + + + [+] Chipset detection : T55xx found + + [+] Try `lf t55xx` commands + ``` + Looks good. + +3) Now lest see what the T5577 detect and info shows + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + [usb] pm3 --> lf t55 detect + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 5 - RF/64 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00148040 + Downlink Mode : default/fixed bit length + ``` + ``` + [usb] pm3 --> lf t55xx info + ``` + result: + ``` + + -- T55x7 Configuration & Tag Information -------------------- + ------------------------------------------------------------- + Safer key : 0 + reserved : 0 + Data bit rate : 5 - RF/64 + eXtended mode : No + Modulation : 8 - Manchester + PSK clock frequency : 0 - RF/2 + AOR - Answer on Request : No + OTP - One Time Pad : No + Max block : 2 + Password mode : No + Sequence Terminator : No + Fast Write : No + Inverse data : No + POR-Delay : No + ------------------------------------------------------------- + Raw Data - Page 0 + Block 0 : 0x00148040 00000000000101001000000001000000 + + Config block match : EM unique, Paxton + ------------------------------------------------------------- + ``` + We can see that the info gave us more information and confirmed what + we decoded by hand. But remember, the detect is still needed so the + proxmark3 software will know how to decode the info block. + + We can see that for the EM4100 emulation we have two blocks of data + (Max Block = 2). On the T5577 these will be Blocks 1 and 2. + +## Exercise 2 + +Using the skills form part 1, see if you can view the data in blocks 1 and 2. + +Note: the EM4100 ID of 1122334455 is encoded, so don’t expect to see + those bytes as such. To learn how to do that, you guessed it, find the + datasheet and review. + +At this point we have an EM4100 card. If we wanted to password protect +it, we can follow the password section and update the config from +00148040 to 00148050. + +***Important : Don’t forget to set a valid password in block 7 and remember it.*** + +## The configuration Block – Block 3 Page 1 diff --git a/doc/t55xx_block0.png b/doc/t55xx_block0.png new file mode 100644 index 0000000000000000000000000000000000000000..2220adf3f2a24b8d4ff26f254b8942fd0c4e9c96 GIT binary patch literal 26953 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 M#N Date: Tue, 24 Nov 2020 18:45:19 +1100 Subject: [PATCH 002/150] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e61260b54..d8fba7354 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| +|T55xx Guide | **[T55xx Guide](doc/T5577_Guide.md)** || ## Notes / helpful documents From 817f7d0a753544fa6bcfc0d1894c2a5704119264 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:49:02 +1100 Subject: [PATCH 003/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8fba7354..d9b48922e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -|T55xx Guide | **[T55xx Guide](doc/T5577_Guide.md)** || +|||**[T55xx Guide](doc/T5577_Guide.md)**| ## Notes / helpful documents From 2718620e334edace30ca5c63d873092a92bce239 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:49:54 +1100 Subject: [PATCH 004/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9b48922e..e7941e483 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -|||**[T55xx Guide](doc/T5577_Guide.md)**| +||**[T55xx Guide](/doc/T5577_Guide.md)**|| ## Notes / helpful documents From cfd848d9ded0a45944fce542ccc1df3f2acfa0d7 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:52:54 +1100 Subject: [PATCH 005/150] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7941e483..a3533ea85 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -||**[T55xx Guide](/doc/T5577_Guide.md)**|| +|||**[T55xx Guide](/doc/T5577_Guide.md)**| ## Notes / helpful documents From 878e9f594f86d0761b49a4ce73e67a353f9f66e5 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:55:35 +1100 Subject: [PATCH 006/150] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9bb8d98f..c1bda4ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added T55xx Guide to assist in learning how to use the T55xx chip (@mwalker33) - Fix 'hf iclass wrbl' - dealing with tags in unsecured vs secured pagemode now is correct (@iceman1001) - Change many commands to cliparser (@iceman1001, @tcprst, @mwalker33,...) - ... From 6c2c0108dff406f91f3cc400cf519f1f1b6ea999 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 24 Nov 2020 09:16:57 +0100 Subject: [PATCH 007/150] Readme: compact table --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a3533ea85..bc7e88854 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ |[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)| ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| -||**[JTAG](/doc/jtag_notes.md)**|| -|||**[T55xx Guide](/doc/T5577_Guide.md)**| +||**[JTAG](/doc/jtag_notes.md)**|[T55xx Guide](/doc/T5577_Guide.md)| ## Notes / helpful documents From f59d57aaabc65b21fb674e71fc1d269b82d96354 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:52:09 +0100 Subject: [PATCH 008/150] lf securakey - now uses cliparser --- client/src/cmdlfsecurakey.c | 97 ++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index b5aab5fa8..7315dc9cd 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -22,22 +22,10 @@ #include "parity.h" // for wiegand parity test #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_securakey_clone(void) { - PrintAndLogEx(NORMAL, "clone a Securakey tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf securakey clone [h] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " b : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf securakey clone b 7FCB400001ADEA5344300000")); - return PM3_SUCCESS; -} - //see ASKDemod for what args are accepted int demodSecurakey(bool verbose) { (void) verbose; // unused so far @@ -120,7 +108,7 @@ int demodSecurakey(bool verbose) { PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); - PrintAndLogEx(INFO, "Help the community identify this format further\n by sharing your tag on the pm3 forum or with forum members"); + PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); return PM3_SUCCESS; } @@ -137,36 +125,37 @@ static int CmdSecurakeyRead(const char *Cmd) { static int CmdSecurakeyClone(const char *Cmd) { - uint32_t blocks[4]; - bool errors = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey clone", + "clone a Securakey tag to a T55x7 tag.", + "lf securakey clone --raw 7FCB400001ADEA5344300000" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int datalen = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &datalen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_securakey_clone(); - case 'b': { - // skip first block, 3*4 = 12 bytes left - uint8_t rawhex[12] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; - - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + if (datalen > 0) { + if (datalen != 12) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } } + CLIParserFree(ctx); - if (errors || cmdp == 0) return usage_lf_securakey_clone(); + uint32_t blocks[4]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_40 | 3 << T55x7_MAXBLOCK_SHIFT; @@ -181,7 +170,35 @@ static int CmdSecurakeyClone(const char *Cmd) { } static int CmdSecurakeySim(const char *Cmd) { - PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); + PrintAndLogEx(INFO, _RED_("To be implemented, feel free to contribute!")); + return PM3_SUCCESS; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey sim", + "Enables simulation of viking card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.", + "lf securakey sim --raw 7FCB400001ADEA5344300000" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int datalen = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &datalen); + + if (datalen > 0) { + if (datalen != 12) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + CLIParserFree(ctx); return PM3_SUCCESS; } From d24e0436dc5106250d12366e8459c82a69734f02 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:52:55 +0100 Subject: [PATCH 009/150] hf 15 wrbl - missing some data output --- client/src/cmdhf15.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 593eaaece..3eebbce74 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -1725,7 +1725,7 @@ static int CmdHF15Write(const char *Cmd) { AddCrc15(req, reqlen); reqlen += 2; - PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data ", pagenum, pagenum); + PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data [ %s ] ", pagenum, pagenum, sprint_hex(req, reqlen)); PacketResponseNG resp; clearCommandBuffer(); From 3cb12356a28cd5e43708057d2925dfb8c8c5faa7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:53:29 +0100 Subject: [PATCH 010/150] textual --- client/src/loclass/elite_crack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index a559b4e7b..3defe5560 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -468,9 +468,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "-- High security custom key (Kcus) --"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); + PrintAndLogEx(SUCCESS, "Standard format " _GREEN_("%s"), sprint_hex(key64_stdformat, 8)); PrintAndLogEx(SUCCESS, "iClass format %s", sprint_hex(key64, 8)); if (master_key != NULL) From 04e0cdc9cf75656de3d00d556c7fb9323b7ffbae Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 15:40:13 +0100 Subject: [PATCH 011/150] lf viking - now uses cliparse --- client/src/cmdlfviking.c | 108 +++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 4a5f534ab..a301a2f59 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -9,13 +9,10 @@ // ASK/Manchester, RF/32, 64 bits (complete) //----------------------------------------------------------------------------- #include "cmdlfviking.h" - #include #include #include - #include "common.h" - #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" @@ -23,36 +20,10 @@ #include "cmdlf.h" #include "lfdemod.h" #include "commonutil.h" // num_to_bytes +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_viking_clone(void) { - PrintAndLogEx(NORMAL, "clone a Viking AM tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf viking clone "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 digit hex viking card number"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking clone 1A337")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking clone 1A337 Q5") " - encode for Q5/T5555 tag"); - return PM3_SUCCESS; -} - -static int usage_lf_viking_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of viking card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "Per viking format, the card number is 8 digit hex number. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf viking sim "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 digit hex viking card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking sim 1A337")); - return PM3_SUCCESS; -} - //see ASKDemod for what args are accepted int demodViking(bool verbose) { (void) verbose; // unused so far @@ -95,31 +66,48 @@ static int CmdVikingRead(const char *Cmd) { } static int CmdVikingClone(const char *Cmd) { - uint32_t id = 0; - uint64_t rawID = 0; - bool Q5 = false; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_viking_clone(); - id = param_get32ex(Cmd, 0, 0, 16); - if (id == 0) return usage_lf_viking_clone(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking clone", + "clone a Viking AM tag to a T55x7 or Q5/T5555 tag.", + "lf viking clone --cn 01A337\n" + "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + ); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 'q') - Q5 = true; + void *argtable[] = { + arg_param_begin, + arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - rawID = getVikingBits(id); + int raw_len = 0; + uint8_t raw[4] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t id = bytes_to_num(raw, raw_len); + if (id == 0) { + PrintAndLogEx(ERR, "Cardnumber can't be zero"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool q5 = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + uint64_t rawID = getVikingBits(id); struct p { bool Q5; uint8_t blocks[8]; } PACKED payload; - payload.Q5 = Q5; + payload.Q5 = q5; num_to_bytes(rawID, 8, &payload.blocks[0]); PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") - , (Q5) ? "Q5/T5555" : "T55x7" + , (q5) ? "Q5/T5555" : "T55x7" , id , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); @@ -138,15 +126,35 @@ static int CmdVikingClone(const char *Cmd) { } static int CmdVikingSim(const char *Cmd) { - uint32_t id = 0; - uint64_t rawID = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_viking_sim(); - id = param_get32ex(Cmd, 0, 0, 16); - if (id == 0) return usage_lf_viking_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking sim", + "Enables simulation of viking card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Per viking format, the card number is 8 digit hex number. Larger values are truncated.", + "lf viking sim --cn 01A337" + ); - rawID = getVikingBits(id); + void *argtable[] = { + arg_param_begin, + arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int raw_len = 0; + uint8_t raw[4] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t id = bytes_to_num(raw, raw_len); + if (id == 0) { + PrintAndLogEx(ERR, "Cardnumber can't be zero"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + uint64_t rawID = getVikingBits(id); PrintAndLogEx(SUCCESS, "Simulating Viking - ID " _YELLOW_("%08X") " raw " _YELLOW_("%08X%08X"), id, (uint32_t)(rawID >> 32), (uint32_t)(rawID & 0xFFFFFFFF)); From c25ad02f88a33d1bbb95dfa3857ac0c420eff3ee Mon Sep 17 00:00:00 2001 From: tcprst Date: Tue, 24 Nov 2020 15:50:10 -0500 Subject: [PATCH 012/150] add iclass keys found on pastebin to dic --- client/dictionaries/iclass_default_keys.dic | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index 22e1ee653..9f03720c9 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -10,3 +10,5 @@ AEA684A6DAB23278 # AA1 F0E1D2C3B4A59687 # Kd from PicoPass 2k documentation 5CBCF1DA45D5FB4F # PicoPass Default Exchange Key 31ad7ebd2f282168 # From HID multiclassSE reader +6EFD46EFCBB3C875 # From pastebin: https://pastebin.com/uHqpjiuU +E033CA419AEE43F9 # From pastebin: https://pastebin.com/uHqpjiuU \ No newline at end of file From 1e6fcd0291e55e4c37be58c3970a170c7f279912 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 10:51:54 +0100 Subject: [PATCH 013/150] added lf securakey sim - untested --- client/src/cmdlfsecurakey.c | 63 +++++++++++++++++++++++-------------- client/src/util.c | 19 +++++++++++ client/src/util.h | 1 + 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 7315dc9cd..4582f1205 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -8,10 +8,8 @@ // ASK/Manchester, RF/40, 96 bits long (unknown cs) //----------------------------------------------------------------------------- #include "cmdlfsecurakey.h" - #include // memcpy #include // tolower - #include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" @@ -133,22 +131,20 @@ static int CmdSecurakeyClone(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int datalen = 0; + int raw_len = 0; // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; - CLIGetHexWithReturn(ctx, 1, raw, &datalen); + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - if (datalen > 0) { - if (datalen != 12) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } CLIParserFree(ctx); @@ -170,35 +166,56 @@ static int CmdSecurakeyClone(const char *Cmd) { } static int CmdSecurakeySim(const char *Cmd) { - PrintAndLogEx(INFO, _RED_("To be implemented, feel free to contribute!")); - return PM3_SUCCESS; CLIParserContext *ctx; CLIParserInit(&ctx, "lf securakey sim", - "Enables simulation of viking card with specified card number.\n" + "Enables simulation of secura card with specified card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", "lf securakey sim --raw 7FCB400001ADEA5344300000" ); void *argtable[] = { arg_param_begin, - arg_int0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int datalen = 0; + + int raw_len = 0; // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; - CLIGetHexWithReturn(ctx, 1, raw, &datalen); + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - if (datalen > 0) { - if (datalen != 12) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + CLIParserFree(ctx); + return PM3_EINVARG; } CLIParserFree(ctx); + + PrintAndLogEx(SUCCESS, "Simulating SecuraKey - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); + + uint8_t bs[sizeof(raw) * 8]; + bytes_to_bytebits(raw, sizeof(raw), bs); + + lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + payload->encoding = 1; + payload->invert = 0; + payload->separator = 0; + payload->clock = 40; + memcpy(payload->data, bs, sizeof(bs)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + sizeof(bs)); + free(payload); + + PacketResponseNG resp; + WaitForResponse(CMD_LF_ASK_SIMULATE, &resp); + + PrintAndLogEx(INFO, "Done"); + if (resp.status != PM3_EOPABORTED) + return resp.status; + return PM3_SUCCESS; } diff --git a/client/src/util.c b/client/src/util.c index 76a2d7885..e9fd171e2 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -446,6 +446,25 @@ void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest) { } } +void bytes_to_bytebits(void* src, size_t srclen, void* dest) { + + uint8_t *s = (uint8_t*)src; + uint8_t *d = (uint8_t*)dest; + + uint32_t i = srclen * 8; + while (srclen--) { + uint8_t b = s[srclen]; + d[--i] = (b >> 0) & 1; + d[--i] = (b >> 1) & 1; + d[--i] = (b >> 2) & 1; + d[--i] = (b >> 3) & 1; + d[--i] = (b >> 4) & 1; + d[--i] = (b >> 5) & 1; + d[--i] = (b >> 6) & 1; + d[--i] = (b >> 7) & 1; + } +} + // aa,bb,cc,dd,ee,ff,gg,hh, ii,jj,kk,ll,mm,nn,oo,pp // to // hh,gg,ff,ee,dd,cc,bb,aa, pp,oo,nn,mm,ll,kk,jj,ii diff --git a/client/src/util.h b/client/src/util.h index 51100731e..28ca74917 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -56,6 +56,7 @@ void print_blocks(uint32_t *data, size_t len); int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen); void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest); void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest); +void bytes_to_bytebits(void* src, size_t srclen, void* dest); // Swap endian on arrays up to 64bytes. uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); From 2ff7221874f68cf2353077f6dc4a30051dbb4819 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 13:34:52 +0100 Subject: [PATCH 014/150] lf viking read -> now renamed and supports contiuous mode --- client/src/cmdlfviking.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index a301a2f59..b388ceff7 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -59,10 +59,30 @@ static int CmdVikingDemod(const char *Cmd) { } //see ASKDemod for what args are accepted -static int CmdVikingRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 10000); - return demodViking(true); +static int CmdVikingReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking reader", + "read a Viking AM tag", + "lf viking reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + + lf_read(false, 10000); + demodViking(true); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdVikingClone(const char *Cmd) { @@ -184,7 +204,7 @@ static int CmdVikingSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdVikingDemod, AlwaysAvailable, "Demodulate a Viking tag from the GraphBuffer"}, - {"read", CmdVikingRead, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, {"clone", CmdVikingClone, IfPm3Lf, "clone Viking tag to T55x7 or Q5/T5555"}, {"sim", CmdVikingSim, IfPm3Lf, "simulate Viking tag"}, {NULL, NULL, NULL, NULL} From 2ad7a5b7f1e601c6dd575369dd65eec67a44af0a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 13:39:32 +0100 Subject: [PATCH 015/150] lf securakey read - renamed and now support contiuous mode --- client/src/cmdlfsecurakey.c | 44 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 4582f1205..4e2dc80f3 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -104,9 +104,11 @@ int demodSecurakey(bool verbose) { if (bitLen <= 32) PrintAndLogEx(SUCCESS, "Wiegand: " _GREEN_("%08X") " parity (%s)", (lWiegand << (bitLen / 2)) | rWiegand, parity ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); - PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); - PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); + if ( verbose ) { + PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); + PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); + PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); + } return PM3_SUCCESS; } @@ -115,10 +117,28 @@ static int CmdSecurakeyDemod(const char *Cmd) { return demodSecurakey(true); } -static int CmdSecurakeyRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 8000); - return demodSecurakey(true); +static int CmdSecurakeyReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey reader", + "read a Securakey tag", + "lf securakey reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 8000); + demodSecurakey(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdSecurakeyClone(const char *Cmd) { @@ -220,11 +240,11 @@ static int CmdSecurakeySim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdSecurakeyDemod, AlwaysAvailable, "Demodulate an Securakey tag from the GraphBuffer"}, - {"read", CmdSecurakeyRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdSecurakeyClone, IfPm3Lf, "clone Securakey tag to T55x7"}, - {"sim", CmdSecurakeySim, IfPm3Lf, "simulate Securakey tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdSecurakeyDemod, AlwaysAvailable, "Demodulate an Securakey tag from the GraphBuffer"}, + {"reader", CmdSecurakeyReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdSecurakeyClone, IfPm3Lf, "clone Securakey tag to T55x7"}, + {"sim", CmdSecurakeySim, IfPm3Lf, "simulate Securakey tag"}, {NULL, NULL, NULL, NULL} }; From 0cb3704eee0e9b710331d44acdd9b237bfa6a725 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 14:43:09 +0100 Subject: [PATCH 016/150] textual --- client/src/cmdlfsecurakey.c | 2 +- client/src/cmdlfviking.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 4e2dc80f3..c5bd2ff0d 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -181,7 +181,7 @@ static int CmdSecurakeyClone(const char *Cmd) { int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify"); return res; } diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index b388ceff7..d4ab057c8 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -77,7 +77,6 @@ static int CmdVikingReader(const char *Cmd) { CLIParserFree(ctx); do { - lf_read(false, 10000); demodViking(true); } while (cm && !kbd_enter_pressed()); @@ -141,7 +140,7 @@ static int CmdVikingClone(const char *Cmd) { return PM3_ETIMEOUT; } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking reader`") " to verify"); return resp.status; } @@ -204,7 +203,7 @@ static int CmdVikingSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdVikingDemod, AlwaysAvailable, "Demodulate a Viking tag from the GraphBuffer"}, - {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, {"clone", CmdVikingClone, IfPm3Lf, "clone Viking tag to T55x7 or Q5/T5555"}, {"sim", CmdVikingSim, IfPm3Lf, "simulate Viking tag"}, {NULL, NULL, NULL, NULL} From 63e92c187c53416c04cd99858dabcd601f2a03d6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 14:43:39 +0100 Subject: [PATCH 017/150] lf visa - now uses cliparser and reader continuous mode --- client/src/cmdlfvisa2000.c | 133 ++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 7fc3f89a5..04e16e727 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -27,41 +27,14 @@ #include "lfdemod.h" // parityTest #include "cmdlft55xx.h" // write verify #include "cmdlfem4x05.h" // +#include "cliparser.h" -#define BL0CK1 0x56495332 +#ifndef VISA2k_BL0CK1 +#define VISA2k_BL0CK1 0x56495332 +#endif static int CmdHelp(const char *Cmd); -static int usage_lf_visa2k_clone(void) { - PrintAndLogEx(NORMAL, "clone a Visa2000 tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf visa2000 clone [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Visa2k card ID"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, " : specify writing to EM4305/4469 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233 q5") " -- encode for Q5/T5555"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233 em4305") " -- encode for EM4305/4469"); - return PM3_SUCCESS; -} - -static int usage_lf_visa2k_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of visa2k card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf visa2000 sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Visa2k card ID"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 sim 112233")); - return PM3_SUCCESS; -} - static uint8_t visa_chksum(uint32_t id) { uint8_t sum = 0; for (uint8_t i = 0; i < 32; i += 4) @@ -165,43 +138,72 @@ static int CmdVisa2kDemod(const char *Cmd) { } // 64*96*2=12288 samples just in case we just missed the first preamble we can still catch 2 of them -static int CmdVisa2kRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 20000); - return demodVisa2k(true); +static int CmdVisa2kReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 reader", + "read a visa2000 tag", + "lf visa2000 reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 20000); + demodVisa2k(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdVisa2kClone(const char *Cmd) { - uint64_t id = 0; - uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_64 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, BL0CK1, 0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 clone", + "clone a Visa2000 tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf visa2000 clone --cn 112233\n" + "lf visa2000 clone --cn 112233 --q5 -> encode for Q5/T5555 tag\n" + "lf visa2000 clone --cn 112233 --em -> encode for EM4305/4469" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_visa2k_clone(); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Visa2k card ID"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - id = param_get32ex(Cmd, 0, 0, 10); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_64 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, VISA2k_BL0CK1, 0}; char cardtype[16] = {"T55x7"}; // Q5 - bool q5 = tolower(param_getchar(Cmd, 1)) == 'q'; if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); } // EM4305 - bool em = tolower(param_getchar(Cmd, 1)) == 'e'; if (em) { blocks[0] = EM4305_VISA2000_CONFIG_BLOCK; snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); } - if (q5 && em) { - PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); - return PM3_EINVARG; - } - blocks[2] = id; blocks[3] = (visa_parity(id) << 4) | visa_chksum(id); @@ -215,22 +217,31 @@ static int CmdVisa2kClone(const char *Cmd) { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf visa2000 read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf visa2000 reader`") " to verify"); return res; } static int CmdVisa2kSim(const char *Cmd) { - uint32_t id = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_visa2k_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 sim", + "Enables simulation of visa2k card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n", + "lf visa2000 sim --cn 1337" + ); - id = param_get32ex(Cmd, 0, 0, 10); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Visa2k card ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + CLIParserFree(ctx); - PrintAndLogEx(SUCCESS, "Simulating Visa2000 - CardId: %u", id); + PrintAndLogEx(SUCCESS, "Simulating Visa2000 - CardId:" _YELLOW_("%u"), id); - uint32_t blocks[3] = { BL0CK1, id, (visa_parity(id) << 4) | visa_chksum(id) }; + uint32_t blocks[3] = { VISA2k_BL0CK1, id, (visa_parity(id) << 4) | visa_chksum(id) }; uint8_t bs[96]; for (int i = 0; i < 3; ++i) @@ -257,11 +268,11 @@ static int CmdVisa2kSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdVisa2kDemod, AlwaysAvailable, "demodulate an VISA2000 tag from the GraphBuffer"}, - {"read", CmdVisa2kRead, IfPm3Lf, "attempt to read and extract tag data from the antenna"}, - {"clone", CmdVisa2kClone, IfPm3Lf, "clone Visa2000 tag to T55x7 or Q5/T5555"}, - {"sim", CmdVisa2kSim, IfPm3Lf, "simulate Visa2000 tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdVisa2kDemod, AlwaysAvailable, "demodulate an VISA2000 tag from the GraphBuffer"}, + {"reader", CmdVisa2kReader, IfPm3Lf, "attempt to read and extract tag data from the antenna"}, + {"clone", CmdVisa2kClone, IfPm3Lf, "clone Visa2000 tag to T55x7 or Q5/T5555"}, + {"sim", CmdVisa2kSim, IfPm3Lf, "simulate Visa2000 tag"}, {NULL, NULL, NULL, NULL} }; From b7c0d6aa5e5cc0287096333790f7525526696182 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 15:13:32 +0100 Subject: [PATCH 018/150] lf viking clone - now supports clone to EM4305/4469 (untested) --- armsrc/appmain.c | 3 ++- armsrc/lfops.c | 7 +++++-- armsrc/lfops.h | 2 +- client/src/cmdlfviking.c | 26 ++++++++++++++++++++------ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 69b628d7a..f43de6f47 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1037,10 +1037,11 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_LF_VIKING_CLONE: { struct p { bool Q5; + bool EM; uint8_t blocks[8]; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; - CopyVikingtoT55xx(payload->blocks, payload->Q5); + CopyVikingtoT55xx(payload->blocks, payload->Q5, payload->EM); break; } case CMD_LF_COTAG_READ: { diff --git a/armsrc/lfops.c b/armsrc/lfops.c index a6fbb7bba..ac34c2a69 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2252,11 +2252,14 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { } // clone viking tag to T55xx -void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5) { +void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em) { uint32_t data[] = {T55x7_BITRATE_RF_32 | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT), 0, 0}; - if (Q5) + if (q5) { data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; + } else if (em) { + data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2) ); + } data[1] = bytes_to_num(blocks, 4); data[2] = bytes_to_num(blocks + 4, 4); diff --git a/armsrc/lfops.h b/armsrc/lfops.h index 0ec050158..703d48b21 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -41,7 +41,7 @@ int lf_io_watch(int findone, uint32_t *high, uint32_t *low); void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an HID card to T5557/T5567 -void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5); +void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em); int copy_em410x_to_t55xx(uint8_t card, uint8_t clock, uint32_t id_hi, uint32_t id_lo); diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index d4ab057c8..2d60c4893 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -88,15 +88,17 @@ static int CmdVikingClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf viking clone", - "clone a Viking AM tag to a T55x7 or Q5/T5555 tag.", + "clone a Viking AM tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "lf viking clone --cn 01A337\n" "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + "lf viking clone --cn 112233 --em -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -104,29 +106,41 @@ static int CmdVikingClone(const char *Cmd) { int raw_len = 0; uint8_t raw[4] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); uint32_t id = bytes_to_num(raw, raw_len); if (id == 0) { PrintAndLogEx(ERR, "Cardnumber can't be zero"); - CLIParserFree(ctx); return PM3_EINVARG; } - bool q5 = arg_get_lit(ctx, 2); - CLIParserFree(ctx); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } uint64_t rawID = getVikingBits(id); struct p { bool Q5; + bool EM; uint8_t blocks[8]; } PACKED payload; payload.Q5 = q5; + payload.EM = em; num_to_bytes(rawID, 8, &payload.blocks[0]); + char cardtype[16] = {"T55x7"}; + if (q5) + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + else if (em) + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") - , (q5) ? "Q5/T5555" : "T55x7" + , cardtype , id , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); @@ -228,7 +242,7 @@ uint64_t getVikingBits(uint32_t id) { ret |= checksum; return ret; } -// by marshmellow + // find viking preamble 0xF200 in already demoded data int detectViking(uint8_t *src, size_t *size) { //make sure buffer has data From a12f32efa648b5b3dd5a80c397767159fabf4dc9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 16:20:44 +0100 Subject: [PATCH 019/150] lf securakey clone - now supports EM4305/4469 (untested) --- client/src/cmdlfsecurakey.c | 42 ++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index c5bd2ff0d..e54d0ff9c 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -21,6 +21,7 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. #include "cliparser.h" +#include "cmdlfem4x05.h" // EM defines static int CmdHelp(const char *Cmd); @@ -145,13 +146,17 @@ static int CmdSecurakeyClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf securakey clone", - "clone a Securakey tag to a T55x7 tag.", - "lf securakey clone --raw 7FCB400001ADEA5344300000" + "clone a Securakey tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf securakey clone --raw 7FCB400001ADEA5344300000\n" + "lf securakey clone --q5 --raw 7FCB400001ADEA5344300000 -> encode for Q5/T5555 tag\n" + "lf securakey clone --em --raw 7FCB400001ADEA5344300000 -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, - arg_str0("r", "raw", "", " raw hex data. 12 bytes"), + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -160,13 +165,19 @@ static int CmdSecurakeyClone(const char *Cmd) { // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } if (raw_len != 12) { PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); - CLIParserFree(ctx); return PM3_EINVARG; } - CLIParserFree(ctx); uint32_t blocks[4]; for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { @@ -175,11 +186,28 @@ static int CmdSecurakeyClone(const char *Cmd) { //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_40 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(40) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone Securakey to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_SECURAKEY_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone Securakey to " _YELLOW_("%s") " with raw hex", cardtype); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify"); return res; From 3de85604561cb8c990132fe79b28158485c36ff5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 16:27:54 +0100 Subject: [PATCH 020/150] lf pyramid - now uses cliparser, contiouos mode, and clone to EM (untested) --- client/src/cmdlfpyramid.c | 166 +++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 65 deletions(-) diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index aba305b5d..1973bad49 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -15,7 +15,6 @@ #include #include #include - #include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" @@ -27,43 +26,11 @@ #include "lfdemod.h" // parityTest #include "crc.h" #include "cmdlft55xx.h" // verifywrite +#include "cliparser.h" +#include "cmdlfem4x05.h" // EM Defines static int CmdHelp(const char *Cmd); -static int usage_lf_pyramid_clone(void) { - PrintAndLogEx(NORMAL, "clone a Farpointe/Pyramid tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated. "); - PrintAndLogEx(NORMAL, "Currently only works on 26bit"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pyramid clone [h] [Q5]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, " Q5 : optional - specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pyramid clone 123 11223")); - return PM3_SUCCESS; -} - -static int usage_lf_pyramid_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Farpointe/Pyramid card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated."); - PrintAndLogEx(NORMAL, "Currently work only on 26bit"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pyramid sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pyramid sim 123 11223")); - return PM3_SUCCESS; -} - //Pyramid Prox demod - FSK RF/50 with preamble of 0000000000000001 (always a 128 bit data stream) //print full Farpointe Data/Pyramid Prox ID and some bit format details if found int demodPyramid(bool verbose) { @@ -215,26 +182,71 @@ static int CmdPyramidDemod(const char *Cmd) { return demodPyramid(true); } -static int CmdPyramidRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 15000); - return demodPyramid(true); +static int CmdPyramidReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid reader", + "read a Farpointe/Pyramid tag", + "lf pyramid reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 15000); + demodPyramid(true); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdPyramidClone(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pyramid_clone(); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_pyramid_clone(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid clone", + "clone a Farpointe/Pyramid tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" + "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" + "Currently only works on 26bit", + "lf pyramid clone --fc 123 --cn 11223\n" + "lf pyramid clone --fc 123 --cn 11223 --q5 -> encode for Q5/T5555 tag\n" + "lf pyramid clone --fc 123 --cn 11223 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "cn", "", "16-bit value card number"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint32_t fc = arg_get_u32_def(ctx, 1, 0); + uint32_t cn = arg_get_u32_def(ctx, 2, 0); + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + uint32_t blocks[5]; uint8_t *bs = calloc(128, sizeof(uint8_t)); if (bs == NULL) { return PM3_EMALLOC; } - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); + uint32_t facilitycode = (fc & 0x000000FF); + uint32_t cardnumber = (cn & 0x0000FFFF); if (getPyramidBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); @@ -243,11 +255,18 @@ static int CmdPyramidClone(const char *Cmd) { //Pyramid - compat mode, FSK2a, data rate 50, 4 data blocks blocks[0] = T55x7_MODULATION_FSK2a | T55x7_BITRATE_RF_50 | 4 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; // Q5 - bool q5 = tolower(param_getchar(Cmd, 2)) == 'q'; - if (q5) + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | T5555_SET_BITRATE(50) | 4 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + // EM4305 + if (em) { + blocks[0] = EM4305_PYRAMID_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } blocks[1] = bytebits_to_byte(bs, 32); blocks[2] = bytebits_to_byte(bs + 32, 32); @@ -256,36 +275,53 @@ static int CmdPyramidClone(const char *Cmd) { free(bs); - PrintAndLogEx(INFO, "Preparing to clone Farpointe/Pyramid to " _YELLOW_("%s") " with Facility Code: %u, Card Number: %u", (q5) ? "Q5/T5555" : "T55x7", facilitycode, cardnumber); + PrintAndLogEx(INFO, "Preparing to clone Farpointe/Pyramid to " _YELLOW_("%s") " with Facility Code: %u, Card Number: %u", cardtype, facilitycode, cardnumber); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pyramid read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pyramid reader`") " to verify"); return res; } static int CmdPyramidSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid sim", + "Enables simulation of Farpointe/Pyramid card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" + "Currently work only on 26bit", + "lf pyramid sim --fc 123 --cn 1337" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pyramid_sim(); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "cn", "", "16-bit value card number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t fc = arg_get_u32_def(ctx, 1, 0); + uint32_t cn = arg_get_u32_def(ctx, 2, 0); + CLIParserFree(ctx); + + uint32_t facilitycode = (fc & 0x000000FF); + uint32_t cardnumber = (cn & 0x0000FFFF); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; uint8_t bs[128]; memset(bs, 0x00, sizeof(bs)); - - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_pyramid_sim(); - - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); - if (getPyramidBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Farpointe/Pyramid - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + PrintAndLogEx(SUCCESS, "Simulating Farpointe/Pyramid - Facility Code: " _YELLOW_("%u") ", CardNumber: " _YELLOW_("%u"), facilitycode, cardnumber); // Pyramid uses: fcHigh: 10, fcLow: 8, clk: 50, invert: 0 lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); @@ -309,11 +345,11 @@ static int CmdPyramidSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "this help"}, - {"demod", CmdPyramidDemod, AlwaysAvailable, "demodulate a Pyramid FSK tag from the GraphBuffer"}, - {"read", CmdPyramidRead, IfPm3Lf, "attempt to read and extract tag data"}, - {"clone", CmdPyramidClone, IfPm3Lf, "clone pyramid tag to T55x7 or Q5/T5555"}, - {"sim", CmdPyramidSim, IfPm3Lf, "simulate pyramid tag"}, + {"help", CmdHelp, AlwaysAvailable, "this help"}, + {"demod", CmdPyramidDemod, AlwaysAvailable, "demodulate a Pyramid FSK tag from the GraphBuffer"}, + {"reader", CmdPyramidReader, IfPm3Lf, "attempt to read and extract tag data"}, + {"clone", CmdPyramidClone, IfPm3Lf, "clone pyramid tag to T55x7 or Q5/T5555"}, + {"sim", CmdPyramidSim, IfPm3Lf, "simulate pyramid tag"}, {NULL, NULL, NULL, NULL} }; From 2b34c64ddeb5780630eba74d91ef76ec163415bd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:28:27 +0100 Subject: [PATCH 021/150] fix coverity 226302 --- client/src/cmdlfvisa2000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 04e16e727..ee145e2b9 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -207,7 +207,7 @@ static int CmdVisa2kClone(const char *Cmd) { blocks[2] = id; blocks[3] = (visa_parity(id) << 4) | visa_chksum(id); - PrintAndLogEx(INFO, "Preparing to clone Visa2000 to " _YELLOW_("%s") " with CardId: %"PRIu64, cardtype, id); + PrintAndLogEx(INFO, "Preparing to clone Visa2000 to " _YELLOW_("%s") " with CardId: %"PRIu32, cardtype, id); print_blocks(blocks, ARRAYLEN(blocks)); int res; From 0d1760ad04adfefa52a1d3115f515264decff682 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:42:45 +0100 Subject: [PATCH 022/150] try to please coverity --- client/src/emv/emvcore.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 913316725..0bcd547b1 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -778,7 +778,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { if (sdad_tlv) { PrintAndLogEx(INFO, "* * Got Signed Dynamic Application Data (9F4B) form GPO. Maybe fDDA..."); - const struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); + struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); if (!atc_db) { PrintAndLogEx(ERR, "Error: Can't recover IDN (ICC Dynamic Number)"); emv_pk_free(pk); @@ -804,9 +804,10 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { emv_pk_free(pk); emv_pk_free(issuer_pk); emv_pk_free(icc_pk); - atc_db = NULL; + tlvdb_free(atc_db); return 9; } + } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); if (dac_db) { From 48b493a77a471a1bf07471caf2b88737d3156e00 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:48:10 +0100 Subject: [PATCH 023/150] lf ti read -> converted to cliparser but hard to test/verify these commands. Most likely the continuous mode doesnt work because of impl of calling wo waiting --- client/src/cmdlfti.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/client/src/cmdlfti.c b/client/src/cmdlfti.c index dc3aeddf1..e2d7716d2 100644 --- a/client/src/cmdlfti.c +++ b/client/src/cmdlfti.c @@ -8,10 +8,10 @@ // Low frequency TI commands //----------------------------------------------------------------------------- +#include "cmdlfti.h" #include #include #include - #include "cmdparser.h" // command_t #include "commonutil.h" #include "comms.h" @@ -19,7 +19,7 @@ #include "ui.h" #include "proxgui.h" #include "graph.h" -#include "cmdlfti.h" +#include "cliparser.h" static int CmdHelp(const char *Cmd); @@ -277,10 +277,27 @@ static int CmdTIDemod(const char *Cmd) { } // read a TI tag and return its ID -static int CmdTIRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_LF_TI_READ, NULL, 0); +static int CmdTIReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf ti reader", + "read a TI tag", + "lf ti reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + clearCommandBuffer(); + SendCommandNG(CMD_LF_TI_READ, NULL, 0); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } @@ -300,15 +317,15 @@ static int CmdTIWrite(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_LF_TI_WRITE, arg0, arg1, arg2, NULL, 0); PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify"); return PM3_SUCCESS; } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdTIDemod, AlwaysAvailable, "Demodulate raw bits for TI-type LF tag from the GraphBuffer"}, - {"read", CmdTIRead, IfPm3Lf, "Read and decode a TI 134 kHz tag"}, - {"write", CmdTIWrite, IfPm3Lf, "Write new data to a r/w TI 134 kHz tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdTIDemod, AlwaysAvailable, "Demodulate raw bits for TI-type LF tag from the GraphBuffer"}, + {"reader", CmdTIReader, IfPm3Lf, "Read and decode a TI 134 kHz tag"}, + {"write", CmdTIWrite, IfPm3Lf, "Write new data to a r/w TI 134 kHz tag"}, {NULL, NULL, NULL, NULL} }; From 6b3f12ffe99fdc94bde7e0bedd96de744c5f4393 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 14:02:52 -0500 Subject: [PATCH 024/150] hf iclass dump - now uses cliparser --- client/src/cmdhficlass.c | 222 +++++++++++++++++++-------------------- doc/cheatsheet.md | 15 ++- 2 files changed, 117 insertions(+), 120 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 284317a47..a3eb00e55 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,27 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_dump(void) { - PrintAndLogEx(NORMAL, "Dump all memory from a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass dump f k c [e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " f : specify a filename to save dump to"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key as 16 hex symbols or 1 hex to select key from memory"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, the key is interpreted as raw block 3/4"); - PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k AAAAAAAAAAAAAAAA e")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_restore(void) { PrintAndLogEx(NORMAL, "Restore data from dumpfile onto a iCLASS tag\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass restore f b l k c e|r\n"); @@ -1359,104 +1338,115 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { } static int CmdHFiClassDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass dump", + "Dump all memory from a iCLASS tag", + "hf iclass dump -k 001122334455667B\n" + "hf iclass dump -k AAAAAAAAAAAAAAAA --credit 001122334455667B\n" + "hf iclass dump -k AAAAAAAAAAAAAAAA --elite\n" + "hf iclass dump --ki 0\n" + "hf iclass dump --ki 0 --ci 2"); - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CreditKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - uint8_t app_limit1 = 0, app_limit2 = 0; - uint8_t fileNameLen = 0; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename to save dump to"), + arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), + arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), + arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char tempStr[50] = {0}; - bool have_credit_key = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool auth = false; - uint8_t cmdp = 0; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_dump(); - case 'c': - auth = true; - have_credit_key = true; - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, CreditKEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8); - PrintAndLogEx(INFO, "AA2 (credit) index %u", keyNbr); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(WARNING, "no filename found after f"); - errors = true; - } - cmdp += 2; - break; - case 'k': - auth = true; - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - PrintAndLogEx(INFO, "AA1 (debit) index %u", keyNbr); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + int key_len = 0; + uint8_t key[8] = {0}; + bool have_debit_key = false; + + CLIGetHexWithReturn(ctx, 2, key, &key_len); + + int deb_key_nr = arg_get_int_def(ctx, 3, -1); + + if (key_len > 0 && deb_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify debit key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (key_len > 0) { + have_debit_key = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Debit key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + if (deb_key_nr >= 0) { + if (deb_key_nr < ICLASS_KEYS_MAX) { + have_debit_key = true; + memcpy(key, iClass_Key_Table[deb_key_nr], 8); + PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } } - if (errors) return usage_hf_iclass_dump(); + int credit_key_len = 0; + uint8_t credit_key[8] = {0}; + bool have_credit_key = false; + + CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); + + int credit_key_nr = arg_get_int_def(ctx, 5, -1); + + if (credit_key_len > 0 && credit_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify credit key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (credit_key_len > 0) { + have_credit_key = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Credit key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + if (credit_key_nr >= 0) { + if (credit_key_nr < ICLASS_KEYS_MAX) { + have_credit_key = true; + memcpy(key, iClass_Key_Table[credit_key_nr], 8); + PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + return PM3_EINVARG; + } + + uint8_t app_limit1 = 0, app_limit2 = 0; uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); @@ -1511,11 +1501,11 @@ static int CmdHFiClassDump(const char *Cmd) { if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)", app_limit1, app_limit1); - if (auth) { + if (have_debit_key) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { - if (auth == false) { + if (have_debit_key == false) { PrintAndLogEx(FAILED, "Run command with keys"); return PM3_ESOFT; } @@ -1528,10 +1518,10 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_credit_key = false, .req.use_replay = use_replay, .req.send_reply = true, - .req.do_auth = auth, + .req.do_auth = have_debit_key, .end_block = app_limit1, }; - memcpy(payload.req.key, KEY, 8); + memcpy(payload.req.key, key, 8); // tags configured for NON SECURE PAGE, acts different if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -1606,7 +1596,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (have_credit_key && pagemap != 0x01) { // AA2 authenticate credit key - memcpy(payload.req.key, CreditKEY, 8); + memcpy(payload.req.key, credit_key, 8); payload.req.use_credit_key = true; payload.start_block = app_limit1 + 1; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 9699fabf0..c466a4784 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -62,9 +62,16 @@ Dump iCLASS card contents ``` Options --- -k : *Access Key as 16 hex symbols or 1 hex to select key from memory +-f, --file filename to save dump to +-k, --key debit key as 16 hex symbols OR NR/MAC for replay + --ki debit key index to select key from memory 'hf iclass managekeys' + --credit credit key as 16 hex symbols + --ci credit key index to select key from memory 'hf iclass managekeys' + --elite elite computations applied to key + --raw raw, the key is interpreted as raw block 3/4 + --nr replay of NR/MAC -m3 --> hf iclass dump k 0 +pm3 --> hf iclass dump --ki 0 ``` Read iCLASS Block @@ -161,7 +168,7 @@ pm3 --> hf iclass sim 3 Simulate iCLASS Sequence ``` -pm3 --> hf iclass dump k 0 +pm3 --> hf iclass dump --ki 0 pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin pm3 --> hf iclass sim 3 ``` @@ -177,7 +184,7 @@ e : If 'e' is specified, elite computations applied to key pm3 --> hf iclass sim 2 pm3 --> hf iclass loclass -f iclass_mac_attack.bin pm3 --> hf iclass managekeys n 7 k -pm3 --> hf iclass dump k 7 e +pm3 --> hf iclass dump --ki 7 --elite ``` Verify custom iCLASS key From 88b7efe69a69d46f96058f3a36f67072509d78a2 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 18:00:32 -0500 Subject: [PATCH 025/150] hf iclass restore - now use cliparser --- armsrc/iclass.c | 4 +- client/src/cmdhficlass.c | 193 ++++++++++++++++----------------------- 2 files changed, 80 insertions(+), 117 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 86d8094b7..f6d338f9c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1999,10 +1999,10 @@ void iClass_Restore(iclass_restore_req_t *msg) { // data + mac if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac)) { - Dbprintf("Write block [%02x] " _GREEN_("successful"), item.blockno); + Dbprintf("Write block [%02d] " _GREEN_("successful"), item.blockno); written++; } else { - Dbprintf("Write block [%02x] " _RED_("failed"), item.blockno); + Dbprintf("Write block [%02d] " _RED_("failed"), item.blockno); } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index a3eb00e55..f647f6aeb 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,25 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_restore(void) { - PrintAndLogEx(NORMAL, "Restore data from dumpfile onto a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass restore f b l k c e|r\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " f : specify a filename to restore"); - PrintAndLogEx(NORMAL, " b : The first block to restore as 2 hex symbols"); - PrintAndLogEx(NORMAL, " l : The last block to restore as 2 hex symbols"); - PrintAndLogEx(NORMAL, " k : Access key as 16 hex symbols or 1 hex to select key from memory"); - PrintAndLogEx(NORMAL, " c : If 'c' is specified, the key set is assumed to be the credit key\n"); - PrintAndLogEx(NORMAL, " e : If 'e' is specified, elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : If 'r' is specified, no computations applied to key (raw)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump.bin b 06 l 1A k 1122334455667788 e")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump b 05 l 19 k 0")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump b 06 l 19 k 0 e")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_writeblock(void) { PrintAndLogEx(NORMAL, "Write data to a iCLASS tag\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass wrbl b d k [c|e|r|v]\n"); @@ -1821,92 +1802,75 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { } static int CmdHFiClassRestore(const char *Cmd) { - char filename[FILE_PATH_SIZE] = { 0x00 }; - char tempStr[50] = {0}; - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t fileNameLen = 0; - uint8_t startblock = 0; - uint8_t endblock = 0; - uint8_t dataLen = 0; - bool got_startblk = false, got_endblk = false; - bool use_credit_key = false; - bool elite = false; - bool rawkey = false; - bool errors = false; - bool verbose = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass restore", + "Restore data from dumpfile onto a iCLASS tag", + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 1A -k 1122334455667788 --elite\n" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 05 --last 19 --ki 0\n" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 19 --ki 0 --elite"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_restore(); - case 'b': - startblock = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_startblk = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT")); - use_credit_key = true; - cmdp++; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(WARNING, "No filename found after f"); - errors = true; - } - cmdp += 2; - break; - case 'k': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'l': - endblock = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_endblk = true; - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "specify a filename to restore"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "first", "", "The first block number to restore as an integer"), + arg_int1(NULL, "last", "", "The last block number to restore as an integer"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 2, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 3, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (got_endblk == false || got_startblk == false) - errors = true; - if (errors || cmdp < 8) return usage_hf_iclass_restore(); + if (key_len > 0) { + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify a key or key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int startblock = arg_get_int_def(ctx, 4, 0); + int endblock = arg_get_int_def(ctx, 5, 0); + + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); + + CLIParserFree(ctx); if (rawkey + elite > 1) { PrintAndLogEx(FAILED, "Can not use both 'e', 'r'"); @@ -1954,7 +1918,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; - memcpy(payload->req.key, KEY, 8); + memcpy(payload->req.key, key, 8); payload->item_cnt = (endblock - startblock + 1); @@ -1971,7 +1935,7 @@ static int CmdHFiClassRestore(const char *Cmd) { free(dump); if (verbose) { - PrintAndLogEx(INFO, "Preparing to restore block range 0x%02x..0x%02x", startblock, endblock); + PrintAndLogEx(INFO, "Preparing to restore block range %02d..%02d", startblock, endblock); PrintAndLogEx(INFO, "------+----------------------"); PrintAndLogEx(INFO, "block | data"); @@ -1979,7 +1943,7 @@ static int CmdHFiClassRestore(const char *Cmd) { for (uint8_t i = 0; i < payload->item_cnt; i++) { iclass_restore_item_t item = payload->blocks[i]; - PrintAndLogEx(INFO, " %02X | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); + PrintAndLogEx(INFO, " %02d | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); } } @@ -2313,9 +2277,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e int i = startblock; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " blk| data | ascii |lck| info"); + PrintAndLogEx(INFO, "blk | data | ascii |lck| info"); PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); - PrintAndLogEx(INFO, "0x00| " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); + PrintAndLogEx(INFO, " 00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); if (i != 1) PrintAndLogEx(INFO, "...."); @@ -2368,14 +2332,14 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e s = info_nonks[i]; } - PrintAndLogEx(INFO, "0x%02X| %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; const char *s = info_ks[6]; if (i < 6) { s = info_ks[i]; } - PrintAndLogEx(INFO, "0x%02X| %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); } i++; } @@ -2388,14 +2352,14 @@ static int CmdHFiClassView(const char *Cmd) { CLIParserInit(&ctx, "hf iclass view", "Print a iCLASS tag dump file", "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" - "hf iclass view --startblock 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin\n"); + "hf iclass view --first 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin\n"); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_int0("s", "startblock", "", "print from this block (default block6)"), - arg_int0("e", "endblock", "", "end printing at this block (default 0, ALL)"), - arg_lit0("v", "verbose", "verbose output"), + arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), + arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2420,8 +2384,7 @@ static int CmdHFiClassView(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3)); - PrintAndLogEx(INFO, "Printing blocks from"); - PrintAndLogEx(INFO, "start " _YELLOW_("0x%02x") " end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock); + PrintAndLogEx(INFO, "Printing blocks from: " _YELLOW_("%02d") " to: " _YELLOW_("%02d"), (startblock == 0) ? 6 : startblock, endblock); } PrintAndLogEx(NORMAL, ""); From f4b100b0684579646788f15f533f73b959407029 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 18:42:22 -0500 Subject: [PATCH 026/150] show both decimal and hex for iclass blocks --- armsrc/iclass.c | 4 ++-- client/src/cmdhficlass.c | 34 ++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f6d338f9c..5cb5d71ae 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1999,10 +1999,10 @@ void iClass_Restore(iclass_restore_req_t *msg) { // data + mac if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac)) { - Dbprintf("Write block [%02d] " _GREEN_("successful"), item.blockno); + Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), item.blockno, item.blockno); written++; } else { - Dbprintf("Write block [%02d] " _RED_("failed"), item.blockno); + Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), item.blockno, item.blockno); } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f647f6aeb..b55f2801c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1348,7 +1348,7 @@ static int CmdHFiClassDump(const char *Cmd) { int key_len = 0; uint8_t key[8] = {0}; - bool have_debit_key = false; + bool auth = false; CLIGetHexWithReturn(ctx, 2, key, &key_len); @@ -1361,7 +1361,7 @@ static int CmdHFiClassDump(const char *Cmd) { } if (key_len > 0) { - have_debit_key = true; + auth = true; if (key_len != 8) { PrintAndLogEx(ERR, "Debit key is incorrect length"); CLIParserFree(ctx); @@ -1371,7 +1371,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (deb_key_nr >= 0) { if (deb_key_nr < ICLASS_KEYS_MAX) { - have_debit_key = true; + auth = true; memcpy(key, iClass_Key_Table[deb_key_nr], 8); PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); } else { @@ -1396,6 +1396,7 @@ static int CmdHFiClassDump(const char *Cmd) { } if (credit_key_len > 0) { + auth = true; have_credit_key = true; if (key_len != 8) { PrintAndLogEx(ERR, "Credit key is incorrect length"); @@ -1406,6 +1407,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (credit_key_nr >= 0) { if (credit_key_nr < ICLASS_KEYS_MAX) { + auth = true; have_credit_key = true; memcpy(key, iClass_Key_Table[credit_key_nr], 8); PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); @@ -1482,11 +1484,11 @@ static int CmdHFiClassDump(const char *Cmd) { if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)", app_limit1, app_limit1); - if (have_debit_key) { + if (auth) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { - if (have_debit_key == false) { + if (auth == false) { PrintAndLogEx(FAILED, "Run command with keys"); return PM3_ESOFT; } @@ -1499,7 +1501,7 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_credit_key = false, .req.use_replay = use_replay, .req.send_reply = true, - .req.do_auth = have_debit_key, + .req.do_auth = auth, .end_block = app_limit1, }; memcpy(payload.req.key, key, 8); @@ -1937,13 +1939,13 @@ static int CmdHFiClassRestore(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "Preparing to restore block range %02d..%02d", startblock, endblock); - PrintAndLogEx(INFO, "------+----------------------"); - PrintAndLogEx(INFO, "block | data"); - PrintAndLogEx(INFO, "------+----------------------"); + PrintAndLogEx(INFO, "---------+----------------------"); + PrintAndLogEx(INFO, " block# | data"); + PrintAndLogEx(INFO, "---------+----------------------"); for (uint8_t i = 0; i < payload->item_cnt; i++) { iclass_restore_item_t item = payload->blocks[i]; - PrintAndLogEx(INFO, " %02d | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); + PrintAndLogEx(INFO, "%3d/0x%02X | %s", item.blockno, item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); } } @@ -2277,9 +2279,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e int i = startblock; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "blk | data | ascii |lck| info"); - PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); - PrintAndLogEx(INFO, " 00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); + PrintAndLogEx(INFO, " block# | data | ascii |lck| info"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); + PrintAndLogEx(INFO, " 0/0x00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); if (i != 1) PrintAndLogEx(INFO, "...."); @@ -2332,18 +2334,18 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e s = info_nonks[i]; } - PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s ", i, i, sprint_hex_ascii(blk, 8), lockstr, s); } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; const char *s = info_ks[6]; if (i < 6) { s = info_ks[i]; } - PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s ", i, i, sprint_hex_ascii(blk, 8), lockstr, s); } i++; } - PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); PrintAndLogEx(NORMAL, ""); } From 5130bb24e204a7c18d87f894b51cce733c48bd44 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 21:16:08 -0500 Subject: [PATCH 027/150] hf iclass rdbl - now use cliparser --- client/src/cmdhficlass.c | 181 +++++++++++++++++---------------------- doc/cheatsheet.md | 13 ++- 2 files changed, 87 insertions(+), 107 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b55f2801c..0ab48a161 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -91,25 +91,6 @@ static int usage_hf_iclass_writeblock(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_readblock(void) { - PrintAndLogEx(NORMAL, "Read a iCLASS block from tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass rdbl b k [c|e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key assumed\n"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, no computations applied to key"); - PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 06 k 0011223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 1B k 0011223344556677 c")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 0A k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_calc_newkey(void) { PrintAndLogEx(NORMAL, "Calculate new key for updating\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o n s [csn] e\n"); @@ -1373,7 +1354,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (deb_key_nr < ICLASS_KEYS_MAX) { auth = true; memcpy(key, iClass_Key_Table[deb_key_nr], 8); - PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); + PrintAndLogEx(SUCCESS, "Using AA1 (debit) key[%d] " _GREEN_("%s"), deb_key_nr, sprint_hex(iClass_Key_Table[deb_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -1410,7 +1391,7 @@ static int CmdHFiClassDump(const char *Cmd) { auth = true; have_credit_key = true; memcpy(key, iClass_Key_Table[credit_key_nr], 8); - PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); + PrintAndLogEx(SUCCESS, "Using AA2 (credit) key[%d] " _GREEN_("%s"), credit_key_nr, sprint_hex(iClass_Key_Table[credit_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -2019,94 +2000,88 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } static int CmdHFiClass_ReadBlock(const char *Cmd) { - uint8_t blockno = 0; - uint8_t keyType = 0x88; //debit key - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t key_idx = 0; - uint8_t key_len = 0; - char tempStr[50] = {0}; - bool got_blockno = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool auth = false; - bool verbose = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass rdbl", + "Read a iCLASS block from tag", + "hf iclass rdbl -b 6 -k 0011223344556677\n" + "hf iclass rdbl -b 27 -k 0011223344556677 --credit\n" + "hf iclass rdbl -b 10 --ki 0"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_readblock(); - case 'b': - blockno = param_get8ex(Cmd, cmdp + 1, 7, 16); - got_blockno = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("KC credit")); - keyType = 0x18; - cmdp++; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'k': - auth = true; - key_len = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (key_len == 16) { - errors = param_gethex(tempStr, 0, KEY, key_len); - } else if (key_len == 1) { - key_idx = param_get8(Cmd, cmdp + 1); - if (key_idx < ICLASS_KEYS_MAX) { - memcpy(KEY, iClass_Key_Table[key_idx], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: key index is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: incorrect key length\n"); - errors = true; - } - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (got_blockno == false) - errors = true; + + bool auth = false; + + if (key_len > 0) { + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify a key or key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int blockno = arg_get_int_def(ctx, 3, 0); + + uint8_t keyType = 0x88; //debit key + if (arg_get_lit(ctx, 4)) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); + keyType = 0x18; //credit key + } + + bool elite = arg_get_lit(ctx, 5); + bool rawkey = arg_get_lit(ctx, 6); + bool use_replay = arg_get_lit(ctx, 7); + bool verbose = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + PrintAndLogEx(ERR, "Can not use a combo of 'e', 'r', 'n'"); + return PM3_EINVARG; } - if (errors) return usage_hf_iclass_readblock(); - if (verbose) { - if (key_len == 1) - PrintAndLogEx(SUCCESS, "Using key[%d] %s", key_idx, sprint_hex(KEY, 8)); - else - PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(KEY, 8)); + if (key_len > 0) + PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(key, 8)); } if (auth == false && verbose) { @@ -2115,7 +2090,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } uint8_t data[8] = {0}; - int res = iclass_read_block(KEY, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); + int res = iclass_read_block(key, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); if (res != PM3_SUCCESS) return res; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index c466a4784..3e092a173 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -78,10 +78,15 @@ Read iCLASS Block ``` Options --- -b : The block number as 2 hex symbols -k : Access Key as 16 hex symbols or 1 hex to select key from memory +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC -pm3 --> hf iclass rdbl b 7 k 0 +pm3 --> hf iclass rdbl -b 7 --ki 0 ``` Write to iCLASS Block @@ -149,7 +154,7 @@ pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin Clone iCLASS Legacy Sequence ``` -pm3 --> hf iclass rdbl b 7 k 0 +pm3 --> hf iclass rdbl -b 7 --ki 0 pm3 --> hf iclass wrbl b 7 d 6ce099fe7e614fd0 k 0 ``` From f723ed03869230d1bcfba1e35206f8b0db628bd7 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:11:54 -0500 Subject: [PATCH 028/150] hf iclass wrbl - now use cliparser --- client/src/cmdhficlass.c | 192 ++++++++++++++++----------------------- doc/cheatsheet.md | 15 ++- 2 files changed, 88 insertions(+), 119 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 0ab48a161..6bb215344 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,26 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_writeblock(void) { - PrintAndLogEx(NORMAL, "Write data to a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass wrbl b d k [c|e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); - PrintAndLogEx(NORMAL, " d : set the Data to write as 16 hex symbols"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key assumed\n"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, no computations applied to key (raw)"); -// PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 0A d AAAAAAAAAAAAAAAA k 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_calc_newkey(void) { PrintAndLogEx(NORMAL, "Calculate new key for updating\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o n s [csn] e\n"); @@ -1675,103 +1655,91 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo } static int CmdHFiClass_WriteBlock(const char *Cmd) { - uint8_t blockno = 0; - uint8_t bldata[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - char tempStr[50] = {0}; - bool got_blockno = false; - bool use_credit_key = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool verbose = false; - bool use_secure_pagemode = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_writeblock(); - case 'b': - blockno = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_blockno = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT")); - use_credit_key = true; - cmdp++; - break; - case 'd': - if (param_gethex(Cmd, cmdp + 1, bldata, 16)) { - PrintAndLogEx(WARNING, "Data must include 16 HEX symbols\n"); - errors = true; - } - cmdp += 2; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'k': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] %s", keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - use_secure_pagemode = true; - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - /* - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - */ - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass wrbl", + "Write data to an iCLASS tag", + "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" + "hf iclass wrbl -b 27 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" + "hf iclass wrbl -b 11 -d AAAAAAAAAAAAAAAA --ki 0"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str1("d", "data", "", "data to write as 16 hex symbols"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool auth = false; + + if (key_len > 0) { + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if (got_blockno == false) - errors = true; - if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + int blockno = arg_get_int_def(ctx, 3, 0); + + int data_len = 0; + uint8_t data[8] = {0}; + CLIGetHexWithReturn(ctx, 4, data, &data_len); + + if (data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 hex characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (errors || cmdp < 4) return usage_hf_iclass_writeblock(); + bool use_credit_key = arg_get_lit(ctx, 5); + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); - int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, use_replay, verbose, use_secure_pagemode); + CLIParserFree(ctx); + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); + return PM3_EINVARG; + } + + int isok = iclass_write_block(blockno, data, key, use_credit_key, elite, rawkey, use_replay, verbose, auth); switch (isok) { case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); + PrintAndLogEx(SUCCESS, "Wrote block %3d/0x%02X successful", blockno, blockno); break; case PM3_ETEAROFF: if (verbose) @@ -1990,7 +1958,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(packet->data, sizeof(packet->data))); + PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data))); PrintAndLogEx(NORMAL, ""); if (out) @@ -2053,10 +2021,6 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } - } else { - PrintAndLogEx(ERR, "Please specify a key or key index"); - CLIParserFree(ctx); - return PM3_EINVARG; } int blockno = arg_get_int_def(ctx, 3, 0); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 3e092a173..409f7e483 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -93,11 +93,16 @@ Write to iCLASS Block ``` Options --- -b : The block number as 2 hex symbols -d : Set the Data to write as 16 hex symbols -k : Access Key as 16 hex symbols or 1 hex to select key from memory +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer +-d, --data data to write as 16 hex symbols + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC -pm3 --> hf iclass wrbl b 07 d 6ce099fe7e614fd0 k 0 +pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` Print keystore @@ -155,7 +160,7 @@ pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin Clone iCLASS Legacy Sequence ``` pm3 --> hf iclass rdbl -b 7 --ki 0 -pm3 --> hf iclass wrbl b 7 d 6ce099fe7e614fd0 k 0 +pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` Simulate iCLASS From 62196c72289f187240a3ac0c1b2cce54a4231257 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:34:30 -0500 Subject: [PATCH 029/150] standardize argument types - hex,dec,etc --- client/src/cmdhficlass.c | 44 ++++++++++++++++++++-------------------- doc/cheatsheet.md | 38 +++++++++++++++++----------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 6bb215344..49784c4f5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -947,8 +947,8 @@ static int CmdHFiClassDecrypt(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of dumpfile"), - arg_str0("d", "data", "", "3DES encrypted data"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str0("d", "data", "", "3DES encrypted data"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1183,8 +1183,8 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("d", "data", "", "data to encrypt"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str1("d", "data", "", "data to encrypt"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1292,10 +1292,10 @@ static int CmdHFiClassDump(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename to save dump to"), - arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), - arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), - arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), - arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), + arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), + arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), + arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), arg_lit0(NULL, "nr", "replay of NR/MAC"), @@ -1664,10 +1664,10 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), - arg_str1("d", "data", "", "data to write as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str1("d", "data", "", "data to write as 16 hex symbols"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -1763,10 +1763,10 @@ static int CmdHFiClassRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "specify a filename to restore"), - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1(NULL, "first", "", "The first block number to restore as an integer"), - arg_int1(NULL, "last", "", "The last block number to restore as an integer"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "first", "", "The first block number to restore as an integer"), + arg_int1(NULL, "last", "", "The last block number to restore as an integer"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -1977,9 +1977,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -2298,8 +2298,8 @@ static int CmdHFiClassView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), - arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), + arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), + arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -3224,7 +3224,7 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("r", "reverse", "reverse permuted key"), - arg_str1(NULL, "key", "", "input key"), + arg_str1(NULL, "key", "", "input key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 409f7e483..e725d8d25 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -63,10 +63,10 @@ Dump iCLASS card contents Options --- -f, --file filename to save dump to --k, --key debit key as 16 hex symbols OR NR/MAC for replay - --ki debit key index to select key from memory 'hf iclass managekeys' - --credit credit key as 16 hex symbols - --ci credit key index to select key from memory 'hf iclass managekeys' +-k, --key debit key as 16 hex symbols OR NR/MAC for replay + --ki debit key index to select key from memory 'hf iclass managekeys' + --credit credit key as 16 hex symbols + --ci credit key index to select key from memory 'hf iclass managekeys' --elite elite computations applied to key --raw raw, the key is interpreted as raw block 3/4 --nr replay of NR/MAC @@ -78,9 +78,9 @@ Read iCLASS Block ``` Options --- --k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer - --ki Key index to select key from memory 'hf iclass managekeys' +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer + --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key --elite elite computations applied to key --raw no computations applied to key (raw) @@ -93,14 +93,14 @@ Write to iCLASS Block ``` Options --- --k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer --d, --data data to write as 16 hex symbols - --ki Key index to select key from memory 'hf iclass managekeys' - --credit key is assumed to be the credit key - --elite elite computations applied to key - --raw no computations applied to key (raw) - --nr replay of NR/MAC +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer +-d, --data data to write as 16 hex symbols + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` @@ -128,8 +128,8 @@ Encrypt iCLASS Block ``` Options --- --d, --data data to encrypt --k, --key 3DES transport key +-d, --data data to encrypt +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass encrypt -d 0000000f2aa3dba8 @@ -140,8 +140,8 @@ Decrypt iCLASS Block / file Options --- -f, --file filename of dumpfile --d, --data 3DES encrypted data --k, --key 3DES transport key +-d, --data 3DES encrypted data +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass decrypt -d 2AD4C8211F996871 From db083034d612831024bc26c76c6f24a3afd37955 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:44:26 -0500 Subject: [PATCH 030/150] make style --- armsrc/lfops.c | 2 +- client/src/cmdlfpyramid.c | 6 +++--- client/src/cmdlfsecurakey.c | 2 +- client/src/cmdlfviking.c | 4 ++-- client/src/emv/emvcore.c | 2 +- doc/commands.md | 19 +++++++++---------- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index ac34c2a69..0d578295f 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2258,7 +2258,7 @@ void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em) { if (q5) { data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; } else if (em) { - data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2) ); + data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2)); } data[1] = bytes_to_num(blocks, 4); diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index 1973bad49..452f3f61e 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -220,10 +220,10 @@ static int CmdPyramidClone(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), - arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -301,7 +301,7 @@ static int CmdPyramidSim(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), arg_param_end }; diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index e54d0ff9c..c489427e2 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -105,7 +105,7 @@ int demodSecurakey(bool verbose) { if (bitLen <= 32) PrintAndLogEx(SUCCESS, "Wiegand: " _GREEN_("%08X") " parity (%s)", (lWiegand << (bitLen / 2)) | rWiegand, parity ? _GREEN_("ok") : _RED_("fail")); - if ( verbose ) { + if (verbose) { PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 2d60c4893..805eb875c 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -98,7 +98,7 @@ static int CmdVikingClone(const char *Cmd) { arg_param_begin, arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), - arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -134,7 +134,7 @@ static int CmdVikingClone(const char *Cmd) { num_to_bytes(rawID, 8, &payload.blocks[0]); char cardtype[16] = {"T55x7"}; - if (q5) + if (q5) snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); else if (em) snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 0bcd547b1..7982ff711 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -807,7 +807,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(atc_db); return 9; } - + } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); if (dac_db) { diff --git a/doc/commands.md b/doc/commands.md index e52c2361d..770d9f4c6 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -11,7 +11,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`auto `|N |`Automated detection process for unknown tags` -|`clear `|Y |`clear screen` +|`clear `|Y |`Clear screen` |`help `|Y |`This help. Use ' help' for details of a particular command.` |`hints `|Y |`Turn hints on / off` |`msleep `|Y |`Add a pause in milliseconds` @@ -256,7 +256,6 @@ Check column "offline" for their availability. |`hf iclass restore `|N |`[options..] Restore a dump file onto a Picopass / iCLASS tag` |`hf iclass sniff `|N |` Eavesdrop Picopass / iCLASS communication` |`hf iclass wrbl `|N |`[options..] Write Picopass / iCLASS block` -|`hf iclass autopwn `|N |`[options..] Automatic key recovery tool for iCLASS` |`hf iclass chk `|N |`[options..] Check keys` |`hf iclass loclass `|Y |`[options..] Use loclass to perform bruteforce reader attack` |`hf iclass lookup `|Y |`[options..] Uses authentication trace to check for key in dictionary file` @@ -850,7 +849,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf pyramid help `|Y |`this help` |`lf pyramid demod `|Y |`demodulate a Pyramid FSK tag from the GraphBuffer` -|`lf pyramid read `|N |`attempt to read and extract tag data` +|`lf pyramid reader `|N |`attempt to read and extract tag data` |`lf pyramid clone `|N |`clone pyramid tag to T55x7 or Q5/T5555` |`lf pyramid sim `|N |`simulate pyramid tag` @@ -863,7 +862,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf securakey help `|Y |`This help` |`lf securakey demod `|Y |`Demodulate an Securakey tag from the GraphBuffer` -|`lf securakey read `|N |`Attempt to read and extract tag data from the antenna` +|`lf securakey reader `|N |`Attempt to read and extract tag data from the antenna` |`lf securakey clone `|N |`clone Securakey tag to T55x7` |`lf securakey sim `|N |`simulate Securakey tag` @@ -876,7 +875,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf ti help `|Y |`This help` |`lf ti demod `|Y |`Demodulate raw bits for TI-type LF tag from the GraphBuffer` -|`lf ti read `|N |`Read and decode a TI 134 kHz tag` +|`lf ti reader `|N |`Read and decode a TI 134 kHz tag` |`lf ti write `|N |`Write new data to a r/w TI 134 kHz tag` @@ -918,7 +917,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf viking help `|Y |`This help` |`lf viking demod `|Y |`Demodulate a Viking tag from the GraphBuffer` -|`lf viking read `|N |`Attempt to read and Extract tag data from the antenna` +|`lf viking reader `|N |`Attempt to read and Extract tag data from the antenna` |`lf viking clone `|N |`clone Viking tag to T55x7 or Q5/T5555` |`lf viking sim `|N |`simulate Viking tag` @@ -931,14 +930,14 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf visa2000 help `|Y |`This help` |`lf visa2000 demod `|Y |`demodulate an VISA2000 tag from the GraphBuffer` -|`lf visa2000 read `|N |`attempt to read and extract tag data from the antenna` +|`lf visa2000 reader `|N |`attempt to read and extract tag data from the antenna` |`lf visa2000 clone `|N |`clone Visa2000 tag to T55x7 or Q5/T5555` |`lf visa2000 sim `|N |`simulate Visa2000 tag` ### mem - { Flash Memory manipulation... } + { Flash memory manipulation... } |command |offline |description |------- |------- |----------- @@ -953,7 +952,7 @@ Check column "offline" for their availability. ### reveng - { CRC calculations from RevEng software } + { CRC calculations from RevEng software... } [=] reveng: no mode switch specified. Use reveng -h for help. @@ -975,7 +974,7 @@ Check column "offline" for their availability. ### script - { Scripting commands } + { Scripting commands... } |command |offline |description |------- |------- |----------- From 2e1c906d9e0eae264a834c987ebfdecaea85c954 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 07:07:33 +0100 Subject: [PATCH 031/150] text --- client/src/cmdlfviking.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 805eb875c..bdf9868fb 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -90,7 +90,7 @@ static int CmdVikingClone(const char *Cmd) { CLIParserInit(&ctx, "lf viking clone", "clone a Viking AM tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "lf viking clone --cn 01A337\n" - "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag\n" "lf viking clone --cn 112233 --em -> encode for EM4305/4469" ); From c59d6cdc71d8230f26bad51f356a7d8c936710d4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 19:42:24 +0100 Subject: [PATCH 032/150] lf presco - now uses cliparser, continuous mode and clone to EM (untested) --- client/src/cmdlfpresco.c | 355 +++++++++++++++++++++++---------------- client/src/cmdlfpresco.h | 5 - 2 files changed, 209 insertions(+), 151 deletions(-) diff --git a/client/src/cmdlfpresco.c b/client/src/cmdlfpresco.c index d8bca9c19..3857cdd83 100644 --- a/client/src/cmdlfpresco.c +++ b/client/src/cmdlfpresco.c @@ -8,51 +8,64 @@ //----------------------------------------------------------------------------- #include "cmdlfpresco.h" - #include #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "protocols.h" // for T55xx config register definitions -#include "lfdemod.h" // parityTest -#include "cmdlft55xx.h" // verifywrite +#include "protocols.h" // for T55xx config register definitions +#include "lfdemod.h" // parityTest +#include "cmdlft55xx.h" // verifywrite +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_presco_clone(void) { - PrintAndLogEx(NORMAL, "clone a Presco tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf presco clone [h] d c "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " d : 9 digit presco card ID"); - PrintAndLogEx(NORMAL, " c : 8 digit hex card number"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf presco clone d 123456789")); +// find presco preamble 0x10D in already demoded data +static int detectPresco(uint8_t *dest, size_t *size) { + if (*size < 128 * 2) return -1; //make sure buffer has data + size_t startIdx = 0; + uint8_t preamble[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) + return -2; //preamble not found + if (*size != 128) return -3; //wrong demoded size + //return start position + return (int)startIdx; +} + +// convert base 12 ID to sitecode & usercode & 8 bit other unknown code +static int getWiegandFromPrintedPresco(void *arr, uint32_t *fullcode) { + char *s = (char*)arr; + uint8_t val = 0; + for (int i = 0; i < strlen(s); ++i) { + // Get value from number string. + if (s[i] == '*') + val = 10; + if (s[i] == '#') + val = 11; + if (s[i] >= 0x30 && s[i] <= 0x39) + val = s[i] - 0x30; + + *fullcode += val; + + // last digit is only added, not multipled. + if (i < strlen(s) - 1) + *fullcode *= 12; + } return PM3_SUCCESS; } -static int usage_lf_presco_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of presco card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf presco sim [h] d or c "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " d : 9 digit presco card number"); - PrintAndLogEx(NORMAL, " c : 8 digit hex card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf presco sim d 123456789")); +// calc not certain - intended to get bitstream for programming / sim +static int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits) { + num_to_bytebits(0x10D00000, 32, prescoBits); + num_to_bytebits(0x00000000, 32, prescoBits + 32); + num_to_bytebits(0x00000000, 32, prescoBits + 64); + num_to_bytebits(fullcode, 32, prescoBits + 96); return PM3_SUCCESS; } @@ -85,15 +98,16 @@ int demodPresco(bool verbose) { uint32_t raw2 = bytebits_to_byte(DemodBuffer + 32, 32); uint32_t raw3 = bytebits_to_byte(DemodBuffer + 64, 32); uint32_t raw4 = bytebits_to_byte(DemodBuffer + 96, 32); - uint32_t cardid = raw4; - PrintAndLogEx(SUCCESS, "Presco - Card: " _GREEN_("%08X") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); + uint32_t fullcode = raw4; + uint32_t usercode = fullcode & 0x0000FFFF; + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; - uint32_t sitecode = 0, usercode = 0, fullcode = 0; - bool Q5 = false; - char cmd[12] = {0}; - sprintf(cmd, "H %08X", cardid); - getWiegandFromPresco(cmd, &sitecode, &usercode, &fullcode, &Q5); - PrintAndLogEx(SUCCESS, "SiteCode: " _GREEN_("%u") " UserCode: " _GREEN_("%u") " FullCode: " _GREEN_("%08X"), sitecode, usercode, fullcode); + PrintAndLogEx(SUCCESS, "Presco Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08X") " Raw: " _YELLOW_("%08X%08X%08X%08X") + , sitecode + , usercode + , fullcode + , raw1, raw2, raw3, raw4 + ); return PM3_SUCCESS; } @@ -103,35 +117,108 @@ static int CmdPrescoDemod(const char *Cmd) { } //see ASKDemod for what args are accepted -static int CmdPrescoRead(const char *Cmd) { - // Presco Number: 123456789 --> Sitecode 30 | usercode 8665 - (void)Cmd; // Cmd is not used so far - lf_read(false, 12000); - return demodPresco(true); +static int CmdPrescoReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco reader", + "read a presco tag", + "lf presco reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 12000); + demodPresco(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } // takes base 12 ID converts to hex // Or takes 8 digit hex ID static int CmdPrescoClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco clone", + "clone a presco tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf presco clone -d 018363467\n" + "lf presco clone -d 018363467 --q5 -> encode for Q5/T5555 tag\n" + "lf presco clone -d 018363467 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("c", NULL, "", "8 digit hex card number"), + arg_str0("d", NULL, "", "9 digit presco card ID"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hex_len = 0; + uint8_t hex[4] = {0,0,0,0}; + CLIGetHexWithReturn(ctx, 1, hex, &hex_len); + + uint8_t idstr[11]; + int slen = 9; + memset(idstr, 0x00, sizeof(idstr)); + CLIGetStrWithReturn(ctx, 2, idstr, &slen); + + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + uint32_t fullcode = 0; + + if (hex_len) { + fullcode = bytes_to_num(hex, hex_len); + } else { + //param get string int param_getstr(const char *line, int paramnum, char * str) + if (slen < 2) { + PrintAndLogEx(ERR, "Must contain atleast 2 digits"); + return PM3_EINVARG; + } + + getWiegandFromPrintedPresco(idstr, &fullcode); + } + + uint32_t usercode = fullcode & 0x0000FFFF; //% 65566 + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; // /= 16777216; - bool Q5 = false; - uint32_t sitecode = 0, usercode = 0, fullcode = 0; uint32_t blocks[5] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT | T55x7_ST_TERMINATOR, 0, 0, 0, 0}; - // get wiegand from printed number. - if (getWiegandFromPresco(Cmd, &sitecode, &usercode, &fullcode, &Q5) == PM3_EINVARG) return usage_lf_presco_clone(); - - if (Q5) + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT | T5555_ST_TERMINATOR; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_PRESCO_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } if ((sitecode & 0xFF) != sitecode) { sitecode &= 0xFF; - PrintAndLogEx(INFO, "Facility-Code Truncated to 8-bits (Presco): %u", sitecode); + PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode); } if ((usercode & 0xFFFF) != usercode) { usercode &= 0xFFFF; - PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (Presco): %u", usercode); + PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode); } blocks[1] = 0x10D00000; //preamble @@ -139,25 +226,84 @@ static int CmdPrescoClone(const char *Cmd) { blocks[3] = 0x00000000; blocks[4] = fullcode; - PrintAndLogEx(INFO, "Preparing to clone Presco to " _YELLOW_("%s") " with SiteCode: %u, UserCode: %u, FullCode: %08x", (Q5) ? "Q5/T5555" : "T55x7", sitecode, usercode, fullcode); + PrintAndLogEx(INFO, "Preparing to clone Presco to " _GREEN_("%s") " with Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08x") + , cardtype + , sitecode + , usercode + , fullcode + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco reader`") " to verify"); return res; } // takes base 12 ID converts to hex // Or takes 8 digit hex ID static int CmdPrescoSim(const char *Cmd) { - uint32_t sitecode = 0, usercode = 0, fullcode = 0; - bool Q5 = false; - // get wiegand from printed number. - if (getWiegandFromPresco(Cmd, &sitecode, &usercode, &fullcode, &Q5) == PM3_EINVARG) - return usage_lf_presco_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco sim", + "Enables simulation of presco card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated.", + "lf presco sim -d 018363467" + ); - PrintAndLogEx(SUCCESS, "Simulating Presco - SiteCode: %u, UserCode: %u, FullCode: %08X", sitecode, usercode, fullcode); + void *argtable[] = { + arg_param_begin, + arg_str0("c", NULL, "", "8 digit hex card number"), + arg_str0("d", NULL, "", "9 digit presco card ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hex_len = 0; + uint8_t hex[4] = {0,0,0,0}; + CLIGetHexWithReturn(ctx, 1, hex, &hex_len); + + uint8_t idstr[11]; + int slen = 9; + memset(idstr, 0x00, sizeof(idstr)); + CLIGetStrWithReturn(ctx, 2, idstr, &slen); + CLIParserFree(ctx); + + uint32_t fullcode = 0; + + if (hex_len) { + fullcode = bytes_to_num(hex, hex_len); + } else { + if (slen < 2) { + PrintAndLogEx(ERR, "Must contain atleast 2 digits"); + return PM3_EINVARG; + } + getWiegandFromPrintedPresco(idstr, &fullcode); + } + + uint32_t usercode = fullcode & 0x0000FFFF; + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; + + if ((sitecode & 0xFF) != sitecode) { + sitecode &= 0xFF; + PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode); + } + + if ((usercode & 0xFFFF) != usercode) { + usercode &= 0xFFFF; + PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode); + } + + PrintAndLogEx(SUCCESS, "Simulating Presco - Site Code: " _GREEN_("%u") " User Code: " _GREEN_("%u") " Full Code: " _GREEN_("%08X") + , sitecode + , usercode + , fullcode) + ; uint8_t bs[128]; getPrescoBits(fullcode, bs); @@ -185,9 +331,9 @@ static int CmdPrescoSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdPrescoDemod, AlwaysAvailable, "demodulate Presco tag from the GraphBuffer"}, - {"read", CmdPrescoRead, IfPm3Lf, "Attempt to read and Extract tag data"}, - {"clone", CmdPrescoClone, IfPm3Lf, "clone presco tag to T55x7 or Q5/T5555"}, - {"sim", CmdPrescoSim, IfPm3Lf, "simulate presco tag"}, + {"reader", CmdPrescoReader, IfPm3Lf, "Attempt to read and Extract tag data"}, + {"clone", CmdPrescoClone, IfPm3Lf, "clone presco tag to T55x7 or Q5/T5555"}, + {"sim", CmdPrescoSim, IfPm3Lf, "simulate presco tag"}, {NULL, NULL, NULL, NULL} }; @@ -201,86 +347,3 @@ int CmdLFPresco(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -// find presco preamble 0x10D in already demoded data -int detectPresco(uint8_t *dest, size_t *size) { - if (*size < 128 * 2) return -1; //make sure buffer has data - size_t startIdx = 0; - uint8_t preamble[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) - return -2; //preamble not found - if (*size != 128) return -3; //wrong demoded size - //return start position - return (int)startIdx; -} - -// convert base 12 ID to sitecode & usercode & 8 bit other unknown code -int getWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode, uint32_t *fullcode, bool *Q5) { - - bool hex = false, errors = false; - uint8_t cmdp = 0; - char id[11]; - int stringlen = 0; - memset(id, 0x00, sizeof(id)); - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return PM3_EINVARG; - case 'c': - hex = true; - //get hex - *fullcode = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'd': - //param get string int param_getstr(const char *line, int paramnum, char * str) - stringlen = param_getstr(Cmd, cmdp + 1, id, sizeof(id)); - if (stringlen < 2) return PM3_EINVARG; - cmdp += 2; - break; - case 'q': - *Q5 = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = 1; - break; - } - } - //Validations - if (errors || cmdp == 0) return PM3_EINVARG; - - if (!hex) { - uint8_t val = 0; - for (int index = 0; index < strlen(id); ++index) { - // Get value from number string. - if (id[index] == '*') - val = 10; - if (id[index] == '#') - val = 11; - if (id[index] >= 0x30 && id[index] <= 0x39) - val = id[index] - 0x30; - - *fullcode += val; - - // last digit is only added, not multipled. - if (index < strlen(id) - 1) - *fullcode *= 12; - } - } - - *usercode = *fullcode & 0x0000FFFF; //% 65566 - *sitecode = (*fullcode >> 24) & 0x000000FF; // /= 16777216; - return PM3_SUCCESS; -} - -// calc not certain - intended to get bitstream for programming / sim -int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits) { - num_to_bytebits(0x10D00000, 32, prescoBits); - num_to_bytebits(0x00000000, 32, prescoBits + 32); - num_to_bytebits(0x00000000, 32, prescoBits + 64); - num_to_bytebits(fullcode, 32, prescoBits + 96); - return PM3_SUCCESS; -} diff --git a/client/src/cmdlfpresco.h b/client/src/cmdlfpresco.h index d90d6c573..961830ab2 100644 --- a/client/src/cmdlfpresco.h +++ b/client/src/cmdlfpresco.h @@ -12,11 +12,6 @@ #include "common.h" int CmdLFPresco(const char *Cmd); - int demodPresco(bool verbose); -int detectPresco(uint8_t *dest, size_t *size); -int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits); -int getWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode, uint32_t *fullcode, bool *Q5); - #endif From a1e94a100a2f7027ad3e6baba22a79293f81bc23 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:07:39 +0100 Subject: [PATCH 033/150] text --- client/src/cmdlfviking.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index bdf9868fb..0da4bcbd5 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -142,7 +142,7 @@ static int CmdVikingClone(const char *Cmd) { PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") , cardtype , id - , sprint_hex(payload.blocks, sizeof(payload.blocks)) + , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); clearCommandBuffer(); From 263215d242544948b6f79618677e6f1285526476 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:08:04 +0100 Subject: [PATCH 034/150] text --- client/src/cmdlfsecurakey.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index c489427e2..9e8158c82 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -175,7 +175,7 @@ static int CmdSecurakeyClone(const char *Cmd) { } if (raw_len != 12) { - PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); return PM3_EINVARG; } @@ -233,13 +233,12 @@ static int CmdSecurakeySim(const char *Cmd) { // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + CLIParserFree(ctx); if (raw_len != 12) { PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); - CLIParserFree(ctx); return PM3_EINVARG; } - CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Simulating SecuraKey - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); From 966ecd6193bd7eebc73628906eb96933bae13e58 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:09:20 +0100 Subject: [PATCH 035/150] added lf paradox sim (experimental), lf paradox clone / reader - now uses cliparser, support continuous mode, EM (untested) --- client/src/cmdlfparadox.c | 238 ++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 102 deletions(-) diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index 1b0aae510..08b67de1c 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -1,4 +1,5 @@ //----------------------------------------------------------------------------- +// Marshmellow // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -8,13 +9,11 @@ // FSK2a, rf/50, 96 bits (completely known) //----------------------------------------------------------------------------- #include "cmdlfparadox.h" - #include #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" @@ -25,40 +24,11 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. #include "crc.h" // maxim +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_paradox_clone(void) { - PrintAndLogEx(NORMAL, "clone a Paradox tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf paradox clone [h] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " b : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf paradox clone b 0f55555695596a6a9999a59a")); - return PM3_SUCCESS; -} - -/* -static int usage_lf_paradox_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Paradox card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf paradox sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf paradox sim 123 11223")); - return PM3_SUCCESS; -} -*/ - const uint8_t paradox_lut[] = { 0xDB, 0xFC, 0x3F, 0xC5, 0x50, 0x14, 0x05, 0x47, 0x9F, 0xED, 0x7D, 0x59, 0x22, 0x84, 0x21, 0x4E, @@ -71,9 +41,9 @@ const uint8_t paradox_lut[] = { #define PARADOX_PREAMBLE_LEN 8 -//by marshmellow -//Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) -//print full Paradox Prox ID and some bit format details if found +// by marshmellow +// Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) +// print full Paradox Prox ID and some bit format details if found int demodParadox(bool verbose) { (void) verbose; // unused so far @@ -204,91 +174,141 @@ static int CmdParadoxDemod(const char *Cmd) { return demodParadox(true); } -//by marshmellow -//see ASKDemod for what args are accepted -static int CmdParadoxRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 10000); - return demodParadox(true); +static int CmdParadoxReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox reader", + "read a Paradox tag", + "lf Paradox reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 10000); + demodParadox(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdParadoxClone(const char *Cmd) { - uint32_t blocks[4]; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox clone", + "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf paradox clone --raw 0f55555695596a6a9999a59a\n" + "lf paradox clone -r 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n" + "lf paradox clone -r 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_paradox_clone(); - case 'b': { - // skip first block, 3*4 =12 bytes left - uint8_t rawhex[12] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes max"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_paradox_clone(); + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } - //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks + uint32_t blocks[4]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } + + // Paradox - FSK2a, data rate 50, 3 data blocks blocks[0] = T55x7_MODULATION_FSK2a | T55x7_BITRATE_RF_50 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_INVERT_OUTPUT | T5555_MODULATION_FSK2 | T5555_SET_BITRATE(50) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone Paradox to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_PARADOX_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone Paradox to " _YELLOW_("%s") " with raw hex", cardtype); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf paradox read`") " to verify"); return res; } static int CmdParadoxSim(const char *Cmd) { - PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); - return PM3_SUCCESS; -} -/* - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_paradox_sim(); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox sim", + "Enables simulation of paradox card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.", + "lf paradox sim --raw 0f55555695596a6a9999a59a" + ); - uint8_t bs[96]; - memset(bs, 0x00, sizeof(bs)); + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + CLIParserFree(ctx); + + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "Simulating Paradox - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); + + uint8_t bs[sizeof(raw) * 8]; + bytes_to_bytebits(raw, sizeof(raw), bs); // Paradox uses: fcHigh: 10, fcLow: 8, clk: 50, invert: 1 FSK2a - uint8_t clk = 50, invert = 1, high = 10, low = 8; - - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim(); - - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); - - // if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { - // PrintAndLogEx(ERR, "Error with tag bitstream generation."); - // return 1; - // } - - PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + uint8_t clk = 50, high = 10, low = 8; lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); payload->fchigh = high; payload->fclow = low; - payload->separator = invert; + payload->separator = 0; payload->clock = clk; memcpy(payload->data, bs, sizeof(bs)); @@ -302,15 +322,29 @@ static int CmdParadoxSim(const char *Cmd) { PrintAndLogEx(INFO, "Done"); if (resp.status != PM3_EOPABORTED) return resp.status; - return PM3_SUCCESS; -} + + return PM3_SUCCESS;} +/* + + if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim(); + + facilitycode = (fc & 0x000000FF); + cardnumber = (cn & 0x0000FFFF); + + // if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { + // PrintAndLogEx(ERR, "Error with tag bitstream generation."); + // return 1; + // } + + PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + */ static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdParadoxDemod, AlwaysAvailable, "Demodulate a Paradox FSK tag from the GraphBuffer"}, - {"read", CmdParadoxRead, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, - {"clone", CmdParadoxClone, IfPm3Lf, "clone paradox tag to T55x7"}, - {"sim", CmdParadoxSim, IfPm3Lf, "simulate paradox tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdParadoxDemod, AlwaysAvailable, "Demodulate a Paradox FSK tag from the GraphBuffer"}, + {"reader", CmdParadoxReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"clone", CmdParadoxClone, IfPm3Lf, "clone paradox tag"}, + {"sim", CmdParadoxSim, IfPm3Lf, "simulate paradox tag"}, {NULL, NULL, NULL, NULL} }; From 3145a36a81cdaf1fea9726bdeab3e90d51ec5e7c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 23:42:59 +0100 Subject: [PATCH 036/150] keys --- client/dictionaries/mfc_default_keys.dic | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index b3bf93312..06e7a5348 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1275,4 +1275,6 @@ AABAFFCC7612 # gamefactory # ozdilek # -17D071403C20 \ No newline at end of file +17D071403C20 +# +534F4C415249 \ No newline at end of file From 90509c6a82a37fc2855884a750666f7cb003741c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 23:43:43 +0100 Subject: [PATCH 037/150] lf pac - now uses cliparser, support continuous mode, and EM (untested) --- client/src/cmdlfpac.c | 251 ++++++++++++++++++++++++++---------------- 1 file changed, 156 insertions(+), 95 deletions(-) diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 211ddbca0..ff543c7e8 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -25,37 +25,11 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone #include "parity.h" +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_pac_clone(void) { - PrintAndLogEx(NORMAL, "clone a PAC/Stanley tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pac clone [h] [c ] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " c : 8 byte card ID"); - PrintAndLogEx(NORMAL, " b : raw hex data. 16 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac clone c CD4F5552 ")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac clone b FF2049906D8511C593155B56D5B2649F ")); - return PM3_SUCCESS; -} -static int usage_lf_pac_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of PAC/Stanley card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The card ID is 8 byte number. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pac sim "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 byte PAC/Stanley card id"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac sim 12345678")); - return PM3_SUCCESS; -} - // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes // all bytes following 8 bit preamble are one start bit (0), 7 data bits (lsb first), odd parity bit, and one stop bit (1) static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { @@ -189,93 +163,180 @@ static int CmdPacDemod(const char *Cmd) { return demodPac(true); } -static int CmdPacRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 4096 * 2 + 20); - return demodPac(true); +static int CmdPacReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac reader", + "read a pac tag", + "lf pac reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 4096 * 2 + 20); + demodPac(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdPacClone(const char *Cmd) { - uint32_t blocks[5]; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac clone", + "clone a PAC/Stanley tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf pac clone --cn CD4F5552\n" + "lf pac clone --cn CD4F5552 --q5 -> encode for Q5/T5555 tag\n" + "lf pac clone --cn CD4F5552 --em -> encode for EM4305/4469\n" + "lf pac clone --raw FF2049906D8511C593155B56D5B2649F" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_pac_clone(); - case 'c': { - // skip first block, 4*4 = 16 bytes left - uint8_t rawhex[16] = {0}; - char cardid[9]; - int res = param_getstr(Cmd, cmdp + 1, cardid, sizeof(cardid)); - if (res < 8) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "cn", "", "8 byte PAC/Stanley card ID"), + arg_str0("r", "raw", "", "raw hex data. 16 bytes max"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - pacCardIdToRaw(rawhex, cardid); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - case 'b': { - // skip first block, 4*4 = 16 bytes left - uint8_t rawhex[16] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + uint8_t cnstr[9]; + int cnlen = 9; + memset(cnstr, 0x00, sizeof(cnstr)); + CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + // skip first block, 4*4 = 16 bytes left + int raw_len = 0; + uint8_t raw[16] = {0}; + CLIGetHexWithReturn(ctx, 2, raw, &raw_len); + + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + if (cnlen && raw_len) { + PrintAndLogEx(FAILED, "Can't specify both CardID and raw hex at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_pac_clone(); + if (cnlen && cnlen < 8) { + PrintAndLogEx(FAILED, "Card ID must be 8 or 9 hex digits (%d)", cnlen); + return PM3_EINVARG; + } - //Pac - compat mode, NRZ, data rate 32, 3 data blocks + if (cnlen == 8 || cnlen == 9) { + pacCardIdToRaw(raw, (char*)cnstr); + } + + uint32_t blocks[5]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } + + // Pac - compat mode, NRZ, data rate 32, 3 data blocks blocks[0] = T55x7_MODULATION_DIRECT | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_DIRECT | T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_PAC_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with raw hex " _GREEN_("%s") + , cardtype + , sprint_hex_inrow(raw, sizeof(raw)) + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pac read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pac reader`") " to verify"); return res; } static int CmdPacSim(const char *Cmd) { - // NRZ sim. - char cardid[9] = { 0 }; - uint8_t rawBytes[16] = { 0 }; - uint32_t rawBlocks[4]; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pac_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac sim", + "Enables simulation of PAC/Stanley card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "The card ID is 8 byte number. Larger values are truncated.", + "lf pac sim --cn CD4F5552\n" + "lf pac sim --raw FF2049906D8511C593155B56D5B2649F" + ); - int res = param_getstr(Cmd, 0, cardid, sizeof(cardid)); - if (res < 8) return usage_lf_pac_sim(); + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "cn", "", "8 byte PAC/Stanley card ID"), + arg_str0("r", "raw", "", "raw hex data. 16 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t bs[128]; - pacCardIdToRaw(rawBytes, cardid); - for (size_t i = 0; i < ARRAYLEN(rawBlocks); i++) { - rawBlocks[i] = bytes_to_num(rawBytes + (i * sizeof(uint32_t)), sizeof(uint32_t)); - num_to_bytebits(rawBlocks[i], sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + uint8_t cnstr[10]; + int cnlen = 9; + memset(cnstr, 0x00, sizeof(cnstr)); + CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); + + // skip first block, 4*4 = 16 bytes left + int raw_len = 0; + uint8_t raw[16] = {0}; + CLIGetHexWithReturn(ctx, 2, raw, &raw_len); + CLIParserFree(ctx); + + if (cnlen && raw_len) { + PrintAndLogEx(FAILED, "Can't specify both CardID and raw hex at the same time"); + return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw "_YELLOW_("%08X%08X%08X%08X"), cardid, rawBlocks[0], rawBlocks[1], rawBlocks[2], rawBlocks[3]); + if (cnlen && cnlen < 8) { + PrintAndLogEx(FAILED, "Card ID must be 8 or 9 hex digits (%d)", cnlen); + return PM3_EINVARG; + } + if (cnlen == 8 || cnlen == 9) { + pacCardIdToRaw(raw, (char*)cnstr); + } + uint8_t bs[128]; + for (size_t i = 0; i < 4; i++) { + uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); + num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + } + + if (cnlen == 8 || cnlen == 9) { + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") + , cnstr + , sprint_hex_inrow(raw, raw_len) + ); + } else { + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, raw_len)); + } + + // NRZ sim. lf_nrzsim_t *payload = calloc(1, sizeof(lf_nrzsim_t) + sizeof(bs)); payload->invert = 0; payload->separator = 0; @@ -297,11 +358,11 @@ static int CmdPacSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate a PAC tag from the GraphBuffer"}, - {"read", CmdPacRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdPacClone, IfPm3Lf, "clone PAC tag to T55x7"}, - {"sim", CmdPacSim, IfPm3Lf, "simulate PAC tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate a PAC tag from the GraphBuffer"}, + {"reader", CmdPacReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdPacClone, IfPm3Lf, "clone PAC tag to T55x7"}, + {"sim", CmdPacSim, IfPm3Lf, "simulate PAC tag"}, {NULL, NULL, NULL, NULL} }; From 026a3022dcead898cb29ccb054d2fb063c697530 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 09:39:36 +0100 Subject: [PATCH 038/150] solari --- client/dictionaries/mfc_default_keys.dic | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 06e7a5348..38c6e2503 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1277,4 +1277,5 @@ AABAFFCC7612 # 17D071403C20 # -534F4C415249 \ No newline at end of file +534F4C415249 +534f4c303232 \ No newline at end of file From edb70fe524560a0f58ee040b968d33ea8f038d8d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 10:48:53 +0100 Subject: [PATCH 039/150] lf pac clone/sim - adapted output to always print cardid --- client/src/cmdlfpac.c | 49 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index ff543c7e8..fbc642850 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -32,7 +32,7 @@ static int CmdHelp(const char *Cmd); // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes // all bytes following 8 bit preamble are one start bit (0), 7 data bits (lsb first), odd parity bit, and one stop bit (1) -static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { +static int pac_buf_to_cardid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { const size_t byteLength = 10; // start bit, 7 data bits, parity bit, stop bit const size_t startIndex = 8 + (3 * byteLength) + 1; // skip 8 bits preamble, STX, '2', '0', and first start bit const size_t dataLength = 9; @@ -67,35 +67,33 @@ static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, return PM3_SUCCESS; } -/* // convert a 16 byte array of raw demod data (FF204990XX...) to 8 bytes of PAC_8byte ID // performs no parity or checksum validation -static void pacRawToCardId(uint8_t* outCardId, const uint8_t* rawBytes) { +static void pac_raw_to_cardid(const uint8_t* src, uint8_t *dst) { for (int i = 4; i < 12; i++) { uint8_t shift = 7 - (i + 3) % 4 * 2; size_t index = i + (i - 1) / 4; - outCardId[i - 4] = reflect8((((rawBytes[index] << 8) | (rawBytes[index + 1])) >> shift) & 0xFE); + dst[i - 4] = reflect8((((src[index] << 8) | (src[index + 1])) >> shift) & 0xFE); } } -*/ // convert 8 bytes of PAC_8byte ID to 16 byte array of raw data (FF204990XX...) -static void pacCardIdToRaw(uint8_t *outRawBytes, const char *cardId) { +static void pac_cardid_to_raw(const char *src, uint8_t *dst) { uint8_t idbytes[10]; // prepend PAC_8byte card type "20" idbytes[0] = '2'; idbytes[1] = '0'; for (size_t i = 0; i < 8; i++) - idbytes[i + 2] = toupper(cardId[i]); + idbytes[i + 2] = toupper(src[i]); // initialise array with start and stop bits for (size_t i = 0; i < 16; i++) - outRawBytes[i] = 0x40 >> (i + 3) % 5 * 2; + dst[i] = 0x40 >> (i + 3) % 5 * 2; - outRawBytes[0] = 0xFF; // mark + stop - outRawBytes[1] = 0x20; // start + reflect8(STX) + dst[0] = 0xFF; // mark + stop + dst[1] = 0x20; // start + reflect8(STX) uint8_t checksum = 0; for (size_t i = 2; i < 13; i++) { @@ -112,8 +110,8 @@ static void pacCardIdToRaw(uint8_t *outRawBytes, const char *cardId) { } pattern <<= shift; - outRawBytes[index] |= pattern >> 8 & 0xFF; - outRawBytes[index + 1] |= pattern & 0xFF; + dst[index] |= pattern >> 8 & 0xFF; + dst[index + 1] |= pattern & 0xFF; } } @@ -150,7 +148,7 @@ int demodPac(bool verbose) { const size_t idLen = 9; // 8 bytes + null terminator uint8_t cardid[idLen]; - int retval = demodbuf_to_pacid(DemodBuffer, DemodBufferLen, cardid, sizeof(cardid)); + int retval = pac_buf_to_cardid(DemodBuffer, DemodBufferLen, cardid, sizeof(cardid)); if (retval == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "PAC/Stanley - Card: " _GREEN_("%s") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); @@ -238,7 +236,9 @@ static int CmdPacClone(const char *Cmd) { } if (cnlen == 8 || cnlen == 9) { - pacCardIdToRaw(raw, (char*)cnstr); + pac_cardid_to_raw((char*)cnstr, raw); + } else { + pac_raw_to_cardid(raw, cnstr); } uint32_t blocks[5]; @@ -261,10 +261,12 @@ static int CmdPacClone(const char *Cmd) { snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); } - PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with raw hex " _GREEN_("%s") + PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with ID " _GREEN_("%s") " raw " _GREEN_("%s") , cardtype + , cnstr , sprint_hex_inrow(raw, sizeof(raw)) ); + print_blocks(blocks, ARRAYLEN(blocks)); int res; @@ -319,22 +321,21 @@ static int CmdPacSim(const char *Cmd) { } if (cnlen == 8 || cnlen == 9) { - pacCardIdToRaw(raw, (char*)cnstr); + pac_cardid_to_raw((char*)cnstr, raw); + } else { + pac_raw_to_cardid(raw, cnstr); } + uint8_t bs[128]; for (size_t i = 0; i < 4; i++) { uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); } - if (cnlen == 8 || cnlen == 9) { - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") - , cnstr - , sprint_hex_inrow(raw, raw_len) - ); - } else { - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, raw_len)); - } + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") + , cnstr + , sprint_hex_inrow(raw, sizeof(raw)) + ); // NRZ sim. lf_nrzsim_t *payload = calloc(1, sizeof(lf_nrzsim_t) + sizeof(bs)); From b2544faaf8342144f46887261bfbe92370167563 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 11:04:05 +0100 Subject: [PATCH 040/150] lf noralsy - now uses cliparser, contiuous mode, EM (untested) --- client/src/cmdlfnoralsy.c | 168 ++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/client/src/cmdlfnoralsy.c b/client/src/cmdlfnoralsy.c index de62f2236..a49093ec8 100644 --- a/client/src/cmdlfnoralsy.c +++ b/client/src/cmdlfnoralsy.c @@ -8,52 +8,23 @@ // ASK/Manchester, STT, RF/32, 96 bits long (some bits unknown) //----------------------------------------------------------------------------- #include "cmdlfnoralsy.h" - #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "protocols.h" // for T55xx config register definitions -#include "lfdemod.h" // parityTest -#include "cmdlft55xx.h" // verifywrite +#include "protocols.h" // for T55xx config register definitions +#include "lfdemod.h" // parityTest +#include "cmdlft55xx.h" // verifywrite +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_noralsy_clone(void) { - PrintAndLogEx(NORMAL, "clone a Noralsy tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf noralsy clone [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Noralsy card ID"); - PrintAndLogEx(NORMAL, " : Tag allocation year"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf noralsy clone 112233")); - return PM3_SUCCESS; -} - -static int usage_lf_noralsy_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Noralsy card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf noralsy sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Noralsy card ID"); - PrintAndLogEx(NORMAL, " : Tag allocation year"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf noralsy sim 112233")); - return PM3_SUCCESS; -} - static uint8_t noralsy_chksum(uint8_t *bits, uint8_t len) { uint8_t sum = 0; for (uint8_t i = 0; i < len; i += 4) @@ -136,28 +107,72 @@ static int CmdNoralsyDemod(const char *Cmd) { return demodNoralsy(true); } -static int CmdNoralsyRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 8000); - return demodNoralsy(true); +static int CmdNoralsyReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy reader", + "read a noralsy tag", + "lf noralsy reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 8000); + demodNoralsy(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdNoralsyClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy clone", + "clone a noralsy tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf noralsy clone --cn 112233\n" + "lf noralsy clone --cn 112233 --q5 -> encode for Q5/T5555 tag\n" + "lf noralsy clone --cn 112233 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Noralsy card ID"), + arg_u64_0("y", "year", "", "tag allocation year"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint32_t id = arg_get_u32_def(ctx, 1, 0); + uint16_t year = arg_get_u32_def(ctx, 2, 2000); + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } - uint16_t year = 0; - uint32_t id = 0; uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, 0, 0}; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_noralsy_clone(); - - id = param_get32ex(Cmd, 0, 0, 10); - year = param_get32ex(Cmd, 1, 2000, 10); - + char cardtype[16] = {"T55x7"}; //Q5 - bool q5 = tolower(param_getchar(Cmd, 2) == 'q'); - if (q5) + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_NORALSY_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } uint8_t *bits = calloc(96, sizeof(uint8_t)); if (getnoralsyBits(id, year, bits) != PM3_SUCCESS) { @@ -172,36 +187,50 @@ static int CmdNoralsyClone(const char *Cmd) { free(bits); - PrintAndLogEx(INFO, "Preparing to clone Noralsy to " _YELLOW_("%s") " with CardId: %u", (q5) ? "Q5/T5555" : "T55x7", id); + PrintAndLogEx(INFO, "Preparing to clone Noralsy to " _YELLOW_("%s") " with Card id: " _GREEN_("%u"), cardtype, id); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf noralsy read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf noralsy reader`") " to verify"); return res; } static int CmdNoralsySim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy sim", + "Enables simulation of Noralsy card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n", + "lf noralsy sim --cn 1337\n" + "lf noralsy sim --cn 1337 --year 2010" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Noralsy card ID"), + arg_u64_0("y", "year", "", "tag allocation year"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + uint16_t year = arg_get_u32_def(ctx, 2, 2000); + CLIParserFree(ctx); + uint8_t bs[96]; memset(bs, 0, sizeof(bs)); - uint16_t year = 0; - uint32_t id = 0; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_noralsy_sim(); - - id = param_get32ex(Cmd, 0, 0, 10); - year = param_get32ex(Cmd, 1, 2000, 10); - if (getnoralsyBits(id, year, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Noralsy - CardId: %u", id); + PrintAndLogEx(SUCCESS, "Simulating Noralsy - CardId: " _YELLOW_("%u") " year " _YELLOW_("%u"), id, year); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); payload->encoding = 1; @@ -225,11 +254,11 @@ static int CmdNoralsySim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdNoralsyDemod, AlwaysAvailable, "Demodulate an Noralsy tag from the GraphBuffer"}, - {"read", CmdNoralsyRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdNoralsyClone, IfPm3Lf, "clone Noralsy tag to T55x7 or Q5/T5555"}, - {"sim", CmdNoralsySim, IfPm3Lf, "simulate Noralsy tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdNoralsyDemod, AlwaysAvailable, "Demodulate an Noralsy tag from the GraphBuffer"}, + {"reader", CmdNoralsyReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdNoralsyClone, IfPm3Lf, "clone Noralsy tag to T55x7 or Q5/T5555"}, + {"sim", CmdNoralsySim, IfPm3Lf, "simulate Noralsy tag"}, {NULL, NULL, NULL, NULL} }; @@ -272,7 +301,6 @@ int getnoralsyBits(uint32_t id, uint16_t year, uint8_t *bits) { return PM3_SUCCESS; } -// by iceman // find Noralsy preamble in already demoded data int detectNoralsy(uint8_t *dest, size_t *size) { if (*size < 96) return -1; //make sure buffer has data From e57e6908cf3f45eb9c7b607739d9dc69c856646b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 13:58:29 +0100 Subject: [PATCH 041/150] textual --- doc/commands.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/commands.md b/doc/commands.md index 770d9f4c6..bc0855668 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -38,6 +38,7 @@ Check column "offline" for their availability. |`analyse nuid `|Y |`create NUID from 7byte UID` |`analyse demodbuff `|Y |`Load binary string to demodbuffer` |`analyse freq `|Y |`Calc wave lengths` +|`analyse foo `|Y |`muxer` ### data @@ -772,7 +773,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf nexwatch help `|Y |`This help` |`lf nexwatch demod `|Y |`Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer` -|`lf nexwatch read `|N |`Attempt to Read and Extract tag data from the antenna` +|`lf nexwatch reader `|N |`Attempt to Read and Extract tag data from the antenna` |`lf nexwatch clone `|N |`clone NexWatch tag to T55x7` |`lf nexwatch sim `|N |`simulate NexWatch tag` @@ -785,7 +786,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf noralsy help `|Y |`This help` |`lf noralsy demod `|Y |`Demodulate an Noralsy tag from the GraphBuffer` -|`lf noralsy read `|N |`Attempt to read and extract tag data from the antenna` +|`lf noralsy reader `|N |`Attempt to read and extract tag data from the antenna` |`lf noralsy clone `|N |`clone Noralsy tag to T55x7 or Q5/T5555` |`lf noralsy sim `|N |`simulate Noralsy tag` @@ -798,7 +799,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf pac help `|Y |`This help` |`lf pac demod `|Y |`Demodulate a PAC tag from the GraphBuffer` -|`lf pac read `|N |`Attempt to read and extract tag data from the antenna` +|`lf pac reader `|N |`Attempt to read and extract tag data from the antenna` |`lf pac clone `|N |`clone PAC tag to T55x7` |`lf pac sim `|N |`simulate PAC tag` @@ -811,8 +812,8 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf paradox help `|Y |`This help` |`lf paradox demod `|Y |`Demodulate a Paradox FSK tag from the GraphBuffer` -|`lf paradox read `|N |`Attempt to read and Extract tag data from the antenna` -|`lf paradox clone `|N |`clone paradox tag to T55x7` +|`lf paradox reader `|N |`Attempt to read and Extract tag data from the antenna` +|`lf paradox clone `|N |`clone paradox tag` |`lf paradox sim `|N |`simulate paradox tag` @@ -836,7 +837,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf presco help `|Y |`This help` |`lf presco demod `|Y |`demodulate Presco tag from the GraphBuffer` -|`lf presco read `|N |`Attempt to read and Extract tag data` +|`lf presco reader `|N |`Attempt to read and Extract tag data` |`lf presco clone `|N |`clone presco tag to T55x7 or Q5/T5555` |`lf presco sim `|N |`simulate presco tag` From e4bbd30f4515b7e501e8be530bd6c31625b4383a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 17:54:34 +0100 Subject: [PATCH 042/150] lf nexwatch - now uses cliparser, support continuous mode, EM (untested) --- client/src/cmdlfnexwatch.c | 386 +++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 183 deletions(-) diff --git a/client/src/cmdlfnexwatch.c b/client/src/cmdlfnexwatch.c index 8eb44c39b..3224e94a7 100644 --- a/client/src/cmdlfnexwatch.c +++ b/client/src/cmdlfnexwatch.c @@ -9,20 +9,21 @@ //----------------------------------------------------------------------------- #include "cmdlfnexwatch.h" -#include // PRIu +#include // PRIu #include -#include // tolower -#include // free, alloc - -#include "commonutil.h" // ARRAYLEN -#include "cmdparser.h" // command_t +#include // tolower +#include // free, alloc +#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" -#include "cmddata.h" // preamblesearch +#include "cmddata.h" // preamblesearch #include "cmdlf.h" #include "lfdemod.h" -#include "protocols.h" // t55xx defines -#include "cmdlft55xx.h" // clone.. +#include "protocols.h" // t55xx defines +#include "cmdlft55xx.h" // clone.. +#include "cmdlfem4x05.h" // +#include "cliparser.h" typedef enum { SCRAMBLE, @@ -31,49 +32,6 @@ typedef enum { static int CmdHelp(const char *Cmd); -static int usage_lf_nexwatch_clone(void) { - PrintAndLogEx(NORMAL, "clone a Nexwatch tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, "You can use raw hex values or create a credential based on id, mode"); - PrintAndLogEx(NORMAL, "and type of credential (Nexkey/Quadrakey)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf nexwatch clone [h] [b ] [c ] [m ] [n|q]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, " c : card id (decimal)"); - PrintAndLogEx(NORMAL, " m : mode (decimal) (0-15, defaults to 1)"); - PrintAndLogEx(NORMAL, " n : Nexkey credential"); - PrintAndLogEx(NORMAL, " q : Quadrakey credential"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone r 5600000000213C9F8F150C")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone c 521512301 m 1 n") " -- Nexkey credential"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone c 521512301 m 1 q") " -- Quadrakey credential"); - return PM3_SUCCESS; -} - -static int usage_lf_nexwatch_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Nexwatch card"); - PrintAndLogEx(NORMAL, "You can use raw hex values or create a credential based on id, mode"); - PrintAndLogEx(NORMAL, "and type of credential (Nexkey/Quadrakey)"); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf nexwatch sim [h] [c ] [m ] [n|q]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : raw hex data. 16 bytes max"); - PrintAndLogEx(NORMAL, " c : card id (decimal)"); - PrintAndLogEx(NORMAL, " m : mode (decimal) (0-15, defaults to 1)"); - PrintAndLogEx(NORMAL, " n : Nexkey credential"); - PrintAndLogEx(NORMAL, " q : Quadrakey credential"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim r 5600000000213C9F8F150C")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim c 521512301 m 1 n") " -- Nexkey credential"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim c 521512301 m 1 q") " -- Quadrakey credential"); - return PM3_SUCCESS; -} - // scramble parity (1234) -> (4231) static uint8_t nexwatch_parity_swap(uint8_t parity) { uint8_t a = (((parity >> 3) & 1)); @@ -263,168 +221,230 @@ static int CmdNexWatchDemod(const char *Cmd) { return demodNexWatch(true); } -//by marshmellow -//see ASKDemod for what args are accepted -static int CmdNexWatchRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 20000); - return demodNexWatch(true); +static int CmdNexWatchReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch reader", + "read a nexwatch tag", + "lf nexwatch reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 20000); + demodNexWatch(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdNexWatchClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch clone", + "clone a Nexwatch tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" + "You can use raw hex values or create a credential based on id, mode\n" + "and type of credential (Nexkey / Quadrakey)", + "lf nexwatch clone --raw 5600000000213C9F8F150C00\n" + "lf nexwatch clone --cn 521512301 -m 1 -n -> Nexkey credential\n" + "lf nexwatch clone --cn 521512301 -m 1 -q -> Quadrakey credential\n" + ); - // 56000000 00213C9F 8F150C00 - uint32_t blocks[4]; - bool use_raw = false; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; - uint8_t magic = 0xBE; - uint32_t cn = 0; - uint8_t rawhex[12] = {0x56, 0}; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_u64_0(NULL, "cn", "", "card id"), + arg_u64_0("m", "mode", "", "mode (decimal) (0-15, defaults to 1)"), + arg_lit0("n", NULL, "Nexkey credential"), + arg_lit0("q", NULL, "Quadrakey credential"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_nexwatch_clone(); - case 'r': { - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0x56, 0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - use_raw = true; - cmdp += 2; - break; - } - case 'c': { - cn = param_get32ex(Cmd, cmdp + 1, 0, 10); - uint32_t scrambled; - nexwatch_scamble(SCRAMBLE, &cn, &scrambled); - num_to_bytes(scrambled, 4, rawhex + 5); - cmdp += 2; - break; - } - case 'm': { - uint8_t mode = param_get8ex(Cmd, cmdp + 1, 1, 10); - mode &= 0x0F; - rawhex[9] |= (mode << 4); - cmdp += 2; - break; - } - case 'n': { - magic = 0x88; - cmdp++; - break; - } - case 'q': { - magic = 0xBE; - cmdp++; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + uint32_t cn = arg_get_u32_def(ctx, 2, -1); + uint32_t mode = arg_get_u32_def(ctx, 3, -1); + bool use_nexkey = arg_get_lit(ctx, 4); + bool use_quadrakey = arg_get_lit(ctx, 5); + bool q5 = arg_get_lit(ctx, 6); + bool em = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + if (use_nexkey && use_quadrakey) { + PrintAndLogEx(FAILED, "Can't specify both Nexkey and Quadrakey at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_nexwatch_clone(); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + // 56000000 00213C9F 8F150C00 + bool use_raw = (raw_len != 0); + + if (use_raw && cn != -1) { + PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time"); + return PM3_EINVARG; + } + + if (cn != -1) { + uint32_t scrambled; + nexwatch_scamble(SCRAMBLE, &cn, &scrambled); + num_to_bytes(scrambled, 4, raw + 5); + } + + if (mode != -1) { + if (mode > 15) { + mode = 1; + } + mode &= 0x0F; + raw[9] |= (mode << 4); + } + + uint8_t magic = 0xBE; + if (use_nexkey) + magic = 0x88; + + if (use_quadrakey) + magic = 0xBE; + + uint32_t blocks[4]; //Nexwatch - compat mode, PSK, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_PSK1 | T55x7_BITRATE_RF_32 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_NEXWATCH_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } if (use_raw == false) { - uint8_t parity = nexwatch_parity(rawhex + 5) & 0xF; - rawhex[9] |= parity; - rawhex[10] |= nexwatch_checksum(magic, cn, parity); + uint8_t parity = nexwatch_parity(raw + 5) & 0xF; + raw[9] |= parity; + raw[10] |= nexwatch_checksum(magic, cn, parity); } for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); } - PrintAndLogEx(INFO, "Preparing to clone NexWatch to T55x7 with raw hex"); + PrintAndLogEx(INFO, "Preparing to clone NexWatch to " _YELLOW_("%s") " raw " _YELLOW_("%s"), cardtype, sprint_hex_inrow(raw, sizeof(raw))); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nexwatch read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nexwatch reader`") " to verify"); return res; } static int CmdNexWatchSim(const char *Cmd) { - uint8_t cmdp = 0; - bool errors = false; - bool use_raw = false; - uint8_t rawhex[12] = {0x56, 0}; - int rawlen = sizeof(rawhex); - uint8_t magic = 0xBE; - uint32_t cn = 0; - uint8_t bs[128]; - memset(bs, 0, sizeof(bs)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch sim", + "Enables simulation of secura card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "You can use raw hex values or create a credential based on id, mode\n" + "and type of credential (Nexkey/Quadrakey)", + "lf nexwatch sim --raw 5600000000213C9F8F150C00\n" + "lf nexwatch sim --cn 521512301 -m 1 -n -> Nexkey credential\n" + "lf nexwatch sim --cn 521512301 -m 1 -q -> Quadrakey credential" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_nexwatch_clone(); - case 'r': { - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &rawlen); - if (res != 0) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_u64_0(NULL, "cn", "", "card id"), + arg_u64_0("m", "mode", "", "mode (decimal) (0-15, defaults to 1)"), + arg_lit0("n", NULL, "Nexkey credential"), + arg_lit0("q", NULL, "Quadrakey credential"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - use_raw = true; - cmdp += 2; - break; - } - case 'c': { - cn = param_get32ex(Cmd, cmdp + 1, 0, 10); - uint32_t scrambled; - nexwatch_scamble(SCRAMBLE, &cn, &scrambled); - num_to_bytes(scrambled, 4, rawhex + 5); - cmdp += 2; - break; - } - case 'm': { - uint8_t mode = param_get8ex(Cmd, cmdp + 1, 1, 10); - mode &= 0x0F; - rawhex[9] |= (mode << 4); - cmdp += 2; - break; - } - case 'n': { - magic = 0x88; - cmdp++; - break; - } - case 'q': { - magic = 0xBE; - cmdp++; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0x56, 0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t cn = arg_get_u32_def(ctx, 2, -1); + uint32_t mode = arg_get_u32_def(ctx, 3, -1); + bool use_nexkey = arg_get_lit(ctx, 4); + bool use_quadrakey = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if (use_nexkey && use_quadrakey) { + PrintAndLogEx(FAILED, "Can't specify both Nexkey and Quadrakey at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_nexwatch_sim(); + bool use_raw = (raw_len != 0); + + if (use_raw && cn != -1) { + PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time"); + return PM3_EINVARG; + } + + if (cn != -1) { + uint32_t scrambled; + nexwatch_scamble(SCRAMBLE, &cn, &scrambled); + num_to_bytes(scrambled, 4, raw + 5); + } + + if (mode != -1) { + if (mode > 15) { + mode = 1; + } + mode &= 0x0F; + raw[9] |= (mode << 4); + } + + uint8_t magic = 0xBE; + if (use_nexkey) + magic = 0x88; + + if (use_quadrakey) + magic = 0xBE; if (use_raw == false) { - uint8_t parity = nexwatch_parity(rawhex + 5) & 0xF; - rawhex[9] |= parity; - rawhex[10] |= nexwatch_checksum(magic, cn, parity); + uint8_t parity = nexwatch_parity(raw + 5) & 0xF; + raw[9] |= parity; + raw[10] |= nexwatch_checksum(magic, cn, parity); } - // hex to bits. - uint32_t rawblocks[3]; - for (size_t i = 0; i < ARRAYLEN(rawblocks); i++) { - rawblocks[i] = bytes_to_num(rawhex + (i * sizeof(uint32_t)), sizeof(uint32_t)); - num_to_bytebits(rawblocks[i], sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + uint8_t bs[96]; + memset(bs, 0, sizeof(bs)); + + // hex to bits. (3 * 32 == 96) + for (size_t i = 0; i < 3; i++) { + uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); + num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); } - PrintAndLogEx(SUCCESS, "Simulating NexWatch - raw: %s", sprint_hex_inrow(rawhex, rawlen)); + PrintAndLogEx(SUCCESS, "Simulating NexWatch - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); payload->carrier = 2; @@ -447,11 +467,11 @@ static int CmdNexWatchSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdNexWatchDemod, AlwaysAvailable, "Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer"}, - {"read", CmdNexWatchRead, IfPm3Lf, "Attempt to Read and Extract tag data from the antenna"}, - {"clone", CmdNexWatchClone, IfPm3Lf, "clone NexWatch tag to T55x7"}, - {"sim", CmdNexWatchSim, IfPm3Lf, "simulate NexWatch tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdNexWatchDemod, AlwaysAvailable, "Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer"}, + {"reader", CmdNexWatchReader, IfPm3Lf, "Attempt to Read and Extract tag data from the antenna"}, + {"clone", CmdNexWatchClone, IfPm3Lf, "clone NexWatch tag to T55x7"}, + {"sim", CmdNexWatchSim, IfPm3Lf, "simulate NexWatch tag"}, {NULL, NULL, NULL, NULL} }; From 97b1562bad3d10f6dadda28d5c86bd10711db75b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 19:13:48 +0100 Subject: [PATCH 043/150] lf motorola - now uses cliparser, supports continuous mode, EM (untested) --- client/src/cmdlfmotorola.c | 132 ++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 47 deletions(-) diff --git a/client/src/cmdlfmotorola.c b/client/src/cmdlfmotorola.c index 28b6bd39b..9f700c043 100644 --- a/client/src/cmdlfmotorola.c +++ b/client/src/cmdlfmotorola.c @@ -9,22 +9,20 @@ // PSK1, RF/32, 64 bits long, at 74 kHz //----------------------------------------------------------------------------- #include "cmdlfmotorola.h" - -#include //tolower - -#include "commonutil.h" // ARRAYLEN +#include // tolower +#include "commonutil.h" // ARRAYLEN #include "common.h" #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "lfdemod.h" // preamble test -#include "protocols.h" // t55xx defines -#include "cmdlft55xx.h" // clone.. -#include "cmdlf.h" // cmdlfconfig -#include "cliparser.h" // cli parse input - +#include "lfdemod.h" // preamble test +#include "protocols.h" // t55xx defines +#include "cmdlft55xx.h" // clone.. +#include "cmdlf.h" // cmdlfconfig +#include "cliparser.h" // cli parse input +#include "cmdlfem4x05.h" // EM defines static int CmdHelp(const char *Cmd); @@ -124,7 +122,22 @@ static int CmdMotorolaDemod(const char *Cmd) { return demodMotorola(true); } -static int CmdMotorolaRead(const char *Cmd) { +static int CmdMotorolaReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf motorola reader", + "read a Motorola tag", + "lf motorola reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + // Motorola Flexpass seem to work at 74 kHz // and take about 4400 samples to befor modulating sample_config sc = { @@ -138,61 +151,86 @@ static int CmdMotorolaRead(const char *Cmd) { }; lf_config(&sc); - // 64 * 32 * 2 * n-ish - lf_read(false, 5000); + do { + // 64 * 32 * 2 * n-ish + lf_read(false, 5000); + demodMotorola(!cm); + } while (cm && !kbd_enter_pressed()); // reset back to 125 kHz sc.divisor = LF_DIVISOR_125; sc.samples_to_skip = 0; lf_config(&sc); - return demodMotorola(true); + + return PM3_SUCCESS; } static int CmdMotorolaClone(const char *Cmd) { - - uint32_t blocks[3] = {0}; - uint8_t data[8]; - int datalen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "lf motorola clone", - "clone Motorola UID to T55x7 or Q5/T5555 tag\n" + "clone Motorola UID to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" "defaults to 64 bit format", - "lf motorola clone -r a0000000a0002021" + "lf motorola clone --raw a0000000a0002021\n" + "lf motorola clone --q5 --raw a0000000a0002021 -> encode for Q5/T5555 tag\n" + "lf motorola clone --em --raw a0000000a0002021 -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, - arg_strx1("r", "raw", "", "raw bytes"), - arg_lit0("q", "Q5", "optional - specify writing to Q5/T5555 tag"), + arg_strx1("r", "raw", "", "raw hex bytes. 8 bytes"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - CLIGetHexWithReturn(ctx, 1, data, &datalen); - bool is_t5555 = arg_get_lit(ctx, 2); + + int raw_len = 0; + uint8_t raw[8]; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); CLIParserFree(ctx); - //TODO add selection of chip for Q5 or T55x7 + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } - PrintAndLogEx(INFO, "Target chip " _YELLOW_("%s"), (is_t5555) ? "Q5/T5555" : "T55x7"); + //TODO add selection of chip for Q5 or T55x7 + uint32_t blocks[3] = {0}; + + blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK1 | (2 << T55x7_MAXBLOCK_SHIFT); + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | 2 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_MOTOROLA_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + blocks[1] = bytes_to_num(raw, 4); + blocks[2] = bytes_to_num(raw + 4, 4); // config for Motorola 64 format (RF/32;PSK1 with RF/2; Maxblock=2) - PrintAndLogEx(INFO, "Preparing to clone Motorola 64bit tag"); - PrintAndLogEx(INFO, "Using raw " _GREEN_("%s"), sprint_hex_inrow(data, datalen)); - - if (is_t5555) - blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | 2 << T5555_MAXBLOCK_SHIFT; - else - blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK1 | (2 << T55x7_MAXBLOCK_SHIFT); - - - blocks[1] = bytes_to_num(data, 4); - blocks[2] = bytes_to_num(data + 4, 4); - + PrintAndLogEx(INFO, "Preparing to clone Motorola 64bit to " _YELLOW_("%s") " with raw " _GREEN_("%s") + , cardtype + , sprint_hex_inrow(raw, sizeof(raw)) + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf motorola read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf motorola reader`") " to verify"); return res; } @@ -205,11 +243,11 @@ static int CmdMotorolaSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdMotorolaDemod, AlwaysAvailable, "Demodulate an MOTOROLA tag from the GraphBuffer"}, - {"read", CmdMotorolaRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdMotorolaClone, IfPm3Lf, "clone MOTOROLA tag to T55x7"}, - {"sim", CmdMotorolaSim, IfPm3Lf, "simulate MOTOROLA tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdMotorolaDemod, AlwaysAvailable, "Demodulate an MOTOROLA tag from the GraphBuffer"}, + {"reader", CmdMotorolaReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdMotorolaClone, IfPm3Lf, "clone MOTOROLA tag to T55x7"}, + {"sim", CmdMotorolaSim, IfPm3Lf, "simulate MOTOROLA tag"}, {NULL, NULL, NULL, NULL} }; @@ -266,5 +304,5 @@ int detectMotorola(uint8_t *dest, size_t *size) { } int readMotorolaUid(void) { - return (CmdMotorolaRead("") == PM3_SUCCESS); + return (CmdMotorolaReader("") == PM3_SUCCESS); } From 00f140683fa8b2a03b36755b5bdf0617d9ba9711 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 19:41:25 +0100 Subject: [PATCH 044/150] lf keri - now supports continuous mode, EM (untested) --- client/src/cmdlfkeri.c | 134 +++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/client/src/cmdlfkeri.c b/client/src/cmdlfkeri.c index c482a7941..93e8a2521 100644 --- a/client/src/cmdlfkeri.c +++ b/client/src/cmdlfkeri.c @@ -179,68 +179,56 @@ static int CmdKeriDemod(const char *Cmd) { return demodKeri(true); } -static int CmdKeriRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 10000); - return demodKeri(true); -} - -static int CmdKeriClone(const char *Cmd) { - - bool q5 = false, em = false; - - uint8_t keritype[2] = {'i'}; // default to internalid - int typeLen = 0; - uint32_t fc = 0; - uint32_t cid = 0; - uint32_t internalid = 0; - uint32_t blocks[3] = { - T55x7_TESTMODE_DISABLED | - T55x7_X_MODE | - T55x7_MODULATION_PSK1 | - T55x7_PSKCF_RF_2 | - 2 << T55x7_MAXBLOCK_SHIFT, - 0, - 0 - }; - - // dynamic bitrate used - blocks[0] |= 0xF << 18; - +static int CmdKeriReader(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf keri clone", - "clone a KERI tag to a T55x7, Q5/T5555 or EM4305/4469 tag", - "lf keri clone -t i --id 12345\n" - "lf keri clone -t m --fc 6 --id 12345\n"); + CLIParserInit(&ctx, "lf keri reader", + "read a keri tag", + "lf keri reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 10000); + demodKeri(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; +} + +static int CmdKeriClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf keri clone", + "clone a KERI tag to a T55x7, Q5/T5555 or EM4305/4469 tag", + "lf keri clone -t i --cn 12345 -> Internal ID\n" + "lf keri clone -t m --fc 6 --cn 12345 -> MS ID\n"); void *argtable[] = { arg_param_begin, - arg_lit0("q", "q5", "specify writing to Q5/T5555 tag"), arg_str0("t", "type", "", "Type m - MS, i - Internal ID"), arg_int0(NULL, "fc", "", "Facility Code"), - arg_int1(NULL, "id", "", "Keri ID"), - arg_lit0(NULL, "em", "specify writing to EM4305/4469 tag"), + arg_int1(NULL, "cn", "", "KERI card ID"), + arg_lit0(NULL, "q5", "specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - char cardtype[16] = {"T55x7"}; - if (arg_get_lit(ctx, 1)) { - blocks[0] = T5555_FIXED | T5555_MODULATION_PSK1 | T5555_SET_BITRATE(32) | T5555_PSK_RF_2 | 2 << T5555_MAXBLOCK_SHIFT; - snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); - q5 = true; - } - if (arg_get_lit(ctx, 5)) { - blocks[0] = EM4305_KERI_CONFIG_BLOCK; - snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); - em = true; - } + uint8_t keritype[2] = {'i'}; // default to internalid + int typeLen = sizeof(keritype); + CLIGetStrWithReturn(ctx, 1, keritype, &typeLen); - typeLen = sizeof(keritype); - CLIGetStrWithReturn(ctx, 2, keritype, &typeLen); - - fc = arg_get_int_def(ctx, 3, 0); - cid = arg_get_int_def(ctx, 4, 0); + uint32_t fc = arg_get_int_def(ctx, 2, 0); + uint32_t cid = arg_get_int_def(ctx, 3, 0); + bool q5 = arg_get_lit(ctx, 4); + bool em = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (q5 && em) { @@ -249,6 +237,7 @@ static int CmdKeriClone(const char *Cmd) { } // Setup card data/build internal id + uint32_t internalid = 0; switch (keritype[0]) { case 'i' : // Internal ID // MSB is ONE @@ -262,6 +251,24 @@ static int CmdKeriClone(const char *Cmd) { return PM3_EINVARG; } + uint32_t blocks[3]; + blocks[0] = T55x7_TESTMODE_DISABLED | T55x7_X_MODE | T55x7_MODULATION_PSK1 | T55x7_PSKCF_RF_2 | 2 << T55x7_MAXBLOCK_SHIFT; + // dynamic bitrate used + blocks[0] |= 0xF << 18; + + char cardtype[16] = {"T55x7"}; + + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_PSK1 | T5555_SET_BITRATE(32) | T5555_PSK_RF_2 | 2 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + if (em) { + blocks[0] = EM4305_KERI_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + // Prepare and write to card // 3 LSB is ONE uint64_t data = ((uint64_t)internalid << 3) + 7; @@ -288,19 +295,18 @@ static int CmdKeriSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf keri sim", - "Enables simulation of KERI card with card number.", - "lf keri sim --id 112233" + "Enables simulation of KERI card with internal ID.\n" + "You supply a KERI card id and it will converted to a KERI internal ID.", + "lf keri sim --cn 112233" ); void *argtable[] = { arg_param_begin, - arg_int1(NULL, "id", "", "KERI Internal ID"), + arg_u64_1(NULL, "id", "", "KERI card ID"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - - uint64_t internalid = arg_get_int_def(ctx, 1, 0); - + uint64_t internalid = arg_get_u64_def(ctx, 1, 0); CLIParserFree(ctx); internalid |= 0x80000000; @@ -314,7 +320,7 @@ static int CmdKeriSim(const char *Cmd) { bs[j++] = ((internalid >> i) & 1); } - PrintAndLogEx(SUCCESS, "Simulating KERI - Internal Id: %" PRIu64, internalid); + PrintAndLogEx(SUCCESS, "Simulating KERI - Internal Id " _YELLOW_("%" PRIu64), internalid); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); payload->carrier = 2; @@ -322,8 +328,6 @@ static int CmdKeriSim(const char *Cmd) { payload->clock = 32; memcpy(payload->data, bs, sizeof(bs)); - PrintAndLogEx(INFO, "Simulating"); - clearCommandBuffer(); SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + sizeof(bs)); free(payload); @@ -338,11 +342,11 @@ static int CmdKeriSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdKeriDemod, AlwaysAvailable, "Demodulate an KERI tag from the GraphBuffer"}, - {"read", CmdKeriRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdKeriClone, IfPm3Lf, "clone KERI tag to T55x7 or Q5/T5555"}, - {"sim", CmdKeriSim, IfPm3Lf, "simulate KERI tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdKeriDemod, AlwaysAvailable, "Demodulate an KERI tag from the GraphBuffer"}, + {"reader", CmdKeriReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdKeriClone, IfPm3Lf, "clone KERI tag to T55x7 or Q5/T5555"}, + {"sim", CmdKeriSim, IfPm3Lf, "simulate KERI tag"}, {NULL, NULL, NULL, NULL} }; From e581a662b47ec13196131ba8efc55c2d78f0156c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 21:41:21 +0100 Subject: [PATCH 045/150] maur --- client/dictionaries/mfc_default_keys.dic | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 38c6e2503..9f716608a 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -105,6 +105,11 @@ c934fe34d934 cccccccccccc dddddddddddd eeeeeeeeeeee +# +# elevator +# data from forum +FFFFFF545846 +# f1a97341a9fc 44ab09010845 # hotel system 85fed980ea5a # hotel system From 1722d2cb43fe15dc70f97ded7da7a288681b00e2 Mon Sep 17 00:00:00 2001 From: tcprst Date: Fri, 27 Nov 2020 15:46:06 -0500 Subject: [PATCH 046/150] hf iclass chk -now use cliparser --- client/src/cmdhficlass.c | 82 ++++++++++++---------------------------- 1 file changed, 24 insertions(+), 58 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 49784c4f5..033f27b02 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -115,21 +115,6 @@ static int usage_hf_iclass_managekeys(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_chk(void) { - PrintAndLogEx(NORMAL, "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass chk [h|e|r] [f (*.dic)]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h Show this help"); - PrintAndLogEx(NORMAL, " f Dictionary file with default iclass keys"); - PrintAndLogEx(NORMAL, " r raw"); - PrintAndLogEx(NORMAL, " e elite"); - PrintAndLogEx(NORMAL, " c credit key (if not use, default is debit)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass chk f dictionaries/iclass_default_keys.dic")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass chk f dictionaries/iclass_default_keys.dic e")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_lookup(void) { PrintAndLogEx(NORMAL, "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u ] [p ] [m ]\n"); @@ -2730,60 +2715,41 @@ static int iclass_chk_keys(uint8_t *keyBlock, uint32_t keycount) { */ static int CmdHFiClassCheckKeys(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass chk", + "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", + "hf iclass chk -f dictionaries/iclass_default_keys.dic\n" + "hf iclass chk -f dictionaries/iclass_default_keys.dic --elite"); - // empty string - if (strlen(Cmd) == 0) return usage_hf_iclass_chk(); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Dictionary file with default iclass keys"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool use_credit_key = arg_get_lit(ctx, 2); + bool use_elite = arg_get_lit(ctx, 3); + bool use_raw = arg_get_lit(ctx, 4); + + CLIParserFree(ctx); uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // elite key, raw key, standard key - bool use_elite = false; - bool use_raw = false; - bool use_credit_key = false; bool found_key = false; //bool found_credit = false; bool got_csn = false; - bool errors = false; - uint8_t cmdp = 0x00; - - char filename[FILE_PATH_SIZE] = {0}; - uint8_t fileNameLen = 0; uint64_t t1 = msclock(); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_chk(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(WARNING, _RED_("no filename found after f")); - errors = true; - } - cmdp += 2; - break; - case 'e': - use_elite = true; - cmdp++; - break; - case 'c': - use_credit_key = true; - cmdp++; - break; - case 'r': - use_raw = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors) return usage_hf_iclass_chk(); - uint8_t *keyBlock = NULL; uint32_t keycount = 0; From 4e1c1f96f8540c75d3569fc95612181f8223212e Mon Sep 17 00:00:00 2001 From: tcprst Date: Fri, 27 Nov 2020 18:27:02 -0500 Subject: [PATCH 047/150] hf iclass managekeys - now use cliparser --- client/src/cmdhficlass.c | 160 +++++++++++++++++---------------------- doc/cheatsheet.md | 11 +-- 2 files changed, 77 insertions(+), 94 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 033f27b02..b2b4b306c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -92,29 +92,6 @@ static int usage_hf_iclass_calc_newkey(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_managekeys(void) { - PrintAndLogEx(NORMAL, "Manage iCLASS Keys in client memory:\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " n : specify the keyNbr to set in memory"); - PrintAndLogEx(NORMAL, " k : set a key in memory"); - PrintAndLogEx(NORMAL, " f : specify a filename to use with load or save operations"); - PrintAndLogEx(NORMAL, " s : save keys in memory to file specified by filename"); - PrintAndLogEx(NORMAL, " l : load keys to memory from file specified by filename"); - PrintAndLogEx(NORMAL, " p : print keys loaded into memory\n"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " -- set key"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys n 0 k 1122334455667788")); - PrintAndLogEx(NORMAL, " -- save key file"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys f mykeys.bin s")); - PrintAndLogEx(NORMAL, " -- load key file"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys f mykeys.bin l")); - PrintAndLogEx(NORMAL, " -- print keys"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys p")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_lookup(void) { PrintAndLogEx(NORMAL, "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u ] [p ] [m ]\n"); @@ -2505,83 +2482,88 @@ static int printKeys(void) { } static int CmdHFiClassManageKeys(const char *Cmd) { - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - uint8_t KEY[8] = {0}; - char filename[FILE_PATH_SIZE]; - uint8_t fileNameLen = 0; - bool errors = false; - uint8_t operation = 0; - char tempStr[20]; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass managekeys", + "Manage iCLASS Keys in client memory", + "hf iclass managekeys --ki 0 -k 1122334455667788 -> set key\n" + "hf iclass managekeys -f mykeys.bin --save -> save key file\n" + "hf iclass managekeys -f mykeys.bin --load -> load key file\n" + "hf iclass managekeys -p -> print keys"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_managekeys(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(ERR, "No filename found"); - errors = true; - } - cmdp += 2; - break; - case 'n': - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr >= ICLASS_KEYS_MAX) { - PrintAndLogEx(ERR, "Invalid block number, MAX is " _YELLOW_("%d"), ICLASS_KEYS_MAX); - errors = true; - } - cmdp += 2; - break; - case 'k': - operation += 3; //set key - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { //ul-c or ev1/ntag key length - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else { - PrintAndLogEx(WARNING, "\nERROR: Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'p': - operation += 4; //print keys in memory - cmdp++; - break; - case 'l': - operation += 5; //load keys from file - cmdp++; - break; - case 's': - operation += 6; //save keys to file - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "Specify a filename to use with load or save operations"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"), + arg_lit0(NULL, "load", "Load keys to memory from file specified by filename"), + arg_lit0("p", "print", "Print keys loaded into memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + int key_len = 0; + uint8_t key[8] = {0}; + CLIGetHexWithReturn(ctx, 2, key, &key_len); + uint8_t operation = 0; + + if (key_len > 0) { + operation += 3; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if (errors) return usage_hf_iclass_managekeys(); + + int key_nr = arg_get_int_def(ctx, 3, -1); + + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + PrintAndLogEx(SUCCESS, "Setting key[%d] " _GREEN_("%s"), key_nr); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + if (arg_get_lit(ctx, 4)) { //save + operation += 6; + } + if (arg_get_lit(ctx, 5)) { //load + operation += 5; + } + if (arg_get_lit(ctx, 6)) { //print + operation += 4; + } + + CLIParserFree(ctx); if (operation == 0) { - PrintAndLogEx(WARNING, "no operation specified (load, save, or print)\n"); - return usage_hf_iclass_managekeys(); + PrintAndLogEx(ERR, "No operation specified (load, save, or print)\n"); + return PM3_EINVARG; } if (operation > 6) { - PrintAndLogEx(WARNING, "Too many operations specified\n"); - return usage_hf_iclass_managekeys(); + PrintAndLogEx(ERR, "Too many operations specified\n"); + return PM3_EINVARG; } - if (operation > 4 && fileNameLen == 0) { - PrintAndLogEx(WARNING, "You must enter a filename when loading or saving\n"); - return usage_hf_iclass_managekeys(); + if (operation > 4 && fnlen == 0) { + PrintAndLogEx(ERR, "You must enter a filename when loading or saving\n"); + return PM3_EINVARG; + } + if (key_len > 0 && key_nr == -1) { + PrintAndLogEx(ERR, "Please specify key index when specifying key"); + return PM3_EINVARG; } switch (operation) { case 3: - memcpy(iClass_Key_Table[keyNbr], KEY, 8); + memcpy(iClass_Key_Table[key_nr], key, 8); return PM3_SUCCESS; case 4: return printKeys(); @@ -2613,7 +2595,7 @@ static void add_key(uint8_t *key) { if (i == ICLASS_KEYS_MAX) { PrintAndLogEx(INFO, "Couldn't find an empty keyslot"); } else { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf iclass managekeys p`") " to view keys"); + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf iclass managekeys -p`") " to view keys"); } } diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index e725d8d25..335084e2a 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -109,19 +109,20 @@ Print keystore ``` Options --- -p : print keys loaded into memory +-p, --print Print keys loaded into memory -pm3 --> hf iclass managekeys p + +pm3 --> hf iclass managekeys -p ``` Add key to keystore [0-7] ``` Options --- -n : specify the keyNbr to set in memory -k : set a key in memory +-f, --file Specify a filename to use with load or save operations + --ki Specify key index to set key in memory -pm3 --> hf iclass managekeys n 3 k AFA785A7DAB33378 +pm3 --> hf iclass managekeys --ki 3 -k AFA785A7DAB33378 ``` Encrypt iCLASS Block From 769e80910456335cf4094705be72e94a67902938 Mon Sep 17 00:00:00 2001 From: tcprst Date: Fri, 27 Nov 2020 19:51:54 -0500 Subject: [PATCH 048/150] hf iclass calcnewkey - now use cliparser --- client/src/cmdhficlass.c | 203 +++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 93 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b2b4b306c..47e61a363 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,27 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_calc_newkey(void) { - PrintAndLogEx(NORMAL, "Calculate new key for updating\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o n s [csn] e\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " o : *specify a key as 16 hex symbols or a key number as 1 symbol"); - PrintAndLogEx(NORMAL, " n : *specify a key as 16 hex symbols or a key number as 1 symbol"); - PrintAndLogEx(NORMAL, " s : specify a card Serial number to diversify the key (if omitted will attempt to read a csn)"); - PrintAndLogEx(NORMAL, " e : specify new key as elite calc"); - PrintAndLogEx(NORMAL, " ee : specify old and new key as elite calc"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " -- e key to e key given csn"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee")); - PrintAndLogEx(NORMAL, " -- std key to e key read csn"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899 e")); - PrintAndLogEx(NORMAL, " -- std to std read csn"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899")); - PrintAndLogEx(NORMAL, "\nNOTE: * = required"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_lookup(void) { PrintAndLogEx(NORMAL, "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u ] [p ] [m ]\n"); @@ -2339,89 +2318,127 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u } static int CmdHFiClassCalcNewKey(const char *Cmd) { - uint8_t OLDKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t NEWKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - char tempStr[50] = {0}; - bool givenCSN = false; - bool old_elite = false; - bool elite = false; - bool errors = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass calcnewkey", + "Calculate new keys for updating (blocks 3 & 4)", + "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 --csn deadbeafdeadbeaf --elite2 -> e key to e key given csn\n" + "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 --elite -> std key to e key read csn\n" + "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 -> std to std read csn"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_calc_newkey(); - case 'e': - dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr)); - if (dataLen == 2) - old_elite = true; - elite = true; - cmdp++; - break; - case 'n': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, NEWKEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(NEWKEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: NewKey Nbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: NewKey is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'o': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, OLDKEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(OLDKEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 's': - givenCSN = true; - if (param_gethex(Cmd, cmdp + 1, CSN, 16)) - return usage_hf_iclass_calc_newkey(); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "old", "", "Specify key as 8 bytes (16 hex symbols)"), + arg_int0(NULL, "oki", "", "Old key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "new", "", "Specify key as 8 bytes (16 hex symbols)"), + arg_int0(NULL, "nki", "", "New key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "csn", "", "Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)"), + arg_lit0(NULL, "elite", "Elite computations applied to new key"), + arg_lit0(NULL, "elite2", "Elite computations applied to both old and new key"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int old_key_len = 0; + uint8_t old_key[8] = {0}; + CLIGetHexWithReturn(ctx, 1, old_key, &old_key_len); + + int old_key_nr = arg_get_int_def(ctx, 2, -1); + + if (old_key_len > 0 && old_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify old key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (old_key_len > 0) { + if (old_key_len != 8) { + PrintAndLogEx(ERR, "Old key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (old_key_nr >= 0) { + if (old_key_nr < ICLASS_KEYS_MAX) { + memcpy(old_key, iClass_Key_Table[old_key_nr], 8); + PrintAndLogEx(SUCCESS, "Using old key[%d] " _GREEN_("%s"), old_key_nr, sprint_hex(iClass_Key_Table[old_key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify an old key or old key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int new_key_len = 0; + uint8_t new_key[8] = {0}; + CLIGetHexWithReturn(ctx, 3, new_key, &new_key_len); + + int new_key_nr = arg_get_int_def(ctx, 4, -1); + + if (new_key_len > 0 && new_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify new key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (new_key_len > 0) { + if (new_key_len != 8) { + PrintAndLogEx(ERR, "New key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (new_key_nr >= 0) { + if (new_key_nr < ICLASS_KEYS_MAX) { + memcpy(new_key, iClass_Key_Table[new_key_nr], 8); + PrintAndLogEx(SUCCESS, "Using new key[%d] " _GREEN_("%s"), new_key_nr, sprint_hex(iClass_Key_Table[new_key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify an new key or old key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int csn_len = 0; + uint8_t csn[8] = {0}; + CLIGetHexWithReturn(ctx, 5, csn, &csn_len); + bool givenCSN = false; + + if (csn_len > 0) { + givenCSN = true; + if (csn_len != 8) { + PrintAndLogEx(ERR, "CSN is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if (errors || cmdp < 4) return usage_hf_iclass_calc_newkey(); + + bool elite = arg_get_lit(ctx, 6); + bool old_elite = false; + + if (arg_get_lit(ctx, 7)) { + elite = true; + old_elite = true; + } + + CLIParserFree(ctx); + + uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_only(CSN, CCNR, true) == false) { + if (select_only(csn, CCNR, true) == false) { DropField(); return PM3_ESOFT; } } - HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, old_elite, true); + HFiClassCalcNewKey(csn, old_key, new_key, xor_div_key, elite, old_elite, true); return PM3_SUCCESS; } From a5ab00357dcf755b8fff18a0b4a63f53d3270014 Mon Sep 17 00:00:00 2001 From: tcprst Date: Fri, 27 Nov 2020 20:28:23 -0500 Subject: [PATCH 049/150] hf iclass lookup - now use cliparser (untested) --- client/src/cmdhficlass.c | 158 +++++++++++++++++---------------------- doc/cheatsheet.md | 13 ++-- 2 files changed, 75 insertions(+), 96 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 47e61a363..7f51da0a9 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,23 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_lookup(void) { - PrintAndLogEx(NORMAL, "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u ] [p ] [m ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h Show this help"); - PrintAndLogEx(NORMAL, " f Dictionary file with default iclass keys"); - PrintAndLogEx(NORMAL, " u CSN"); - PrintAndLogEx(NORMAL, " p EPURSE"); - PrintAndLogEx(NORMAL, " m macs"); - PrintAndLogEx(NORMAL, " r raw"); - PrintAndLogEx(NORMAL, " e elite"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic e")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int cmp_uint32(const void *a, const void *b) { @@ -2900,89 +2883,84 @@ out: // this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in. // Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work. static int CmdHFiClassLookUp(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass lookup", + "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file", + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic\n" + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic --elite"); - uint8_t CSN[8]; - uint8_t EPURSE[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - uint8_t MACS[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - uint8_t CCNR[12]; - uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; - - // elite key, raw key, standard key - bool use_elite = false; - bool use_raw = false; - bool errors = false; - uint8_t cmdp = 0x00; + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Dictionary file with default iclass keys"), + arg_str1(NULL, "csn", "", "Specify CSN as 8 bytes (16 hex symbols)"), + arg_str1(NULL, "epurse", "", "Specify ePurse as 8 bytes (16 hex symbols)"), + arg_str1(NULL, "macs", "", "MACs"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "elite", "Elite computations applied to key"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - iclass_prekey_t *prekey = NULL; - int len = 0; - // if empty string - if (strlen(Cmd) == 0) errors = true; - // time - uint64_t t1 = msclock(); + int csn_len = 0; + uint8_t csn[8] = {0}; + CLIGetHexWithReturn(ctx, 2, csn, &csn_len); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_lookup(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)) < 1) { - PrintAndLogEx(WARNING, "No filename found after f"); - errors = true; - } - cmdp += 2; - break; - case 'u': - param_gethex_ex(Cmd, cmdp + 1, CSN, &len); - if (len >> 1 != sizeof(CSN)) { - PrintAndLogEx(WARNING, "Wrong CSN length, expected %zu got [%d]", sizeof(CSN), len >> 1); - errors = true; - } - cmdp += 2; - break; - case 'm': - param_gethex_ex(Cmd, cmdp + 1, MACS, &len); - if (len >> 1 != sizeof(MACS)) { - PrintAndLogEx(WARNING, "Wrong MACS length, expected %zu got [%d] ", sizeof(MACS), len >> 1); - errors = true; - } else { - memcpy(MAC_TAG, MACS + 4, 4); - } - cmdp += 2; - break; - case 'p': - param_gethex_ex(Cmd, cmdp + 1, EPURSE, &len); - if (len >> 1 != sizeof(EPURSE)) { - PrintAndLogEx(WARNING, "Wrong EPURSE length, expected %zu got [%d] ", sizeof(EPURSE), len >> 1); - errors = true; - } - cmdp += 2; - break; - case 'e': - use_elite = true; - cmdp++; - break; - case 'r': - use_raw = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + if (csn_len > 0) { + if (csn_len != 8) { + PrintAndLogEx(ERR, "CSN is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if (errors) return usage_hf_iclass_lookup(); + int epurse_len = 0; + uint8_t epurse[8] = {0}; + CLIGetHexWithReturn(ctx, 3, epurse, &epurse_len); + + if (epurse_len > 0) { + if (epurse_len != 8) { + PrintAndLogEx(ERR, "ePurse is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + int macs_len = 0; + uint8_t macs[8] = {0}; + CLIGetHexWithReturn(ctx, 4, macs, &macs_len); + + if (macs_len > 0) { + if (macs_len != 8) { + PrintAndLogEx(ERR, "MAC is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + bool use_elite = arg_get_lit(ctx, 5); + bool use_raw = arg_get_lit(ctx, 6); + + CLIParserFree(ctx); + + uint8_t CCNR[12]; + uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; + + iclass_prekey_t *prekey = NULL; + + // time + uint64_t t1 = msclock(); // stupid copy.. CCNR is a combo of epurse and reader nonce - memcpy(CCNR, EPURSE, 8); - memcpy(CCNR + 8, MACS, 4); + memcpy(CCNR, epurse, 8); + memcpy(CCNR + 8, macs, 4); - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(CSN, sizeof(CSN))); - PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(EPURSE, sizeof(EPURSE))); - PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(MACS, sizeof(MACS))); + PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); + PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse))); + PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, sizeof(macs))); PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); @@ -3004,7 +2982,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Generating diversified keys..."); - GenerateMacKeyFrom(CSN, CCNR, use_raw, use_elite, keyBlock, keycount, prekey); + GenerateMacKeyFrom(csn, CCNR, use_raw, use_elite, keyBlock, keycount, prekey); if (use_elite) PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 335084e2a..abdf37ef6 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -202,13 +202,14 @@ Verify custom iCLASS key ``` Options --- -f : Dictionary file with default iCLASS keys -u : CSN -p : EPURSE -m : macs -e : elite +-f, --file Dictionary file with default iclass keys + --csn Specify CSN as 8 bytes (16 hex symbols) + --epurse Specify ePurse as 8 bytes (16 hex symbols) + --macs MACs + --raw no computations applied to key (raw) + --elite Elite computations applied to key -pm3 --> hf iclass lookup u 010a0ffff7ff12e0 p feffffffffffffff m 66348979153c41b9 f iclass_default_keys e +pm3 --> hf iclass lookup --csn 010a0ffff7ff12e0 --epurse feffffffffffffff --macs 66348979153c41b9 -f iclass_default_keys --elite ``` ## MIFARE From 448a0546b1b929c0714afdccc8b639e7476ed1f9 Mon Sep 17 00:00:00 2001 From: tcprst Date: Fri, 27 Nov 2020 21:22:23 -0500 Subject: [PATCH 050/150] hf iclass sim - now use cliparser --- client/src/cmdhficlass.c | 69 +++++++++++++++++--------------- client/src/loclass/elite_crack.c | 2 +- doc/cheatsheet.md | 15 ++++--- doc/loclass_notes.md | 4 +- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 7f51da0a9..b25cb463f 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -51,27 +51,6 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; -static int usage_hf_iclass_sim(void) { - PrintAndLogEx(NORMAL, "Simulate a iCLASS legacy/standard tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iCLASS sim [h]

"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " address - memory address to read. (0-15)"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_read 1"); - PrintAndLogEx(NORMAL, " lf em 4x05_read 1 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_write(void) { - PrintAndLogEx(NORMAL, "Write EM4x05/4x69. Tag must be on antenna. "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_write [h]
"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " address - memory address to write to. (0-13, 99 for Protection Words)"); - PrintAndLogEx(NORMAL, " data - data to write (hex)"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de"); - PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_info(void) { - PrintAndLogEx(NORMAL, "Tag information EM4205/4305/4469//4569 tags. Tag must be on antenna."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_info [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_info"); - PrintAndLogEx(NORMAL, " lf em 4x05_info deadc0de"); - return PM3_SUCCESS; -} +static int CmdHelp(const char *Cmd); // 1 = EM4x69 // 2 = EM4x05 @@ -140,7 +88,6 @@ static bool em4x05_col_parity_test(uint8_t *bs, size_t size, uint8_t rows, uint8 return true; } - // download samples from device and copy to Graphbuffer static bool em4x05_download_samples(void) { @@ -387,6 +334,7 @@ static bool em4x05_verify_write(uint8_t addr, uint32_t pwd, bool use_pwd, uint32 uint32_t r = 0; int res = em4x05_read_word_ext(addr, pwd, use_pwd, &r); if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "%08x == %08x", r, data); return (r == data); } return false; @@ -506,7 +454,40 @@ int em4x05_write_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t dat return PM3_SUCCESS; } +static int em4x05_protect(uint32_t pwd, bool use_pwd, uint32_t data) { + struct { + uint32_t password; + uint32_t data; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.data = data; + payload.usepwd = use_pwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000)) { + PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + int CmdEM4x05Demod(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05 demod", + "Try to find EM 4x05 preamble, if found decode / descramble data", + "lf em 4x05 demod" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); uint32_t dummy = 0; return em4x05_demod_resp(&dummy, false); } @@ -514,11 +495,11 @@ int CmdEM4x05Demod(const char *Cmd) { int CmdEM4x05Dump(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em dump", + CLIParserInit(&ctx, "lf em 4x05 dump", "Dump EM4x05/EM4x69. Tag must be on antenna.", - "lf em dump\n" - "lf em dump -p 11223344\n" - "lf em dump -f myfile -p 11223344" + "lf em 4x05 dump\n" + "lf em 4x05 dump -p 11223344\n" + "lf em 4x05 dump -f myfile -p 11223344" ); void *argtable[] = { @@ -734,29 +715,43 @@ int CmdEM4x05Dump(const char *Cmd) { } int CmdEM4x05Read(const char *Cmd) { - uint8_t addr; - uint32_t pwd; - bool usePwd = false; - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_read(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05 read", + "Read EM4x05/EM4x69. Tag must be on antenna.", + "lf em 4x05 read -a 1\n" + "lf em 4x05 read --addr 1 --pwd 11223344" + ); - addr = param_get8ex(Cmd, 0, 50, 10); - pwd = param_get32ex(Cmd, 1, 0xFFFFFFFF, 16); + void *argtable[] = { + arg_param_begin, + arg_int1("a", "addr", "", "memory address to read. (0-15)"), + arg_str0("p", "pwd", "", "optional - password, 4 bytes hex"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint8_t addr = (uint8_t)arg_get_int_def(ctx, 1, 50); + uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 2, 0xFFFFFFFFFFFFFFFF); + CLIParserFree(ctx); + + uint32_t pwd = 0; + bool use_pwd = false; if (addr > 15) { - PrintAndLogEx(WARNING, "Address must be between 0 and 15"); - return PM3_ESOFT; + PrintAndLogEx(ERR, "Address must be between 0 and 15"); + return PM3_EINVARG; } - if (pwd == 0xFFFFFFFF) { + + if (inputpwd == 0xFFFFFFFFFFFFFFFF) { PrintAndLogEx(INFO, "Reading address %02u", addr); } else { - usePwd = true; + pwd = (inputpwd & 0xFFFFFFFF); + use_pwd = true; PrintAndLogEx(INFO, "Reading address %02u using password %08X", addr, pwd); } uint32_t word = 0; - int status = em4x05_read_word_ext(addr, pwd, usePwd, &word); + int status = em4x05_read_word_ext(addr, pwd, use_pwd, &word); if (status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Address %02d | %08X - %s", addr, word, (addr > 13) ? "Lock" : ""); else if (status == PM3_EFAILED) @@ -767,55 +762,61 @@ int CmdEM4x05Read(const char *Cmd) { } int CmdEM4x05Write(const char *Cmd) { - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_write(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05 write", + "Write EM4x05/EM4x69. Tag must be on antenna.", + "lf em 4x05 write -a 1 -d deadc0de\n" + "lf em 4x05 write --addr 1 --pwd 11223344 --data deadc0de\n" + "lf em 4x05 write --po --pwd 11223344 --data deadc0de\n" + ); - bool usePwd = false; - uint8_t addr; - uint32_t data, pwd; - - addr = param_get8ex(Cmd, 0, 50, 10); - data = param_get32ex(Cmd, 1, 0, 16); - pwd = param_get32ex(Cmd, 2, 0xFFFFFFFF, 16); - bool protectOperation = addr == 99; // will do better with cliparser... - - if ((addr > 13) && (!protectOperation)) { + void *argtable[] = { + arg_param_begin, + arg_int0("a", "addr", "", "memory address to write to. (0-13)"), + arg_str1("d", "data", "", "data to write, 4 bytes hex"), + arg_str0("p", "pwd", "", "optional - password, 4 bytes hex"), + arg_lit0(NULL, "po", "protect operation"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint8_t addr = (uint8_t)arg_get_int_def(ctx, 1, 50); + uint32_t data = arg_get_u32(ctx, 2); + uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 3, 0xFFFFFFFFFFFFFFFF); + bool protect_operation = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if ((addr > 13) && (protect_operation == false)) { PrintAndLogEx(WARNING, "Address must be between 0 and 13"); return PM3_EINVARG; } + + bool use_pwd = false; + uint32_t pwd = ( inputpwd != 0xFFFFFFFFFFFFFFFF) ? (inputpwd & 0xFFFFFFFF) : 0; if (pwd == 0xFFFFFFFF) { - if (protectOperation) + if (protect_operation) PrintAndLogEx(INFO, "Writing protection words data %08X", data); else PrintAndLogEx(INFO, "Writing address %d data %08X", addr, data); } else { - usePwd = true; - if (protectOperation) + use_pwd = true; + if (protect_operation) PrintAndLogEx(INFO, "Writing protection words data %08X using password %08X", data, pwd); else PrintAndLogEx(INFO, "Writing address %d data %08X using password %08X", addr, data, pwd); } - if (protectOperation) { // set Protect Words - struct { - uint32_t password; - uint32_t data; - uint8_t usepwd; - } PACKED payload; - - payload.password = pwd; - payload.data = data; - payload.usepwd = usePwd; - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000)) { - PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); - return PM3_ETIMEOUT; + int res = PM3_SUCCESS; + // set Protect Words + if (protect_operation) { + res = em4x05_protect(pwd, use_pwd, data); + if ( res != PM3_SUCCESS) { + return res; } } else { - em4x05_write_word_ext(addr, pwd, usePwd, data); + res = em4x05_write_word_ext(addr, pwd, use_pwd, data); + if ( res != PM3_SUCCESS) { + return res; + } } if (em4x05_download_samples() == false) @@ -826,86 +827,111 @@ int CmdEM4x05Write(const char *Cmd) { if (status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Data written and verified"); else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied %s operation", protectOperation ? "Protect" : "Write"); + PrintAndLogEx(ERR, "Tag denied %s operation", protect_operation ? "Protect" : "Write"); else PrintAndLogEx(DEBUG, "No answer from tag"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05_read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05 read`") " to verify"); return status; } int CmdEM4x05Wipe(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05 wipe", + "Wipe EM4x05/EM4x69. Tag must be on antenna.", + "lf em 4x05 wipe --4305 -p 11223344 -> wipe EM 4305 w pwd\n" + "lf em 4x05 wipe --4205 -> wipe EM 4205\n" + "lf em 4x05 wipe --4369 -> wipe EM 4369" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "4205", "target chip type EM 4205"), + arg_lit0(NULL, "4305", "target chip type EM 4305 (default)"), + arg_lit0(NULL, "4369", "target chip type EM 4369"), + arg_lit0(NULL, "4369", "target chip type EM 4469"), + arg_str0("p", "pwd", "", "optional - password, 4 bytes hex"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool target_4205 = arg_get_lit(ctx, 1); + bool target_4305 = arg_get_lit(ctx, 2); + bool target_4369 = arg_get_lit(ctx, 3); + bool target_4469 = arg_get_lit(ctx, 4); + uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 5, 0xFFFFFFFFFFFFFFFF); + CLIParserFree(ctx); + + uint8_t foo = target_4205 + target_4305 + target_4369 + target_4469; + + if (foo > 1) { + PrintAndLogEx(ERR, "Can't target multiple chip types at the same time"); + return PM3_EINVARG; + } + uint8_t addr = 0; + uint32_t chip_info = 0x00040072; // Chip info/User Block normal 4305 Chip Type + uint32_t chip_UID = 0x614739AE; // UID normally readonly, but just in case + uint32_t block_data = 0x00000000; // UserBlock/Password (set to 0x00000000 for a wiped card1 + uint32_t config = 0x0001805F; // Default config (no password) + + if (target_4205) { + chip_info = 0x00040070; + } + if (target_4369) { + chip_info = 0x00020078; // found on HID Prox + } + if (target_4469) { +// chip_info = 0x00020078; // need to get a normal 4469 chip info block + } + + bool use_pwd = false; uint32_t pwd = 0; - uint8_t cmdp = 0; - uint8_t chipType = 1; // em4305 - uint32_t chipInfo = 0x00040072; // Chip info/User Block normal 4305 Chip Type - uint32_t chipUID = 0x614739AE; // UID normally readonly, but just in case - uint32_t blockData = 0x00000000; // UserBlock/Password (set to 0x00000000 for a wiped card1 - uint32_t config = 0x0001805F; // Default config (no password) - int success = PM3_SUCCESS; - char cmdStr [100]; - char optchk[10]; - - while (param_getchar(Cmd, cmdp) != 0x00) { - // check if cmd is a 1 byte option - param_getstr(Cmd, cmdp, optchk, sizeof(optchk)); - if (strlen(optchk) == 1) { // Have a single character so option not part of password - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'c': // chip type - if (param_getchar(Cmd, cmdp) != 0x00) - chipType = param_get8ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'h': // return usage_lf_em4x05_wipe(); - default : // Unknown or 'h' send help - return usage_lf_em4x05_wipe(); - break; - }; - } else { // Not a single character so assume password - pwd = param_get32ex(Cmd, cmdp, 1, 16); - cmdp++; - } + if ( inputpwd != 0xFFFFFFFFFFFFFFFF) { + pwd = (inputpwd & 0xFFFFFFFF); + use_pwd = true; } - - switch (chipType) { - case 0 : // em4205 - chipInfo = 0x00040070; - config = 0x0001805F; - break; - case 1 : // em4305 - chipInfo = 0x00040072; - config = 0x0001805F; - break; - default : // Type 0/Default : EM4305 - chipInfo = 0x00040072; - config = 0x0001805F; - } - // block 0 : User Data or Chip Info - sprintf(cmdStr, "%d %08X %08X", 0, chipInfo, pwd); - CmdEM4x05Write(cmdStr); + int res = em4x05_write_word_ext(0, pwd, use_pwd, chip_info); + if ( res != PM3_SUCCESS) { + return res; + } + // block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others - sprintf(cmdStr, "%d %08X %08X", 1, chipUID, pwd); - CmdEM4x05Write(cmdStr); + res = em4x05_write_word_ext(1, pwd, use_pwd, chip_UID); + if ( res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "UID block write failed"); + } + // block 2 : password - sprintf(cmdStr, "%d %08X %08X", 2, blockData, pwd); - CmdEM4x05Write(cmdStr); - pwd = blockData; // Password should now have changed, so use new password + res = em4x05_write_word_ext(2, pwd, use_pwd, block_data); + if ( res != PM3_SUCCESS) { + return res; + } + + // Password should now have changed, so use new password + pwd = block_data; // block 3 : user data - sprintf(cmdStr, "%d %08X %08X", 3, blockData, pwd); - CmdEM4x05Write(cmdStr); + res = em4x05_write_word_ext(3, pwd, use_pwd, block_data); + if ( res != PM3_SUCCESS) { + return res; + } + // block 4 : config - sprintf(cmdStr, "%d %08X %08X", 4, config, pwd); - CmdEM4x05Write(cmdStr); + res = em4x05_write_word_ext(4, pwd, use_pwd, config); + if ( res != PM3_SUCCESS) { + return res; + } // Remainder of user/data blocks for (addr = 5; addr < 14; addr++) {// Clear user data blocks - sprintf(cmdStr, "%d %08X %08X", addr, blockData, pwd); - CmdEM4x05Write(cmdStr); + res = em4x05_write_word_ext(addr, pwd, use_pwd, block_data); + if ( res != PM3_SUCCESS) { + return res; + } } - - return success; + return PM3_SUCCESS; } static const char *printEM4x05_known(uint32_t word) { @@ -1174,24 +1200,37 @@ static void printEM4x05ProtectionBits(uint32_t word, uint8_t addr) { } } - //quick test for EM4x05/EM4x69 tag bool em4x05_isblock0(uint32_t *word) { return (em4x05_read_word_ext(0, 0, false, word) == PM3_SUCCESS); } int CmdEM4x05Info(const char *Cmd) { - uint32_t pwd; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05 info", + "Tag information EM4205/4305/4469//4569 tags. Tag must be on antenna.", + "lf em 4x05 info\n" + "lf em 4x05 info -p 11223344" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pwd", "", "optional - password, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 1, 0xFFFFFFFFFFFFFFFF); + CLIParserFree(ctx); + + bool use_pwd = false; + uint32_t pwd = 0; + if (inputpwd != 0xFFFFFFFFFFFFFFFF) { + pwd = inputpwd & 0xFFFFFFFF; + use_pwd = true; + } + uint32_t word = 0, block0 = 0, serial = 0; - bool usePwd = false; - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_lf_em4x05_info(); - - // for now use default input of 1 as invalid (unlikely 1 will be a valid password...) - pwd = param_get32ex(Cmd, 0, 0xFFFFFFFF, 16); - - if (pwd != 0xFFFFFFFF) - usePwd = true; // read word 0 (chip info) // block 0 can be read even without a password. @@ -1209,7 +1248,7 @@ int CmdEM4x05Info(const char *Cmd) { // read word 4 (config block) // needs password if one is set - if (em4x05_read_word_ext(EM_CONFIG_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) { + if (em4x05_read_word_ext(EM_CONFIG_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "(CmdEM4x05Info) failed to read CONFIG BLOCK"); return PM3_ESOFT; } @@ -1221,7 +1260,7 @@ int CmdEM4x05Info(const char *Cmd) { if (card_type == EM_4205 || card_type == EM_4305) { // read word 14 and 15 to see which is being used for the protection bits - if (em4x05_read_word_ext(EM4305_PROT1_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) { + if (em4x05_read_word_ext(EM4305_PROT1_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) { return PM3_ESOFT; } @@ -1229,7 +1268,7 @@ int CmdEM4x05Info(const char *Cmd) { printEM4x05ProtectionBits(word, EM4305_PROT1_BLOCK); return PM3_SUCCESS; } else { // if status bit says this is not the used protection word - if (em4x05_read_word_ext(EM4305_PROT2_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) + if (em4x05_read_word_ext(EM4305_PROT2_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) return PM3_ESOFT; if (word & 0x8000) { printEM4x05ProtectionBits(word, EM4305_PROT2_BLOCK); @@ -1238,7 +1277,7 @@ int CmdEM4x05Info(const char *Cmd) { } } else if (card_type == EM_4369 || card_type == EM_4469) { // read word 3 to see which is being used for the protection bits - if (em4x05_read_word_ext(EM4469_PROT_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) { + if (em4x05_read_word_ext(EM4469_PROT_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) { return PM3_ESOFT; } printEM4x05ProtectionBits(word, EM4469_PROT_BLOCK); @@ -1259,11 +1298,11 @@ static bool is_cancelled(void) { int CmdEM4x05Chk(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_chk", + CLIParserInit(&ctx, "lf em 4x05 chk", "This command uses a dictionary attack against EM4205/4305/4469/4569", - "lf em 4x05_chk\n" - "lf em 4x05_chk -e 000022B8 -> remember to use 0x for hex\n" - "lf em 4x05_chk -f t55xx_default_pwds -> use T55xx default dictionary" + "lf em 4x05 chk\n" + "lf em 4x05 chk -e 000022B8 -> remember to use 0x for hex\n" + "lf em 4x05 chk -f t55xx_default_pwds -> use T55xx default dictionary" ); void *argtable[] = { @@ -1360,12 +1399,12 @@ int CmdEM4x05Chk(const char *Cmd) { int CmdEM4x05Brute(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_brute", + CLIParserInit(&ctx, "lf em 4x05 brute", "This command tries to bruteforce the password of a EM4205/4305/4469/4569\n", "Note: if you get many false positives, change position on the antenna" - "lf em 4x05_brute\n" - "lf em 4x05_brute -n 1 -> stop after first candidate found\n" - "lf em 4x05_brute -s 000022B8 -> remember to use 0x for hex" + "lf em 4x05 brute\n" + "lf em 4x05 brute -n 1 -> stop after first candidate found\n" + "lf em 4x05 brute -s 000022B8 -> remember to use 0x for hex" ); void *argtable[] = { @@ -1464,11 +1503,11 @@ static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t v int CmdEM4x05Unlock(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_unlock", + CLIParserInit(&ctx, "lf em 4x05 unlock", "execute tear off against EM4205/4305/4469/4569", - "lf em 4x05_unlock\n" - "lf em 4x05_unlock -s 4100 -e 4100 -> lock on and autotune at 4100us\n" - "lf em 4x05_unlock -n 10 -s 3000 -e 4400 -> scan delays 3000us -> 4400us" + "lf em 4x05 unlock\n" + "lf em 4x05 unlock -s 4100 -e 4100 -> lock on and autotune at 4100us\n" + "lf em 4x05 unlock -n 10 -s 3000 -e 4400 -> scan delays 3000us -> 4400us" ); void *argtable[] = { @@ -2082,3 +2121,29 @@ int CmdEM4x05Sniff(const char *Cmd) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"brute", CmdEM4x05Brute, IfPm3Lf, "Bruteforce password"}, + {"chk", CmdEM4x05Chk, IfPm3Lf, "Check passwords from dictionary"}, + {"demod", CmdEM4x05Demod, AlwaysAvailable, "demodulate a EM4x05/EM4x69 tag from the GraphBuffer"}, + {"dump", CmdEM4x05Dump, IfPm3Lf, "dump EM4x05/EM4x69 tag"}, + {"info", CmdEM4x05Info, IfPm3Lf, "tag information EM4x05/EM4x69"}, + {"read", CmdEM4x05Read, IfPm3Lf, "read word data from EM4x05/EM4x69"}, + {"sniff", CmdEM4x05Sniff, AlwaysAvailable, "Attempt to recover em4x05 commands from sample buffer"}, + {"unlock", CmdEM4x05Unlock, IfPm3Lf, "execute tear off against EM4x05/EM4x69"}, + {"wipe", CmdEM4x05Wipe, IfPm3Lf, "wipe EM4x05/EM4x69 tag"}, + {"write", CmdEM4x05Write, IfPm3Lf, "write word data to EM4x05/EM4x69"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFEM4X05(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} \ No newline at end of file diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 2f768c62b..be0b17bb1 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -1,6 +1,8 @@ //----------------------------------------------------------------------------- // Copyright (C) 2020 tharexde // +// modified iceman, 2020 +// // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. @@ -10,11 +12,14 @@ #include "cmdlfem4x50.h" #include +#include "cmdparser.h" // command_t #include "fileutils.h" #include "comms.h" #include "commonutil.h" #include "em4x50.h" +static int CmdHelp(const char *Cmd); + static int usage_lf_em4x50_info(void) { PrintAndLogEx(NORMAL, "Read all information of EM4x50. Tag must be on antenna."); PrintAndLogEx(NORMAL, ""); @@ -749,3 +754,25 @@ int CmdEM4x50Wipe(const char *Cmd) { return PM3_SUCCESS; } + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"}, + {"info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"}, + {"write", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"}, + {"write_password", CmdEM4x50WritePassword, IfPm3EM4x50, "change password of EM4x50 tag"}, + {"read", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"}, + {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe data from EM4x50"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFEM4X50(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdlfem4x50.h b/client/src/cmdlfem4x50.h index 01417aa1e..36e87ce70 100644 --- a/client/src/cmdlfem4x50.h +++ b/client/src/cmdlfem4x50.h @@ -14,6 +14,8 @@ #include"common.h" #include "em4x50.h" +int CmdLFEM4X50(const char *Cmd); + int read_em4x50_uid(void); bool detect_4x50_block(void); int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out, bool verbose); diff --git a/doc/cliparser_todo.txt b/doc/cliparser_todo.txt index 4d85a78fd..4e1dc76cd 100644 --- a/doc/cliparser_todo.txt +++ b/doc/cliparser_todo.txt @@ -174,30 +174,11 @@ lf simfsk lf simpsk lf simbidir lf sniff -lf tune -lf em 410x_demod -lf em 410x_read -lf em 410x_sim -lf em 410x_brute -lf em 410x_watch -lf em 410x_spoof -lf em 410x_clone -lf em 4x05_demod -lf em 4x05_dump -lf em 4x05_wipe -lf em 4x05_info -lf em 4x05_read -lf em 4x05_write -lf em 4x50_dump -lf em 4x50_info -lf em 4x50_write -lf em 4x50_write_password -lf em 4x50_read -lf em 4x50_wipe -lf hitag info +lf em 410x +lf em 4x05 +lf em 4x50 lf hitag reader lf hitag sim -lf hitag sniff lf hitag writer lf hitag dump lf hitag cc diff --git a/doc/commands.md b/doc/commands.md index 6a9b61b40..f496ef3fe 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -572,34 +572,14 @@ Check column "offline" for their availability. ### lf em - { EM4X CHIPs & RFIDs... } + { EM CHIPs & RFIDs... } |command |offline |description |------- |------- |----------- |`lf em help `|Y |`This help` -|`lf em 410x_demod `|Y |`demodulate a EM410x tag from the GraphBuffer` -|`lf em 410x_read `|N |`attempt to read and extract tag data` -|`lf em 410x_sim `|N |`simulate EM410x tag` -|`lf em 410x_brute `|N |`reader bruteforce attack by simulating EM410x tags` -|`lf em 410x_watch `|N |`watches for EM410x 125/134 kHz tags (option 'h' for 134)` -|`lf em 410x_spoof `|N |`watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)` -|`lf em 410x_clone `|N |`write EM410x UID to T55x7 or Q5/T5555 tag` -|`lf em 4x05_chk `|N |`Check passwords from dictionary` -|`lf em 4x05_demod `|Y |`demodulate a EM4x05/EM4x69 tag from the GraphBuffer` -|`lf em 4x05_dump `|N |`dump EM4x05/EM4x69 tag` -|`lf em 4x05_wipe `|N |`wipe EM4x05/EM4x69 tag` -|`lf em 4x05_info `|N |`tag information EM4x05/EM4x69` -|`lf em 4x05_read `|N |`read word data from EM4x05/EM4x69` -|`lf em 4x05_write `|N |`write word data to EM4x05/EM4x69` -|`lf em 4x05_unlock `|N |`execute tear off against EM4x05/EM4x69` -|`lf em 4x05_sniff `|Y |`Attempt to recover em4x05 commands from sample buffer` -|`lf em 4x05_brute `|N |`Bruteforce password` -|`lf em 4x50_dump `|N |`dump EM4x50 tag` -|`lf em 4x50_info `|N |`tag information EM4x50` -|`lf em 4x50_write `|N |`write word data to EM4x50` -|`lf em 4x50_write_password`|N |`change password of EM4x50 tag` -|`lf em 4x50_read `|N |`read word data from EM4x50` -|`lf em 4x50_wipe `|N |`wipe data from EM4x50` +|`lf em 410x `|Y |`EM 410x commands...` +|`lf em 4x05 `|Y |`EM 4x05 commands...` +|`lf em 4x50 `|Y |`EM 4x50 commands...` ### lf fdxb From 0f5442134555e26aed197dda9f70b8dbd4c4be27 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 00:13:37 +0100 Subject: [PATCH 137/150] forgot a file --- client/src/cmdlfem410x.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 client/src/cmdlfem410x.h diff --git a/client/src/cmdlfem410x.h b/client/src/cmdlfem410x.h new file mode 100644 index 000000000..62d35cf55 --- /dev/null +++ b/client/src/cmdlfem410x.h @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// 2016, 2017 marshmellow, iceman +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency EM 410x commands +//----------------------------------------------------------------------------- + +#ifndef CMDLFEM410X_H__ +#define CMDLFEM410X_H__ + +#include "common.h" + +int CmdLFEM410X(const char *Cmd); + +int demodEM410x(bool verbose); +void printEM410x(uint32_t hi, uint64_t id); + +int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo); +int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose); + +#endif From f22c0e87bcabdf54748984b8c88e1dea637f7a8d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 00:14:18 +0100 Subject: [PATCH 138/150] forgot a file --- client/src/cmdlfem410x.c | 653 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 653 insertions(+) create mode 100644 client/src/cmdlfem410x.c diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c new file mode 100644 index 000000000..ef35e95a0 --- /dev/null +++ b/client/src/cmdlfem410x.c @@ -0,0 +1,653 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency EM4x commands +//----------------------------------------------------------------------------- + +#include "cmdlfem410x.h" +#include "cmdlfem4x50.h" + +#include +#include +#include +#include +#include + +#include "fileutils.h" +#include "cmdparser.h" // command_t +#include "comms.h" +#include "commonutil.h" +#include "common.h" +#include "util_posix.h" +#include "protocols.h" +#include "ui.h" +#include "proxgui.h" +#include "graph.h" +#include "cmddata.h" +#include "cmdlf.h" +#include "lfdemod.h" +#include "generator.h" +#include "cliparser.h" +#include "cmdhw.h" + +static uint64_t g_em410xid = 0; + +static int CmdHelp(const char *Cmd); + +//////////////// 410x commands +static int usage_lf_em410x_demod(void) { + PrintAndLogEx(NORMAL, "Usage: lf em 410x_demod [h] [clock] <0|1> [maxError]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " clock - set clock as integer, optional, if not set, autodetect."); + PrintAndLogEx(NORMAL, " <0|1> - 0 normal output, 1 for invert output"); + PrintAndLogEx(NORMAL, " maxerror - set maximum allowed errors, default = 100."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod") " = demod an EM410x Tag ID from GraphBuffer"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 32") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 32 1") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 1") " = demod an EM410x Tag ID from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 64 1 0") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/64 and inverting data and allowing 0 demod errors"); + return PM3_SUCCESS; +} +static int usage_lf_em410x_watch(void) { + PrintAndLogEx(NORMAL, "Enables IOProx compatible reader mode printing details of scanned tags."); + PrintAndLogEx(NORMAL, "By default, values are printed and logged until the button is pressed or another USB command is issued."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 410x_watch"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_watch")); + return PM3_SUCCESS; +} + +static int usage_lf_em410x_clone(void) { + PrintAndLogEx(NORMAL, "Writes EM410x ID to a T55x7 or Q5/T5555 tag"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 410x_clone [h] [clock]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " - ID number"); + PrintAndLogEx(NORMAL, " - 0|1 0 = Q5/T5555, 1 = T55x7"); + PrintAndLogEx(NORMAL, " - 16|32|40|64, optional, set R/F clock rate, defaults to 64"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_clone 0F0368568B 1") " = write ID to t55x7 card"); + return PM3_SUCCESS; +} +static int usage_lf_em410x_ws(void) { + PrintAndLogEx(NORMAL, "Watch 'nd Spoof, activates reader, waits until a EM410x tag gets presented then it starts simulating the found UID"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 410x_spoof [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_spoof")); + return PM3_SUCCESS; +} +static int usage_lf_em410x_sim(void) { + PrintAndLogEx(NORMAL, "Simulating EM410x tag"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 410x_sim [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " uid - uid (10 HEX symbols)"); + PrintAndLogEx(NORMAL, " clock - clock (32|64) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_sim 0F0368568B")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_sim 0F0368568B 32")); + return PM3_SUCCESS; +} +static int usage_lf_em410x_brute(void) { + PrintAndLogEx(NORMAL, "Bruteforcing by emulating EM410x tag"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 410x_brute [h] ids.txt [d 2000] [c clock]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " ids.txt - file with UIDs in HEX format, one per line"); + PrintAndLogEx(NORMAL, " d (2000) - pause delay in milliseconds between UIDs simulation, default 1000 ms (optional)"); + PrintAndLogEx(NORMAL, " c (32) - clock (32|64), default 64 (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt c 32")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt d 3000")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt d 3000 c 32")); + return PM3_SUCCESS; +} + +/* Read the ID of an EM410x tag. + * Format: + * 1111 1111 1 <-- standard non-repeatable header + * XXXX [row parity bit] <-- 10 rows of 5 bits for our 40 bit tag ID + * .... + * CCCC <-- each bit here is parity for the 10 bits above in corresponding column + * 0 <-- stop bit, end of tag + */ + +// Construct the graph for emulating an EM410X tag +static void ConstructEM410xEmulGraph(const char *uid, const uint8_t clock) { + + int i, j, binary[4], parity[4]; + uint32_t n; + /* clear our graph */ + ClearGraph(true); + + /* write 16 zero bit sledge */ + for (i = 0; i < 20; i++) + AppendGraph(false, clock, 0); + + /* write 9 start bits */ + for (i = 0; i < 9; i++) + AppendGraph(false, clock, 1); + + /* for each hex char */ + parity[0] = parity[1] = parity[2] = parity[3] = 0; + for (i = 0; i < 10; i++) { + /* read each hex char */ + sscanf(&uid[i], "%1x", &n); + for (j = 3; j >= 0; j--, n /= 2) + binary[j] = n % 2; + + /* append each bit */ + AppendGraph(false, clock, binary[0]); + AppendGraph(false, clock, binary[1]); + AppendGraph(false, clock, binary[2]); + AppendGraph(false, clock, binary[3]); + + /* append parity bit */ + AppendGraph(false, clock, binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + + /* keep track of column parity */ + parity[0] ^= binary[0]; + parity[1] ^= binary[1]; + parity[2] ^= binary[2]; + parity[3] ^= binary[3]; + } + + /* parity columns */ + AppendGraph(false, clock, parity[0]); + AppendGraph(false, clock, parity[1]); + AppendGraph(false, clock, parity[2]); + AppendGraph(false, clock, parity[3]); + + /* stop bit */ + AppendGraph(true, clock, 0); +} + +//by marshmellow +//print 64 bit EM410x ID in multiple formats +void printEM410x(uint32_t hi, uint64_t id) { + + if (!id && !hi) return; + + PrintAndLogEx(SUCCESS, "EM410x%s pattern found", (hi) ? " XL" : ""); + + uint64_t n = 1; + uint64_t id2lo = 0; + uint8_t m, i; + for (m = 5; m > 0; m--) { + for (i = 0; i < 8; i++) { + id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8))); + } + } + + if (hi) { + //output 88 bit em id + PrintAndLogEx(NORMAL, "\nEM TAG ID : "_YELLOW_("%06X%016" PRIX64), hi, id); + PrintAndLogEx(NORMAL, "Clock rate : "_YELLOW_("RF/%d"), g_DemodClock); + } else { + //output 40 bit em id + PrintAndLogEx(NORMAL, "\nEM TAG ID : "_YELLOW_("%010" PRIX64), id); + PrintAndLogEx(NORMAL, "Clock rate : "_YELLOW_("RF/%d"), g_DemodClock); + PrintAndLogEx(NORMAL, "\nPossible de-scramble patterns\n"); + PrintAndLogEx(NORMAL, "Unique TAG ID : %010" PRIX64, id2lo); + PrintAndLogEx(NORMAL, "HoneyWell IdentKey {"); + PrintAndLogEx(NORMAL, "DEZ 8 : %08" PRIu64, id & 0xFFFFFF); + PrintAndLogEx(NORMAL, "DEZ 10 : %010" PRIu64, id & 0xFFFFFFFF); + PrintAndLogEx(NORMAL, "DEZ 5.5 : %05" PRIu64 ".%05" PRIu64, (id >> 16LL) & 0xFFFF, (id & 0xFFFF)); + PrintAndLogEx(NORMAL, "DEZ 3.5A : %03" PRIu64 ".%05" PRIu64, (id >> 32ll), (id & 0xFFFF)); + PrintAndLogEx(NORMAL, "DEZ 3.5B : %03" PRIu64 ".%05" PRIu64, (id & 0xFF000000) >> 24, (id & 0xFFFF)); + PrintAndLogEx(NORMAL, "DEZ 3.5C : %03" PRIu64 ".%05" PRIu64, (id & 0xFF0000) >> 16, (id & 0xFFFF)); + PrintAndLogEx(NORMAL, "DEZ 14/IK2 : %014" PRIu64, id); + PrintAndLogEx(NORMAL, "DEZ 15/IK3 : %015" PRIu64, id2lo); + PrintAndLogEx(NORMAL, "DEZ 20/ZK : %02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64, + (id2lo & 0xf000000000) >> 36, + (id2lo & 0x0f00000000) >> 32, + (id2lo & 0x00f0000000) >> 28, + (id2lo & 0x000f000000) >> 24, + (id2lo & 0x0000f00000) >> 20, + (id2lo & 0x00000f0000) >> 16, + (id2lo & 0x000000f000) >> 12, + (id2lo & 0x0000000f00) >> 8, + (id2lo & 0x00000000f0) >> 4, + (id2lo & 0x000000000f) + ); + uint64_t paxton = (((id >> 32) << 24) | (id & 0xffffff)) + 0x143e00; + PrintAndLogEx(NORMAL, "}\nOther : %05" PRIu64 "_%03" PRIu64 "_%08" PRIu64, (id & 0xFFFF), ((id >> 16LL) & 0xFF), (id & 0xFFFFFF)); + PrintAndLogEx(NORMAL, "Pattern Paxton : %" PRIu64 " [0x%" PRIX64 "]", paxton, paxton); + + uint32_t p1id = (id & 0xFFFFFF); + uint8_t arr[32] = {0x00}; + int j = 23; + for (int k = 0 ; k < 24; ++k, --j) { + arr[k] = (p1id >> k) & 1; + } + + uint32_t p1 = 0; + + p1 |= arr[23] << 21; + p1 |= arr[22] << 23; + p1 |= arr[21] << 20; + p1 |= arr[20] << 22; + + p1 |= arr[19] << 18; + p1 |= arr[18] << 16; + p1 |= arr[17] << 19; + p1 |= arr[16] << 17; + + p1 |= arr[15] << 13; + p1 |= arr[14] << 15; + p1 |= arr[13] << 12; + p1 |= arr[12] << 14; + + p1 |= arr[11] << 6; + p1 |= arr[10] << 2; + p1 |= arr[9] << 7; + p1 |= arr[8] << 1; + + p1 |= arr[7] << 0; + p1 |= arr[6] << 8; + p1 |= arr[5] << 11; + p1 |= arr[4] << 3; + + p1 |= arr[3] << 10; + p1 |= arr[2] << 4; + p1 |= arr[1] << 5; + p1 |= arr[0] << 9; + PrintAndLogEx(NORMAL, "Pattern 1 : %d [0x%X]", p1, p1); + + uint16_t sebury1 = id & 0xFFFF; + uint8_t sebury2 = (id >> 16) & 0x7F; + uint32_t sebury3 = id & 0x7FFFFF; + PrintAndLogEx(NORMAL, "Pattern Sebury : %d %d %d [0x%X 0x%X 0x%X]", sebury1, sebury2, sebury3, sebury1, sebury2, sebury3); + } +} +/* Read the ID of an EM410x tag. + * Format: + * 1111 1111 1 <-- standard non-repeatable header + * XXXX [row parity bit] <-- 10 rows of 5 bits for our 40 bit tag ID + * .... + * CCCC <-- each bit here is parity for the 10 bits above in corresponding column + * 0 <-- stop bit, end of tag + */ +int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo) { + size_t idx = 0; + uint8_t bits[512] = {0}; + size_t size = sizeof(bits); + if (!getDemodBuff(bits, &size)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x problem during copy from ASK demod"); + return PM3_ESOFT; + } + + int ans = Em410xDecode(bits, &size, &idx, hi, lo); + if (ans < 0) { + + if (ans == -2) + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x not enough samples after demod"); + else if (ans == -4) + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x preamble not found"); + else if (ans == -5) + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x Size not correct: %zu", size); + else if (ans == -6) + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x parity failed"); + + return PM3_ESOFT; + } + if (!lo && !hi) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x decoded to all zeros"); + return PM3_ESOFT; + } + + //set GraphBuffer for clone or sim command + setDemodBuff(DemodBuffer, (size == 40) ? 64 : 128, idx + 1); + setClockGrid(g_DemodClock, g_DemodStartIdx + ((idx + 1)*g_DemodClock)); + + PrintAndLogEx(DEBUG, "DEBUG: Em410x idx: %zu, Len: %zu, Printing Demod Buffer:", idx, size); + if (g_debugMode) { + printDemodBuff(0, false, false, true); + } + + if (verbose) + printEM410x(*hi, *lo); + + return PM3_SUCCESS; +} + +int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose) { + bool st = true; + + // em410x simulation etc uses 0/1 as signal data. This must be converted in order to demod it back again + if (isGraphBitstream()) { + convertGraphFromBitstream(); + } + if (ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, false, false, 1, &st) != PM3_SUCCESS) + return PM3_ESOFT; + return AskEm410xDecode(verbose, hi, lo); +} + +// this read loops on device side. +// uses the demod in lfops.c +static int CmdEM410xWatch(const char *Cmd) { + uint8_t c = tolower(param_getchar(Cmd, 0)); + if (c == 'h') return usage_lf_em410x_watch(); + + PrintAndLogEx(SUCCESS, "Watching for EM410x cards - place tag on antenna"); + PrintAndLogEx(INFO, "Press pm3-button to stop reading cards"); + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM410X_WATCH, NULL, 0); + PacketResponseNG resp; + WaitForResponse(CMD_LF_EM410X_WATCH, &resp); + PrintAndLogEx(INFO, "Done"); + return resp.status; +} + +//by marshmellow +//takes 3 arguments - clock, invert and maxErr as integers +//attempts to demodulate ask while decoding manchester +//prints binary found and saves in graphbuffer for further commands +int demodEM410x(bool verbose) { + (void) verbose; // unused so far + uint32_t hi = 0; + uint64_t lo = 0; + return AskEm410xDemod(0, 0, 100, 0, false, &hi, &lo, true); +} + +static int CmdEM410xDemod(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) > 10 || cmdp == 'h') return usage_lf_em410x_demod(); + + uint32_t hi = 0; + uint64_t lo = 0; + int clk = 0; + int invert = 0; + int maxErr = 100; + size_t maxLen = 0; + char amp = tolower(param_getchar(Cmd, 0)); + sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); + bool amplify = amp == 'a'; + if (AskEm410xDemod(clk, invert, maxErr, maxLen, amplify, &hi, &lo, true) != PM3_SUCCESS) + return PM3_ESOFT; + + g_em410xid = lo; + return PM3_SUCCESS; +} + +// this read is the "normal" read, which download lf signal and tries to demod here. +static int CmdEM410xRead(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) > 10 || cmdp == 'h') return usage_lf_em410x_demod(); + + uint32_t hi = 0; + uint64_t lo = 0; + int clk = 0; + int invert = 0; + int maxErr = 100; + size_t maxLen = 0; + char amp = tolower(param_getchar(Cmd, 0)); + sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); + bool amplify = amp == 'a'; + lf_read(false, 12288); + return AskEm410xDemod(clk, invert, maxErr, maxLen, amplify, &hi, &lo, true); +} + +// emulate an EM410X tag +static int CmdEM410xSim(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_lf_em410x_sim(); + + uint8_t uid[5] = {0x00}; + + /* clock is 64 in EM410x tags */ + uint8_t clk = 64; + + if (param_gethex(Cmd, 0, uid, 10)) { + PrintAndLogEx(FAILED, "UID must include 10 HEX symbols"); + return PM3_EINVARG; + } + + param_getdec(Cmd, 1, &clk); + + PrintAndLogEx(SUCCESS, "Starting simulating UID "_YELLOW_("%02X%02X%02X%02X%02X")" clock: "_YELLOW_("%d"), uid[0], uid[1], uid[2], uid[3], uid[4], clk); + PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation"); + + ConstructEM410xEmulGraph(Cmd, clk); + + CmdLFSim("0"); //240 start_gap. + return PM3_SUCCESS; +} + +static int CmdEM410xBrute(const char *Cmd) { + char filename[FILE_PATH_SIZE] = {0}; + FILE *f = NULL; + char buf[11]; + uint32_t uidcnt = 0; + uint8_t stUidBlock = 20; + uint8_t *uidBlock = NULL, *p = NULL; + uint8_t uid[5] = {0x00}; + /* clock is 64 in EM410x tags */ + uint8_t clock1 = 64; + /* default pause time: 1 second */ + uint32_t delay = 1000; + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_lf_em410x_brute(); + + cmdp = tolower(param_getchar(Cmd, 1)); + if (cmdp == 'd') { + delay = param_get32ex(Cmd, 2, 1000, 10); + param_getdec(Cmd, 4, &clock1); + } else if (cmdp == 'c') { + param_getdec(Cmd, 2, &clock1); + delay = param_get32ex(Cmd, 4, 1000, 10); + } + + int filelen = param_getstr(Cmd, 0, filename, FILE_PATH_SIZE); + if (filelen == 0) { + PrintAndLogEx(ERR, "Error: Please specify a filename"); + return PM3_EINVARG; + } + + if ((f = fopen(filename, "r")) == NULL) { + PrintAndLogEx(ERR, "Error: Could not open UIDs file ["_YELLOW_("%s")"]", filename); + return PM3_EFILE; + } + + uidBlock = calloc(stUidBlock, 5); + if (uidBlock == NULL) { + fclose(f); + return PM3_ESOFT; + } + + while (fgets(buf, sizeof(buf), f)) { + if (strlen(buf) < 10 || buf[9] == '\n') continue; + while (fgetc(f) != '\n' && !feof(f)); //goto next line + + //The line start with # is comment, skip + if (buf[0] == '#') continue; + + if (param_gethex(buf, 0, uid, 10)) { + PrintAndLogEx(FAILED, "UIDs must include 10 HEX symbols"); + free(uidBlock); + fclose(f); + return PM3_ESOFT; + } + + buf[10] = 0; + + if (stUidBlock - uidcnt < 2) { + p = realloc(uidBlock, 5 * (stUidBlock += 10)); + if (!p) { + PrintAndLogEx(WARNING, "Cannot allocate memory for UIDs"); + free(uidBlock); + fclose(f); + return PM3_ESOFT; + } + uidBlock = p; + } + memset(uidBlock + 5 * uidcnt, 0, 5); + num_to_bytes(strtoll(buf, NULL, 16), 5, uidBlock + 5 * uidcnt); + uidcnt++; + memset(buf, 0, sizeof(buf)); + } + + fclose(f); + + if (uidcnt == 0) { + PrintAndLogEx(FAILED, "No UIDs found in file"); + free(uidBlock); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Loaded "_YELLOW_("%d")" UIDs from "_YELLOW_("%s")", pause delay:"_YELLOW_("%d")" ms", uidcnt, filename, delay); + + // loop + for (uint32_t c = 0; c < uidcnt; ++c) { + char testuid[11]; + testuid[10] = 0; + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\nAborted via keyboard!\n"); + free(uidBlock); + return PM3_EOPABORTED; + } + + sprintf(testuid, "%010" PRIX64, bytes_to_num(uidBlock + 5 * c, 5)); + PrintAndLogEx(NORMAL, "Bruteforce %d / %d: simulating UID %s, clock %d", c + 1, uidcnt, testuid, clock1); + + ConstructEM410xEmulGraph(testuid, clock1); + + CmdLFSim("0"); //240 start_gap. + + msleep(delay); + } + + free(uidBlock); + return PM3_SUCCESS; +} + +//currently only supports manchester modulations +static int CmdEM410xWatchnSpoof(const char *Cmd) { + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_lf_em410x_ws(); + + // loops if the captured ID was in XL-format. + CmdEM410xWatch(Cmd); + PrintAndLogEx(SUCCESS, "# Replaying captured ID: "_YELLOW_("%010" PRIx64), g_em410xid); + CmdLFaskSim(""); + return PM3_SUCCESS; +} + +static int CmdEM410xClone(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 0x00 || cmdp == 'h') return usage_lf_em410x_clone(); + + uint64_t id = param_get64ex(Cmd, 0, -1, 16); + uint8_t card = param_get8ex(Cmd, 1, 0xFF, 10); + uint8_t clock1 = param_get8ex(Cmd, 2, 0, 10); + + // Check ID + if (id == 0xFFFFFFFFFFFFFFFF) { + PrintAndLogEx(ERR, "error, ID is required\n"); + usage_lf_em410x_clone(); + return PM3_EINVARG; + } + if (id >= 0x10000000000) { + PrintAndLogEx(ERR, "error, given EM410x ID is longer than 40 bits\n"); + usage_lf_em410x_clone(); + return PM3_EINVARG; + } + + // Check Card + if (card > 1) { + PrintAndLogEx(FAILED, "error, bad card type selected\n"); + usage_lf_em410x_clone(); + return PM3_EINVARG; + } + + // Check Clock + if (clock1 == 0) + clock1 = 64; + + // Allowed clock rates: 16, 32, 40 and 64 + if ((clock1 != 16) && (clock1 != 32) && (clock1 != 64) && (clock1 != 40)) { + PrintAndLogEx(FAILED, "error, clock rate" _RED_("%d")" not valid", clock1); + PrintAndLogEx(INFO, "supported clock rates: " _YELLOW_("16, 32, 40, 60") "\n"); + usage_lf_em410x_clone(); + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "Writing " _YELLOW_("%s") " tag with UID 0x%010" PRIx64 " (clock rate: %d)", (card == 1) ? "T55x7" : "Q5/T5555", id, clock1); + // NOTE: We really should pass the clock in as a separate argument, but to + // provide for backwards-compatibility for older firmware, and to avoid + // having to add another argument to CMD_LF_EM410X_WRITE, we just store + // the clock rate in bits 8-15 of the card value + + struct { + uint8_t card; + uint8_t clock; + uint32_t high; + uint32_t low; + } PACKED params; + + params.card = card; + params.clock = clock1; + params.high = (uint32_t)(id >> 32); + params.low = (uint32_t)id; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM410X_WRITE, (uint8_t *)¶ms, sizeof(params)); + + PacketResponseNG resp; + WaitForResponse(CMD_LF_EM410X_WRITE, &resp); + switch (resp.status) { + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "Done"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 410x_read`") " to verify"); + break; + } + default: { + PrintAndLogEx(WARNING, "Something went wrong"); + break; + } + } + return resp.status; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + //{"demod", CmdEMdemodASK, IfPm3Lf, "Extract ID from EM410x tag on antenna)"}, + {"demod", CmdEM410xDemod, AlwaysAvailable, "demodulate a EM410x tag from the GraphBuffer"}, + {"read", CmdEM410xRead, IfPm3Lf, "attempt to read and extract tag data"}, + {"sim", CmdEM410xSim, IfPm3Lf, "simulate EM410x tag"}, + {"brute", CmdEM410xBrute, IfPm3Lf, "reader bruteforce attack by simulating EM410x tags"}, + {"watch", CmdEM410xWatch, IfPm3Lf, "watches for EM410x 125/134 kHz tags (option 'h' for 134)"}, + {"spoof", CmdEM410xWatchnSpoof, IfPm3Lf, "watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" }, + {"clone", CmdEM410xClone, IfPm3Lf, "write EM410x UID to T55x7 or Q5/T5555 tag"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFEM410X(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} From 1b3d9fbb760dd61e48ceb6d3b0c0e639e664acaa Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 00:18:11 +0100 Subject: [PATCH 139/150] adapt test --- tools/pm3_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index baf08ad5d..ad61dc13d 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -363,7 +363,7 @@ while true; do if ! CheckExecute slow "lf T55 awid 50 test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_awid_50.pm3; lf awid demod'" \ "AWID - len: 50 FC: 2001 Card: 13371337 - Wiegand: 20fa201980f92, Raw: 0128b12eb1811d7117e22111"; then break; fi if ! CheckExecute slow "lf T55 em410x test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_em410x.pm3; lf search 1'" "EM410x ID found"; then break; fi - if ! CheckExecute slow "lf T55 em410x test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_em410x.pm3; lf em 410x_demod demod'" \ + if ! CheckExecute slow "lf T55 em410x test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_em410x.pm3; lf em 410x demod'" \ "EM TAG ID : 0F0368568B"; then break; fi if ! CheckExecute slow "lf T55 fdxb_animal test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf search 1'" "FDX-B ID found"; then break; fi if ! CheckExecute slow "lf T55 fdxb_animal test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf fdxb demod'" \ From c040e63fb0220bf1c1823f8b169a8d0997305db5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 11:29:38 +0100 Subject: [PATCH 140/150] remove dublicates --- client/dictionaries/t55xx_default_pwds.dic | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index afc00bddf..348c79e0d 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -3,7 +3,14 @@ 51243648 000D8787 19920427 -65857569 //chinese "handheld RFID writer" blue cloner from circa 2013 (also sold by xfpga.com) +# ZX-copy3 T55xx / EM4305 +# ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662 +# default PROX +50524F58 +# blue gun EM4305 +F9DCEBA0 +# chinese "handheld RFID writer" blue cloner from circa 2013 (also sold by xfpga.com) +65857569 # ref. http://kazus.ru/forums/showpost.php?p=1045937&postcount=77 05D73B9F # ref. http://www.proxmark.org/forum/viewtopic.php?= @@ -20,19 +27,10 @@ A5B4C3D2 00434343 44B44CAE 88661858 -# -# ZX-copy3 T55xx / EM4305 -# ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662 -19920427 -84AC15E2 # paxton bullit? 575F4F4B # 50520901 -# default PROX -50524F58 -# blue gun EM4305 -F9DCEBA0 # Default pwd, simple: 00000000 11111111 From b115c82156b73cc3efa60749675214d503f47e7a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 22:25:04 +0100 Subject: [PATCH 141/150] text --- client/src/cmdwiegand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index 0e4b31b1c..c38758679 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -151,8 +151,8 @@ int CmdWiegandDecode(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdWiegandList, AlwaysAvailable, "List available wiegand formats"}, - {"encode", CmdWiegandEncode, AlwaysAvailable, "Encode to wiegand raw hex"}, - {"decode", CmdWiegandDecode, AlwaysAvailable, "Convert raw hex to decoded wiegand format"}, + {"encode", CmdWiegandEncode, AlwaysAvailable, "Encode to wiegand raw hex (currently for HID Prox)"}, + {"decode", CmdWiegandDecode, AlwaysAvailable, "Convert raw hex to decoded wiegand format (currently for HID Prox)"}, {NULL, NULL, NULL, NULL} }; From 7fc852be12f8facc58776a7deaf3afb52f06d693 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 22:30:15 +0100 Subject: [PATCH 142/150] text --- client/src/cmdlfhid.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 674aad661..c975eb781 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -318,16 +318,15 @@ static int CmdHIDClone(const char *Cmd) { CLIParserInit(&ctx, "lf hid clone", "clone a HID Prox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" "Tag must be on the antenna when issuing this command.", - "lf hid clone -r 2006ec0c86 -> HID 10301 26 bit\n" - "lf hid clone -r 2e0ec00c87 -> HID Corporate 35 bit\n" - "lf hid clone -r 01f0760643c3 -> HID P10001 40 bit\n" - "lf hid clone -r 01400076000c86 -> HID Corporate 48 bit\n" - "lf hid clone -w H10301 --fc 118 --cn 1603 -> HID 10301 26 bit\n" + "lf hid clone -r 2006ec0c86 -> write raw value (HID 10301 26 bit)\n" + "lf hid clone -r 2e0ec00c87 -> write raw value (HID Corporate 35 bit)\n" + "lf hid clone -r 01f0760643c3 -> write raw value (HID P10001 40 bit)\n" + "lf hid clone -r 01400076000c86 -> write raw value (HID Corporate 48 bit)\n" + "lf hid clone -w H10301 --fc 118 --cn 1603 -> write raw value (HID 10301 26 bit)\n" "lf hid clone -w H10301 --fc 118 --cn 1603 --q5 -> HID 10301 26 bit, encode for Q5/T5555 tag\n" "lf hid clone -w H10301 --fc 118 --cn 1603 --em -> HID 10301 26 bit, encode for EM4305/4469" ); - void *argtable[] = { arg_param_begin, arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), From 4830bf7fec7408509f02704e433ab3a33cb22874 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 22:41:24 +0100 Subject: [PATCH 143/150] lf hid brute - fix param --- client/src/cmdlfhid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index c975eb781..e507da53b 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -464,13 +464,14 @@ static int CmdHIDBrute(const char *Cmd) { "lf hid brute -w H10301 --fc 224\n" "lf hid brute -w H10301 --fc 21 -d 2000\n" "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000\n" + "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000 --up -v\n" ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "verbose logging, show all tries"), arg_str1("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), - arg_int0(NULL, "fn", "", "facility code"), + arg_int0(NULL, "fc", "", "facility code"), arg_int0(NULL, "cn", "", "card number to start with"), arg_int0("i", "issue", "", "issue level"), arg_int0("o", "oem", "", "OEM code"), From 2dc27cf2b7b00f578c5c8db4c49b267c781fbe66 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 22:42:52 +0100 Subject: [PATCH 144/150] fix double.. --- client/src/cmdlfhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index e507da53b..d0acb2924 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -464,7 +464,7 @@ static int CmdHIDBrute(const char *Cmd) { "lf hid brute -w H10301 --fc 224\n" "lf hid brute -w H10301 --fc 21 -d 2000\n" "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000\n" - "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000 --up -v\n" + "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000 --up\n" ); void *argtable[] = { From 80fd00c104b715917cf176c2438cf6c4d83b4aa1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 22:59:57 +0100 Subject: [PATCH 145/150] text --- client/src/cmdlfhid.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index d0acb2924..52f777967 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -63,8 +63,14 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo return PM3_ESOFT; } - if (verbose) - PrintAndLogEx(INFO, "Trying FC: %u; CN: %"PRIu64"; Issue level: %u; OEM: %u", card->FacilityCode, card->CardNumber, card->IssueLevel, card->OEM); + if (verbose) { + PrintAndLogEx(INFO, "Trying FC: " _YELLOW_("%u") " CN: " _YELLOW_("%"PRIu64) " Issue level: " _YELLOW_("%u") " OEM: " _YELLOW_("%u") + , card->FacilityCode + , card->CardNumber + , card->IssueLevel + , card->OEM + ); + } lf_hidsim_t payload; payload.hi2 = packed.Top; @@ -517,20 +523,20 @@ static int CmdHIDBrute(const char *Cmd) { PrintAndLogEx(INFO, "Card#............ %" PRIu64, cn_hi.CardNumber); switch (direction) { case 0: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("BOTH")); + PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("BOTH") " delay " _YELLOW_("%d"), delay); break; case 1: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("UP")); + PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("UP") " delay " _YELLOW_("%d"), delay); break; case 2: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("DOWN")); + PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("DOWN") " delay " _YELLOW_("%d"), delay); break; default: break; } } - PrintAndLogEx(INFO, "Brute-forcing HID reader"); - PrintAndLogEx(INFO, "Press pm3-button to abort simulation or press `enter` to exit"); + PrintAndLogEx(INFO, "Started brute-forcing HID Prox reader"); + PrintAndLogEx(INFO, "Press pm3-button to abort simulation or press " _GREEN_("``") " to exit"); // copy values to low. cn_low = cn_hi; @@ -556,7 +562,9 @@ static int CmdHIDBrute(const char *Cmd) { if (direction != 2) { if (cn_hi.CardNumber < 0xFFFF) { cn_hi.CardNumber++; - if (sendTry(format_idx, &cn_hi, delay, verbose) != PM3_SUCCESS) return PM3_ESOFT; + if (sendTry(format_idx, &cn_hi, delay, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } } else { fin_hi = true; } @@ -566,7 +574,9 @@ static int CmdHIDBrute(const char *Cmd) { if (direction != 1) { if (cn_low.CardNumber > 0) { cn_low.CardNumber--; - if (sendTry(format_idx, &cn_low, delay, verbose) != PM3_SUCCESS) return PM3_ESOFT; + if (sendTry(format_idx, &cn_low, delay, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } } else { fin_low = true; } From c25eb722dc7161dee9cce288c8fffa798b6a423e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 23:16:15 +0100 Subject: [PATCH 146/150] lf hid brute - set fmtlen.. --- client/src/cmdlfhid.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 52f777967..3e10def56 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -60,7 +60,7 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo if (HIDPack(format_idx, card, &packed) == false) { PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); - return PM3_ESOFT; + return PM3_ESOFT; } if (verbose) { @@ -76,6 +76,7 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo payload.hi2 = packed.Top; payload.hi = packed.Mid; payload.lo = packed.Bot; + payload.longFMT = (packed.Mid > 0xFFF); clearCommandBuffer(); @@ -246,8 +247,8 @@ static int CmdHIDSim(const char *Cmd) { arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), arg_u64_0(NULL, "fc", "", "facility code"), arg_u64_0(NULL, "cn", "", "card number"), - arg_int0("i", NULL, "", "issue level"), - arg_int0("o", "oem", "", "OEM code"), + arg_u64_0("i", NULL, "", "issue level"), + arg_u64_0("o", "oem", "", "OEM code"), arg_strx0("r", "raw", "", "raw bytes"), arg_param_end }; @@ -477,11 +478,11 @@ static int CmdHIDBrute(const char *Cmd) { arg_param_begin, arg_lit0("v", "verbose", "verbose logging, show all tries"), arg_str1("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), - arg_int0(NULL, "fc", "", "facility code"), - arg_int0(NULL, "cn", "", "card number to start with"), - arg_int0("i", "issue", "", "issue level"), - arg_int0("o", "oem", "", "OEM code"), - arg_int0("d", "delay", "", "delay betweens attempts in ms. Default 1000ms"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number to start with"), + arg_u64_0("i", "issue", "", "issue level"), + arg_u64_0("o", "oem", "", "OEM code"), + arg_u64_0("d", "delay", "", "delay betweens attempts in ms. Default 1000ms"), arg_lit0(NULL, "up", "direction to increment card number. (default is both directions)"), arg_lit0(NULL, "down", "direction to decrement card number. (default is both directions)"), arg_param_end @@ -499,11 +500,11 @@ static int CmdHIDBrute(const char *Cmd) { return PM3_EINVARG; } - cn_hi.FacilityCode = arg_get_int_def(ctx, 3, 0); - cn_hi.CardNumber = arg_get_int_def(ctx, 4, 0); - cn_hi.IssueLevel = arg_get_int_def(ctx, 5, 0); - cn_hi.OEM = arg_get_int_def(ctx, 6, 0); - delay = arg_get_int_def(ctx, 7, 1000); + cn_hi.FacilityCode = arg_get_u32_def(ctx, 3, 0); + cn_hi.CardNumber = arg_get_u32_def(ctx, 4, 0); + cn_hi.IssueLevel = arg_get_u32_def(ctx, 5, 0); + cn_hi.OEM = arg_get_u32_def(ctx, 6, 0); + delay = arg_get_u32_def(ctx, 7, 1000); if (arg_get_lit(ctx, 8) && arg_get_lit(ctx, 9)) { direction = 0; From 4bde614006b97032cae63789329040ac1872d779 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 23:32:47 +0100 Subject: [PATCH 147/150] text --- client/src/cmdlfindala.c | 29 +++++++++++++++-------------- client/src/cmdlfio.c | 28 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index e0ca3d74e..6d2c37905 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -84,7 +84,7 @@ static void encodeHeden2L(uint8_t *dest, uint32_t cardnumber) { dest[i / 8] = bytebits_to_byte(template + i, 8); } - PrintAndLogEx(INFO, "Heden-2L card number " _GREEN_("%u"), cardnumber); + PrintAndLogEx(INFO, "Heden-2L card number %u", cardnumber); } static void decodeHeden2L(uint8_t *bits) { @@ -109,7 +109,7 @@ static void decodeHeden2L(uint8_t *bits) { if (bits[offset + 7]) cardnumber += 16384; if (bits[offset + 23]) cardnumber += 32768; - PrintAndLogEx(SUCCESS, "\tHeden-2L | " _GREEN_("%u"), cardnumber); + PrintAndLogEx(SUCCESS, " Heden-2L | %u", cardnumber); } // Indala 26 bit decode @@ -206,8 +206,8 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { ); PrintAndLogEx(SUCCESS, "Possible de-scramble patterns"); - PrintAndLogEx(SUCCESS, "\tPrinted | __%04d__ [0x%X]", p1, p1); - PrintAndLogEx(SUCCESS, "\tInternal ID | %" PRIu64, foo); + PrintAndLogEx(SUCCESS, " Printed | __%04d__ [0x%X]", p1, p1); + PrintAndLogEx(SUCCESS, " Internal ID | %" PRIu64, foo); decodeHeden2L(DemodBuffer); } else { @@ -218,7 +218,7 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { uint32_t uid7 = bytebits_to_byte(DemodBuffer + 192, 32); PrintAndLogEx( SUCCESS - , "Indala - len " _GREEN_("%zu") " Raw: %x%08x%08x%08x%08x%08x%08x" + , "Indala (len %zu) Raw: " _GREEN_("%x%08x%08x%08x%08x%08x%08x") , DemodBufferLen , uid1 , uid2 @@ -245,7 +245,7 @@ static int CmdIndalaDemod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala demod", - "Tries to psk demodulate the graphbuffer as Indala Prox", + "Tries to psk demodulate the graphbuffer as Indala", "lf indala demod\n" "lf indala demod --clock 32 -> demod a Indala tag from GraphBuffer using a clock of RF/32\n" "lf indala demod --clock 32 -i -> demod a Indala tag from GraphBuffer using a clock of RF/32 and inverting data\n" @@ -276,7 +276,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala altdemod", - "Tries to psk demodulate the graphbuffer as Indala Prox\n" + "Tries to psk demodulate the graphbuffer as Indala\n" "This is uses a alternative way to demodulate and was used from the beginning in the Pm3 client.\n" "It's now considered obsolete but remains because it has sometimes its advantages.", "lf indala altdemod\n" @@ -493,7 +493,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { static int CmdIndalaReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala reader", - "read a Indala Prox tag", + "read a Indala tag", "lf indala reader -@ -> continuous reader mode" ); @@ -527,7 +527,7 @@ static int CmdIndalaSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala sim", - "Enables simulation of IOProx card with specified facility-code and card number.\n" + "Enables simulation of Indala card with specified facility-code and card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", "lf indala sim --heden 888\n" "lf indala sim --raw a0000000a0002021\n" @@ -616,7 +616,8 @@ static int CmdIndalaClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala clone", - "clone INDALA UID to T55x7 or Q5/T5555 tag", + "clone INDALA UID to T55x7 or Q5/T5555 tag\n" + _RED_("\nWarning, encoding with FC/CN doesn't always work"), "lf indala clone --heden 888\n" "lf indala clone --fc 123 --cn 1337\n" "lf indala clone -r a0000000a0002021\n" @@ -626,8 +627,8 @@ static int CmdIndalaClone(const char *Cmd) { arg_param_begin, arg_strx0("r", "raw", "", "raw bytes"), arg_int0(NULL, "heden", "", "Cardnumber for Heden 2L format"), - arg_int0(NULL, "fc", "", "Facility Code (26 bit format)"), - arg_int0(NULL, "cn", "", "Cardnumber (26 bit format)"), + arg_int0(NULL, "fc", "", "Facility Code (26 bit H10301 format)"), + arg_int0(NULL, "cn", "", "Cardnumber (26 bit H10301 format)"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end @@ -769,8 +770,8 @@ static int CmdIndalaClone(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "this help"}, {"demod", CmdIndalaDemod, AlwaysAvailable, "demodulate an indala tag (PSK1) from GraphBuffer"}, - {"altdemod", CmdIndalaDemodAlt, AlwaysAvailable, "alternative method to Demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"}, - {"reader", CmdIndalaReader, IfPm3Lf, "read an Indala Prox tag from the antenna"}, + {"altdemod", CmdIndalaDemodAlt, AlwaysAvailable, "alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"}, + {"reader", CmdIndalaReader, IfPm3Lf, "read an Indala tag from the antenna"}, {"clone", CmdIndalaClone, IfPm3Lf, "clone Indala tag to T55x7 or Q5/T5555"}, {"sim", CmdIndalaSim, IfPm3Lf, "simulate Indala tag"}, {NULL, NULL, NULL, NULL} diff --git a/client/src/cmdlfio.c b/client/src/cmdlfio.c index b82439263..3adc0ed90 100644 --- a/client/src/cmdlfio.c +++ b/client/src/cmdlfio.c @@ -34,7 +34,7 @@ static int CmdHelp(const char *Cmd); static int CmdIOProxWatch(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf io watch", - "Enables IOProx compatible reader mode printing details.\n" + "Enables ioProx compatible reader mode printing details.\n" "By default, values are printed and logged until the button is pressed or another USB command is issued.", "lf io watch" ); @@ -57,7 +57,7 @@ static int CmdIOProxWatch(const char *Cmd) { } //IO-Prox demod - FSK RF/64 with preamble of 000000001 -//print ioprox ID and some format details +//print ioProx ID and some format details int demodIOProx(bool verbose) { (void) verbose; // unused so far int idx = 0, retval = PM3_SUCCESS; @@ -157,7 +157,7 @@ int demodIOProx(bool verbose) { static int CmdIOProxDemod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf io demod", - "Try to find IOProx preamble, if found decode / descramble data", + "Try to find ioProx preamble, if found decode / descramble data", "lf io demod" ); @@ -173,7 +173,7 @@ static int CmdIOProxDemod(const char *Cmd) { static int CmdIOProxReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf io reader", - "read a IOProx tag", + "read a ioProx tag", "lf io reader -@ -> continuous reader mode" ); @@ -198,7 +198,7 @@ static int CmdIOProxSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf io sim", - "Enables simulation of IOProx card with specified facility-code and card number.\n" + "Enables simulation of ioProx card with specified facility-code and card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", "lf io sim --vn 1 --fc 101 --cn 1337" ); @@ -220,10 +220,10 @@ static int CmdIOProxSim(const char *Cmd) { if ((cn & 0xFFFF) != cn) { cn &= 0xFFFF; - PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (IOProx): %u", cn); + PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (ioProx): %u", cn); } - PrintAndLogEx(SUCCESS, "Simulating IOProx version: " _YELLOW_("%u") " FC: " _YELLOW_("%u (0x%02x)") " CN: " _YELLOW_("%u"), version, fc, fc, cn); + PrintAndLogEx(SUCCESS, "Simulating ioProx version: " _YELLOW_("%u") " FC: " _YELLOW_("%u (0x%02x)") " CN: " _YELLOW_("%u"), version, fc, fc, cn); PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); uint8_t bs[64]; @@ -233,7 +233,7 @@ static int CmdIOProxSim(const char *Cmd) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - // IOProx uses: fcHigh: 10, fcLow: 8, clk: 64, invert: 1 + // ioProx uses: fcHigh: 10, fcLow: 8, clk: 64, invert: 1 // arg1 --- fcHigh<<8 + fcLow // arg2 --- Invert and clk setting // size --- 64 bits == 8 bytes @@ -259,7 +259,7 @@ static int CmdIOProxClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf io clone", - "Enables simulation of IOProx card with specified facility-code and card number.\n" + "Enables simulation of ioProx card with specified facility-code and card number.\n" "Tag must be on the antenna when issuing this command.", "lf io clone --vn 1 --fc 101 --cn 1337" ); @@ -292,7 +292,7 @@ static int CmdIOProxClone(const char *Cmd) { if ((cn & 0xFFFF) != cn) { cn &= 0xFFFF; - PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (IOProx): %u", cn); + PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (ioProx): %u", cn); } if (getIOProxBits(version, fc, cn, bits) != PM3_SUCCESS) { @@ -317,7 +317,7 @@ static int CmdIOProxClone(const char *Cmd) { blocks[1] = bytebits_to_byte(bits, 32); blocks[2] = bytebits_to_byte(bits + 32, 32); - PrintAndLogEx(INFO, "Preparing to clone IOProx to " _YELLOW_("%s") " with Version: " _GREEN_("%u") " FC: " _GREEN_("%u (0x%02x)") " CN: " _GREEN_("%u") + PrintAndLogEx(INFO, "Preparing to clone ioProx to " _YELLOW_("%s") " with Version: " _GREEN_("%u") " FC: " _GREEN_("%u (0x%02x)") " CN: " _GREEN_("%u") , cardtype , version , fc @@ -339,10 +339,10 @@ static int CmdIOProxClone(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "this help"}, - {"demod", CmdIOProxDemod, AlwaysAvailable, "demodulate an IOProx tag from the GraphBuffer"}, + {"demod", CmdIOProxDemod, AlwaysAvailable, "demodulate an ioProx tag from the GraphBuffer"}, {"reader", CmdIOProxReader, IfPm3Lf, "attempt to read and extract tag data"}, - {"clone", CmdIOProxClone, IfPm3Lf, "clone IOProx tag to T55x7 or Q5/T5555"}, - {"sim", CmdIOProxSim, IfPm3Lf, "simulate IOProx tag"}, + {"clone", CmdIOProxClone, IfPm3Lf, "clone ioProx tag to T55x7 or Q5/T5555"}, + {"sim", CmdIOProxSim, IfPm3Lf, "simulate ioProx tag"}, {"watch", CmdIOProxWatch, IfPm3Lf, "continuously watch for cards. Reader mode"}, {NULL, NULL, NULL, NULL} }; From 697463fb69f8cd247472ba0d24ac5047e35ef8c0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Dec 2020 23:38:40 +0100 Subject: [PATCH 148/150] text --- client/src/cmdlfindala.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 6d2c37905..df2bd4f46 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -151,7 +151,7 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { uint64_t foo = uid2 & 0x7FFFFFFF; if (DemodBufferLen == 64) { - PrintAndLogEx(SUCCESS, "Indala - len " _GREEN_("%zu") " Raw: %x%08x", DemodBufferLen, uid1, uid2); + PrintAndLogEx(SUCCESS, "Indala (len %zu) Raw: " _GREEN_("%x%08x"), DemodBufferLen, uid1, uid2); uint16_t p1 = 0; p1 |= DemodBuffer[32 + 3] << 8; @@ -616,7 +616,7 @@ static int CmdIndalaClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala clone", - "clone INDALA UID to T55x7 or Q5/T5555 tag\n" + "clone Indala UID to T55x7 or Q5/T5555 tag\n" _RED_("\nWarning, encoding with FC/CN doesn't always work"), "lf indala clone --heden 888\n" "lf indala clone --fc 123 --cn 1337\n" @@ -769,7 +769,7 @@ static int CmdIndalaClone(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "this help"}, - {"demod", CmdIndalaDemod, AlwaysAvailable, "demodulate an indala tag (PSK1) from GraphBuffer"}, + {"demod", CmdIndalaDemod, AlwaysAvailable, "demodulate an Indala tag (PSK1) from GraphBuffer"}, {"altdemod", CmdIndalaDemodAlt, AlwaysAvailable, "alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"}, {"reader", CmdIndalaReader, IfPm3Lf, "read an Indala tag from the antenna"}, {"clone", CmdIndalaClone, IfPm3Lf, "clone Indala tag to T55x7 or Q5/T5555"}, From ee3c0faee16c7707819d35c1c64a3c8c81781df1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 5 Dec 2020 13:27:39 +0100 Subject: [PATCH 149/150] hf iclass -helptexts --- client/src/cmdhficlass.c | 85 +++++++++++++++++++--------------------- client/src/cmdtrace.c | 10 ++--- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b68081746..cd6017c8a 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -354,7 +354,7 @@ static int CmdHFiClassSniff(const char *Cmd) { WaitForResponse(CMD_HF_ICLASS_SNIFF, &resp); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -f hf_iclass_mytrace") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -371,8 +371,8 @@ static int CmdHFiClassSim(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_int1("t", "type", NULL, "Simulation type to use"), - arg_str0(NULL, "csn", "", "Specify CSN as 8 bytes (16 hex symbols) to use with sim type 0"), + arg_int1("t", "type", "<0-4> ", "Simulation type to use"), + arg_str0(NULL, "csn", "", "Specify CSN as 8 hex bytes to use with sim type 0"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -807,7 +807,7 @@ static int CmdHFiClassESave(const char *Cmd) { saveFileJSON(filename, jsfIclass, dump, bytes, NULL); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass readtagfile ") "` to view dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view") "` to view dump file"); return PM3_SUCCESS; } @@ -917,7 +917,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { if (enc_data_len > 0) { if (enc_data_len != 8) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)"); CLIParserFree(clictx); return PM3_EINVARG; } @@ -933,7 +933,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { if (key_len > 0) { if (key_len != 16) { - PrintAndLogEx(ERR, "Transport key must be 16 bytes (32 HEX characters)"); + PrintAndLogEx(ERR, "Transport key must be 16 hex bytes (32 HEX characters)"); CLIParserFree(clictx); return PM3_EINVARG; } @@ -1116,7 +1116,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIParserInit(&clictx, "hf iclass encrypt", "3DES encrypt data\n" "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" - "in the resources directory. The file should be 16 bytes binary data", + "in the resources directory. The file should be 16 hex bytes of binary data", "hf iclass encrypt -d 0102030405060708\n" "hf iclass encrypt -d 0102030405060708 -k 00112233445566778899AABBCCDDEEFF"); @@ -1135,7 +1135,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIGetHexWithReturn(clictx, 1, blk_data, &blk_data_len); if (blk_data_len != 8) { - PrintAndLogEx(ERR, "Block data must be 8 bytes (16 HEX characters)"); + PrintAndLogEx(ERR, "Block data must be 8 hex bytes (16 HEX symbols)"); CLIParserFree(clictx); return PM3_EINVARG; } @@ -1149,7 +1149,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { if (key_len > 0) { if (key_len != 16) { - PrintAndLogEx(ERR, "Transport key must be 16 bytes (32 HEX characters)"); + PrintAndLogEx(ERR, "Transport key must be 16 hex ytes (32 HEX characters)"); CLIParserFree(clictx); return PM3_EINVARG; } @@ -1231,9 +1231,9 @@ static int CmdHFiClassDump(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename to save dump to"), - arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), + arg_str0("k", "key", "", "debit key or NR/MAC for replay as 8 hex bytes"), arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), - arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), + arg_str0(NULL, "credit", "", "credit key as 8 hex bytes"), arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), @@ -1598,18 +1598,18 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { CLIParserInit(&ctx, "hf iclass wrbl", "Write data to an iCLASS tag", "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" - "hf iclass wrbl -b 27 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" - "hf iclass wrbl -b 11 -d AAAAAAAAAAAAAAAA --ki 0"); + "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" + "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA --ki 0"); void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), - arg_str1("d", "data", "", "data to write as 16 hex symbols"), + arg_int1("b", "block", "", "The block number to read"), + arg_str1("d", "data", "", "data to write as 8 hex bytes"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), - arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "nr", "replay of NR/MAC"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -1657,7 +1657,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { CLIGetHexWithReturn(ctx, 4, data, &data_len); if (data_len != 8) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 hex characters)"); + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)"); CLIParserFree(ctx); return PM3_EINVARG; } @@ -1695,20 +1695,21 @@ static int CmdHFiClassRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass restore", "Restore data from dumpfile onto a iCLASS tag", - "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 1A -k 1122334455667788 --elite\n" - "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 05 --last 19 --ki 0\n" - "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 19 --ki 0 --elite"); + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0\n" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n" + ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "specify a filename to restore"), - arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1(NULL, "first", "", "The first block number to restore as an integer"), - arg_int1(NULL, "last", "", "The last block number to restore as an integer"), + arg_int1(NULL, "first", "", "The first block number to restore"), + arg_int1(NULL, "last", "", "The last block number to restore"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), - arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1916,12 +1917,12 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), + arg_int1("b", "block", "", "The block number to read"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), - arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "nr", "replay of NR/MAC"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -2232,12 +2233,12 @@ static int CmdHFiClassView(const char *Cmd) { CLIParserInit(&ctx, "hf iclass view", "Print a iCLASS tag dump file", "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" - "hf iclass view --first 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin\n"); + "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin\n"); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), + arg_int0(NULL, "first", "", "Begin printing from this block (default block 6)"), arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -2325,9 +2326,9 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0(NULL, "old", "", "Specify key as 8 bytes (16 hex symbols)"), + arg_str0(NULL, "old", "", "Specify key as 8 hex bytes"), arg_int0(NULL, "oki", "", "Old key index to select key from memory 'hf iclass managekeys'"), - arg_str0(NULL, "new", "", "Specify key as 8 bytes (16 hex symbols)"), + arg_str0(NULL, "new", "", "Specify key as 8 hex bytes"), arg_int0(NULL, "nki", "", "New key index to select key from memory 'hf iclass managekeys'"), arg_str0(NULL, "csn", "", "Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)"), arg_lit0(NULL, "elite", "Elite computations applied to new key"), @@ -2500,7 +2501,7 @@ static int CmdHFiClassManageKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass managekeys", "Manage iCLASS Keys in client memory", - "hf iclass managekeys --ki 0 -k 1122334455667788 -> set key\n" + "hf iclass managekeys --ki 0 -k 1122334455667788 -> set key 1122334455667788 at index 0\n" "hf iclass managekeys -f mykeys.bin --save -> save key file\n" "hf iclass managekeys -f mykeys.bin --load -> load key file\n" "hf iclass managekeys -p -> print keys"); @@ -2508,7 +2509,7 @@ static int CmdHFiClassManageKeys(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "Specify a filename to use with load or save operations"), - arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"), arg_lit0(NULL, "load", "Load keys to memory from file specified by filename"), @@ -2908,10 +2909,10 @@ static int CmdHFiClassLookUp(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "Dictionary file with default iclass keys"), - arg_str1(NULL, "csn", "", "Specify CSN as 8 bytes (16 hex symbols)"), - arg_str1(NULL, "epurse", "", "Specify ePurse as 8 bytes (16 hex symbols)"), + arg_str1(NULL, "csn", "", "Specify CSN as 8 hex bytes"), + arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), arg_str1(NULL, "macs", "", "MACs"), - arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "elite", "Elite computations applied to key"), arg_param_end }; @@ -3288,16 +3289,13 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("r", "reverse", "reverse permuted key"), - arg_str1(NULL, "key", "", "input key"), + arg_lit0("r", "reverse", "reverse permuted key"), + arg_str1(NULL, "key", "", "input key, 8 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool isReverse = arg_get_lit(ctx, 1); - CLIGetHexWithReturn(ctx, 2, data, &len); - CLIParserFree(ctx); memcpy(key, data, 8); @@ -3349,7 +3347,7 @@ static command_t CommandTable[] = { {"list", CmdHFiClassList, AlwaysAvailable, " List iclass history"}, {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "[options..] Read Picopass / iCLASS block"}, {"reader", CmdHFiClassReader, IfPm3Iclass, " Act like an Picopass / iCLASS reader"}, - {"restore", CmdHFiClassRestore, IfPm3Iclass, "[options..] Restore a dump file onto a Picopass / iCLASS tag"}, + {"restore", CmdHFiClassRestore, IfPm3Iclass, "[options..] Restore a dump file onto a Picopass / iCLASS tag"}, {"sniff", CmdHFiClassSniff, IfPm3Iclass, " Eavesdrop Picopass / iCLASS communication"}, {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "[options..] Write Picopass / iCLASS block"}, @@ -3371,7 +3369,6 @@ static command_t CommandTable[] = { {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "[options..] Manage keys to use with iclass commands"}, {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, {"view", CmdHFiClassView, AlwaysAvailable, "[options..] Display content from tag dump file"}, - {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 534fbbc45..ad5b38df7 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -519,8 +519,8 @@ static int CmdTraceLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "trace load", "Load protocol data from binary file to trace buffer\n" - "File extension is (.trace)", - "trace load -f mytracefile" + "File extension is <.trace>", + "trace load -f mytracefile -> w/o file extension" ); void *argtable[] = { @@ -555,13 +555,13 @@ static int CmdTraceSave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "trace save", "Save protocol data from trace buffer to binary file\n" - "File extension is (.trace)", - "trace save -f mytracefile" + "File extension is <.trace>", + "trace save -f mytracefile -> w/o file extension" ); void *argtable[] = { arg_param_begin, - arg_strx0("f", "file", "", "trace file to load"), + arg_strx0("f", "file", "", "trace file to save"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); From 54634b33b082cfdd257c7aad336c12eb0515a7a0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 5 Dec 2020 23:51:00 +0100 Subject: [PATCH 150/150] text --- client/src/cmdlfem.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/cmdlfem.c b/client/src/cmdlfem.c index fbc7ac476..a89a4e2be 100644 --- a/client/src/cmdlfem.c +++ b/client/src/cmdlfem.c @@ -12,7 +12,6 @@ #include "cmdlfem410x.h" #include "cmdlfem4x05.h" #include "cmdlfem4x50.h" - #include #include #include "cmdparser.h" // command_t @@ -23,9 +22,9 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"410x", CmdLFEM410X, AlwaysAvailable, "EM 410x commands..."}, - {"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4x05 commands..."}, - {"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4x50 commands..."}, + {"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."}, + {"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."}, + {"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."}, {NULL, NULL, NULL, NULL} };