Skip to content

SessionKey handshake for V1-initial S7-1200 PLCs#724

Open
gijzelaerr wants to merge 34 commits into
masterfrom
research/harpo-port-incomplete
Open

SessionKey handshake for V1-initial S7-1200 PLCs#724
gijzelaerr wants to merge 34 commits into
masterfrom
research/harpo-port-incomplete

Conversation

@gijzelaerr
Copy link
Copy Markdown
Owner

Summary

Complete port of the HarpoS7 Family-0 session-key authentication chain, enabling browse() and other S7CommPlus data operations on V1-initial S7-1200 PLCs (FW < 4.5) that currently drop the connection after session setup.

  • Full crypto chain: 11 transpiled monoliths, BigInt operations, Transform7/12/13, PreSeedTransform, KeyDerivationTransform, SeedTransform, LutGenerator, ChecksumTransform, HarpoFingerprint, RealPlcAuthenticator — all byte-identical against HarpoS7 C# test vectors for both S7-1500 and S7-1200 key families
  • 25 vendored Siemens public keys for key lookup by fingerprint
  • Wired into _setup_session(): when the PLC's CreateObject response contains a known public key fingerprint and a session challenge, the 180-byte SecurityKeyEncryptedKey blob is automatically generated and included at address 1830
  • 104 session-auth tests including end-to-end vector tests matching the C# reference implementation

What this fixes

V1-initial S7-1200 PLCs (FW v4.0–v4.4) require a SessionKey blob in the session-setup SetMultiVariables write. Without it, the PLC drops the connection. This PR generates that blob using the same crypto as TIA Portal, ported from HarpoS7 (MIT).

Test plan

  • 104 session-auth unit tests (monoliths, transforms, fingerprint, end-to-end blob vectors)
  • Full test suite: 1673 passed, 0 failed
  • Needs real-PLC verification@xBiggs can you test this branch against your S7-1200 FW v4.2.2? Enable debug logging and try browse().

New dependency

  • cryptography (for AES-ECB in the authenticator)

Closes #710, closes #717

🤖 Generated with Claude Code

gijzelaerr and others added 26 commits April 27, 2026 08:28
V1-initial S7-1200 PLCs (e.g. FW v4.2.x) only return ServerSessionVersion
when the client introduces itself with a fuller CreateObject attribute set.
A minimal request (ClientRID only) gets an incomplete session and every
subsequent CommPlus op fails with ERROR2 (0x05A9). Match TIA Portal's
attribute set so the PLC fills in ServerSessionVersion, then echo it back
verbatim in a V2 SetMultiVariables.

ServerSessionVersion is a Struct(314) on real hardware, not a UDInt; capture
it verbatim and echo unchanged. Session-setup write uses V2 framing,
transport flags 0x34, and no IntegrityId regardless of what the PLC
negotiated on initial connect (matches thomas-v2 SetSessionSetupData). Also
fixes the broken STRUCT case in _skip_typed_value (4-byte ID + key/value
pairs + 0x00 terminator, not VLQ count + element list).

Sync client only for now — async will mirror once verified against real
hardware. Refs #710, #712.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CreateObject response header is 10 bytes long with no SessionId field;
the actual session IDs are returned as a list of UInt32 VLQ values in the
ResponseSet body (after ReturnValue + ObjectIdCount). Our code was reading
14 bytes as if a regular response header, picking up garbage as the session
ID. The wrong InObjectId then caused the V2 SetMultiVariables session-setup
to be rejected by real PLCs (TCP RST). Refs #710 #712.

Reference: thomas-v2/S7CommPlusDriver CreateObjectResponse.Deserialize.

Also updates the test emulator to emit responses in the same format real
PLCs use (10-byte header, ObjectIds list), so the unit tests stay
representative.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The V1-initial S7-1200 drops the V2 SetMultiVariables session-setup
connection if we write its own device identity (element 319 of the
ServerSessionVersion struct, e.g. "1;6ES7 215-1BG40-0XB0 ;V4.2") back
to it verbatim. TIA Portal handles this by stripping element 319 to an
empty WString before echoing — replicating that here.

The rest of the captured Struct(314) value is echoed unchanged.

Refs #710 #712.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frame-by-frame comparison of TIA Portal V19's pcap (against an S7-1200
FW 4.2.2, V1-initial firmware) versus our previous SetMultiVariables
shows TIA writes *two* items in the session-setup frame:

  1. address 1830 (SESSION_SETUP_LEGITIMATION) — a Struct(1800) with
     four nested fields plus a 180-byte Blob. The blob carries the
     PLC's 8-byte OMS session UUID at offset 32 (little-endian); the
     remaining bytes look like opaque identity/integrity material.
  2. address 306 (SERVER_SESSION_VERSION) — the existing PAOM-stripped
     echo.

Without the address-1830 item the V1-initial PLC silently closes the
TCP connection right after the setup write — the symptom @xBiggs
reports on #710 / #713.

We capture the OMS UUID from the CreateObject response's
ObjectVariableTypeName attribute (id 233, "01:HEX" WString) and patch
it into the blob; everything else is replayed verbatim from the TIA
reference capture as a first-pass experiment. If the PLC validates
other byte ranges, we'll have to reverse those next.

The two-item form is gated on having an OMS UUID. The C# driver's
existing one-item form (SERVER_SESSION_VERSION only) still runs on
PLCs that don't surface an OMS UUID, matching the C# reference exactly.

Reference: thomas-v2/S7CommPlusDriver SetMultiVariablesRequest.cs;
TIAPortalV19AccessibleDevices.pcapng frame 31 (#710 / #713).

Refs #710 #712 #713

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After 0c9af7c the parser kept scanning past the SERVER_SESSION_VERSION
attribute (so it could also grab ObjectVariableTypeName for the V2
legitimation), which made the trailing "ServerSessionVersion not found"
debug fire on every successful capture. Gate it on the captured fields
so it only fires when we actually didn't find it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wireshark's s7comm-plus dissector reveals that the value at address
1830 is a Struct(1800) named ``StructSecurityKey`` carrying a
``SessionKey`` — specifically a 180-byte ``SecurityKeyEncryptedKey``
blob with the layout:

  0-3   uint32 LE  magic = 0xFEE1DEAD
  4-7   uint32 LE  blobsize (0xB4)
  16-23 uint64 LE  symmetric_key_checksum
  32-39 uint64 LE  public_key_checksum    (what attribute 233 carries)
  48-N            encrypted_random_seed   (RSA-encrypted)
  N..   16 bytes   AES-CBC IV
  N+16..56 bytes   encrypted_challenge

So generating this requires Siemens' RSA public key (identified by the
8-byte fingerprint in attribute 233's "01:HEX" string) plus the KDF
that turns the seed into the AES-CBC key. We don't have either, so the
TIA-replay we shipped in a2111e7 was always going to fail against any
new session — the PLC cannot decrypt a static blob.

Roll back the address-1830 send. Keep the public-key checksum
extraction as a diagnostic capture (renamed from `_oms_session_uuid`
to `_public_key_checksum` to match the dissector's terminology); a
future commit can use it to dispatch to a key-aware handshake once we
have the keys, or to clearly log "this firmware needs a SessionKey we
can't generate" when we don't.

Net effect on V1-initial S7-1200 (FW < 4.5): same as before this PR —
SetupSession still fails, the unified client falls back to legacy
PUT/GET, db_read works, browse() requires CommPlus and so still
raises. PR #713 stays a draft pending the key situation.

Refs #710 #712 #713

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First slice of the S7CommPlus session-key handshake (refs #717). Adds
a fingerprint parser and an in-memory public-key lookup for the three
PLC families HarpoS7 1.1.0 supports: S7-1500 (family 0), S7-1200
(family 1) and PlcSim (family 3). Key bytes are vendored verbatim
from bonk-dev/HarpoS7's MIT-licensed binaries.

@xBiggs's PLC fingerprint `01:BD426B091F08731A` is in the bundle, so
once the rest of the handshake (AES, SHA-256, the proprietary monolith
transforms, and the auth orchestration) lands we'll be able to
generate a valid SecurityKeyEncryptedKey for that PLC.

This commit alone changes no runtime behaviour — the new module is
not yet wired into `_setup_session`. That happens in a later slice
once the handshake produces verified output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Second slice of the HarpoS7 port (refs #717). Adds the small SHA-256
fingerprinting function that computes the symmetric/public key id
fields embedded in the SecurityKeyEncryptedKey blob.

The function is just `SHA-256(key[:24] + b"DERIVE")[:8]` — well-defined
in HarpoS7's `KeyExtensions.DeriveKeyId`. We verify byte-correctness in
two ways:

  1. The three test vectors HarpoS7 ships in `KeyExtensionsTests.cs`
     (a 64-byte PlcSim key plus two repeating-byte cases).
  2. A self-consistency check: every bundled public key, when run
     through `derive_key_id`, must produce the LE byte-reversal of
     the BE-display hex it's keyed on. This catches both algorithm
     bugs and key-byte transcription errors in #b44792d.

@xBiggs's PLC fingerprint `01:BD426B091F08731A` round-trips correctly,
so once the next slices land we can compute exactly the
publickeychecksum / symmetrickeychecksum the PLC expects to see.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Third slice of the HarpoS7 port (refs #717). Produces the leading 48
bytes of the 180/216-byte SessionKey blob — magic, length, key
fingerprints, and per-family flags — matching the layout the
Wireshark s7comm-plus dissector decodes.

The headline regression test compares our output against TIA Portal
V19's actual wire bytes from @xBiggs's pcap (frame 31 of
TIAPortalV19AccessibleDevices.pcapng), modulo the per-session
symmetric_key_id slot (which TIA randomises). Every other byte in
the 48-byte header matches verbatim.

Ported from HarpoS7's `BlobMetadataWriter.WriteMetadata` plus the
per-family `Get{Public,Symmetric}KeyFlags` and `GetBlobLength`
helpers. The PlcSim path is preserved for completeness, though the
seed handling on that family differs and isn't exercised yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fourth slice of the HarpoS7 port (refs #717). Adds three pure-SHA-256
KDFs the handshake uses to spin off the various AES keys:

- derive_challenge_encryption_key — 16-byte AES-128 key for encrypting
  the PLC challenge
- derive_seed_encryption_key_and_iv — 32-byte AES-256 key + 16-byte IV
  for encrypting the random seed (counter-mode SHA-256 chain over
  reverse(a2) || a3 || counter_byte)
- derive_legitimation_challenge_key — 24-byte key for encrypting the
  legitimation challenge after session establishment

All three verified byte-for-byte against the test vectors HarpoS7
ships in `KeyUtilitiesTests.cs`.

The fourth helper from upstream — `derive_session_key` — is
intentionally left out of this slice: it depends on
`HarpoFingerprint.FingerprintChallenge`, which lives in the Family-0
"monolith" transforms and lands in a later slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ash_block)

Fifth slice of the HarpoS7 port (refs #717). Adds the proprietary
128-bit transform that powers HarpoAesCtr's integrity-MAC computation
and the encrypted-seed generation in the SessionKey blob.

Three primitives — all pure-Python, all verified byte-for-byte against
HarpoS7's vector tests:

  - lut1: one round of the transform, 16 → 16 bytes
  - generate_lookup_table: 16-byte key → 4 KB working table
  - hash_block: 16-byte input → 16-byte output, using the working table

The full 4096-byte expected output of generate_lookup_table is pinned
verbatim from HarpoHashTests.cs as a regression vector — covers both
sub-loops in the C# implementation (the lut1 cascade and the
xor-and-replicate fill phase).

The 512-byte LUT_SEED constant doubles as HarpoHashSeed (upstream
stores it twice for ergonomic byte vs ushort access; we just reslice
when needed).

This unblocks the next slice: HarpoAes / HarpoAesCtr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sixth slice of the HarpoS7 port (refs #717). Thin wrapper around the
``cryptography`` library's AES-128-ECB primitive, matching HarpoS7's
``HarpoAes`` API. Used as a building block by ``HarpoAesCtr`` for
counter encryption and integrity-MAC checksum derivation.

Tests verify the canonical NIST FIPS-197 §C.1 vector plus basic
construction invariants. Skipped when the optional ``cryptography``
package is unavailable, matching the existing ``test_s7_v2.py``
pattern for code under the ``s7commplus`` extra.

Also includes a one-time `ruff format` cleanup of the slice-4 KDF
tests — collapsing a few short multi-line hex literals onto one line
each. No behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seventh slice of the HarpoS7 port (refs #717). Ports the Init and
EncryptCtr halves of HarpoAesCtr — the custom AES-CTR-with-MAC
primitive that encrypts the seed and challenge inside the
SecurityKeyEncryptedKey blob.

Init derives a 4 KB working table from `AES-ECB(key, 0)` and folds
the IV into a 16-byte counter through HarpoHash rounds. EncryptCtr
increments the counter, AES-encrypts it for keystream, XORs against
plaintext, and feeds ciphertext into a running HarpoHash accumulator.

The two end-to-end vectors from HarpoAesCtrTests pass byte-for-byte:

- TestInit: counter state after Init(0xCC * 16) matches the expected
  16 bytes.
- TestEncrypt2Times: encrypting 16 bytes then 24 bytes back-to-back
  produces the expected ciphertexts — exercises the partial-block
  carryover code path.

CalculateChecksum is intentionally deferred to a follow-up slice; it
needs the full mock-state plumbing the upstream test uses, plus the
4096-byte mockChecksumLut as a vendored test asset.

The 12-byte-IV and unaligned-tail paths are left as
NotImplementedError — same as upstream — since the SessionKey
handshake never hits them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eighth slice of the HarpoS7 port (refs #717). Completes HarpoAesCtr
by porting the CalculateChecksum finaliser. Folds the bit-lengths of
encrypted regions into the running MAC, runs one final HarpoHash
round, AES-encrypts the original IV extension, and XORs the two for
the output checksum bytes.

Verified against HarpoS7's CalculateChecksumTest vector via direct
field injection (Python equivalent of the C# reflection-based
mocking). The 4096-byte mockChecksumLut from the upstream test is
vendored as `tests/fixtures/harpo_aes_ctr_mock_checksum_lut.bin`.

This finishes "bucket 1" of the port — every standard primitive
(SHA-256 KDFs, AES-128-ECB, HarpoHash, HarpoAesCtr) now passes
byte-for-byte against HarpoS7's own ground-truth test vectors. What
remains is the Family-0 monolith transforms (the proprietary block
cipher) and the auth orchestration layer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ninth slice of the HarpoS7 port (refs #717). Adds a mechanical C#-to-
Python transpiler in tools/ and runs it against HarpoS7's Family-0
Monoliths 1-8 and 11 — straight-line bitwise transforms that compute
intermediate state for the SecurityKeyEncryptedKey blob.

Each generated monolith passes byte-for-byte against HarpoS7's own
test vectors (vendored as `tests/fixtures/family0/monoliths/*.bin`).
Total ~8.5k transpiled statements verified, no manual edits to the
generated code.

Strategy notes:

- Each MonolithN.Execute is a stream of `;`-terminated statements:
  variable declarations (discardable), `uVar = expr` assignments,
  `dst_dwords[i] = expr` writes, and a single optional `return`. No
  internal control flow. The transpiler treats the body as flat text:
  splits on `;`, normalises src/dst aliases, masks every assignment
  to uint32 (Python ints don't wrap), and emits a Python function
  with src/dst-dword views.
- Monoliths 9 and 10 are orchestrators that call into Nine/Part1..11
  and Ten/Part1..3 — those use a different signature taking a shared
  `locals` array. They're not yet ported and arrive in a follow-up
  slice that extends the transpiler's signature handling.

The Transforms layer (the public API into Family-0) and the auth
orchestration come after; this slice is pure infrastructure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rators

Tenth slice of the HarpoS7 port (refs #717). Extends the transpiler
to handle Family-0 Part subprograms (which take a shared `locals`
uint array in addition to or instead of source/destination buffers),
runs it on all 14 Parts (Nine/Part1..11 + Ten/Part1..3), and adds
hand-written Monolith9/Monolith10 orchestrators that chain them.

The transpiler now detects the C# `Execute` parameter list and
emits the matching Python signature: `Execute(source, locals)`,
`Execute(locals)`, and `Execute(destination, locals)` are all
supported. `locals` is renamed `locals_` to avoid the Python
builtin.

Mechanical translation: ~17k more transpiled statements across the
14 Parts. All compile and run, no exceptions.

Vector status:
  - Monolith9 / Monolith10 produce non-byte-exact output against the
    upstream blob fixtures (xfail-marked, refs #717). Monolith10
    matches for the first ~219 bytes then diverges, suggesting the
    bug is in a specific Part rather than the orchestrator structure.
    Investigation deferred — not on the critical path for the
    SessionKey handshake, which uses the simpler Monoliths and the
    Transforms layer (next slice).

  - The `test_monolith9_and_10_at_least_run` smoke test catches
    regressions in transpiler signature handling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eleventh slice of the HarpoS7 port (refs #717). Manual port of
``HarpoS7.Family0.BitOperations.BigIntOperations`` — short, idiomatic
helpers for packing/unpacking 192-bit integers between the wire
format and the proprietary 30-bits-per-limb representation the
SessionKey curve operations use internally.

Five operations: prepare (6→5 uints), finalize (5→6 uints),
prepare_finalize (in-place fusion), rotate_right_30, rotate_left_31.
All exact-byte verified against HarpoS7's vector fixtures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Twelfth slice of the HarpoS7 port (refs #717). Manual port of the
four ``BigInt*`` Transform classes plus the BigIntegerCompressor
helper. Python's built-in ``int`` is arbitrary-precision, so we
don't need a separate BigInteger library — just careful use of
``int.to_bytes`` and ``int.from_bytes`` with the right signedness.

Operations:
  - big_int_addition (8 fixtures, 2 sub-cases)
  - big_int_subtraction (11 fixtures, 4 sub-cases incl. all the
    negative-result corner cases that need the upstream's signed
    sign-extension dance)
  - big_int_multiplication (9 fixtures, 3 sub-cases)
  - big_int_square (10 fixtures, 2 sub-cases)

All 11 vector tests pass byte-for-byte against the upstream blob
fixtures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thirteenth slice of the HarpoS7 port (refs #717). Manual port of the
two GHASH-style transforms — both vector-verified against HarpoS7's
fixtures.

LutGenerator builds a 4 KB table of 256 UInt128 values from a 16-byte
seed by iterated GF(2¹²⁸) doubling under the AES-GCM-style reduction
polynomial x¹²⁸ + x⁷ + x² + x + 1.

ChecksumTransform consumes that table plus a 16-byte key to produce
a 16-byte integrity tag, walking key bytes MSB→LSB and rotating the
work buffer one byte after every 4-byte block.

Neither depends on the still-broken Monolith9/10 path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of part of the Monolith9/10 divergence: C#'s uint << N
truncates the result to 32 bits, but Python's int << N doesn't. When
the overflow bits get picked up by a subsequent ~ (negative int) &
operation, the high bits differ from C#. Same for * 2.

Fix: add & 0xFFFFFFFF after every << and * 2 in the transpiled output.
Since << has higher precedence than & in Python, the mask applies to
the shift result before any surrounding operator sees it.

Result: Ten/Part1 is now byte-perfect (0 diffs vs C#). Ten/Part2 still
has 57 diffs from a different root cause (under investigation — need
more C# binary-search checkpoints). Monoliths 1-8/11 continue to pass.

Diagnosed using dotnet test against HarpoS7 with intermediate locals[]
dumps compared to Python output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root causes diagnosed using .NET-side intermediate locals[] dumps
compared statement-by-statement against the Python port:

1. Left-shift overflow (Part1): C# uint << N truncates to 32 bits;
   Python int << N doesn't. When overflow bits get picked up by a
   subsequent ~ (negative) & operation, the result diverges. Fix:
   mask << results with & 0xFFFFFFFF in subexpressions.

2. Right-shift on identifiers (Part2/3): `ident >> N` wasn't
   wrapped in _shr() for uint32 masking. Only `) >> N` was handled.

3. ~(expr) >> N paren matching (Part3): the _fix_right_shifts walker
   found `) >> N`, walked back to the matching `(`, but stripped the
   leading `~` — making `~(X) >> N` into `_shr(X, N)` instead of
   `_shr(~(X), N)`. Fix: include any leading `~` chain as part of
   the operand.

All 11 monoliths + both orchestrators now pass byte-for-byte.
Removed the xfail markers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port remaining transforms and orchestration for pre-TLS V1
session-key handshake, verified byte-identical against HarpoS7
C# test vectors for both S7-1500 and S7-1200 key families.

New modules:
- transform7: core ECC scalar multiplication (monolith wrappers +
  Transform12 dispatch + BigInt operations)
- seed_transform: generates encrypted seed via Transform7 +
  Monolith1/2/8/11 + Transform13
- pre_seed_transform, key_derivation_transform, transform13:
  Monolith9/10-based key preparation transforms
- monolith_wrappers: WithCopy wrappers for Monoliths 3-7
- fingerprint: HarpoFingerprint challenge fingerprinting with
  vendored data tables (ContextMutator + FingerprintConsts)
- authenticator: RealPlcAuthenticator blob builder (metadata +
  seed + AES-ECB encrypted challenge + GHASH checksum)
- legacy_auth: top-level entry point (authenticate_real_plc)
- key_derivation.derive_session_key: HMAC-SHA256 session key via
  fingerprint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Includes vendored binary data (transform1_data, transform7_data,
transform7_indexes) that were already referenced by code but not
staged, plus test fixtures and vector tests for PreSeedTransform,
KeyDerivationTransform, and Transform13.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inline small constant tables as documented Python literals in
data/_constants.py — hex ints for opaque values, named tuples
for structured data (ECC coordinates, dispatch tables, mutation
operations). The 4 large tables (transform12 metadata 244K,
big-int aux 18K, fingerprint data1/data2 67K) stay as .bin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the CreateObject response contains a public key fingerprint
and a 20-byte session challenge, and we have a matching Siemens
public key in our vendored key store, _setup_session() now generates
the 180-byte SecurityKeyEncryptedKey blob and includes it as a
second item (address 1830) in the SetMultiVariables request.

This is the pre-TLS authentication mechanism needed for V1-initial
S7-1200 PLCs (FW < 4.5) where browse() currently fails.

Changes:
- Capture session challenge from CreateObject response (any 20-byte
  BLOB attribute in the PObject tree)
- Store public key fingerprint string for key lookup
- _try_session_key_auth(): look up public key, call
  authenticate_real_plc() to generate blob + session key
- _encode_security_key_struct(): build the Struct(1800) PObject
  wrapping the blob with key descriptors
- _setup_session(): conditionally include SecurityKey as Item 1
  alongside ServerSessionVersion as Item 2
- Add SecurityKey struct IDs to protocol.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread s7/session_auth/family0/authenticator.py Dismissed
Comment thread s7/session_auth/family0/authenticator.py Dismissed
Ruff format applied to all new files, f-string lint fixes in
transform13.py, and .bin files excluded from trailing-whitespace
and end-of-file-fixer hooks (they corrupt binary test fixtures).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@xBiggs
Copy link
Copy Markdown

xBiggs commented May 11, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 5b 79 d4 c1 16 13 d7 2e 2a 60 97 63 ff cf 11 da 32 c5 56 27 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 5b 79 d4 c1 16 13 d7 2e 2a 60 97 63 ff cf 11 da 32 c5 56 27 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 5b 79 d4 c1 16 13 d7 2e 2a 60 97 63 ff cf 11 da 32 c5 56 27 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
DEBUG:s7.connection:SessionKey auth: no challenge captured from CreateObject
DEBUG:s7.connection:=== SetupSession === sending (116 bytes): 72 02 00 6c 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 01 01 82 32 01 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 123 bytes: 03 00 00 7b 02 f0 80 72 02 00 6c 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 01 01 82 32 01 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
DEBUG:s7.client:S7CommPlus connection failed: Receive error: [WinError 10054] An existing connection was forcibly closed by the remote host
INFO:s7.client:S7CommPlus not available, using legacy S7 for 192.168.0.1:102
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
protocol: Protocol.LEGACY
DEBUG:snap7.connection:Sent 31 bytes: 03 00 00 1f 02 f0 80 32 01 00 00 00 02 00 0e 00 00 04 01 12 0a 10 02 00 02 00 07 84 00 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 02 00 02 00 06 00 00 04 01 ff 04 00 10 80 00
DEBUG:snap7.client:[192.168.0.1 R0/S1] db_read db=7 start=0 size=2 (8.8ms)
db7[0:2]: bytearray(b'\x80\x00')
Traceback (most recent call last):
  File "\python-snap7-research-harpo-port-incomplete\main.py", line 10, in <module>
    print("browse:", client.browse())
                     ~~~~~~~~~~~~~^^
  File "\python-snap7-research-harpo-port-incomplete\s7\client.py", line 302, in browse
    raise RuntimeError("browse() requires S7CommPlus connection")
RuntimeError: browse() requires S7CommPlus connection

43d5df5.zip

The PLC sends the 20-byte challenge as a USINT array (flags=0x10,
type=0x02) at attribute 303, not as a BLOB. Confirmed from xBiggs's
S7-1200 FW v4.2.2 debug logs. The previous heuristic looking for
a BLOB never matched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs Thanks for the logs! Found the issue — the challenge is at attribute 303 as a USINT array, not a BLOB. Fixed in f80dab3.

Your key 01:BD426B091F08731A is in our store (S7-1200 family). With this fix, the flow should be:

  1. Public key fingerprint captured: 01:BD426B091F08731A
  2. ✅ → Session challenge captured (20 bytes): 5b79d4c1... ← this was missing before
  3. SecurityKey blob included in session setup

Can you pull and test again?

```bash
pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete
```

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 12, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 4d 5f 75 d9 80 fb 69 6f cf ed 1c 44 56 33 da b7 d7 ef c5 50 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 4d 5f 75 d9 80 fb 69 6f cf ed 1c 44 56 33 da b7 d7 ef c5 50 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 4d 5f 75 d9 80 fb 69 6f cf ed 1c 44 56 33 da b7 d7 ef c5 50 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): 4d5f75d980fb696fcfed1c445633dab7d7efc550
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (389 bytes): 72 02 01 7d 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 a3 09 00 04 00 a3 0a 00 03 00 00 a3 0b 00 17 00 00 07 21 a3 22 00 05 de d0 cd b0 c8 fc 90 f3 1a a3 23 00 04 82 10 a3 24 00 04 00 00 a3 0c 00 17 00 00 07 21 a3 22 00 05 d1 85 cc 83 ac b4 93 d1 42 a3 23 00 04 82 01 a3 24 00 04 00 00 a3 0d 00 14 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 a2 a6 6f 7a 78 c7 b8 92 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 eb 8a 46 f7 bd ff 6a 37 f1 39 84 2d 94 37 fe 59 5b 3d 12 ef 03 d0 c4 7e 08 1a 81 e7 a8 5b a2 e0 73 33 9b ad 1b 46 17 55 e6 50 f4 6f 49 7a 3e 07 09 6a d3 06 4a 39 b3 83 68 e1 19 0f 4c a0 24 82 a2 3d cb 34 16 70 69 e9 13 33 86 9f ec 02 f5 0a c1 a5 0e 68 f3 7a aa 5c f6 80 2e 82 51 d5 be e2 67 69 a9 7e 86 64 dc 1f 8d 7b bd 72 06 75 d9 b4 63 0a 76 da 5b e4 74 5a f7 ca 2a 6c 9b 03 26 01 39 00 fa f2 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 396 bytes: 03 00 01 8c 02 f0 80 72 02 01 7d 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 a3 09 00 04 00 a3 0a 00 03 00 00 a3 0b 00 17 00 00 07 21 a3 22 00 05 de d0 cd b0 c8 fc 90 f3 1a a3 23 00 04 82 10 a3 24 00 04 00 00 a3 0c 00 17 00 00 07 21 a3 22 00 05 d1 85 cc 83 ac b4 93 d1 42 a3 23 00 04 82 01 a3 24 00 04 00 00 a3 0d 00 14 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 a2 a6 6f 7a 78 c7 b8 92 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 eb 8a 46 f7 bd ff 6a 37 f1 39 84 2d 94 37 fe 59 5b 3d 12 ef 03 d0 c4 7e 08 1a 81 e7 a8 5b a2 e0 73 33 9b ad 1b 46 17 55 e6 50 f4 6f 49 7a 3e 07 09 6a d3 06 4a 39 b3 83 68 e1 19 0f 4c a0 24 82 a2 3d cb 34 16 70 69 e9 13 33 86 9f ec 02 f5 0a c1 a5 0e 68 f3 7a aa 5c f6 80 2e 82 51 d5 be e2 67 69 a9 7e 86 64 dc 1f 8d 7b bd 72 06 75 d9 b4 63 0a 76 da 5b e4 74 5a f7 ca 2a 6c 9b 03 26 01 39 00 fa f2 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 02 00 17 32 00 00 05 42 00 00 00 02 34 d1 80 d1 80 80 a1 f7 ff 01 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (31 bytes): 72 02 00 17 32 00 00 05 42 00 00 00 02 34 d1 80 d1 80 80 a1 f7 ff 01 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
WARNING:s7.connection:SetupSession: PLC returned error 71171969
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
DEBUG:s7.client:S7CommPlus session setup not OK, disconnecting
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 04 d4 00 00 00 03 00 00 03 9b 36 00 00 00 00 72 01 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:s7.client:S7CommPlus not available, using legacy S7 for 192.168.0.1:102
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
Traceback (most recent call last):
  File "\python-snap7-research-harpo-port-incomplete\main.py", line 8, in <module>
    variables = client.browse()
  File "\python-snap7-research-harpo-port-incomplete\s7\client.py", line 302, in browse
    raise RuntimeError("browse() requires S7CommPlus connection")
RuntimeError: browse() requires S7CommPlus connection

f80dab3.zip

Inside S7CommPlus Structs, attributes are encoded as VLQ(absolute_id)
without the 0xA3 PObject tag prefix. We were emitting 0xA3 + relative
IDs (9, 10, ...) but the PLC expects VLQ-encoded absolute IDs
(1801, 1802, ...). Also fix SecurityLevel type: USINT not UINT.

Diagnosed from xBiggs's error 71171969 — the PLC couldn't parse
our malformed struct.
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs Found the bug — the SecurityKey struct was using PObject-style attribute encoding (0xA3 tag + relative IDs) but inside a Struct the protocol expects VLQ(absolute_id) without any tag prefix. Fixed in bbcaf6f.

Can you test once more? Same install command:

pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 12, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 12 a0 7f a8 ec a7 5d f6 1f c6 1d a9 e1 b6 c4 c3 d3 b7 80 30 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 12 a0 7f a8 ec a7 5d f6 1f c6 1d a9 e1 b6 c4 c3 d3 b7 80 30 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 12 a0 7f a8 ec a7 5d f6 1f c6 1d a9 e1 b6 c4 c3 d3 b7 80 30 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): 12a07fa8eca75df61fc61da9e1b6c4c3d3b78030
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (388 bytes): 72 02 01 7c 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 82 01 8e 24 00 04 00 00 8e 0d 00 14 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 69 95 17 70 88 69 c3 e9 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 69 c4 b7 75 a2 40 2d 1f ef e8 e5 78 8c 07 9c 8d 1b 86 17 61 d1 d2 d2 4a a4 a4 8d b8 19 5e 0a 14 b5 85 30 36 5f c8 6e d3 dc 28 7e 0e 74 c3 d1 8f 33 a6 4f 1e 2d dd f4 c4 3c 48 07 9c 09 bc f6 c7 70 33 31 d1 96 2e 48 55 1e ec 52 78 8f 74 fa 1a ea 48 35 12 a1 81 59 60 e7 e4 36 29 ea 62 e0 14 ce 4e 02 9d 9e d2 15 3d 03 da bd 19 83 88 74 43 eb 72 7e 4c 70 78 98 bf 06 73 cf 96 15 cd 4f 5e 2f d6 e4 b2 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 395 bytes: 03 00 01 8b 02 f0 80 72 02 01 7c 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 82 01 8e 24 00 04 00 00 8e 0d 00 14 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 69 95 17 70 88 69 c3 e9 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 69 c4 b7 75 a2 40 2d 1f ef e8 e5 78 8c 07 9c 8d 1b 86 17 61 d1 d2 d2 4a a4 a4 8d b8 19 5e 0a 14 b5 85 30 36 5f c8 6e d3 dc 28 7e 0e 74 c3 d1 8f 33 a6 4f 1e 2d dd f4 c4 3c 48 07 9c 09 bc f6 c7 70 33 31 d1 96 2e 48 55 1e ec 52 78 8f 74 fa 1a ea 48 35 12 a1 81 59 60 e7 e4 36 29 ea 62 e0 14 ce 4e 02 9d 9e d2 15 3d 03 da bd 19 83 88 74 43 eb 72 7e 4c 70 78 98 bf 06 73 cf 96 15 cd 4f 5e 2f d6 e4 b2 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 02 00 17 32 00 00 05 42 00 00 00 02 34 d1 80 d1 a0 80 98 a9 ff e2 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (31 bytes): 72 02 00 17 32 00 00 05 42 00 00 00 02 34 d1 80 d1 a0 80 98 a9 ff e2 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
WARNING:s7.connection:SetupSession: PLC returned error 6530527488
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
DEBUG:s7.client:S7CommPlus session setup not OK, disconnecting
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 04 d4 00 00 00 03 00 00 03 9b 36 00 00 00 00 72 01 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:s7.client:S7CommPlus not available, using legacy S7 for 192.168.0.1:102
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
Traceback (most recent call last):
  File "\python-snap7-research-harpo-port-incomplete-bbcaf6f\main.py", line 8, in <module>
    variables = client.browse()
  File "\python-snap7-research-harpo-port-incomplete-bbcaf6f\s7\client.py", line 302, in browse
    raise RuntimeError("browse() requires S7CommPlus connection")
RuntimeError: browse() requires S7CommPlus connection

bbcaf6f.zip

Two remaining byte-level mismatches vs the HarpoS7 PoC template:
1. BLOB inside Struct has an extra 0x00 before the VLQ length
2. Symmetric key flags in the SetMulti struct use flags | 0x10000
   (e.g. 0x10101 for S7-1200, not 0x101)
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs Two more byte-level fixes in 1253598:

  1. Extra 0x00 padding byte in the BLOB length encoding
  2. Symmetric key flags need bit 16 set (0x10101 instead of 0x101)

Same install:

pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 12, 2026

Looks like the connection setup completes now, but browse() is returning an empty list.

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 db c2 2d 7c 62 6b f2 5a cc ed 05 4f 44 57 a8 05 f4 e9 76 b0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 db c2 2d 7c 62 6b f2 5a cc ed 05 4f 44 57 a8 05 f4 e9 76 b0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 db c2 2d 7c 62 6b f2 5a cc ed 05 4f 44 57 a8 05 f4 e9 76 b0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): dbc22d7c626bf25acced054f4457a805f4e976b0
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (390 bytes): 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 a1 77 f1 8b 2c 92 49 70 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 cb cd 6d 09 c5 7a 9e 40 a2 54 2c 3c 1f 67 38 59 16 ec d5 3a 1d 8f b5 c6 7a 48 c0 4d 26 9a 04 48 c4 16 42 8b fd 86 63 d5 cf 63 1d fa cb c0 7f 0c 80 6d 16 1d 05 6f fb ea 2b 3d 82 53 ca 00 e6 79 55 d0 ca 9a b7 6a 05 b5 5e 08 7c 10 9c d6 77 1c cb e1 24 01 cb ae 11 85 63 10 e2 41 2f f4 f2 12 44 5b fb e1 8b ce 81 65 0c b4 c3 58 93 dd 95 08 36 9d f6 6b 0a 3d 75 b4 4a b2 d8 4a a2 2c 07 1a 58 20 47 34 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 397 bytes: 03 00 01 8d 02 f0 80 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 a1 77 f1 8b 2c 92 49 70 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 cb cd 6d 09 c5 7a 9e 40 a2 54 2c 3c 1f 67 38 59 16 ec d5 3a 1d 8f b5 c6 7a 48 c0 4d 26 9a 04 48 c4 16 42 8b fd 86 63 d5 cf 63 1d fa cb c0 7f 0c 80 6d 16 1d 05 6f fb ea 2b 3d 82 53 ca 00 e6 79 55 d0 ca 9a b7 6a 05 b5 5e 08 7c 10 9c d6 77 1c cb e1 24 01 cb ae 11 85 63 10 e2 41 2f f4 f2 12 44 5b fb e1 8b ce 81 65 0c b4 c3 58 93 dd 95 08 36 9d f6 6b 0a 3d 75 b4 4a b2 d8 4a a2 2c 07 1a 58 20 47 34 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=32 payload (28 bytes): 02 f0 80 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (25 bytes): 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
INFO:s7.connection:Session setup completed successfully
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
INFO:s7.client:Connected to 192.168.0.1:102 using S7CommPlus
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=3 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (13 bytes): 03 00 01 00 02 81 69 93 59 00 00 00 00
DEBUG:s7.connection:  Full frame (35 bytes): 72 01 00 1b 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 42 bytes: 03 00 00 2a 02 f0 80 72 01 00 1b 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=209 payload (205 bytes): 02 f0 80 72 fe 00 c6 00 00 00 00 00 00 02 63 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 00 00 00 00 9d 70 00 00 00 03 00 00 00 00 9d 71 00 00 00 09 a2 01 4c 00 2a 08 fe a0 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 05 42 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 00 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 00 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (202 bytes): 72 fe 00 c6 00 00 00 00 00 00 02 63 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 00 00 00 00 9d 70 00 00 00 03 00 00 00 00 9d 71 00 00 00 09 a2 01 4c 00 2a 08 fe a0 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 05 42 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 00 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 00 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:  Frame header: version=V254, data_length=198, header_size=4
DEBUG:s7.connection:  Response data (198 bytes): 00 00 00 00 00 00 02 63 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 00 00 00 00 9d 70 00 00 00 03 00 00 00 00 9d 71 00 00 00 09 a2 01 4c 00 2a 08 fe a0 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 05 42 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 00 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 00 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:  Response header: opcode=0x00 function=0x0000 seq=25344 session=0x00000000 transport=0x00
DEBUG:s7.connection:  Response payload (184 bytes): 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 00 00 00 00 9d 70 00 00 00 03 00 00 00 00 9d 71 00 00 00 09 a2 01 4c 00 2a 08 fe a0 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 05 42 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 00 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 00 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00   
[]
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 04 d4 00 00 00 04 00 00 03 9b 36 00 00 00 00 72 01 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:snap7.client:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.client:Disconnected from 192.168.0.1:102

1253598.zip

@gijzelaerr
Copy link
Copy Markdown
Owner Author

The SessionKey handshake works! Session setup completed successfully — the PLC accepted the blob and kept the connection alive. That's the core problem from #710 solved.

The empty browse() is a separate issue — the EXPLORE response comes back with version=V254 (0xFE) which is a different frame format our parser doesn't handle yet. That's not auth-related, it's response parsing. I'll investigate that next.

Thanks for the patience testing all these iterations!

After the SessionKey handshake succeeds, all data operations must
use V3 framing with a 32-byte HMAC-SHA256 digest prefix. Without
this, the PLC sends V254 non-responses and may RST the connection.

V3 frame format:
  72 03 [data_len] [0x20] [32-byte HMAC] [standard payload]
  trailer: 72 03 00 00

The HMAC is computed as HMAC-SHA256(session_key[:24], payload).
Confirmed from TIA Portal v19 pcaps captured by xBiggs.

Also handle V3 response frames by stripping the HMAC prefix
before parsing the standard response header.
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs ac84da7 adds V3 HMAC integrity framing. After the SessionKey handshake, all data ops now use V3 frames with a 32-byte HMAC-SHA256 prefix (computed from the session key), matching what TIA Portal does in your ProgramBlocks pcap.

Can you test again?

pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete

This should fix the V254 responses — the PLC was rejecting our unsigned V1 requests after auth.

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 13, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 0f 57 b9 49 85 d1 89 76 4f 23 ba 01 e4 84 fe a4 a4 43 9f a0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 0f 57 b9 49 85 d1 89 76 4f 23 ba 01 e4 84 fe a4 a4 43 9f a0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 0f 57 b9 49 85 d1 89 76 4f 23 ba 01 e4 84 fe a4 a4 43 9f a0 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): 0f57b94985d189764f23ba01e484fea4a4439fa0
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (390 bytes): 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 c1 b3 05 32 2a f2 38 98 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 dc b5 a6 64 45 b4 1c 14 f6 26 69 59 63 e5 76 cb 07 0b ff c7 32 12 e9 af 5f f9 58 7e 5f 13 70 7e 95 93 1d 75 92 f9 d4 09 25 4c 4d 15 93 84 9d 88 79 0b d4 c0 cb 27 95 34 cd 47 75 4e 6a 66 0a 4d 0c 65 56 b2 f0 8d 1d 77 40 fc 19 e4 3a ba 61 e4 5c 49 fb 08 13 bb 54 da f9 e9 6d e8 1f 24 a8 26 54 90 7e a4 0a 0e e2 2f 90 d0 8f 79 38 a0 83 b1 3d 4e c5 c0 b7 14 56 7c 71 2b 2f 9f 58 9a 6a 49 fb 3b 24 27 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 397 bytes: 03 00 01 8d 02 f0 80 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 c1 b3 05 32 2a f2 38 98 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 dc b5 a6 64 45 b4 1c 14 f6 26 69 59 63 e5 76 cb 07 0b ff c7 32 12 e9 af 5f f9 58 7e 5f 13 70 7e 95 93 1d 75 92 f9 d4 09 25 4c 4d 15 93 84 9d 88 79 0b d4 c0 cb 27 95 34 cd 47 75 4e 6a 66 0a 4d 0c 65 56 b2 f0 8d 1d 77 40 fc 19 e4 3a ba 61 e4 5c 49 fb 08 13 bb 54 da f9 e9 6d e8 1f 24 a8 26 54 90 7e a4 0a 0e e2 2f 90 d0 8f 79 38 a0 83 b1 3d 4e c5 c0 b7 14 56 7c 71 2b 2f 9f 58 9a 6a 49 fb 3b 24 27 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=32 payload (28 bytes): 02 f0 80 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (25 bytes): 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
INFO:s7.connection:Session setup completed successfully
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
INFO:s7.client:Connected to 192.168.0.1:102 using S7CommPlus
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=3 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (13 bytes): 03 00 01 00 02 81 69 93 59 00 00 00 00
DEBUG:s7.connection:  Full frame (68 bytes): 72 03 00 3c 20 5c 6a 33 29 27 34 0d ad 20 3b e8 1c aa 1a 58 d5 c8 4b 7b e8 df 31 75 d8 0e 43 bd c4 3b ce 52 b5 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Sent 75 bytes: 03 00 00 4b 02 f0 80 72 03 00 3c 20 5c 6a 33 29 27 34 0d ad 20 3b e8 1c aa 1a 58 d5 c8 4b 7b e8 df 31 75 d8 0e 43 bd c4 3b ce 52 b5 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=209 payload (205 bytes): 02 f0 80 72 fe 00 c6 00 00 00 00 00 00 02 84 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 03 00 00 9d 71 00 00 00 09 a2 01 3b 00 01 39 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (202 bytes): 72 fe 00 c6 00 00 00 00 00 00 02 84 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 03 00 00 9d 71 00 00 00 09 a2 01 3b 00 01 39 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 12 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:  Frame header: version=V254, data_length=198, header_size=4
DEBUG:s7.connection:  V254 frame: returning raw data (198 bytes)
[]
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 04 d4 00 00 00 04 00 00 03 9b 36 00 00 00 00 72 01 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:snap7.client:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.client:Disconnected from 192.168.0.1:102

ac84da7.zip

@gijzelaerr
Copy link
Copy Markdown
Owner Author

V3 HMAC framing works — the PLC accepted the signed EXPLORE request (no RST). But the V254 response is actually the PLC's native format for EXPLORE on V1-initial firmware. Looking at your TIA Portal ProgramBlocks pcap, TIA doesn't use EXPLORE at all — it uses GET_VAR_SUBSTREAMED (0x0586) for browsing.

So the connection and framing are fully working now. The remaining browse() issue is that we need to use GET_VAR_SUBSTREAMED instead of EXPLORE for V1-initial PLCs, which is a separate change.

For the immediate fix: can you test db_read() or other data operations? Those should work now with the V3 HMAC framing:

import logging
logging.basicConfig(level=logging.DEBUG)
from s7 import Client

client = Client()
client.connect("192.168.0.1", 0, 1)
print("protocol:", client.protocol)
data = client.db_read(7, 0, 2)
print("db7[0:2]:", data)
client.disconnect()

V1-initial PLCs expect a 4-byte big-endian InObjectId in the EXPLORE
payload instead of a VLQ-encoded explore_id. Match the format observed
in TIA Portal v19 pcaps: InObjectId + fixed params + sequence byte.

Default explore target is 0x38 (system object for PLC program tree),
matching what TIA Portal sends.
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs ef9aee0 fixes the EXPLORE payload format. Our code was sending a VLQ-encoded explore_id, but V1-initial PLCs expect a 4-byte big-endian InObjectId (like TIA Portal sends). Also targets object 0x38 (system PLC program tree) instead of root.

pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete

Try both explore() and browse():

import logging
logging.basicConfig(level=logging.DEBUG)
from s7 import Client

client = Client()
client.connect("192.168.0.1", 0, 1)
print("explore:", client.explore())
print("browse:", client.browse())
client.disconnect()

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 13, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 23 c4 91 23 89 54 04 bb 88 35 a6 4f 18 ab 0d 7c 71 cf 24 c4 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 23 c4 91 23 89 54 04 bb 88 35 a6 4f 18 ab 0d 7c 71 cf 24 c4 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 23 c4 91 23 89 54 04 bb 88 35 a6 4f 18 ab 0d 7c 71 cf 24 c4 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): 23c49123895404bb8835a64f18ab0d7c71cf24c4
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (390 bytes): 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 21 93 db fd f7 a6 ca 34 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 4a 2a 34 e2 75 ac ff 2c cd 10 b7 f5 db c3 25 78 a0 6f 16 7d 64 a0 be 74 ef 27 2a 52 c8 05 bf 0b 35 68 a9 f7 f1 c8 5c 95 6a 53 ed ce 4f d3 a9 27 1d ec a1 00 95 3d e6 17 39 aa 39 27 b7 e8 2b d0 70 88 b0 7d 86 d3 24 ec ca 7d bf 34 e8 a3 27 20 6d ff 08 1d 02 f8 d5 e4 e7 af 20 50 11 fe 00 f1 05 f4 9c a6 fb 42 09 b6 55 e6 48 55 98 dc fc bc 91 79 1d 49 cb 66 bf 33 16 cc 22 45 cf 35 65 2c 5c 10 4e a5 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 397 bytes: 03 00 01 8d 02 f0 80 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 21 93 db fd f7 a6 ca 34 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 4a 2a 34 e2 75 ac ff 2c cd 10 b7 f5 db c3 25 78 a0 6f 16 7d 64 a0 be 74 ef 27 2a 52 c8 05 bf 0b 35 68 a9 f7 f1 c8 5c 95 6a 53 ed ce 4f d3 a9 27 1d ec a1 00 95 3d e6 17 39 aa 39 27 b7 e8 2b d0 70 88 b0 7d 86 d3 24 ec ca 7d bf 34 e8 a3 27 20 6d ff 08 1d 02 f8 d5 e4 e7 af 20 50 11 fe 00 f1 05 f4 9c a6 fb 42 09 b6 55 e6 48 55 98 dc fc bc 91 79 1d 49 cb 66 bf 33 16 cc 22 45 cf 35 65 2c 5c 10 4e a5 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=32 payload (28 bytes): 02 f0 80 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (25 bytes): 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
INFO:s7.connection:Session setup completed successfully
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
INFO:s7.client:Connected to 192.168.0.1:102 using S7CommPlus
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=3 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (16 bytes): 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00
DEBUG:s7.connection:  Full frame (71 bytes): 72 03 00 3f 20 f6 b3 e1 71 92 2d db b3 09 2a 31 1e f1 4a 82 c8 5a 70 d2 f9 07 75 bd ad 3f 56 0f 4a 81 cf 55 8b 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Sent 78 bytes: 03 00 00 4e 02 f0 80 72 03 00 3f 20 f6 b3 e1 71 92 2d db b3 09 2a 31 1e f1 4a 82 c8 5a 70 d2 f9 07 75 bd ad 3f 56 0f 4a 81 cf 55 8b 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=430 payload (426 bytes): 02 f0 80 72 03 01 9f 20 49 e3 67 b4 a3 b5 6c 35 9a 37 cb ee 9e 0c 10 b0 c5 4b 14 67 ba 6d 66 d1 80 6a 79 7e 4c ac 23 73 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00 72 03 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (423 bytes): 72 03 01 9f 20 49 e3 67 b4 a3 b5 6c 35 9a 37 cb ee 9e 0c 10 b0 c5 4b 14 67 ba 6d 66 d1 80 6a 79 7e 4c ac 23 73 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00 72 03 00 00
DEBUG:s7.connection:  Frame header: version=V3, data_length=415, header_size=4
DEBUG:s7.connection:  V3 HMAC (32 bytes): 49e367b4a3b56c359a37cbee9e0c10b0c54b1467ba6d66d1806a797e4cac2373
DEBUG:s7.connection:  Response data (382 bytes): 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00
DEBUG:s7.connection:  Response header: opcode=0x32 function=0x04BB seq=3 session=0x34000000 transport=0x00
DEBUG:s7.connection:  Response payload (368 bytes): 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00
DEBUG:s7.connection:  Trailer (4 bytes): 72 03 00 00
explore:  b'\x03\r\xa1\x00\x00\x00\x01\x93.0\x00\xa3\x81i\x00\x15\x06ASRoot\xa3\x93\x15\x00\x05\x00\xa3\x93\x16\x00\x04\x00\xa3\x93\x1b\x10\x02\x08\x13\x00\x00\x00\x0e\x00\x00\x00\xa3\xa3R\x00\x14\x00\x00\xa3\xbeV\x10\x02\x04\x00\x00\x00\x00\xa3\x93/\x00\x04\x00\xa3\x93\x1c\x00\x15\x0bAOM.R506_02\xa3\x93\x1d\x00\x15\x0805.06.01\xa3\x93\x1e\x00\x15\x0b05.06.02.04\xa3\x93\x1f\x00\x15\x0fPAOM.R506_04_04\xa3\x9e<\x10\x02\x10r\x01\xa7\x89 \xbbu@\x8a\xea\x1b\xee9l\xe7\x9b\xa1\x00\x00\x00\x03\x93X0\x00\xa3\x81i\x00\x15\nPLCProgram\xa3\x93\x15\x00\x05\x8c\xaa\xbc\xca\xff\xac\x91\xd8d\xa3\x93\x16\x00\x04\x00\xa3\x9c3\x00\x01\x01\xa3\x9d"\x00\x05\x00\xa3\xa1/\x00\x03\x04\t\xa3\xa3?\x10\x03\x02\xea`\xeeG\xa3\xa4\x14\x00\x01\x00\xa3\x93/\x00\x042\xa3\xbb&\x00\x0c\x00\x00\x00\x00\xa3\xbc1\x00\x0c\x00\x00\x00\x00\xa3\xbc2\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\xa1\x00\x00\x008\xbeh0\x00\xa3\x93\x15\x00\x05\x00\xa3\x93\x16\x00\x04\x00\xa3\xc09 \x03\x01\x04\t\xa3\xbfx\x10\x02\x08\ta\xc6\xdc\xdb\xb8\xca\xbf\xa3\xbfz\x10\x02\x08\x9c\x9f\xd3\xc5=\xf07\x82\xa3\xbf|\x00\x052\xa3\xbfy\x00\x01\x00\xa3\xbf{\x00\x052\xa3\xbf}\x00\x01\x00\xa2\xa2\xa2\x00\x00\x00\x00'
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=4 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (13 bytes): 03 00 01 00 02 81 69 93 59 00 00 00 00
DEBUG:s7.connection:  Full frame (68 bytes): 72 03 00 3c 20 5c f3 32 f6 bf 7f 9a 02 e2 c9 93 b1 e5 d9 9f f2 c1 f6 3f e5 1e 2e 8b c4 c1 8d 26 d9 f6 25 75 97 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Sent 75 bytes: 03 00 00 4b 02 f0 80 72 03 00 3c 20 5c f3 32 f6 bf 7f 9a 02 e2 c9 93 b1 e5 d9 9f f2 c1 f6 3f e5 1e 2e 8b c4 c1 8d 26 d9 f6 25 75 97 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36 03 00 01 00 02 81 69 93 59 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=209 payload (205 bytes): 02 f0 80 72 fe 00 c6 00 00 00 00 00 00 02 c3 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 04 00 00 9d 71 00 00 00 09 a2 01 3b 00 01 39 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 11 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (202 bytes): 72 fe 00 c6 00 00 00 00 00 00 02 c3 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 04 00 00 9d 71 00 00 00 09 a2 01 3b 00 01 39 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 11 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:  Frame header: version=V254, data_length=198, header_size=4
DEBUG:s7.connection:  V254 frame: returning raw data (198 bytes)
browse:  []
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 04 d4 00 00 00 05 00 00 03 9b 36 00 00 00 00 72 01 00 00
INFO:snap7.connection:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:snap7.client:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.client:Disconnected from 192.168.0.1:102

ef9aee0.zip

The first explore() call worked with V3 format, but list_datablocks()
and browse() still used the old VLQ-based _build_explore_request.
Route all EXPLORE calls through _build_explore_payload_v3 when a
session key is present.
@gijzelaerr
Copy link
Copy Markdown
Owner Author

@xBiggs Great news — the first EXPLORE returned real data (ASRoot, PLCProgram with proper PObject tags)! The second call (from list_datablocks) was still using the old payload format. Fixed in 2b78b3c — all EXPLORE calls now use V3 format.

pip install --force-reinstall git+https://github.com/gijzelaerr/python-snap7.git@research/harpo-port-incomplete

@xBiggs
Copy link
Copy Markdown

xBiggs commented May 13, 2026

DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=16
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:s7.connection:=== InitSSL === sending (26 bytes): 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 33 bytes: 03 00 00 21 02 f0 80 72 01 00 12 31 00 00 05 b3 00 00 00 00 00 00 00 00 30 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=38 payload (34 bytes): 02 f0 80 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== InitSSL === received (31 bytes): 72 01 00 17 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:InitSSL response: version=V1, data_length=23
DEBUG:s7.connection:InitSSL response body (27 bytes): 32 00 00 05 a9 00 00 00 00 34 d1 80 b3 a0 80 86 b9 ff 89 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === sending (192 bytes): 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Sent 199 bytes: 03 00 00 c7 02 f0 80 72 01 00 b8 31 00 00 04 ca 00 00 00 01 00 00 01 20 36 00 00 01 1d 00 04 00 00 00 00 00 a1 00 00 00 d3 82 1f 00 00 a3 81 69 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 21 00 15 15 31 3a 3a 3a 36 2e 30 3a 3a 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 28 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 29 00 15 00 a3 82 2a 00 15 0c 70 79 74 68 6f 6e 2d 73 6e 61 70 37 a3 82 2b 00 04 01 a3 82 2c 00 12 80 c3 c9 01 a3 82 2d 00 15 00 a1 00 00 00 d3 81 7f 00 00 a3 81 69 00 15 15 53 75 62 73 63 72 69 70 74 69 6f 6e 43 6f 6e 74 61 69 6e 65 72 a2 a2 00 00 00 00 72 01 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=215 payload (211 bytes): 02 f0 80 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 7e 69 4b 14 13 52 36 4b 72 63 c9 01 8a 2e f7 ff 62 c4 3c c5 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:=== CreateObject === received (208 bytes): 72 01 00 c8 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 7e 69 4b 14 13 52 36 4b 72 63 c9 01 8a 2e f7 ff 62 c4 3c c5 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response: version=V1, data_length=200
DEBUG:s7.connection:CreateObject response body (204 bytes): 32 00 00 04 ca 00 00 00 01 36 00 02 87 1b 87 42 a1 00 00 01 20 82 1f 00 00 a3 81 69 00 15 13 30 31 3a 42 44 34 32 36 42 30 39 31 46 30 38 37 33 31 41 a3 82 2b 00 04 82 80 80 80 02 a3 82 2d 00 15 10 4f 4d 53 50 2e 52 45 4c 2e 38 30 38 39 2e 32 31 a3 82 2f 10 02 14 7e 69 4b 14 13 52 36 4b 72 63 c9 01 8a 2e f7 ff 62 c4 3c c5 a3 82 32 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 1b 31 3b 36 45 53 37 20 32 31 35 2d 31 42 47 34 30 2d 30 58 42 30 20 3b 56 34 2e 32 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 a2 00 00 00 00 72 01 00 00
DEBUG:s7.connection:CreateObject response header: opcode=0x32 function=0x04CA seq=1 transport=0x36
DEBUG:s7.connection:CreateObject response: return_value=0 object_ids=['0x39b', '0x3c2']
DEBUG:s7.connection:Session created: id=0x0000039B (923), version=V1
INFO:s7.connection:Public key fingerprint captured: 01:BD426B091F08731A
INFO:s7.connection:Session challenge captured (20 bytes): 7e694b141352364b7263c9018a2ef7ff62c43cc5
INFO:s7.connection:ServerSessionVersion struct captured (84 bytes)
INFO:s7.connection:SessionKey auth blob generated (180 bytes)
INFO:s7.connection:SecurityKey blob included in session setup
DEBUG:s7.connection:=== SetupSession === sending (390 bytes): 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 14 d3 46 bb 29 a4 ae e5 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 a2 e8 75 23 89 32 b3 c8 6b fa 01 77 a3 14 d6 57 3e 34 17 36 af 2c 3f d3 af 86 5e 06 5c 11 31 55 f7 15 d7 c1 2b 1b dc 60 8f 84 1f 6e 1c 45 2b 11 46 18 3c 7a 75 56 aa 0d f2 47 b9 cc 10 70 42 9e 5d 5a b7 2c 00 83 67 78 90 f3 16 88 48 79 99 0d 21 56 46 85 14 11 30 22 03 6c 52 90 34 6f 8c fe 33 f3 3b a4 20 32 5f 4b 05 43 23 af 7c e2 c3 4b 50 63 60 d3 91 24 09 7e 1d aa fe 85 29 5d c5 77 e9 56 3d 09 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Sent 397 bytes: 03 00 01 8d 02 f0 80 72 02 01 7e 31 00 00 05 42 00 00 00 02 00 00 03 9b 34 00 00 03 9b 02 02 8e 26 82 32 01 00 17 00 00 07 08 8e 09 00 04 00 8e 0a 00 02 00 8e 0b 00 17 00 00 07 21 8e 22 00 05 de d0 cd b0 c8 fc 90 f3 1a 8e 23 00 04 82 10 8e 24 00 04 00 00 8e 0c 00 17 00 00 07 21 8e 22 00 05 d1 85 cc 83 ac b4 93 d1 42 8e 23 00 04 84 82 01 8e 24 00 04 00 00 8e 0d 00 14 00 81 34 ad de e1 fe b4 00 00 00 01 00 00 00 01 00 00 00 14 d3 46 bb 29 a4 ae e5 01 01 00 00 00 00 00 00 1a 73 08 1f 09 6b 42 bd 10 01 00 00 00 00 00 00 a2 e8 75 23 89 32 b3 c8 6b fa 01 77 a3 14 d6 57 3e 34 17 36 af 2c 3f d3 af 86 5e 06 5c 11 31 55 f7 15 d7 c1 2b 1b dc 60 8f 84 1f 6e 1c 45 2b 11 46 18 3c 7a 75 56 aa 0d f2 47 b9 cc 10 70 42 9e 5d 5a b7 2c 00 83 67 78 90 f3 16 88 48 79 99 0d 21 56 46 85 14 11 30 22 03 6c 52 90 34 6f 8c fe 33 f3 3b a4 20 32 5f 4b 05 43 23 af 7c e2 c3 4b 50 63 60 d3 91 24 09 7e 1d aa fe 85 29 5d c5 77 e9 56 3d 09 00 02 00 17 00 00 01 3a 82 3b 00 04 84 00 82 3c 00 04 84 00 82 3d 00 04 84 80 c2 00 82 3e 00 04 84 80 c2 00 82 3f 00 15 00 82 40 00 15 06 32 3b 31 30 38 32 82 41 00 03 00 03 00 00 00 00 04 e8 89 69 00 12 00 00 00 00 89 6a 00 13 00 89 6b 00 04 00 00 00 00 00 00 72 02 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=32 payload (28 bytes): 02 f0 80 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:=== SetupSession === received (25 bytes): 72 02 00 11 32 00 00 05 42 00 00 00 02 34 00 00 00 00 00 00 00 72 02 00 00
DEBUG:s7.connection:SetupSession response: function=0x0542
INFO:s7.connection:Session setup completed successfully
INFO:s7.connection:S7CommPlus connected to 192.168.0.1:102, version=V1, session=923, tls=False
INFO:s7.client:Connected to 192.168.0.1:102 using S7CommPlus
INFO:snap7.client:S7Client initialized (pure Python implementation)
DEBUG:snap7.connection:TCP connected to 192.168.0.1:102
DEBUG:snap7.connection:Sent COTP Connection Request
DEBUG:snap7.connection:Negotiated PDU size: 1024
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc1, length=2
DEBUG:snap7.connection:Unsupported COTP parameter: code=0xc2, length=2
DEBUG:snap7.connection:Received COTP Connection Confirm
INFO:snap7.connection:Connected to 192.168.0.1:102, PDU size: 1024
DEBUG:snap7.connection:Sent 25 bytes: 03 00 00 19 02 f0 80 32 01 00 00 00 01 00 08 00 00 f0 00 00 01 00 01 01 e0
DEBUG:snap7.connection:Received TPKT: version=3 length=27 payload (23 bytes): 02 f0 80 32 03 00 00 00 01 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
INFO:snap7.client:Negotiated PDU length: 240
INFO:snap7.client:[192.168.0.1 R0/S1] Connected to 192.168.0.1:102 rack 0 slot 1
INFO:snap7.client:Auto-tuned max_parallel=2 (PDU=240)
INFO:s7.client:Legacy S7 connected to 192.168.0.1:102
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=3 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (16 bytes): 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00
DEBUG:s7.connection:  Full frame (71 bytes): 72 03 00 3f 20 37 09 80 43 76 91 0e c5 76 09 6e c7 d6 ac 45 4b 35 4f f9 8e ac 6e fa 3f dd ae 46 a1 92 52 d3 9d 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Sent 78 bytes: 03 00 00 4e 02 f0 80 72 03 00 3f 20 37 09 80 43 76 91 0e c5 76 09 6e c7 d6 ac 45 4b 35 4f f9 8e ac 6e fa 3f dd ae 46 a1 92 52 d3 9d 31 00 00 04 bb 00 00 00 03 00 00 03 9b 36 00 00 00 38 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=430 payload (426 bytes): 02 f0 80 72 03 01 9f 20 a5 a5 8f f8 3d 48 aa ff db a9 df 42 a1 95 26 2e 2b 6b 75 94 1e 02 db 05 8c 80 59 4f 1f e1 af b6 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00 72 03 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (423 bytes): 72 03 01 9f 20 a5 a5 8f f8 3d 48 aa ff db a9 df 42 a1 95 26 2e 2b 6b 75 94 1e 02 db 05 8c 80 59 4f 1f e1 af b6 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00 72 03 00 00
DEBUG:s7.connection:  Frame header: version=V3, data_length=415, header_size=4
DEBUG:s7.connection:  V3 HMAC (32 bytes): a5a58ff83d48aaffdba9df42a195262e2b6b75941e02db058c80594f1fe1afb6
DEBUG:s7.connection:  Response data (382 bytes): 32 00 00 04 bb 00 00 00 03 34 00 00 00 00 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00
DEBUG:s7.connection:  Response header: opcode=0x32 function=0x04BB seq=3 session=0x34000000 transport=0x00
DEBUG:s7.connection:  Response payload (368 bytes): 03 0d a1 00 00 00 01 93 2e 30 00 a3 81 69 00 15 06 41 53 52 6f 6f 74 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 93 1b 10 02 08 13 00 00 00 0e 00 00 00 a3 a3 52 00 14 00 00 a3 be 56 10 02 04 00 00 00 00 a3 93 2f 00 04 00 a3 93 1c 00 15 0b 41 4f 4d 2e 52 35 30 36 5f 30 32 a3 93 1d 00 15 08 30 35 2e 30 36 2e 30 31 a3 93 1e 00 15 0b 30 35 2e 30 36 2e 30 32 2e 30 34 a3 93 1f 00 15 0f 50 41 4f 4d 2e 52 35 30 36 5f 30 34 5f 30 34 a3 9e 3c 10 02 10 72 01 a7 89 20 bb 75 40 8a ea 1b ee 39 6c e7 9b a1 00 00 00 03 93 58 30 00 a3 81 69 00 15 0a 50 4c 43 50 72 6f 67 72 61 6d a3 93 15 00 05 8c aa bc ca ff ac 91 d8 64 a3 93 16 00 04 00 a3 9c 33 00 01 01 a3 9d 22 00 05 00 a3 a1 2f 00 03 04 09 a3 a3 3f 10 03 02 ea 60 ee 47 a3 a4 14 00 01 00 a3 93 2f 00 04 32 a3 bb 26 00 0c 00 00 00 00 a3 bc 31 00 0c 00 00 00 00 a3 bc 32 00 10 00 00 00 00 00 00 00 00 a1 00 00 00 38 be 68 30 00 a3 93 15 00 05 00 a3 93 16 00 04 00 a3 c0 39 20 03 01 04 09 a3 bf 78 10 02 08 09 61 c6 dc db b8 ca bf a3 bf 7a 10 02 08 9c 9f d3 c5 3d f0 37 82 a3 bf 7c 00 05 32 a3 bf 79 00 01 00 a3 bf 7b 00 05 32 a3 bf 7d 00 01 00 a2 a2 a2 00 00 00 00
DEBUG:s7.connection:  Trailer (4 bytes): 72 03 00 00
explore:  b'\x03\r\xa1\x00\x00\x00\x01\x93.0\x00\xa3\x81i\x00\x15\x06ASRoot\xa3\x93\x15\x00\x05\x00\xa3\x93\x16\x00\x04\x00\xa3\x93\x1b\x10\x02\x08\x13\x00\x00\x00\x0e\x00\x00\x00\xa3\xa3R\x00\x14\x00\x00\xa3\xbeV\x10\x02\x04\x00\x00\x00\x00\xa3\x93/\x00\x04\x00\xa3\x93\x1c\x00\x15\x0bAOM.R506_02\xa3\x93\x1d\x00\x15\x0805.06.01\xa3\x93\x1e\x00\x15\x0b05.06.02.04\xa3\x93\x1f\x00\x15\x0fPAOM.R506_04_04\xa3\x9e<\x10\x02\x10r\x01\xa7\x89 \xbbu@\x8a\xea\x1b\xee9l\xe7\x9b\xa1\x00\x00\x00\x03\x93X0\x00\xa3\x81i\x00\x15\nPLCProgram\xa3\x93\x15\x00\x05\x8c\xaa\xbc\xca\xff\xac\x91\xd8d\xa3\x93\x16\x00\x04\x00\xa3\x9c3\x00\x01\x01\xa3\x9d"\x00\x05\x00\xa3\xa1/\x00\x03\x04\t\xa3\xa3?\x10\x03\x02\xea`\xeeG\xa3\xa4\x14\x00\x01\x00\xa3\x93/\x00\x042\xa3\xbb&\x00\x0c\x00\x00\x00\x00\xa3\xbc1\x00\x0c\x00\x00\x00\x00\xa3\xbc2\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\xa1\x00\x00\x008\xbeh0\x00\xa3\x93\x15\x00\x05\x00\xa3\x93\x16\x00\x04\x00\xa3\xc09 \x03\x01\x04\t\xa3\xbfx\x10\x02\x08\ta\xc6\xdc\xdb\xb8\xca\xbf\xa3\xbfz\x10\x02\x08\x9c\x9f\xd3\xc5=\xf07\x82\xa3\xbf|\x00\x052\xa3\xbfy\x00\x01\x00\xa3\xbf{\x00\x052\xa3\xbf}\x00\x01\x00\xa2\xa2\xa2\x00\x00\x00\x00'
DEBUG:s7.connection:=== SEND REQUEST === function_code=0x04BB seq=4 session=0x0000039B
DEBUG:s7.connection:  Request header (14 bytes): 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36
DEBUG:s7.connection:  Request payload (16 bytes): 00 00 00 03 00 01 00 01 00 00 0a 00 00 00 00 00
DEBUG:s7.connection:  Full frame (71 bytes): 72 03 00 3f 20 68 05 65 de 50 0a d0 78 a7 d5 66 b5 2d 39 b0 41 19 99 f8 7e a0 5e 90 a0 23 c2 5d 48 04 38 55 1b 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36 00 00 00 03 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Sent 78 bytes: 03 00 00 4e 02 f0 80 72 03 00 3f 20 68 05 65 de 50 0a d0 78 a7 d5 66 b5 2d 39 b0 41 19 99 f8 7e a0 5e 90 a0 23 c2 5d 48 04 38 55 1b 31 00 00 04 bb 00 00 00 04 00 00 03 9b 36 00 00 00 03 00 01 00 01 00 00 0a 00 00 00 00 00 72 03 00 00
DEBUG:snap7.connection:Received TPKT: version=3 length=209 payload (205 bytes): 02 f0 80 72 fe 00 c6 00 00 00 00 00 00 02 c6 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 04 00 00 9d 71 00 00 00 09 a2 01 3b 00 02 36 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 11 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:=== RECV RESPONSE === raw frame (202 bytes): 72 fe 00 c6 00 00 00 00 00 00 02 c6 00 00 00 00 00 00 00 00 00 00 00 17 00 00 9d 6c 00 00 9d 6d 00 00 00 08 00 00 00 02 00 00 9d 6e 00 00 00 04 00 00 03 9b 00 00 9d 6f 00 00 00 04 00 00 04 bb 00 00 9d 70 00 00 00 03 00 04 00 00 9d 71 00 00 00 09 a2 01 3b 00 02 36 ff 88 00 00 9d 72 00 00 00 04 00 00 00 00 00 00 9d 73 00 00 00 04 00 00 04 bb 00 00 9d 74 00 00 00 04 00 00 00 e9 00 00 9d 75 00 00 00 04 00 00 00 00 00 00 9d 76 00 00 00 03 00 06 00 00 9d 77 00 00 00 03 00 00 00 00 9d 78 00 00 00 03 10 00 00 00 9d 79 00 00 00 03 10 11 00 00 9d 7a 00 00 00 03 08 01 00 00 9d 7b 00 00 00 03 10 29 00 00 00 00
DEBUG:s7.connection:  Frame header: version=V254, data_length=198, header_size=4
DEBUG:s7.connection:  V254 frame: returning raw data (198 bytes)
browse:  []
INFO:snap7.connection:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.connection:Disconnected from 192.168.0.1:102
INFO:snap7.client:Disconnected from 192.168.0.1:102
DEBUG:snap7.client:Heartbeat stopped
INFO:snap7.client:Disconnected from 192.168.0.1:102

2b78b3c.zip

@gijzelaerr
Copy link
Copy Markdown
Owner Author

Good progress. The EXPLORE to 0x38 works and returns the program tree (ASRootPLCProgram). The second EXPLORE to object 3 gets V254 — the PLC only accepts EXPLORE for certain object IDs.

The program tree doesn't contain individual DBs — those need a deeper explore. TIA Portal uses 0x8A11FFFF as the explore target for DB discovery, which is DB_ACCESS_AREA_BASE + 0x3FFFF (a wildcard). The browse flow needs to be reworked for V1-initial PLCs to use this two-level explore pattern.

The auth handshake and V3 HMAC framing are fully working now — this is purely a browse-level issue. I'll track the remaining browse work separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Research: S7CommPlus session-key handshake (P2/P3) on V1-initial S7-1200 S7-1200 Session Setup Incomplete

3 participants