[GH-ISSUE #3494] [Feature] Static format inspection for RSLogix 500 .RSS files (Allen-Bradley SLC-500 / MicroLogix) #1304

Open
opened 2026-10-16 21:03:58 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @dogukanozen on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3494

Problem

RSLogix 500 .RSS project files (Allen-Bradley SLC-500 / MicroLogix PLC programs) have no public parser. Searching turns up only vendor blog posts, several of which describe the format incorrectly (e.g. "encrypted binary blob"). In reality the file is a standard OLE2 / CFBF compound file, and most of its content is reachable with ordinary static inspection — but today the only way to read one is the proprietary RSLogix 500 IDE on Windows.

This leaves a gap for offline analysis of industrial control programs: auditing legacy ladder logic, diffing a field controller's saved program against a known-good copy, or extracting address documentation without the vendor tool.

Desired outcome

A read-only, static analyzer that, given a .RSS file, returns:

  • Stream inventory — the OLE storages/streams, which are zlib-compressed (78 9c after a 16-byte header) and which are raw (PROCESSOR, IO CONFIGURATION, CHANNEL CONFIGURATION, REPORT OPTIONS).
  • Address documentation from MEM DATABASE — address, symbol, description triples.
  • Ladder program from PROGRAM FILES — rungs as instruction lists, including parallel-branch structure, with each instruction's operands and (when the file was uploaded from a PLC) stored values. Unknown opcodes reported as unknown rather than guessed.
  • Processor / controller metadata (type, name).

No runtime, no PLC communication, no file modification.

Prior art / evidence

I have reverse-engineered the container and the PROGRAM FILES serialization from files I own and from clean test files I generated myself in RSLogix 500 (one instruction per rung, distinct literals, so each opcode is unambiguous). Findings so far:

  • Container: OLE2; <name>/ObjectData streams are 16-byte header + zlib, except the four raw streams above.
  • PROGRAM FILES: MFC serialization (CProgHolder -> CLadFile -> CRung -> CBranchLeg / CBranch -> CIns). Classes are declared once with FF FF + name, then referenced by a per-file 0x8000|index tag, so the class table must be resolved per file, not hard-coded.
  • Instruction layout: CIns = WORD count + count x (length-prefixed text) + WORD flag + WORD opcode.
  • Opcodes (measured): 32 EQU, 33 NEQ, 34 GRT, 35 GEQ, 36 LES, 37 LEQ, 39 XIC, 2F OTE, 0C END. Branch markers: 00/01 rung start/end, 02/03 BST/BND, 04/05 branch-leg start/end (NXB is not a separate record — each leg is a separate CBranchLeg). Additional opcodes (MOV/ADD/SUB/MUL/DIV, XIO, OTL/OTU, FLL, COP, TON, JSR, etc.) confirmed from other files; the measurement method generalizes.

Scope boundary

The format supports an optional program/master password. When it is set, RSLogix requires it to open the file, so it is an access-control lock. This proposal is explicit that the analyzer detects protection (from the presence/length of the password fields in PROCESSOR) and stops — it does not read, decode, or bypass the protection, and it never emits the password value. The optional "Encrypt" option only encodes those password fields; it does not encrypt the program, which stays readable, but a protected file is still treated as locked and is not decoded. This keeps the tool within REA's contribution scope (no access-control bypass) and within the "format inspection" category the contributing guide lists first.

Questions for maintainers

  1. Is an .RSS format inspector in scope, and would you prefer it modeled on an existing provider (e.g. the managed-code / SQLite static inspectors) or as a new tool?
  2. For test fixtures: are self-generated .RSS files committed to the repo acceptable, given the format is proprietary and real programs cannot be shared?
  3. Preferred shape — a standalone inspect_* MCP tool plus a CLI command, matching docs/tool-design.md?

I can open a draft PR with the container + program parser and self-generated fixtures once direction is confirmed.

Originally created by @dogukanozen on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3494 ### Problem RSLogix 500 `.RSS` project files (Allen-Bradley SLC-500 / MicroLogix PLC programs) have no public parser. Searching turns up only vendor blog posts, several of which describe the format incorrectly (e.g. "encrypted binary blob"). In reality the file is a standard OLE2 / CFBF compound file, and most of its content is reachable with ordinary static inspection — but today the only way to read one is the proprietary RSLogix 500 IDE on Windows. This leaves a gap for offline analysis of industrial control programs: auditing legacy ladder logic, diffing a field controller's saved program against a known-good copy, or extracting address documentation without the vendor tool. ### Desired outcome A read-only, static analyzer that, given a `.RSS` file, returns: - **Stream inventory** — the OLE storages/streams, which are zlib-compressed (`78 9c` after a 16-byte header) and which are raw (`PROCESSOR`, `IO CONFIGURATION`, `CHANNEL CONFIGURATION`, `REPORT OPTIONS`). - **Address documentation** from `MEM DATABASE` — address, symbol, description triples. - **Ladder program** from `PROGRAM FILES` — rungs as instruction lists, including parallel-branch structure, with each instruction's operands and (when the file was uploaded from a PLC) stored values. Unknown opcodes reported as `unknown` rather than guessed. - **Processor / controller metadata** (type, name). No runtime, no PLC communication, no file modification. ### Prior art / evidence I have reverse-engineered the container and the `PROGRAM FILES` serialization from files I own and from clean test files I generated myself in RSLogix 500 (one instruction per rung, distinct literals, so each opcode is unambiguous). Findings so far: - **Container:** OLE2; `<name>/ObjectData` streams are `16-byte header + zlib`, except the four raw streams above. - **`PROGRAM FILES`:** MFC serialization (`CProgHolder` -> `CLadFile` -> `CRung` -> `CBranchLeg` / `CBranch` -> `CIns`). Classes are declared once with `FF FF` + name, then referenced by a per-file `0x8000|index` tag, so the class table must be resolved per file, not hard-coded. - **Instruction layout:** `CIns` = `WORD count` + count x (length-prefixed text) + `WORD flag` + `WORD opcode`. - **Opcodes (measured):** `32` EQU, `33` NEQ, `34` GRT, `35` GEQ, `36` LES, `37` LEQ, `39` XIC, `2F` OTE, `0C` END. Branch markers: `00`/`01` rung start/end, `02`/`03` BST/BND, `04`/`05` branch-leg start/end (`NXB` is not a separate record — each leg is a separate `CBranchLeg`). Additional opcodes (MOV/ADD/SUB/MUL/DIV, XIO, OTL/OTU, FLL, COP, TON, JSR, etc.) confirmed from other files; the measurement method generalizes. ### Scope boundary The format supports an optional program/master password. When it is set, RSLogix requires it to open the file, so it is an **access-control lock**. This proposal is explicit that the analyzer **detects** protection (from the presence/length of the password fields in `PROCESSOR`) and **stops** — it does not read, decode, or bypass the protection, and it never emits the password value. The optional "Encrypt" option only encodes those password fields; it does not encrypt the program, which stays readable, but a protected file is still treated as locked and is not decoded. This keeps the tool within REA's contribution scope (no access-control bypass) and within the "format inspection" category the contributing guide lists first. ### Questions for maintainers 1. Is an `.RSS` format inspector in scope, and would you prefer it modeled on an existing provider (e.g. the managed-code / SQLite static inspectors) or as a new tool? 2. For test fixtures: are self-generated `.RSS` files committed to the repo acceptable, given the format is proprietary and real programs cannot be shared? 3. Preferred shape — a standalone `inspect_*` MCP tool plus a CLI command, matching `docs/tool-design.md`? I can open a draft PR with the container + program parser and self-generated fixtures once direction is confirmed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1304
No description provided.