Files view: per-file tracking (counters, image, device/card changes)
- new `simtrace_analyser/filetrack.py`: a single-pass tracker over a session's decoded messages, keyed by (DF, FID) and trace-wide (resets only wipe the per-channel selection): counters (`select`/`read`/`update`, successful operations only), a sparse image of the observed bytes (transparent files by offset, record files per record number) and the change log - changes: **device** = an UPDATE seen in the trace (applied to the image, `no_change` flags a provable no-op); **card** = a READ whose bytes differ from the image on bytes already known (logged at the surfacing read with the changed ranges and the before/after hex; never-seen bytes just fill the image, they are an observation, not a change) - SFI-addressed accesses join the same entry as path/FID ones: the SFI ↔ FID relations come from the normative tables and from every surfaced FCP `88` DO (including the spec's "SFI = 5 LSBs of the FID" rule when the DO is absent); an SFI used before its relation is learned stays provisional and merges into the resolved entry - the DF identity follows TS 102 221 §8.4/Table 8.2: `selected_df_fid()` now resolves an EF path selection to its parent DF and returns `aid:<hex>` for applications selected by an unknown AID (named from EF.DIR when the trace surfaced it); `Database._decode_rows` gained an `on_message` hook and `track_files()` - `GET /api/sessions/<id>/files`: the operation timeline, the per-file counters/coverage/change log, the SFI relations and the AID labels - PWA: the side panel's Map/Stats tabs became a view selector (Map / Stats / Files); the Files view is a timeline of Read / Update / Changed (card) rows (colour-coded, the same file names as the Map view), a row click jumps to the APDU (loading the pages it needs) and a card-change row's Δ opens the changed ranges with the previous and the new bytes; EN/RU - bounds: the image freezes past 64 KB of observed bytes per file, the change log keeps the last 1000 events per file (counters keep counting) - tests: tests/test_filetrack.py (counters, resets, channels, image/diff semantics, record files, SFI resolution and the provisional merge, EF.DIR labels, caps, the endpoint) and Node tests for the Files-view helpers and wiring - v2.3.0; MIN_SERVER_VERSION 2.3.0; SW cache simtrace-analyser-v63
This commit is contained in:
1 parent
b8d9c4cc63
commit
c494bf4111
14 files changed
+1302
-36
No files matched your search
@@ -522,26 +522,53 @@ def _is_df_fid(fid):
|
||||
return fid == '3f00' or fid.startswith('7f') or fid.startswith('5f')
|
||||
|
||||
|
||||
def selected_df_fid(d):
|
||||
"""Return the current DF after a SELECT, or None if the SELECT does not
|
||||
(re)enter a DF. Handles SELECT by FID (P1 00/01), by path (P1 08/09),
|
||||
and by DF name/AID (P1 04)."""
|
||||
def selected_df_fid(d, current_df=None):
|
||||
"""Return the current DF after a SELECT, or None if it is unchanged.
|
||||
|
||||
TS 102 221 §8.4.1: a DF/MF/ADF selection makes that file the current
|
||||
directory; selecting an EF leaves the *parent of that EF* as the current
|
||||
directory. So a path selection that ends in an EF also (re)enters the
|
||||
DF the EF lives in (Table 8.2: ``'EF1'`` from MF, ``'7FFF||DF3||EF4'``,
|
||||
…). ``current_df`` is used for "path from the current DF" paths.
|
||||
|
||||
Handles SELECT by FID (P1 00/01), by path (P1 08/09), and by DF name/AID
|
||||
(P1 04; the special FID '7fff' always means the ADF of the current active
|
||||
application, §8.3).
|
||||
"""
|
||||
if d.get('ins_hex') != 'a4':
|
||||
return None
|
||||
p1 = (d.get('p1') or {}).get('raw')
|
||||
h = (d.get('body') or {}).get('hex') or ''
|
||||
|
||||
def resolve(fid):
|
||||
# '7FFF' is the running application's ADF: when that application was
|
||||
# selected by an unknown AID, keep the AID as the DF identity.
|
||||
if fid == '7fff' and (current_df or '').startswith('aid:'):
|
||||
return current_df
|
||||
return fid
|
||||
|
||||
if p1 in ('00', '01'):
|
||||
return h if len(h) == 4 and _is_df_fid(h) else None
|
||||
return resolve(h) if len(h) == 4 and _is_df_fid(h) else None
|
||||
if p1 in ('08', '09'):
|
||||
comps = [h[i:i + 4] for i in range(0, len(h) - 3, 4)]
|
||||
for comp in reversed(comps):
|
||||
if not comps:
|
||||
return None
|
||||
target = comps[-1]
|
||||
if _is_df_fid(target):
|
||||
return resolve(target)
|
||||
# The selected file is an EF: the current directory is its parent.
|
||||
for comp in reversed(comps[:-1]):
|
||||
if _is_df_fid(comp):
|
||||
return comp
|
||||
return None
|
||||
return resolve(comp)
|
||||
if p1 == '08':
|
||||
return '3f00' # path from MF: directly under MF
|
||||
return current_df or None # path from the current DF
|
||||
if p1 == '04': # select by DF name (AID) → map known application AIDs
|
||||
if h.startswith('a0000000871002'): # USIM AID
|
||||
return '7fff'
|
||||
return None
|
||||
# Unknown application: keep the AID as the DF identity so files under
|
||||
# it stay distinguishable from the previous DF's (TS 102 221 §11.1.1).
|
||||
return f'aid:{h.lower()}' if h else None
|
||||
return None
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user