Part 2 — Getting it running
Part 1 described the toolkit in the abstract. This part is concrete: what must be installed, what the radios can and cannot do, and one worked session from an empty machine to a case that contains evidence. §2.1 is the bench, §2.2 is the radio, §2.3 is the first hour.
2.1 The bench: dependencies and pre-flight
IMSISting is a shell toolkit wrapped around external programs. It does not
vendor decoders, drivers or protocol stacks, and it does not pretend to work
without them: a module that needs tshark and cannot find it refuses, names
the binary, and exits 2. That behaviour is only pleasant if you can see the
whole dependency picture before you are in the middle of a survey, which is what
the dependency module exists for.
00_deps — Dependency Manager
modules/00_deps.sh --preflight # radio, gates and capture state in one page
modules/00_deps.sh --status-tsv # one row per registered tool, 6 TSV fields
modules/00_deps.sh --missing-only # only what is not usable
modules/00_deps.sh --check-versions # present / absent / broken, with versions
modules/00_deps.sh --report # write loot/deps/deps_report.tsv
Start with --preflight. It answers three questions in one screen: is there a
radio, are the capture paths usable, and are the gates clean. The order matters
— a broken capture path makes every later result suspect, and it is cheaper to
find that now than after an hour of survey.
--status-tsv is the machine-readable form and the one to use in a script. Each
row describes one tool with six fields: the tool name, its class
(sdr, decode, protocol, sim, net, …), whether it is usable, the
package that provides it on Debian, the package that provides it on Arch, and a
note. "Usable" is not the same as "installed": a binary that exists but cannot
run is reported as broken, because a toolkit that counts a broken decoder as
present is a toolkit that reports empty results as findings.
--install-missing installs what is absent through the distribution's package
manager, and asks before it does. --install-pip covers the Python protocol
libraries that are not packaged. Neither is required to use the toolkit: most
modules degrade in a stated way rather than failing, and the ones that cannot
degrade refuse.
05_field_kit — Field Kit
The field kit is the answer to a specific bad habit: claiming a capability
because a binary was on $PATH. It performs two things.
--capability reports what radio hardware is present and what that implies for
the work you might do next — receive only, transmit capable, full duplex, both
directions but one chain at a time. This is a stronger statement than
01_radio --list-tsv, which lists what answered a probe; the field kit maps the
answer onto consequences.
--guard-check is the honesty test the rest of the tree leans on. It runs every
radio-gated verb in the toolkit and verifies that each one fails correctly
when no radio is present: refusing with a named missing clause, or handing off,
never succeeding with empty output. If any gated verb returns 0 with no radio,
the field kit reports it as a defect. That is the mechanism which keeps
loot/ honest when the hardware is unplugged, and it is why the modules can be
trusted in a lab.
--emit writes synthetic GSMTAP onto the loopback interface (or into a pcap)
from a set of built-in scenarios, and --scenarios lists them. This exists so
that the decoders, the stores and the interfaces can be exercised end to end on
a machine with no radio and no live network — and so that a reader of this
manual can reproduce every GSMTAP example in Part 5 without radiating anything.
The synthetic frames are labelled synthetic in the store's source column: a
scenario is evidence about the decoder, never about the air.
tools/fetch-reference-data.sh — the public reference tables
A capture tells you what the air carried. It does not tell you who owns it. The MCC/MNC register, the ITU's ISPC register and the IANA enterprise numbers are what turn an identity into an operator, and they are public but not shipped: they are hundreds of thousands of rows, they are revised, and a stale copy that looks authoritative is worse than no copy at all.
tools/fetch-reference-data.sh # all three tables
tools/fetch-reference-data.sh --only ispc # one
tools/fetch-reference-data.sh --check # present / silent, no download
| Table | Source | Rows | Read by |
|---|---|---|---|
data/public/plmn.tsv |
the full public MCC/MNC register | ~2 500 | 02_cell_recon, 04_tower_map, 16_sim |
data/public/ispc.tsv |
ITU Operational Bulletin, Q.708 annex | ~6 300 | 26_ss7_map --ispc |
data/public/pen.tsv |
IANA enterprise numbers | ~67 000 | Diameter vendor ids |
Everything lands under data/public/, which is gitignored. Nothing is committed
and nothing is fetched at module run time: a module either finds a table or says
it did not.
The lookups report three states, and the difference is the point:
| State | Means | What a caller does |
|---|---|---|
ok |
the row was found | print the operator |
absent |
the table was read and holds no such row | that is the finding |
silent |
the table is not on this machine | report unobserved, never "absent" |
silent and absent are routinely confused and they never mean the same
thing. A PLMN that resolves to nothing in a downloaded register is an operator
missing from the public record; a PLMN that resolves to nothing because nobody
ran the fetch is a hole in the analyst's bench. Collapsing the second into the
first manufactures findings, which is the one failure this toolkit exists to
avoid.
data/opops/ — the operator's own tables
data/opops/ is the private half: tables an operator supplies about its own
network. The toolkit ships the loader and the schema, never the contents —
which is also the reason it is useful, because the private numbers a core
network runs on are exactly the ones that must not be in a git repository.
| File | Header | Answers |
|---|---|---|
gt.tsv |
gt_prefix, plmn, stp_type, note |
which PLMN and which STP a global-title prefix belongs to |
cellblock.tsv |
plmn, range_lo, range_hi, kind, note |
whether a cell identity sits inside the block an operator was allocated |
iprange.tsv |
plmn, cidr, role, note |
which core function owns an address |
smsc.tsv |
plmn, smsc_gt, note |
the SMSC a home network routes short messages through |
Validation is an operator-internal binary: a subscriber who watches a spoofed
command from their handset will accept the command when the SMSC is approved,
and the SMSC identity is core-internal. These are not exported by any public
registry, published by any SIM vendor, or in any OEM code. A GT table is the
only thing that turns op=470012000001 into a statement about the operator
whose HLR is being queried.
With data/opops/ empty the toolkit is correct and shallow: it decodes, it
correlates, and it reports the store's contents as unobserved where an operator
table would have answered. That is the honest floor, not a bug.
Each table ships as a tracked *.dist template in the same directory. Copy the
one you need, drop the .dist, and replace the example rows:
cp data/opops/iprange.tsv.dist data/opops/iprange.tsv
tools/lib/refdata.sh # source, not run: refdata_status_line
The templates carry the schema and one obvious example row each — never a real operator's numbers, which is the whole reason the directory is private.
Screening an interconnect peer
iprange.tsv is what turns a source address into a statement about a peer.
11_lte_core reads it for two questions that a border either answers or does
not, and answers both in the same three states the rest of the toolkit uses:
modules/11_lte_core.sh --gtp-screen-peer 198.51.100.5 32
modules/11_lte_core.sh --diameter-screen 16777251 316
The first asks two things in order. Is this address inside a prefix a contracted peer was granted, and — the half a peer-level allow rule never asks — may a peer originate this GTP-C message type at all. A message can come from the right address and still be wrong: MBMS Session Start, or any node-initiated procedure, is not something a roaming partner sends.
The second asks whether a Diameter (application, command) pair is one a roaming peer may originate, or whether it belongs to an interface that only ever traverses one operator's domain. S6a Update-Location arriving over an interconnect is the identity-theft shape, not a protocol error, and it is named as such.
Two things both verbs refuse to do. They do not resolve longest-prefix — the
first matching row wins, so put narrow prefixes above broad ones. And with no
iprange.tsv installed they answer unknown, not deny: a missing file must
never manufacture a blocked-peer finding.