← Technical notesView source on GitHub ↗

The 1RCP screenshot worker

The recovered launcher implements the worker half of a local screenshot interface. This section reconstructs its activation, message format, pixel layout and local dataflow. The external peer and initial worker launcher remain unrecovered.

Activation and process context

The worker is selected with:

PackClientLauncher.exe /scr_cap_worker <endpoint-utf8> [monitor-index]

Main calls the worker dispatcher at B RVA 0x45C3 -> 0xA38E before the normal Core/session path. The handler requires at least three arguments, exact argv[1] == /scr_cap_worker, and non-null argv[2]. Optional argv[3] supplies the monitor index; default is zero. The wrapper passes the worker result to ExitProcess.

The worker inherits its existing token, integrity level, session, environment and parent relationship. This branch does not create another process or change session context.

Endpoint boundary

argv[2] is converted from UTF-8 into a 260-WCHAR buffer and passed to CreateFileW at 0xA4DE:

Argument Recovered value
Filename Direct UTF-16 conversion of supplied endpoint
Access 0xC0000000, read and write
Share mode Zero
Security attributes Null
Creation disposition OPEN_EXISTING
Flags and template handle Zero/null

B opens the supplied path once with OPEN_EXISTING; endpoint creation and peer identity are external to the recovered worker.

Named-pipe use is strongly inferred from the duplex protocol and diagnostics. CreateFileW also accepts other path types, so this call alone does not prove a pipe namespace. Microsoft's CreateFileW contract and named-pipe client documentation describe the relevant API behavior.

No worker-launch or peer-creation path was found outside B in the recovered components. The session-relaunch logic preserves /scr_cap_worker, its endpoint and optional monitor index across relaunches, but does not create the initial worker invocation.

Header and message semantics

Every message starts with five little-endian DWORDs, exactly 20 bytes. Magic is 0x50435231, wire bytes 31 52 43 50 (1RCP).

Offset Field Worker → peer: READY Worker → peer: Frame Peer → worker: Recapture Peer → worker: Exit
0x00 Magic 1RCP 1RCP 1RCP 1RCP
0x04 Type 1 2 3 5
0x08 Width Width Width Ignored Ignored
0x0C Height Height Height Ignored Ignored
0x10 Payload size Zero Pixel-byte count Ignored Ignored

The reconstructed exchange is:

  1. Worker captures once, then sends type 1 READY with dimensions and no pixel body.
  2. Peer sends type 3 to request recapture.
  3. On successful capture, worker sends type 2 plus the pixel body.
  4. Peer can repeat recapture or send type 5; exit has no reply.

The request tail DWORDs are ignored, not required to be zero. The worker reads no extra request body from the last DWORD; its next operation is another exact 20-byte header read. Unknown types with correct magic are ignored. Wrong magic, failed reads or zero-byte reads end the loop without a protocol error response.

Reads and writes loop until the requested byte count completes. These operations do not require message-mode pipe boundaries. Writer 0xA731 sends the header, then writes a payload only when both pointer and size are nonzero; 0xA6E6 performs exact writes.

Desktop and pixel layout

Before capturing, the worker opens winsta0, temporarily selects that window station, attempts to open the input desktop, and falls back to Winlogon. It restores and closes the handles after use and depends on its inherited context having the required access.

Capture at 0x9EB0 enumerates monitor rectangles. The worker uses the requested monitor when the index is valid; otherwise it falls back to monitor zero. Positive dimensions are calculated from the selected rectangle:

width        = right - left
height       = bottom - top
row_stride   = width * 4
payload_size = width * height * 4

The DIB header is 40 bytes, with positive width, negative height, planes 1, bit count 32 and BI_RGB. Negative height selects top-down row order. BitBlt uses SRCCOPY | CAPTUREBLT (0x40CC0020). Each pixel is blue, green, red, unused/reserved: BGRX, with no meaningful alpha established and no extra row padding beyond width * 4.

IA-32 multiplication/shift calculates the allocation length; no separate overflow guard was observed.

Within B the framebuffer dataflow ends at WriteFile on the supplied handle. No B-local edge reaches Winsock, PLK1, the lower encrypted transport, JPEG or PV10. Historical Core traffic contains 15 PV10 JPEG frames consistent with Core's built-in GDI/WIC producer. No recovered code connects that producer to the Launcher's raw-BGRX 1RCP worker; any bridge would belong to the unidentified external endpoint peer.

DIB lifetime hazard and observed failure

The recovered call order is CreateDIBSection, select bitmap, BitBlt, restore old selection, DeleteObject, DC cleanup, then copy from the saved bits pointer. Microsoft's CreateDIBSection contract ties the bits to the bitmap's lifetime, while DeleteObject can fail.

Attempt Captured observation
PID 8892 Copy source 0x03260000, length 0x510000; source not resolved in the captured memory view
PID 8148 Breakpoint before deletion, then later rep movsb from source 0x02830000, length 0x510000
PID 8040 First-chance AV at RVA 0xA567, push [edi+8], with EDI=0x13C, EAX=1; peer received 0 of 20 READY-header bytes

These were separate debugger sessions and included manual intervention, so they do not form a single controlled execution trace.

0x510000 equals 1536 * 864 * 4, matching the expected framebuffer size for 1536 × 864 but not establishing the lifetime of the source allocation. The final fault is separate from the earlier copy-source observations.

Relevant sites: CreateDIBSection at 0xA010; deletion near 0xA066; copy near 0xA0F9/0xA0FE; final recorded fault 0xA567.

The call order creates a possible use-after-free if DeleteObject succeeds, because the saved bits pointer is copied afterward. The debugger sessions confirm the later copy and a separate worker fault, but they do not record the deletion result or establish that the fault was caused by the bitmap lifetime.

The synthetic validation appendix reproduces the 1RCP framing and BGRX serialization with a local peer and simulator. A complete exchange with the real worker was not captured. See evidence and limitations for the remaining boundaries.

Figure

Enlarged figure

Open figure at full resolution ↗