Introduction
The I2C (Inter-Integrated Circuit) bus is a ubiquitous serial communication protocol found in nearly every modern electronic device, including Android smartphones and tablets. It’s the backbone for communication between the system-on-chip (SoC) and a multitude of peripherals like sensors (accelerometers, gyroscopes), power management ICs (PMICs), camera modules, audio codecs, and especially EEPROMs (Electrically Erasable Programmable Read-Only Memory) which store vital configuration data or device firmware. Understanding and interacting with the I2C buses on a rooted Android device opens up a powerful avenue for hardware reverse engineering, diagnostics, and even custom hardware integration.
This expert-level tutorial will guide you through the process of enumerating I2C devices, dumping the contents of attached EEPROMs, and inspecting or modifying device register configurations on a rooted Android device. We will leverage standard Linux `i2c-tools` to achieve these objectives, providing practical, step-by-step commands suitable for security researchers, hardware enthusiasts, and embedded developers.
Prerequisites
- A rooted Android device (required for shell access to `/dev/i2c-*` nodes and installing tools).
- ADB (Android Debug Bridge) installed and configured on your host machine.
- Basic familiarity with the Linux command line.
- A terminal emulator (e.g., Termux) on the Android device, or primarily using `adb shell`.
Understanding I2C on Android’s Linux Kernel
Android’s underlying Linux kernel exposes I2C buses as character devices, typically found under `/dev/i2c-*`. Each number represents a distinct I2C bus master controlled by the SoC. The kernel also provides a sysfs interface at `/sys/bus/i2c/devices/`, where you can find more information about detected I2C adapters and attached devices, though direct interaction with devices usually occurs via the `/dev` nodes.
The `i2c-tools` suite is a collection of utilities designed to interact with I2C devices from the Linux command line. Key tools include `i2cdetect` for bus scanning, `i2cdump` for reading memory blocks (like EEPROMs), `i2cget` for reading specific registers, and `i2cset` for writing to registers.
Step 1: Establish Rooted ADB Shell Access
Ensure your device is rooted and ADB is set up. We’ll start by gaining a root shell on the device.
adb rootadb shell
If `adb root` fails, your device’s root solution (e.g., Magisk) might require you to use `su` within a non-root `adb shell` session:
adb shellsu
Confirm you have root privileges by checking the user ID:
whoami
You should see `root`.
Step 2: Identifying Available I2C Buses
First, identify which I2C buses are present on your device. These are usually named `i2c-0`, `i2c-1`, etc., corresponding to `/dev/i2c-0`, `/dev/i2c-1`, and so on.
ls /dev/i2c*
You might see output similar to:
/dev/i2c-0/dev/i2c-1/dev/i2c-2/dev/i2c-3/dev/i2c-4/dev/i2c-5
Each of these represents a distinct I2C bus controller on your SoC. The number of buses varies significantly between devices.
Step 3: Installing I2C Tools on Android
Most Android devices do not come with `i2c-tools` pre-installed. You’ll need to obtain ARM/ARM64 static binaries and push them to your device. Pre-compiled binaries can often be found in custom ROM repositories, Magisk modules, or you can cross-compile them yourself from the `i2c-tools` source code.
For simplicity, let’s assume you have a static `i2cdetect`, `i2cdump`, `i2cget`, and `i2cset` binary (e.g., for `arm64`) on your host machine in the current directory.
adb push i2cdetect /data/local/tmp/adb push i2cdump /data/local/tmp/adb push i2cget /data/local/tmp/adb push i2cset /data/local/tmp/
Now, on your device’s shell, make them executable:
chmod +x /data/local/tmp/i2cdetectchmod +x /data/local/tmp/i2cdumpchmod +x /data/local/tmp/i2cgetchmod +x /data/local/tmp/i2cset
You can now execute them directly using their full path, e.g., `/data/local/tmp/i2cdetect`.
Step 4: Enumerating I2C Devices with `i2cdetect`
Now, we can scan each identified I2C bus for connected devices. `i2cdetect` attempts to probe common 7-bit I2C addresses and reports which ones respond. The `-y` flag disables interactive prompts, and `<bus_number>` is the numerical part from `/dev/i2c-<bus_number>`.
Let’s scan bus 0 as an example:
/data/local/tmp/i2cdetect -y 0
The output is a grid showing detected device addresses. `UU` indicates a device detected, while `–` means no device responded at that address. `0x` is the standard prefix for hexadecimal numbers.
0 1 2 3 4 5 6 7 8 9 a b c d e f00: -- -- -- -- -- -- -- -- -- -- -- -- --10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --50: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- --60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
In this example, a device is detected at address `0x50` on bus 0. This is a common address for I2C EEPROMs.
Repeat this process for all identified buses (e.g., `i2cdetect -y 1`, `i2cdetect -y 2`, etc.) to get a full picture of connected I2C peripherals.
Step 5: Dumping EEPROM Data with `i2cdump`
Once you’ve identified a potential EEPROM (e.g., at `0x50`), you can dump its contents. `i2cdump` allows reading a block of memory from an I2C device.
Syntax: `/data/local/tmp/i2cdump -y <bus_number> <device_address> [mode]`
Common modes:
- `b`: Byte mode (default, reads 8-bit registers).
- `w`: Word mode (reads 16-bit registers).
- `c`: Combined mode (byte mode with `read_word_data` for 16-bit addressing, common for larger EEPROMs).
- `i`: I2C mode (uses specific I2C transactions, often required for certain PMICs or sensors).
For a typical EEPROM, byte mode (`b`) is usually sufficient, possibly `c` for larger ones. Let’s dump the device at `0x50` on bus 0:
/data/local/tmp/i2cdump -y 0 0x50 b
The output will be a hexadecimal dump, typically 256 bytes by default, showing memory addresses on the left and data bytes on the right, along with an ASCII representation (if printable). For a 24Cxxx series EEPROM, this would look like:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef00: 41 6E 64 72 6F 69 64 20 45 45 50 52 4F 4D 20 44 Android EEPROM D10: 75 6D 70 00 01 02 03 04 05 06 07 08 09 0a 0b 0c ump.............20: 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c ................... (continues for 16 lines or more)
To save the dump to a file on your device and then pull it to your host machine:
/data/local/tmp/i2cdump -y 0 0x50 b > /data/local/tmp/eeprom_bus0_addr50.bindb pull /data/local/tmp/eeprom_bus0_addr50.bin .
You can then analyze `eeprom_bus0_addr50.bin` with a hex editor (like `HxD` or `GHex`) or use tools like `strings` to look for readable text.
Step 6: Reading and Writing Register Configurations with `i2cget` and `i2cset`
Beyond dumping contiguous memory, `i2cget` and `i2cset` allow precise interaction with individual device registers. This is crucial for understanding device states, configuring sensors, or debugging PMICs. **Warning: Incorrect use of `i2cset` can cause device instability or damage. Always consult the device’s datasheet before writing to registers.**
Reading a Register with `i2cget`
To read a specific register from an I2C device, you need to know its register address (often 8-bit or 16-bit). This information is typically found in the device’s datasheet. Let’s assume a device at `0x42` on bus 1 has a status register at `0x01`.
Syntax: `/data/local/tmp/i2cget -y <bus_number> <device_address> <register_address> [mode]`
/data/local/tmp/i2cget -y 1 0x42 0x01 b
Output will be a single hexadecimal byte, e.g., `0xAA`.
Writing to a Register with `i2cset`
To modify a register, you use `i2cset`. This requires knowing the register address and the desired value, all in hexadecimal. Again, *exercise extreme caution*.
Syntax: `/data/local/tmp/i2cset -y <bus_number> <device_address> <register_address> <value> [mode]`
Let’s write `0x55` to register `0x01` of the device at `0x42` on bus 1:
/data/local/tmp/i2cset -y 1 0x42 0x01 0x55 b
Some devices might require 16-bit word writes or specific bit-mask operations; consult the datasheet for details. The `i2cset` tool supports various modes (e.g., `w` for 16-bit words) and can handle combined reads/writes for certain register types.
Advanced Considerations
While `i2c-tools` are powerful, some advanced scenarios might require further steps:
- Kernel Drivers: Sometimes, I2C devices are tightly controlled by kernel drivers, making direct `i2c-tools` access difficult or leading to conflicts. You might need to unload the driver temporarily (if possible and safe).
- Device Tree Overlays: On modern Android kernels, I2C devices are often defined in the device tree. Analyzing `/proc/device-tree/` or decompiling `dtb` files can reveal device addresses, register maps, and driver bindings.
- Hardware Sniffing: For protocols that are not standard I2C or where direct interaction is failing, a logic analyzer can be used to sniff I2C communication directly on the hardware pins, providing an unbiased view of the data.
Conclusion
Mastering I2C interaction on a rooted Android device is an essential skill for anyone involved in hardware reverse engineering, security research, or embedded systems development. By following the steps outlined in this tutorial, you can effectively enumerate I2C devices, dump critical EEPROM data, and read/write device register configurations. This capability provides deep insights into the low-level operation of your Android device’s peripherals, paving the way for advanced analysis, custom modifications, and understanding proprietary hardware implementations. Always proceed with caution, especially when writing to device registers, and prioritize understanding the target hardware’s specifications.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →