Buspirate I2C LCD

I2C

Sniffing mode

The goal of this article is to use the Bus Pirate to sniff an I2C bus between an Arduino Uno and an I2C LCD screen. Then, we’ll inject values to take control of the LCD screen.

Here’s a photo of the setup: Setup-1

Steps to sniff an I2C bus: Setup-2

In this example, we clear the LCD screen and display a number at the cursor position (which is already at (0,0)).

The setup for sniffing is as follows:

Bus pirate <-> LCD screen
GND        <-> GND
MOSi       <-> SDA
CLK        <-> SCL

References for the different protocols supported by the Bus Pirate v3: here

Arduino Code

Arduino code (running on an Arduino Uno):

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows

int c = 0;

void setup()
{
  lcd.init(); // initialize the lcd
  lcd.backlight();
}

void loop()
{
  lcd.clear();                 // clear display
  lcd.setCursor(0, 0);         // move cursor to   (0, 0)
  lcd.print("Hello: ");        // print message at (0, 0)
  lcd.setCursor(2, 1);         // move cursor to   (2, 1)
  lcd.print(c);
  delay(2000);                 // display the above for two seconds
  c++;
  if (c > 10) {
    c = 0;
  }
}

The Arduino/LCD setup is as follows:

Arduino UNO <-> Ecran LCD
GND        <-> GND
A4         <-> SDA
A5         <-> SCL
VCC        <-> VCC

For the reverse engineering, I’ll use the code above.

Reverse engineering

For the captures, I isolated the Arduino code as much as possible to match each line of code with the I2C communication. Also, we have to do it line by line because the Bus Pirate’s buffer is limited.

Capture of clear

Capture for line lcd.clear();

[0x4E+0x08+][0x4E+0x0C+][0x4E+0x08+][0x4E+0x18+][0x4E+0x1C+][0x4E+0x18+]

Capture of setCursor(x, y)

The LCD screen is a 16x2 matrix, with coordinates noted as (column, row).

Capture for the line lcd.setCursor(0, 0);

[0x4E+0x88+][0x4E+0x8C+][0x4E+0x88+][0x4E+0x08+][0x4E+0x0C+][0x4E+0x08+]

Capture for the line lcd.setCursor(1, 0);

[0x4E+0x88+][0x4E+0x8C+][0x4E+0x88+][0x4E+0x18+][0x4E+0x1C+][0x4E+0x18+]

Capture for the line lcd.setCursor(0,1)

[0x4E+0xC8+][0x4E+0xCC+][0x4E+0xC8+][0x4E+0x08+][0x4E+0x0C+][0x4E+0x08+]

Analyse

After several injections from the Bus Pirate to the LCD screen, we can conclude:

setCursor(x,y) To change the value of x, we write to the memory area: [0x4E+0x88+][0x4E+0x8C+][0x4E+0x88+], then send the data: [0x4E+0xp8+][0x4E+0xpC+][0x4E+0xp8+], where p is the value of x, and x ranges from 0x1 to 0xF.

To change the value of y, we write to the memory area: [0x4E+0xC8+][0x4E+0xCC+][0x4E+0xC8+], then send the data: [0x4E+0xp8+][0x4E+0xpC+][0x4E+0xp8+], where p is the value of y, and y ranges from 0x1 to 0xF.

In summary:

setCursor(p, 0): [0x4E+0x88+][0x4E+0x8C+][0x4E+0x88+][0x4E+0xp8+][0x4E+0xpC+][0x4E+0xp8+]

setCursor(0, p): [0x4E+0xC8+][0x4E+0xCC+][0x4E+0xC8+][0x4E+0xp8+][0x4E+0xpC+][0x4E+0xp8+]

For each capture, the screen had to be cleared before displaying anything. So each sequence includes a clear followed by a print.

Capture for the line lcd.print(c); with the cursor at (0,0):

// displaying a 1
[0x4E+0x08+][0x4E+0x0C+][0x4E+0x08+][0x4E+0x18+][0x4E+0x1C+][0x4E+0x18+][0x4E+0x39+][0x4E+0x3D+][0x4E+0x39+][0x4E+0x19+][0x4E+0x1D+][0x4E+0x19+]

// displaying a 6
[0x4E+0x08+][0x4E+0x0C+][0x4E+0x08+][0x4E+0x18+][0x4E+0x1C+][0x4E+0x18+][0x4E+0x39+][0x4E+0x3D+][0x4E+0x39+][0x4E+0x69+][0x4E+0x6D+][0x4E+0x69+]

If we remove the clear command, we get:

// displaying a 1
[0x4E+0x39+][0x4E+0x3D+][0x4E+0x39+][0x4E+0x19+][0x4E+0x1D+][0x4E+0x19+]

// displaying a 6
[0x4E+0x39+][0x4E+0x3D+][0x4E+0x39+][0x4E+0x69+][0x4E+0x6D+][0x4E+0x69+]

Analysis

Just like with setCursor, a memory area is written before sending the data: [0x4E+0x39+][0x4E+0x3D+][0x4E+0x39+], then a value is sent for display: [0x4E+0xp9+][0x4E+0xpD+][0x4E+0xp9+], where p is the value to display (a digit between 0–9). Note: we’re only analyzing digits here, but the same approach could be used for letters.

Sending a Command

We send a 6 to the LCD screen.

Initial state: Initial-state

Injection of the digit 6: Injection

Final state: Final-state

Ref

Libraries for the I2C LCD screen: here

(Guide to using the LCD screen here)

NoSoWhiteHat

Une voiture aux roues carrées pourra rouler sur une route au revêtement composé de tronçons de cosinus hyperboliques (une citation volée: clx.freeshell.org).


2025-03-18