Fix pcap timestamp wrap, ATR-hunt EOF crash, start.bat protocol; tighten launchers + vs_reader

- pd.py ts_from_samplenum: drop c_uint32 truncation + dead sampleOverflowCount
  (pcap timestamps wrapped backwards past ~4.5 min at 16 MHz).
- pd.py handle_atr: guard None reads at EOF (was TypeError (None & 0x80) on a
  capture truncated mid-ATR-hunt).
- start.bat: add --protocol (+ :protocol=T=0 default) to match start.sh's
  mid-session decode; correct the 'flag-for-flag' header comment.
- start.sh: reject --rst/--vcc assigned to the same pin (was unchecked).
- setup.bat: set DEVICE_MSG on blocked/skipped probe so the trailing
  'unknown' message is suppressed.
- vs_reader.py: RESULT now fails on CONCAT / payload mismatch / suspicious
  CLA, not just GARBAGE (OK text unchanged; clean traces stay identical).
This commit is contained in:
catarrh committed 2026-09-11 01:21:02 +03:00
1 parent 9d00827837
commit e6ff3a2e14
5 files changed
+46 -12

No files matched your search

+12 -5
View File
@@ -20,7 +20,6 @@ import struct
import sys
import time
import codecs
import ctypes
import traceback
from collections import deque
@@ -450,7 +449,6 @@ class Decoder(srd.Decoder):
# card was already in reset" indistinguishable from a live session.
self._primed = False
self.lastSamplePositive = True
self.sampleOverflowCount = 0
# Rolling parity-error monitor: warns when the wire degrades
# (e.g. GSM TX bursts coupling into the probe wiring).
self.parity_hist = deque(maxlen=64)
@@ -755,9 +753,12 @@ class Decoder(srd.Decoder):
self.wrote_pcap_header = True
def ts_from_samplenum(self, sample):
x = ctypes.c_uint32(sample).value;
ovrflow = ctypes.c_uint32(int(self.sampleOverflowCount * 0xFFFFFFFF * self.secs_per_sample)).value
ts = float(x) * self.secs_per_sample + ovrflow + self._pcap_base_time
# Use the full sample number. The previous c_uint32 truncation (plus
# an overflow counter that was never incremented) made pcap timestamps
# wrap and go backwards once a capture passed 2^32 samples (~4.5 min
# at 16 MHz). float64 holds sample numbers well past 2^53, so the
# whole sample domain is representable.
ts = float(sample) * self.secs_per_sample + self._pcap_base_time
return (int(ts), int((ts % 1.0) * 1e6))
def signal_quality(self, err):
@@ -1556,6 +1557,10 @@ class Decoder(srd.Decoder):
self.put(atr_start, self.samplenum, self.out_ann,
[Ann.ANN_WARN, ["Invalid TS byte 0x{byte:02x} - skipped".format(byte=byte)]])
byte = self.read_byte()
if byte is None:
# End of capture mid-hunt: stop cleanly instead of raising
# TypeError (None & 0x80) in the next loop iteration.
return
atr_start = self.ss
ts_attempts += 1
if (byte not in (0x3b, 0x3f)):
@@ -1599,6 +1604,8 @@ class Decoder(srd.Decoder):
ts=byte, conv="inverse" if self._inverse_convention else "direct"))
t0 = self.read_byte()
if t0 is None:
return
self.ATR.append(t0)
# Lock ETU from the first two ATR bytes (TS + T0). The ATR is sent
# at the default ETU, so its edge spacings are the authoritative
+2
View File
@@ -106,10 +106,12 @@ for %%r in (1M 2M 4M 8M 12M 16M 20M 24M) do (
-P "iso7816:clk=D0:data=D1" >"%TEMP%\iso7816-probe.txt" 2>&1
type "%TEMP%\iso7816-probe.txt" | findstr /c:"Unable to claim" >nul && (
echo blocked - another program holds the device
set "DEVICE_MSG=1"
goto :probedone
)
type "%TEMP%\iso7816-probe.txt" | findstr /c:"Failed to open" /c:"No devices found" >nul && (
echo skipped - no FX2 device connected
set "DEVICE_MSG=1"
goto :probedone
)
type "%TEMP%\iso7816-probe.txt" | findstr /r /c:"invalid argument" /c:"Unable to sample" /c:"Could not start" >nul || set MAX_RATE=%%r
+11 -1
View File
@@ -1,6 +1,7 @@
@echo off
rem sigrok_iso7816_stream launcher (Windows): capture ISO 7816 and stream
rem GSMTAP to :4729. Flag-for-flag equivalent of start.sh.
rem GSMTAP to :4729. Mirrors the core start.sh flags (channel/samplerate/
rem clock/gsmtap/pcap/protocol/debug); start.sh has the full flag set.
setlocal EnableExtensions EnableDelayedExpansion
@@ -15,6 +16,7 @@ set "PORT=4729"
set "PCAP="
set "DEBUG="
set "ANNOT=iso7816=apdus,iso7816=warnings"
set "PROTOCOL=T=0"
:argloop
if "%~1"=="" goto :argsdone
@@ -28,6 +30,7 @@ if /i "%~1"=="--host" ( set "HOST=%~2" & shift & shift & goto :arg
if /i "%~1"=="--port" ( set "PORT=%~2" & shift & shift & goto :argloop )
if /i "%~1"=="--pcap" ( set "PCAP=%~2" & shift & shift & goto :argloop )
if /i "%~1"=="--debug" ( set "DEBUG=1" & set "ANNOT=iso7816" & shift & goto :argloop )
if /i "%~1"=="--protocol" ( set "PROTOCOL=%~2" & shift & shift & goto :argloop )
if /i "%~1"=="--no-rst" ( set "RST=" & shift & goto :argloop )
if /i "%~1"=="--no-vcc" ( set "VCC=" & shift & goto :argloop )
if /i "%~1"=="-h" ( goto :usage )
@@ -49,6 +52,7 @@ echo --pcap=FILE Also write decoded events to FILE (no spaces in path)
echo --debug Full decoder + sigrok output (all annotation rows + -l 4).
echo
echo By default only decoded APDUs and decoder warnings/errors are shown.
echo --protocol=MODE T=0 (default), T=1, or auto (mid-session decode)
echo --no-rst Disable RST tracking
echo --no-vcc Disable VCC tracking
echo.
@@ -82,11 +86,17 @@ exit /b 1
:found
rem Normalize --protocol to the decoder's accepted form (T=0 / T=1 / auto).
if /i "%PROTOCOL%"=="T0" set "PROTOCOL=T=0"
if /i "%PROTOCOL%"=="T1" set "PROTOCOL=T=1"
if /i "%PROTOCOL%"=="AUTO" set "PROTOCOL=auto"
set "OPTS=clk=%CLK%:data=%DATA%:clock_option=%CLOCK%"
set "OPTS=%OPTS%:gsmtap_host=%HOST%:gsmtap_port=%PORT%"
if defined RST set "OPTS=%OPTS%:rst=%RST%:rst_detect=true"
if defined VCC set "OPTS=%OPTS%:vcc=%VCC%:vcc_detect=true"
if defined PCAP set "OPTS=%OPTS%:pcap_file=%PCAP%"
set "OPTS=%OPTS%:protocol=%PROTOCOL%"
set "CMDLINE=sigrok-cli -d fx2lafw --config samplerate=%SAMPLERATE% --continuous -C D0,D1,D2,D3,D4,D5,D6,D7"
if defined DEBUG set "CMDLINE=%CMDLINE% -l 4"
+1
View File
@@ -189,6 +189,7 @@ check_dup clk "${CLK}" rst "${RST}"
check_dup clk "${CLK}" vcc "${VCC}"
check_dup data "${DATA}" rst "${RST}"
check_dup data "${DATA}" vcc "${VCC}"
check_dup rst "${RST}" vcc "${VCC}"
echo "Channels: clk=${CLK} data=${DATA}${RST:+ rst=${RST}}${VCC:+ vcc=${VCC}}"
+20 -6
View File
@@ -380,12 +380,26 @@ def main():
print('\nWARNING: capture contains 0 APDUs but reader log has %d exchange(s).'
% len(reader))
ok = (len(pcap_garbage) == 0 and not no_apdu_failure)
print('\nRESULT: %s' % ('OK - capture fully explained (reader + CAT + valid retries)'
if ok else
'UNCLEAN - %d mis-framed capture APDU(s)%s'
% (len(pcap_garbage),
', 0 capture APDUs' if no_apdu_failure else '')))
ok = (len(pcap_garbage) == 0
and len(concat_indices) == 0
and len(payload_mismatches) == 0
and len(suspicious_cla) == 0
and not no_apdu_failure)
if ok:
print('\nRESULT: OK - capture fully explained (reader + CAT + valid retries)')
else:
problems = []
if pcap_garbage:
problems.append('%d garbage APDU(s)' % len(pcap_garbage))
if concat_indices:
problems.append('%d CONCAT packet(s)' % len(concat_indices))
if payload_mismatches:
problems.append('%d payload mismatch(es)' % len(payload_mismatches))
if suspicious_cla:
problems.append('%d suspicious CLA' % len(suspicious_cla))
if no_apdu_failure:
problems.append('0 capture APDUs')
print('\nRESULT: UNCLEAN - %s' % ', '.join(problems))
sys.exit(0 if ok else 1)