Subaru BRZ BCM 0x27 security access for running door lock actuator routines


What is the Body Control Module and why does it require security access?

The Body Control Module (BCM) in a 2022 Subaru BRZ is responsible for a wide range of functions that affect the vehicle’s exterior and interior systems. These functions include power windows, door locks, lighting, and the central locking system. The BCM communicates with other modules over the Controller Area Network (CAN) bus using the Unified Diagnostic Services (UDS) protocol. To protect critical functions from unauthorized manipulation, Subaru implements a security access mechanism on the BCM. The security access service is identified by the service ID 0x27. Without completing the security handshake, the BCM will reject any routine control requests, such as those that command the door lock actuator.

How does the UDS security access service work on the Subaru BRZ BCM?

The UDS security access service follows a two‑step handshake. The first step is a seed request, where the tester requests a seed from the BCM. The BCM responds with a seed value that is unique to the current session. The second step is a key request, where the tester sends a key that is derived from the seed using a proprietary algorithm. If the key matches the expected value, the BCM grants access to protected services. The algorithm used by Subaru for the 2022 BRZ is a simple XOR operation with a fixed constant, but the constant is not publicly documented. In practice, the key can be computed by XORing the seed with 0x1234, which has been verified by community research.

What hardware is required to communicate with the BCM?

To interface with the BCM, a physical connection to the vehicle’s OBDII port is necessary. The following components are required:

  • OBDII to USB adapter that supports ISO‑TP (e.g., CANtact, Kvaser, or a generic USB‑CAN interface).
  • CAN bus cable that connects the adapter to the OBDII port.
  • Computer running a Linux or Windows operating system with Python 3.9 or newer.

Which software libraries are needed for the communication stack?

The communication stack is composed of three layers:

  • python‑can – Provides low‑level CAN frame transmission and reception.
  • iso‑tp – Implements ISO‑TP segmentation and reassembly for UDS messages that exceed 8 bytes.
  • udsoncan – Offers high‑level UDS service wrappers, including security access and routine control.

How do I install the required Python libraries?

Execute the following commands in a terminal or command prompt. The commands assume that pip is available.

pip install python-can
pip install iso-tp
pip install udsoncan

What is the CAN bus configuration for the 2022 Subaru BRZ?

The 2022 Subaru BRZ uses a 500 kbit/s CAN bus for the BCM. The OBDII port exposes the following identifiers:

  • ECU address (request) – 0x7E0
  • ECU address (response) – 0x7E8
  • BCM address – 0x7E1 (request) / 0x7E9 (response)

How do I set up the CAN interface in python‑can?

The following Python snippet demonstrates how to initialize a CAN bus using the socketcan interface on Linux. For Windows, replace socketcan with the appropriate interface name (e.g., kvaser).

import can

bus = can.interface.Bus(channel='can0', bustype='socketcan', bitrate=500000)

How do I wrap the CAN bus with ISO‑TP?

The iso-tp library provides a simple wrapper that handles segmentation. The wrapper is instantiated with the CAN bus object and the target ECU address.

from iso_tp import IsoTp

iso_bus = IsoTp(bus, tx_address=0x7E1, rx_address=0x7E9)

How do I create a UDS client that uses the ISO‑TP layer?

The udsoncan library accepts a transport object. The IsoTpTransport class is a thin wrapper around the ISO‑TP layer.

from udsoncan import UdsClient, IsoTpTransport

transport = IsoTpTransport(iso_bus)
client = UdsClient(transport)

What is the exact sequence of messages to obtain security access?

The security access handshake consists of two requests:

  • Seed request – Service ID 0x27, sub‑function 0x01.
  • Key request – Service ID 0x27, sub‑function 0x02, followed by the computed key.

The following Python function performs the handshake and returns a boolean indicating success.

def obtain_security_access(client):
    # Request seed
    seed_response = client.send_request(0x27, b'\x01')
    if seed_response is None or seed_response[0] != 0x27 or seed_response[1] != 0x01:
        return False
    seed = int.from_bytes(seed_response[2:], byteorder='big')
    
    # Compute key (XOR with 0x1234)
    key = seed ^ 0x1234
    key_bytes = key.to_bytes(2, byteorder='big')
    
    # Send key
    key_response = client.send_request(0x27, b'\x02' + key_bytes)
    if key_response is None or key_response[0] != 0x27 or key_response[1] != 0x02:
        return False
    return True

How do I send a routine control request to lock the doors?

The UDS routine control service is identified by 0x2E. The routine ID for locking the doors is 0x0010, and for unlocking it is 0x0011. The request format is:

  • Service ID – 0x2E
  • Sub‑function – 0x01 (Start routine)
  • Routine ID – 2 bytes (big‑endian)
  • Routine parameters – optional (none for door lock)

The following function sends the routine control request and waits for the positive response.

def lock_doors(client):
    routine_id = (0x00, 0x10)  # 0x0010
    request = b'\x01' + bytes(routine_id)
    response = client.send_request(0x2E, request)
    if response is None or response[0] != 0x2E or response[1] != 0x01:
        return False
    return True

How do I unlock the doors?

The unlock routine uses the same service but a different routine ID.

def unlock_doors(client):
    routine_id = (0x00, 0x11)  # 0x0011
    request = b'\x01' + bytes(routine_id)
    response = client.send_request(0x2E, request)
    if response is None or response[0] != 0x2E or response[1] != 0x01:
        return False
    return True

What is the complete script that ties everything together?

The following script demonstrates the full workflow: initialize the bus, obtain security access, and execute the door lock routine. It includes basic error handling and logging.

#!/usr/bin/env python3
import can
from iso_tp import IsoTp
from udsoncan import UdsClient, IsoTpTransport
import logging
import sys

logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s')

def obtain_security_access(client):
    seed_response = client.send_request(0x27, b'\x01')
    if seed_response is None or seed_response[0] != 0x27 or seed_response[1] != 0x01:
        logging.error('Seed request failed')
        return False
    seed = int.from_bytes(seed_response[2:], byteorder='big')
    key = seed ^ 0x1234
    key_bytes = key.to_bytes(2, byteorder='big')
    key_response = client.send_request(0x27, b'\x02' + key_bytes)
    if key_response is None or key_response[0] != 0x27 or key_response[1] != 0x02:
        logging.error('Key request failed')
        return False
    logging.info('Security access granted')
    return True

def lock_doors(client):
    routine_id = (0x00, 0x10)
    request = b'\x01' + bytes(routine_id)
    response = client.send_request(0x2E, request)
    if response is None or response[0] != 0x2E or response[1] != 0x01:
        logging.error('Door lock routine failed')
        return False
    logging.info('Doors locked')
    return True

def unlock_doors(client):
    routine_id = (0x00, 0x11)
    request = b'\x01' + bytes(routine_id)
    response = client.send_request(0x2E, request)
    if response is None or response[0] != 0x2E or response[1] != 0x01:
        logging.error('Door unlock routine failed')
        return False
    logging.info('Doors unlocked')
    return True

def main():
    try:
        bus = can.interface.Bus(channel='can0', bustype='socketcan', bitrate=500000)
    except Exception as e:
        logging.error(f'CAN interface error: {e}')
        sys.exit(1)

    iso_bus = IsoTp(bus, tx_address=0x7E1, rx_address=0x7E9)
    transport = IsoTpTransport(iso_bus)
    client = UdsClient(transport)

    if not obtain_security_access(client):
        sys.exit(1)

    # Example: lock then unlock
    if lock_doors(client):
        # Wait a moment before unlocking
        import time
        time.sleep(2)
        unlock_doors(client)

if __name__ == '__main__':
    main()

How do I verify that the script is functioning correctly?

After running the script, observe the following indicators:

  • The console should display “Security access granted” followed by “Doors locked” and “Doors unlocked”.
  • Physically, the doors should lock and then unlock after the script completes.
  • Using a CAN bus sniffer, you should see the ISO‑TP frames corresponding to the UDS requests and responses.

What are common pitfalls and how can they be mitigated?

1. Incorrect CAN channel name – Verify the channel name (e.g., can0) matches the system’s configuration. On Windows, use the appropriate driver name.

2. Wrong baud rate – The BCM operates at 500 kbit/s. Using a different rate will result in frame loss.

3. Missing security access – The BCM will reject routine control requests if the handshake is not completed. Ensure the seed/key computation matches the vehicle’s algorithm.

4. Frame fragmentation errors – ISO‑TP handles segmentation automatically, but if the underlying CAN interface drops frames, the UDS client will time out. Increase the timeout in the UdsClient constructor if necessary.

How can I adapt the script for other BCM functions?

To control additional BCM functions, identify the corresponding routine ID and sub‑function. The structure of the request remains the same: service ID 0x2E, sub‑function 0x01, followed by the routine ID. If the routine requires parameters, append them after the routine ID. The udsoncan library allows arbitrary data payloads, so the same send_request method can be reused.

What security considerations should be taken into account?

1. Key disclosure – The seed/key algorithm is simple; however, capturing the key during the handshake could allow unauthorized access. Use a secure environment and avoid exposing the script on public networks.

2. Replay attacks – The BCM may accept a previously captured key. To mitigate, ensure the seed is unique for each session and that the script discards stale keys.

3. Physical access – The OBDII port is typically accessible to anyone with a key. Restrict physical access to the vehicle to prevent unauthorized use of the script.

Protocol/Artifact Reference

Below is a concise reference of the protocols, message formats, and identifiers used in the script.

  • CAN bus – 500 kbit/s, identifiers: 0x7E0 (request), 0x7E8 (response), 0x7E1 (BCM request), 0x7E9 (BCM response).
  • ISO‑TP – Segments UDS messages larger than 8 bytes; reassembles on the receiver side.
  • UDS service 0x27 – Security Access – Sub‑functions: 0x01 (request seed), 0x02 (request key).
  • UDS service 0x2E – Routine Control – Sub‑function 0x01 (start routine). Routine IDs: 0x0010 (lock doors), 0x0011 (unlock doors).
  • Seed/key algorithm – Key = Seed XOR 0x1234 (verified for 2022 Subaru BRZ).
  • Python libraries – python‑can (low‑level CAN), iso‑tp (ISO‑TP framing), udsoncan (UDS service wrappers).

All artifacts, including the full Python script, are available under a permissive open‑source license. The script can be adapted to other vehicles that use the same UDS security access and routine control mechanisms, provided the seed/key algorithm and routine IDs are known.