-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Description
Environment
- Protocol: S7CommPlus V3 (0x03)
- Test Tools: TIA Portal V17 + S7-PLCSIM Advanced V5.0
- Code Platform: .NET/C#
- Critical Constraint: Max packet size = 1024 bytes (per TCP transmission)
Problem Description
When sending single packets ≤1024 bytes, the calculated IntegrityPart
passes PLC verification successfully. However, when sending fragmented packets (>1024 bytes) where:
- Each fragment has its own
IntegrityPart
computed - Fragments are sent sequentially according to protocol
The PLC rejects the IntegrityPart of fragmented packets despite correct session keys and protocol version handling.
Code Analysis Highlight
Key logic in SendS7plusPDUdata
method:
private int SendS7plusPDUdata(byte[] sendPduData, int bytesToSend, byte protoVersion, byte[] sessionKey)
{
m_LastError = 0;
int curSize;
int sourcePos = 0;
int sendLen;
int NegotiatedIsoPduSize = 1024;// TODO: Respect the negotiated TPDU size
// 4 Byte TPKT Header
// 3 Byte ISO-Header
// 4 Byte S7CommPlus Header
// 4 Byte S7CommPlus Trailer (must fit into last PDU)
int MaxSize = NegotiatedIsoPduSize - 4 - 3 - 4 - (protoVersion == 0x03 ? 33 : 0);
byte[] packet = new byte[MaxSize + 4 + (protoVersion == 0x03 ? 33 : 0)]; //max packet size is always MaxSize + PDU Header
while (bytesToSend > 0)
{
if (bytesToSend > MaxSize)
{
curSize = MaxSize;
bytesToSend -= MaxSize;
}
else
{
curSize = bytesToSend;
bytesToSend -= curSize;
}
// Header
packet[0] = 0x72;
packet[1] = protoVersion;
packet[2] = (byte)((curSize + (protoVersion == 0x03 ? 33 : 0)) >> 8);
packet[3] = (byte)((curSize + (protoVersion == 0x03 ? 33 : 0)) & 0x00FF);
// Data part
byte[] curData = new byte[curSize];
Array.Copy(sendPduData, sourcePos, curData, 0, curSize);
if (protoVersion == 0x03)
{
byte[] data = new byte[curData.Length];
byte[] digestBuffer = new byte[32];
Buffer.BlockCopy(curData, 0, data, 0, curData.Length);
CalculateDigest(digestBuffer.AsSpan(), data, sessionKey);
IntegrityPart integrityPart = new IntegrityPart();
integrityPart.DigetLength = (byte)digestBuffer.Length;
integrityPart.PacketDigest = digestBuffer;
MemoryStream stream1 = new MemoryStream();
integrityPart.Serialize(stream1);
Array.Copy(stream1.ToArray(), 0, packet, 4, 33);
}
Array.Copy(sendPduData, sourcePos, packet, 4 + (protoVersion == 0x03 ? 33 : 0), curSize);
sourcePos += curSize;
sendLen = 4 + curSize + (protoVersion == 0x03 ? 33 : 0);
// Trailer only in last packet
if (bytesToSend == 0)
{
Array.Resize(ref packet, sendLen + 4); //resize only the last package to sendLen + TrailerLen
packet[sendLen] = 0x72;
sendLen++;
packet[sendLen] = protoVersion;
sendLen++;
packet[sendLen] = 0;
sendLen++;
packet[sendLen] = 0;
sendLen++;
}
m_client.Send(packet);
}
return m_LastError;
}
Suspected Causes
Based on S7CommPlus V3 specification:
-
Fragment Sequence Handling
- PLC may require cumulative digest across fragments
- Current code computes per-fragment digest independently
-
Header Composition
- When
protoVersion=0x03
, the header includes:[0x72][ProtoVersion][LengthHi][LengthLo] + 33-byte IntegrityPart
- Trailer (0x72 + ProtoVersion + 00 00) only applied to last packet
- When
-
Digest Calculation Scope
- Possible missing elements:
- Fragment sequence counter
- Session context persistence between fragments
- Cumulative payload hash
- Possible missing elements:
Request for Assistance
Please help verify:
- Does
CalculateDigest()
need to include fragment sequence metadata? - Is the trailer (0x72...) required for every fragment or only the terminal one?
- Should the integrity digest cover all previous fragments in multi-packet transmissions?
Reproduction Steps
- Send packet with 900 bytes payload → Verification PASS
- Send packet with 1500 bytes payload (split into two fragments):
- Fragment1: 1009 bytes + IntegrityPart
- Fragment2: 491 bytes + IntegrityPart + Trailer
- Observe PLC rejects both fragments' authentication
Metadata
Metadata
Assignees
Labels
No labels