mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-05 20:57:28 +00:00
data_available() and data_available_fast() are how the device notices that the host has asked a running command to stop. They branch on USB and on WITH_FPC_USART_HOST, and never learned about the Proxmark5 BWM. A PM5 with the wireless module builds PLATFORM=PM5 PLATFORM_EXTRAS=BWM, which defines WITH_BWM_FORWARD. BTADDON errors out on PM5 and BWM errors out on anything else (common_arm/Makefile.hal), so WITH_FPC_USART_HOST is never defined on that build and both functions compile down to a USB-only check. Commands still arrive, because receive_ng() does poll the module (armsrc/cmd.c). What was blind was the abort path: CMD_BREAK_LOOP is never observed over BLE or WiFi, so every loop whose only exit is this check runs until the button is pressed. That is 75 call sites across 26 files in armsrc/, including the sniff, simulate and reader loops, so in practice a wireless session cannot be interrupted at all. ReaderHitag() is the worst case: with no tag on the antenna it has no other exit, so `lf search` alone leaves the device answering nothing. This is reachable with upstream tooling only: client/src/uart/ble_posix.c is the native BLE transport, selected with `-p ble:<address-or-name>`, and doc/md/PM5_Start_Here/PM5-BWM-USAGE.md documents both that and `-p tcp:<host>:<port>` over the module's WiFi. Both share the same inbound de-framer, so both are affected. Folded into the existing preprocessor chain as an #elif, mirroring receive_ng(), since the two transports are mutually exclusive by build. USB is still tested first, so the module is only polled when USB has nothing, exactly as the FPC branch behaves. bwm_fwd_rxdata_available() is the same call receive_ng() already uses. When the de-frame FIFO is empty it costs a flag test and one DMA counter read (bwm_uart_rx_available()); it only walks bytes when bytes have actually arrived, and those bytes are the abort. That mirrors the FPC branch, where usart_rxdata_available() already calls usart_rx_poll() from these same two functions. data_available_fast()'s only caller is the ReadLF_realtime() sampling loop, which reaches it once per 64 saved samples; it is included here so the two functions cannot drift apart again, which is how this bug arose. Object code is byte-identical on every platform that does not define WITH_BWM_FORWARD. Built PLATFORM=PM3RDV4, PM3RDV4 with BTADDON, PM5 without BWM and PM5 with BWM; only the last one changes: PM3RDV4 74f7f7a1... -> 74f7f7a1... identical PM3RDV4 BTADDON f94c31de... -> f94c31de... identical PM5 e3f0a203... -> e3f0a203... identical PM5 BWM e3f0a203... -> efbba2f0... changed Passes `make style` unmodified. Verified on a Proxmark5 with the BWM fitted, over BLE, with the stock client. Before, one command left the device deaf for good: [fpc|tcp] pm5 --> hw ping [+] Ping response received in 111 ms and content ( ok ) [fpc|tcp] pm5 --> lf hitag read --ht2 --pwd -k 4D494B52 [!] timeout while waiting for reply [fpc|tcp] pm5 --> hw ping [!] Ping response timeout [fpc|tcp] pm5 --> hw ping [!] Ping response timeout After, the same sequence on the same device: [fpc|tcp] pm5 --> hw ping [+] Ping response received in 107 ms and content ( ok ) [fpc|tcp] pm5 --> lf hitag read --ht2 --pwd -k 4D494B52 [!] timeout while waiting for reply [fpc|tcp] pm5 --> hw ping [+] Ping response received in 102 ms and content ( ok ) [fpc|tcp] pm5 --> hw ping [+] Ping response received in 85 ms and content ( ok ) A USB command recovers a device stuck this way, which is why the bug is easy to miss on a bench with a cable attached: after the failing sequence above, `hw ping` over USB answered in 1 ms and the wireless link resumed.
391 lines
10 KiB
C
391 lines
10 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (C) Jonathan Westhues, Sept 2005
|
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// See LICENSE.txt for the text of the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Utility functions used in many places, not specific to any piece of code.
|
|
//-----------------------------------------------------------------------------
|
|
#include "util.h"
|
|
|
|
#include "proxmark3_arm.h"
|
|
#include "ticks_apis.h"
|
|
#include "commonutil.h"
|
|
#include "dbprint.h"
|
|
#include "string.h"
|
|
#include "usb_cdc_apis.h"
|
|
#include "usart.h"
|
|
#ifdef WITH_BWM_FORWARD
|
|
#include "bwm_forward.h"
|
|
#endif
|
|
#include "BigBuf.h" // trace_restart_timeline
|
|
#ifdef WITH_SMARTCARD
|
|
#include "i2c.h" // sc_log_trace_reset
|
|
#endif
|
|
|
|
size_t nbytes(size_t nbits) {
|
|
return (nbits >> 3) + ((nbits % 8) > 0);
|
|
}
|
|
|
|
//convert hex digit to integer
|
|
uint8_t hex2int(char x) {
|
|
switch (x) {
|
|
case '0':
|
|
return 0;
|
|
case '1':
|
|
return 1;
|
|
case '2':
|
|
return 2;
|
|
case '3':
|
|
return 3;
|
|
case '4':
|
|
return 4;
|
|
case '5':
|
|
return 5;
|
|
case '6':
|
|
return 6;
|
|
case '7':
|
|
return 7;
|
|
case '8':
|
|
return 8;
|
|
case '9':
|
|
return 9;
|
|
case 'a':
|
|
case 'A':
|
|
return 10;
|
|
case 'b':
|
|
case 'B':
|
|
return 11;
|
|
case 'c':
|
|
case 'C':
|
|
return 12;
|
|
case 'd':
|
|
case 'D':
|
|
return 13;
|
|
case 'e':
|
|
case 'E':
|
|
return 14;
|
|
case 'f':
|
|
case 'F':
|
|
return 15;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
The following methods comes from Rfidler sourcecode.
|
|
https://github.com/ApertureLabsLtd/RFIDler/blob/master/firmware/Pic32/RFIDler.X/src/
|
|
*/
|
|
// convert hex to sequence of 0/1 bit values
|
|
// returns number of bits converted
|
|
int hex2binarray(char *target, const char *source) {
|
|
return hex2binarray_n(target, source, strlen(source));
|
|
}
|
|
|
|
int hex2binarray_n(char *target, const char *source, int sourcelen) {
|
|
int count = 0;
|
|
|
|
// process 4 bits (1 hex digit) at a time
|
|
while (sourcelen--) {
|
|
|
|
char x = *(source++);
|
|
|
|
*(target++) = (x >> 7) & 1;
|
|
*(target++) = (x >> 6) & 1;
|
|
*(target++) = (x >> 5) & 1;
|
|
*(target++) = (x >> 4) & 1;
|
|
*(target++) = (x >> 3) & 1;
|
|
*(target++) = (x >> 2) & 1;
|
|
*(target++) = (x >> 1) & 1;
|
|
*(target++) = (x & 1);
|
|
|
|
count += 8;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int binarray2hex(const uint8_t *bs, int bs_len, uint8_t *hex) {
|
|
|
|
int count = 0;
|
|
int byte_index = 0;
|
|
|
|
// Clear output buffer
|
|
memset(hex, 0, bs_len >> 3);
|
|
|
|
for (int i = 0; i < bs_len; i++) {
|
|
|
|
// Set the appropriate bit in hex
|
|
if (bs[i] == 1) {
|
|
hex[byte_index] |= (1 << (7 - (count % 8)));
|
|
}
|
|
|
|
count++;
|
|
|
|
// Move to the next byte if 8 bits have been filled
|
|
if (count % 8 == 0) {
|
|
byte_index++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
void LEDsoff(void) {
|
|
LED_A_OFF();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
LED_D_OFF();
|
|
}
|
|
|
|
//ICEMAN: LED went from 1,2,3,4 -> 1,2,4,8
|
|
void LED(int led, int ms) {
|
|
if (led & LED_A) // Proxmark3 historical mapping: LED_ORANGE
|
|
LED_A_ON();
|
|
if (led & LED_B) // Proxmark3 historical mapping: LED_GREEN
|
|
LED_B_ON();
|
|
if (led & LED_C) // Proxmark3 historical mapping: LED_RED
|
|
LED_C_ON();
|
|
if (led & LED_D) // Proxmark3 historical mapping: LED_RED2
|
|
LED_D_ON();
|
|
|
|
if (!ms)
|
|
return;
|
|
|
|
SpinDelay(ms);
|
|
|
|
if (led & LED_A)
|
|
LED_A_OFF();
|
|
if (led & LED_B)
|
|
LED_B_OFF();
|
|
if (led & LED_C)
|
|
LED_C_OFF();
|
|
if (led & LED_D)
|
|
LED_D_OFF();
|
|
}
|
|
|
|
void SpinOff(uint32_t pause) {
|
|
LED_A_OFF();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
LED_D_OFF();
|
|
SpinDelay(pause);
|
|
}
|
|
|
|
// Blinks..
|
|
// A = 1, B = 2, C = 4, D = 8
|
|
void SpinErr(uint8_t led, uint32_t speed, uint8_t times) {
|
|
SpinOff(speed);
|
|
NTIME(times) {
|
|
|
|
if (led & LED_A) // Proxmark3 historical mapping: LED_ORANGE
|
|
LED_A_INV();
|
|
if (led & LED_B) // Proxmark3 historical mapping: LED_GREEN
|
|
LED_B_INV();
|
|
if (led & LED_C) // Proxmark3 historical mapping: LED_RED
|
|
LED_C_INV();
|
|
if (led & LED_D) // Proxmark3 historical mapping: LED_RED2
|
|
LED_D_INV();
|
|
|
|
SpinDelay(speed);
|
|
}
|
|
LED_A_OFF();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
LED_D_OFF();
|
|
}
|
|
|
|
void SpinDown(uint32_t speed) {
|
|
SpinOff(speed);
|
|
LED_D_ON();
|
|
SpinDelay(speed);
|
|
LED_D_OFF();
|
|
LED_C_ON();
|
|
SpinDelay(speed);
|
|
LED_C_OFF();
|
|
LED_B_ON();
|
|
SpinDelay(speed);
|
|
LED_B_OFF();
|
|
LED_A_ON();
|
|
SpinDelay(speed);
|
|
LED_A_OFF();
|
|
}
|
|
|
|
void SpinUp(uint32_t speed) {
|
|
SpinOff(speed);
|
|
LED_A_ON();
|
|
SpinDelay(speed);
|
|
LED_A_OFF();
|
|
LED_B_ON();
|
|
SpinDelay(speed);
|
|
LED_B_OFF();
|
|
LED_C_ON();
|
|
SpinDelay(speed);
|
|
LED_C_OFF();
|
|
LED_D_ON();
|
|
SpinDelay(speed);
|
|
LED_D_OFF();
|
|
}
|
|
|
|
// Determine if a button is double clicked, single clicked,
|
|
// not clicked, or held down (for ms || 1sec)
|
|
// In general, don't use this function unless you expect a
|
|
// double click, otherwise it will waste 500ms -- use BUTTON_HELD instead
|
|
// Note: StartTickCount required.
|
|
int BUTTON_CLICKED(int ms) {
|
|
// If we're not even pressed, forget about it!
|
|
if (BUTTON_PRESS() == false)
|
|
return BUTTON_NO_CLICK;
|
|
|
|
ms = ms ? ms : 1000; // use ms param if valid.
|
|
|
|
int letoff = 0;
|
|
for (;;) {
|
|
|
|
// Using a very short delay period to count how long has passed is much safer than using other methods,
|
|
// as other timers may be stopped/reset
|
|
SpinDelay(1);
|
|
--ms;
|
|
|
|
// We haven't let off the button yet
|
|
if (!letoff) {
|
|
// We just let it off!
|
|
if (BUTTON_PRESS() == false) {
|
|
letoff = 1;
|
|
|
|
// reset our timer for 500ms next press waiting
|
|
ms = 500;
|
|
}
|
|
|
|
// Still haven't let it off
|
|
else
|
|
// Have we held down a full second?
|
|
if (ms <= 0)
|
|
return BUTTON_HOLD;
|
|
}
|
|
|
|
// We already let off, did we click again?
|
|
else
|
|
// Sweet, double click!
|
|
if (BUTTON_PRESS())
|
|
return BUTTON_DOUBLE_CLICK;
|
|
|
|
// Have we ran out of time to double click?
|
|
else if (ms <= 0)
|
|
// At least we did a single click
|
|
return BUTTON_SINGLE_CLICK;
|
|
|
|
WDT_HIT();
|
|
}
|
|
|
|
// We should never get here
|
|
return BUTTON_ERROR;
|
|
}
|
|
|
|
// Determine if a button is held down
|
|
// Note: StartTickCount required.
|
|
int BUTTON_HELD(int ms) {
|
|
// If we're not even pressed, forget about it!
|
|
if (BUTTON_PRESS() == false) {
|
|
return BUTTON_NO_CLICK;
|
|
}
|
|
|
|
ms = ms ? ms : 1000; // use ms param if valid.
|
|
|
|
for (;;) {
|
|
|
|
// As soon as our button let go, we didn't hold long enough
|
|
if (BUTTON_PRESS() == false) {
|
|
return BUTTON_SINGLE_CLICK;
|
|
}
|
|
|
|
// Using a very short delay period to count how long has passed is much safer than using other methods,
|
|
// as other timers may be stopped/reset
|
|
SpinDelay(1);
|
|
--ms;
|
|
|
|
// Have we waited the full second?
|
|
if (ms <= 0) {
|
|
return BUTTON_HOLD;
|
|
}
|
|
|
|
WDT_HIT();
|
|
}
|
|
|
|
// We should never get here
|
|
return BUTTON_ERROR;
|
|
}
|
|
|
|
// This function returns false if no data is available or
|
|
// the USB connection is invalid.
|
|
bool data_available(void) {
|
|
#if defined(WITH_BWM_FORWARD)
|
|
// The BWM (BLE / WiFi) link is the host connection on a Proxmark5, so it
|
|
// has to be polled here too or CMD_BREAK_LOOP is never seen over it.
|
|
return usb_poll_validate_length() || (bwm_fwd_rxdata_available() > 0);
|
|
#elif defined(WITH_FPC_USART_HOST)
|
|
return usb_poll_validate_length() || (usart_rxdata_available() > 0);
|
|
#else
|
|
return usb_poll_validate_length();
|
|
#endif
|
|
}
|
|
|
|
// This function doesn't check if the USB connection is valid.
|
|
// In most of the cases, you should use data_available() unless
|
|
// the timing is critical.
|
|
bool data_available_fast(void) {
|
|
#if defined(WITH_BWM_FORWARD)
|
|
return usb_available_length() || (bwm_fwd_rxdata_available() > 0);
|
|
#elif defined(WITH_FPC_USART_HOST)
|
|
return usb_available_length() || (usart_rxdata_available() > 0);
|
|
#else
|
|
return usb_available_length();
|
|
#endif
|
|
}
|
|
|
|
// Combined function to convert an unsigned int to an array of hex values corresponding to the last three bits of k1
|
|
void convertToHexArray(uint32_t num, uint8_t *partialkey) {
|
|
char binaryStr[25]; // 24 bits for binary representation + 1 for null terminator
|
|
binaryStr[24] = '\0'; // Null-terminate the string
|
|
|
|
// Convert the number to binary string
|
|
for (int i = 23; i >= 0; i--) {
|
|
binaryStr[i] = (num % 2) ? '1' : '0';
|
|
num /= 2;
|
|
}
|
|
|
|
// Split the binary string into groups of 3 and convert to hex
|
|
for (int i = 0; i < 8 ; i++) {
|
|
char group[4] = {'0', '0', '0', '\0'}; // Ensure group is initialized correctly
|
|
memcpy(group, binaryStr + i * 3, 3); // Use memcpy to copy exactly 3 characters
|
|
group[3] = '\0'; // Null-terminate the group string
|
|
partialkey[i] = (uint8_t)strtoul(group, NULL, 2);
|
|
}
|
|
}
|
|
|
|
void switch_clock_to_ticks(void) {
|
|
StopTicks();
|
|
StartTicks();
|
|
#ifdef WITH_SMARTCARD
|
|
sc_log_trace_reset();
|
|
#endif
|
|
trace_restart_timeline();
|
|
}
|
|
|
|
void switch_clock_to_countsspclk(void) {
|
|
StopTicks();
|
|
StartCountSspClk();
|
|
trace_restart_timeline();
|
|
}
|