pysap.SAPNWRFC module¶
SAP NetWeaver RFC (NWRFC) protocol constants and TLV parser.
The NetWeaver RFC SDK (libsapnwrfc) is the modern SAP client library used by
pyrfc, SAP JCo, SAP .NET Connector, and the SAP ABAP kernel’s own outbound
RFC stack. It differs from the classic SAPRFC/CPIC framing (see
pysap.SAPRFC) in two key areas:
Different magic bytes: NWRFC frames begin with
\x06\xcb\x02\x00rather than the classic\x06\x03\x02\x00.TLV encoding: All fields (username, password, function module name, client, language, hostname, …) are carried in a Type-Length-Value (TLV) stream using 2-byte big-endian tags and 2-byte big-endian lengths. Values are encoded in UTF-16LE.
Both frame types are transported inside SAP NI framing (4-byte big-endian payload length prefix) on the same RFC ports (default: TCP/3300).
TLV tag assignments¶
The tag-to-semantic mapping below was determined by analysis of live packet captures from multiple NWRFC SDK clients (pyrfc 3.x, npl_rfc, SAP ABAP kernel). Tags not listed here are observed in captures but their semantics are not yet confirmed.
Tag Semantic Encoding Size (bytes, typ./max.)
------ ----------------------- ---------- -----------------------
0x0006 RFC destination name UTF-16LE 2 – 40
0x0007 Client IP address UTF-16LE 4 – 32
0x0008 Server hostname UTF-16LE 8 – 120
0x0009 Username (tertiary) UTF-16LE 4 – 48
0x0100 Program / task name UTF-16LE 10 – 120
0x0102 Function module name UTF-16LE 8 – 80
0x0111 Username (primary) UTF-16LE 4 – 48
0x0114 SAP client number UTF-16LE 2 – 8 ("001", "100", …)
0x0117 ab_scramble password binary 6 – 84 [4B LE seed][UTF-16LE]
0x0119 Username (secondary) UTF-16LE 4 – 48
0x0152 Language key UTF-16LE 2 – 4 ("E", "D", …)
0x0201 RFC param name UTF-16LE 2 – 120
0x0203 RFC param value UTF-16LE variable
Password field (tag 0x0117)¶
The password TLV value is a raw binary field, not a UTF-16LE string:
[ 4 bytes little-endian seed ][ N×2 bytes ab_scrambled UTF-16LE payload ]
Total field length = 4 + (password_length × 2). Minimum length is 6 bytes
(seed + one UTF-16LE character). Use pysap.utils.crypto.rfc.ab_descramble()
with encoding="utf-16-le" to recover the plaintext.
Hostname / SID extraction¶
The server hostname value (tag 0x0008) frequently encodes the SAP System
ID (SID) in the pattern <hostname>_<SID>_<instance_number>, for example
sapnw752_NPL_00. The regex NWRFC_SID_RE matches this pattern.
References¶
Protocol reverse-engineered from libsapnwrfc (SAP NetWeaver RFC SDK).
TLV tag assignments confirmed from live PCAP captures.
See also:
pysap.SAPRFCfor classic RFC/CPIC framing.
- pysap.SAPNWRFC.NWRFC_MAGIC = b'\x06\xcb\x02\x00'¶
Magic bytes that identify an NWRFC frame (bytes 0-3 of the NI payload). Classic SAPRFC frames use
b'\\x06\\x03\\x02\\x00'instead.
- pysap.SAPNWRFC.NWRFC_PASSWORD_SEED_SIZE = 4¶
Minimum byte count of the ab_scramble seed prefix inside a password field.
- pysap.SAPNWRFC.NWRFC_SID_RE = re.compile('_([A-Z][A-Z0-9]{2})_\\d{2}(?:\\s|$)')¶
Regex that extracts the SAP System ID (SID) from a server hostname value. Matches the
_SID_NNsuffix, e.g.sapnw752_NPL_00→NPL.
- pysap.SAPNWRFC.NWRFC_TAGS = {6: 'dest', 7: 'ip', 8: 'hostname', 9: 'username', 256: 'program', 258: 'function_module', 273: 'username', 276: 'client', 279: 'password', 281: 'username', 338: 'language', 513: 'param_name', 515: 'param_value'}¶
Confirmed NWRFC TLV tag assignments. Keys are integer tag values; values are human-readable semantic names. All string values are encoded in UTF-16LE unless noted otherwise.
- pysap.SAPNWRFC.NWRFC_TAG_CONSTRAINTS = {6: (2, 40), 7: (4, 32), 8: (4, 120), 9: (4, 48), 256: (10, 120), 258: (8, 80), 273: (4, 48), 276: (2, 8), 279: (6, 84), 281: (4, 48), 338: (2, 4), 513: (2, 120)}¶
Per-tag minimum and maximum value length in bytes (before decoding). Used to reject spurious TLV matches during scanning.
- pysap.SAPNWRFC.NWRFC_TLV_HEADER_SIZE = 4¶
Size of a TLV header in bytes (2-byte tag + 2-byte length, both big-endian).
- pysap.SAPNWRFC.NWRFC_USERNAME_TAGS = (273, 281, 9)¶
Username tags, in priority order (highest priority first). When multiple username tags are present the highest-priority non-empty value should be used.
- pysap.SAPNWRFC.SAPRFC_MAGIC = b'\x06\x03\x02\x00'¶
Magic bytes for classic SAPRFC/CPIC frames, shown here for comparison.
- pysap.SAPNWRFC.decode_string(raw)[source]¶
Decode a UTF-16LE TLV value, stripping null and space padding.
- Args:
raw (bytes): Raw TLV value bytes.
- Returns:
str: Decoded string, or an empty string if decoding fails.
- pysap.SAPNWRFC.decode_value(raw)[source]¶
Decode a TLV value of unknown encoding, trying UTF-16LE then ASCII.
Most NWRFC TLV values are UTF-16LE (see
decode_string()), but some fields observed in F_SAP_SEND bodies (e.g. CPIC marker fields on older SDK versions) are plain ASCII. This heuristically picks UTF-16LE when raw “looks like” UTF-16LE (every other byte is0x00, as is the case for ASCII-range characters encoded that way), falling back to ASCII (with replacement of invalid bytes) otherwise.- Args:
raw (bytes | None): Raw TLV value bytes.
- Returns:
str | None: Decoded string with null/space padding stripped, or
Noneif raw isNone.
- pysap.SAPNWRFC.extract_rfc_params(raw)[source]¶
Extract RFC function call parameters from an F_SAP_SEND body.
Scans for
0x0201(param_name) TLVs carrying a valid identifier, then reads the immediately following0x0203(param_value) TLV viafind_tlv_field_by_marker().- Args:
raw (bytes): Raw F_SAP_SEND body to scan.
- Returns:
dict[str, str]: Mapping of parameter name to decoded value, in the order they appear in raw.
- pysap.SAPNWRFC.extract_xml_data(raw)[source]¶
Extract XML-encoded parameters and table rows from an RFC call body.
The NetWeaver RFC SDK serialises table/structure parameters as ASCII XML fragments embedded in the F_SAP_SEND body, e.g.:
<IT_MODULE><item><FIELD>value</FIELD></item></IT_MODULE> <IV_GUID>base64==</IV_GUID>
- Args:
- raw (bytes): Raw F_SAP_SEND body. Everything before the first
< is treated as binary and ignored; the remainder is decoded as ASCII (XML is always ASCII in NWRFC).
- raw (bytes): Raw F_SAP_SEND body. Everything before the first
- Returns:
dict[str, str | list]: Mapping of parameter name to value, where value is either a plain string (scalar) or a list of rows (table parameter). Table rows are dicts of sub-field name to value, except rows of a single unnamed field which are returned as plain strings. A sub-field whose value itself contains
<item>entries (nested table, e.g. ABAP source lines) is returned as a list of strings — but note rows containing a nested<item>-bearing sub-field are not split correctly, as the non-greedy outer<item>match ends at the first inner</item>.
- pysap.SAPNWRFC.find_tlv_field_by_marker(data, marker, search_start=0)[source]¶
Find a TLV field by its 2-byte tag, scanning byte-by-byte.
This is an alternative to
parse_tlv()for buffers where the field order varies across NWRFC SDK versions andparse_tlv()’s sequential scan may miss a field that is preceded by an unrecognised or misaligned entry. It scans for any 4-byte delimiter of the form[2-byte end-of-prev][2-byte tag]followed by a 2-byte big-endian length, i.e.:[end-of-prev (2)][tag (2)][length (2)][value (length)]
- Args:
data (bytes): Raw frame payload to scan. marker (int | bytes): Tag to search for, either as an integer (e.g.
0x0117) or as 2 raw bytes (e.g.b"\x01\x17").search_start (int): Offset to start scanning from.
- Returns:
tuple[bytes | None, int]:
(value, end_offset)if found, where end_offset is the offset just past the value (suitable as search_start for a subsequent call); otherwise(None, search_start).
- pysap.SAPNWRFC.find_tlv_field_by_padd(data, padd)[source]¶
Find a TLV field by its full 4-byte padding marker.
The 4-byte padd value encodes
[end-of-prev-field (2)][tag (2)]and only matches when the immediately preceding field is the one the caller expects, making this more precise thanfind_tlv_field_by_marker()when the field order is known and fixed.Layout:
[padd (4)][length (2, big-endian)][value (length)]
- Args:
data (bytes): Raw frame payload to scan. padd (bytes): 4-byte padding marker to search for.
- Returns:
bytes | None: The value bytes, or
Noneif not found or invalid.
- pysap.SAPNWRFC.parse_tlv(data, tags=None)[source]¶
Parse NWRFC TLV fields from a raw byte buffer.
Scans data for TLV entries whose 2-byte big-endian tag appears in tags (or in
NWRFC_TAGSwhen tags isNone). Length constraints fromNWRFC_TAG_CONSTRAINTSare enforced to suppress false positives; unrecognised or out-of-range entries are skipped.The buffer does not need to start at a TLV boundary — the function searches forward byte-by-byte, which handles frames where the TLV stream is preceded by other header fields.
- Args:
- data (bytes): Raw frame payload (NI payload, without the 4-byte NI
length header).
- tags (collection | None): Iterable of integer tag values to extract.
Defaults to all keys in
NWRFC_TAGS.
- Yields:
tuple[int, bytes]:
(tag, raw_value)pairs in the order they appear in data. The caller is responsible for decoding raw_value (typicallyraw_value.decode("utf-16-le").rstrip("\x00")for string fields, or passing topysap.utils.crypto.rfc.ab_descramble()for the password tag).
Example:
>>> from pysap.SAPNWRFC import parse_tlv, NWRFC_TAGS >>> for tag, value in parse_tlv(frame_data): ... print(hex(tag), NWRFC_TAGS.get(tag), value)