ICSim Scripting with Python and Shell Scripts

  • Author: Dr. Jim Marquardson (jimarqua@nmu.edu)
  • Updated 2024-06-15

You can script some of your exploration using different scripting languages. This exercise introduces sample programs using Python and Bash. These programs can help you focus your exploration. You can tweak this code to suit your needs.

Learning Objectives

In this exercise, you will learn to:

  • run Python programs,
  • run bash scripts, and
  • change script inputs to focus your exploration.

Prerequisites

This exercise assumes that the following are available:

  • Kali Linux VM with a graphical user interface,
  • can-utils has been installed,
  • ICSim has been installed to ~/ICSim.

Setup a CAN Network

Your CAN network may already be configured. If not, run the following commands.

  • Open a terminal.
  • Navigate to the ~/ICSim directory.
cd ~/ICSim
  • Create the vcan0 network. This vcan0 network essentially simulates a physical wire to sensors in a bus topology.
sudo sh setup_vcan.sh

Enter the password (kali) if prompted.

  • You may not see any output. The command likely worked. Check for the vcan0 network using ifconfig.
ifconfig

You should see vcan0 in the list of network adapters.

vcan0: flags=193<UP,RUNNING,NOARP>  mtu 72
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • If you run sudo sh setup_vcan.sh and see a message like, "RTNETLINK answers: File exists," this means that the vcan0 network has already been set up. You can ignore this message and continue.

Launch the Simulator

  • Run the following command to launch the vehicle simulator. Ensure that your working directory is ~/ICSim before running this command. You will likely have to press the enter twice in the terminal to return to the terminal prompt.
cd ~/ICSim
./icsim vcan0 &

The Python Program

I created a short Python program that sends CAN messages. This program is useful if you know the arbitration ID and the data length the system wants, but you don't know how the data will be interpreted. The program contains the following Python code.

import sys
import time
from subprocess import call

def send_can_codes(network, template, delay):
    num_x = template.count("x")
    i = 0
    while i < 4**(num_x*2):
        hex_str = hex(i)[2:].zfill(num_x)
        can_message = template.replace("x"*num_x, hex_str)
        can_cmd = f"cansend {network} {can_message}"
        print(f"Running {can_cmd} - press control+c to stop execution.")
        call(["cansend", network, can_message])
        time.sleep(delay)
        i += 1

def print_syntax():
    print("This script sends a sequence of CAN messages with a variable part.")
    print("It is useful if you know the arbitration ID and the data bytes of a message, but you don't know how the data will be interpreted.")
    print()
    print("Syntax: python3 seq.py <network> <template> <delay>")
    print("network: the CAN network to send the messages")
    print("         Example: vcan0")
    print("template: the CAN message to send. Use 'x' as a placeholder for the variable part")
    print("         Example: 19B#0000xx000000")
    print("delay: the delay between messages in seconds")
    print("         Example: 0.1")
    print()
    print("1 x will produce 16 messages. 2 x's will produce 256 messages. 3 x's will produce 4096 messages, etc.")
    print()
    print("Example: python3 seq.py vcan0 19B#0000xx000000 0.1")

if __name__=="__main__":
    if len(sys.argv) != 4:
        print_syntax()
        sys.exit(1)
    network = sys.argv[1]
    template = sys.argv[2]
    delay = float(sys.argv[3])
    send_can_codes(network, template, delay)

Running the Python Program

  • In a Kali terminal, make a new folder.
cd ~
mkdir scripts
cd scripts
  • Download seq.py to your /home/kali/scripts directory. You can open Firefox in Kali and open this web page to download the file. You may need to right-click on the link and choose "save as."
    • Alternatively, you can use wget to download the URL. Copy the URL to your clipboard, then paste the URL into the wget command.
wget paste_the_url_here_that_ends_with_seq.py
  • In the terminal, run the following command to see the program's syntax.
python3 seq.py
  • Test the script using the following command. The program will send canplayer messages using the 19B arbitration ID. The program will replace the x's in the data with every possible combination of hex characters.
python3 seq.py vcan0 19B#0000xx000000 .2
  • Run the program again using different delays. For example:
python3 seq.py vcan0 19B#0000xx000000 .01
python3 seq.py vcan0 19B#0000xx000000 .5
python3 seq.py vcan0 19B#0000xx000000 2
  • Run the program again with different arbitration IDs. For example:
python3 seq.py vcan0 188#xx00 .1
  • Change different 0's to x's in the data and see what happens. Note that the x's must be contiguous.
python3 seq.py vcan0 188#0x00 .5 # Works fine
python3 seq.py vcan0 188#xx00 .5 # Works fine
python3 seq.py vcan0 188#00xx .5 # Works fine
python3 seq.py vcan0 188#0x0x .5 # Busted - there is a 0 between the two x's

Bash Scripting

The following shell script largely implements the same logic as the Python program above. You can download it here. However, this shell script is meant to stand alone and not require the use of candump or cansniffer to record the messages.

  • Make the file executable.
chmod +x seq.sh
  • Run the program to see its syntax and sample usage.
./seq.sh

The code is found below.

#!/bin/bash

# Check if cansend is installed
cansendInstalled=false
if command -v cansend >/dev/null 2>&1; then
    cansendInstalled=true
    echo "cansend found. Running."
else
    echo "cansend not found. Please install can-utils."
    echo "Press an key to start without cansend."
    echo "The codes will be printed, but not sent to the CAN network."
    read -n 1
fi

# If there are no parameters, print usage
if [[ $# -eq 0 ]]; then
    echo "This script generates sequential can codes and gives and opportunity to save codes to a file."
    echo ""
    echo "Usage: ./seq.sh <can_code_template> [-network <network>] [-delay <delay>] [-out <out>]"
    echo "  -can_code_template : Required. The arbitration ID, #, and data. E.g., 19B#0000xx000000"
    echo "                       Each x will be replaced with all possible hex values."
    echo "  -network <network> : Optional. The network to send the CAN commands on. Default is vcan0."
    echo "  -delay <delay>     : Optional. The delay between each command. Default is 1.0 seconds."
    echo "  -out <out>         : Optional. The file to save the commands to. Default is saved.txt."
    echo ""
    echo "Examples:"
    echo "  ./seq.sh 19B#0000xx000000"
    echo "  ./seq.sh 19x#000000030000 -delay 0.5"
    echo "  ./seq.sh 188#00xx -delay 0.5"
    echo "  ./seq.sh 188#xx00 -delay 0.5"
    echo "  ./seq.sh 18x#0000 -delay 2.0 -network vcan0"
    echo "  ./seq.sh 188#00xx -network can0"
    echo "  ./seq.sh 188#00xx -network can0 -out try2.txt"
    echo "  ./seq.sh 188#00xx -network vcan0 -delay 0.5 -out try3.txt"
    exit
fi

# Set the default values
can_code_template="19B#0000xx000000"
network="vcan0"
delay=1.0
out="saved.txt"

# Parse the parameters
while [[ $# -gt 0 ]]
do
    key="$1"
    case $key in
        -network)
            network="$2"
            shift
            shift
            ;;
        -delay)
            delay="$2"
            shift
            shift
            ;;
        -out)
            out="$2"
            shift
            shift
            ;;
        *)
            can_code_template="$1"
            shift
            ;;
    esac
done

# find the numer of x characters in $can_code_template
num_x=$(echo $can_code_template | grep -o "x" | wc -l)
x_str=$(printf 'x%.0s' $(seq 1 $num_x))
stop=$((4**($num_x*2)))
for ((i=0; i<$stop; i++))
do
    # convert $i to base 4 padded with 0s
    hex=$(printf "%0${num_x}x\n" $i)
    # replace x characters in $can_code_template with $can_command
    can_command=$(echo $can_code_template | sed "s/$x_str/$hex/g")
    echo "($((i+1)) of $stop) cansend $network $can_command. Press 's' to save the CAN message, 'x' to exit, 'p' to pause."
    if [ "$cansendInstalled" = true ]; then
        cansend $network $can_command
    fi
    read -t $delay -N 1 input
    if [[ $input = "s" ]]; then
        echo " Appending $can_command to $out."
        echo $can_command >> $out
    elif [[ $input = "x" ]]; then
        echo "Exiting."
        exit
    elif [[ $input = "p" ]]; then
        echo "Paused. Press any key to continue."
        read -n 1
    fi
done
  • Execute the script to see the help documentation.
./seq.sh
  • Explore different uses of the script.
./seq.sh 19B#0000xx000000
./seq.sh 19x#000000030000 -delay 0.5
./seq.sh 188#00xx -delay 0.5
./seq.sh 188#xx00 -delay 0.5
./seq.sh 18x#0000 -delay 2.0 -network vcan0
./seq.sh 188#00xx -network can0
./seq.sh 188#00xx -network can0 -out try2.txt
./seq.sh 188#00xx -network vcan0 -delay 0.5 -out try3.txt
  • Run some of the examples.
  • As the script executes, press s to save the CAN message. Press p to pause. If you want to exit early, press x.
  • You may want to increase the delay between sending different messages if you do not have enough time to press s so save.

Challenge

  • Edit the scripts using nano. Add an extra features.
  • Create a minimal Python script that sends CAN messages. Extract the useful bits from the Python source code above. It might only be a couple of lines of code.

Shutting Down

  • Close the ICSim window. You may have to click Yes to confirm closing it.
  • Close all terminals.

Reflection

  • Why would it be important to specify a reasonable delay?
  • At what point in your investigation would this technique be useful?