Introduction

Two months ago, I bought a new battery for my Lenovo laptop (a ThinkPad X230T). I was about to go away on holidays and wanted a battery that could last me through a plane flight; the original battery was by then barely lasting ten minutes. Little did I know that I was about to be in for an adventure.

I installed the new battery, and everything went fine for a few hours… that is, until I had to plug the laptop in to charge. The battery was not charging. Odd. I rebooted, only to find this message displayed on the screen by the BIOS:

The system does not support batteries that are not genuine Lenovo-made or authorized. The system will continue to boot, but may not charge unauthorized batteries. ATTENTION: Lenovo has no responsibility for the performance or safety of unauthorized batteries, and provides no warranties for failures or damage arising out of their use. Press the ESC key to continue.



“May not charge”? That was quite a non-definite message, but this battery was definitely not charging, even with the laptop off. Oh dear.

Now certainly at this point it was possible that the battery was faulty in addition to being non-genuine, but I did not like this idea of Lenovo locking me down to using only ‘genuine’ batteries. Firstly, I had a battery in my hands that might have been working perfectly fine otherwise. Secondly, it meant that Lenovo could charge artificially high prices for their ‘genuine’ batteries. Thirdly, should Lenovo discontinue replacement batteries for my laptop, I would indeed be left with a useless laptop.

First steps: investigating the battery

My first avenue of investigation would be to ‘sniff’ the communication between the laptop and the two batteries, to compare the replacement battery to the original battery. I easily found the pin-out for Lenovo batteries on the Internet, which for easy reference is:

Pin Name Description 1,2 VBATT (+) Battery voltage (or charging voltage) 3 SCL SMBus clock 4 SDA SMBus data 5 T Thermistor 6,7 GND (-) Ground

In this case I was interested in the communication that happens via the SMBus pins, SCL and SDA. To sniff this communication, I used a basic logic analyser that I had at hand (a USBee SX). Conveniently, there is enough spare length in the battery contacts that it is possible to tap out signals while the battery is connected to the system as you can see in this photo:

(Trick 1: I soldered two 2-pin headers end-to-end ( ) such that the end pins could be wedged around the SDA/SCL contacts, the middle could pass through the narrow gap to the outside of the laptop, and the logic analyser could be connected to the end header.)

(Trick 2: Ground can be attached to the VGA connector, this is a generally useful trick when probing laptops.)

The USBee SX software has the advantage of having built-in I2C/SMBus decoding at a single click. I don’t find the output particularly readable though, so I put it through a quick one-liner to clean it up a bit (perl -pe ‘s/

/ /gs’ |sed ‘s/[[:space:]]\+/ /g;s/START/

START/g;s/ACK \([0-9A-F]\) /ACK 0\1 /g;s/ ACK / /g’). I’ve uploaded the SMBus captures from the original and replacement batteries here and here for those interested.

Now, for those not in the know, most modern smart batteries implement a standard called Smart Battery Specification (SBS). The communication that we’ve just captured is, indeed, based around the Smart Battery Specification, albeit using a few vendor-specific commands.

I’ve decoded the communication according to the SBS protocol below. I’ve interleaved the two batteries, with green being the original battery and black being the replacement battery. I’ve called out interesting lines with bold red annotation that I’ll refer to below.

Read SpecificationInfo -> 0x0031 (SBS 1.1 with PEC support)

Read SpecificationInfo -> 0x0031 (SBS 1.1 with PEC support)

Read BatteryMode -> 0xe004 (No internal charge controller, etc.)

Read BatteryMode -> 0xe005**bad PEC checksum** (1)

Read BatteryMode -> 0xe005 (Internal charge controller present, etc.) (2)

Write BatteryMode 0xe004

Write BatteryMode 0xe005

Read BatteryMode -> 0xe004

Read BatteryMode -> 0xe005

Read DesignCapacity -> 0x1848 (62.16Wh)

Read DesignCapacity -> 0x1314 (48.84Wh)

Read DesignVoltage -> 0x2b5c (11.1V)

Read DesignVoltage -> 0x2b5c (11.1V)

Read ManufactureDate -> 0x4308 (2013-08-08)

Read ManufactureDate -> 0x46f2 (2015-07-18)

Read SerialNumber -> 0x036d (877)

Read SerialNumber -> 0x18c7 (6343)

Read ManufacturerName -> “LGC 11”

Read ManufacturerName -> “SANYO” (3)

Read DeviceName -> “LNV-45N1079”

Read DeviceName -> “LNV-45N1079”

Read DeviceChemistry -> “LION”

Read DeviceChemistry -> “LION”

(authentication sequence 1)

Write OptionalMfgFunction4 0x39 0x55 0x48 0x28

Read OptionalMfgFunction4 -> “Lenovo Japan” 0x3b 0x7b 0x8c 0x44

Write OptionalMfgFunction4 0x0f 0x35 0x48 0x28

Read OptionalMfgFunction4 -> “”

Write OptionalMfgFunction4 0x10 0x68 0x48 0x28**fail (NACK)**

Write OptionalMfgFunction4 0x10 0x6f 0x48 0x28

Read OptionalMfgFunction4 -> “”

Write OptionalMfgFunction4 0x11 0xa3 0x48 0x28**fail (NACK)**

Write OptionalMfgFunction4 0x11 0xb1 0x48 0x28

Read OptionalMfgFunction4 -> “”

Write OptionalMfgFunction4 0x12 0xe5 0x48 0x28**fail (NACK)**

Write OptionalMfgFunction4 0x12 0xec 0x48 0x28

Read OptionalMfgFunction4 -> “”

Write OptionalMfgFunction4 0x14 0x20 0x48 0x28**fail (NACK)**

Read unknown 0x35 -> 0x00c0

Read unknown 0x37 -> 0x01 0x00 0x3A 0x00 0x00 0x01 0xFB 0x01

Read OptionalMfgFunction5 -> “1ZL1J38L1W3”

Read OptionalMfgFunction5 -> “” (4)

Read OptionalMfgFunction2 -> 0x0021 (33)

Read OptionalMfgFunction2 -> 0x0021 (33)

Read OptionalMfgFunction1 -> 0x4325 (17189)

Read OptionalMfgFunction1 -> 0x42a3 (17059)

Read ManufacturerAccess -> 0x0010

Read ManufacturerAccess -> 0x0018

Read BatteryStatus -> 0x02a0 (FULLY_CHARGED, etc.)

Read BatteryStatus -> 0x02c0 (DISCHARGING, etc.)

Read ChargingCurrent -> 0x0000 (0)

Read ChargingCurrent -> 0x0dac (3500mA)

Read ChargingVoltage -> 0x0000 (0)

Read ChargingVoltage -> 0x3138 (12.6V)

Read Temperature -> 0x0bcf (302.3°K)

Read Temperature -> 0x0bd5 (302.9°K)

Read unknown 0x3b -> 0x0bd6 (303.0°K)

Read unknown 0x3b -> 0x0bc5 (301.3°K)

Read Voltage -> 0x30fd (12.541V)

Read Voltage -> 0x2870 (10.352V)

Read RemainingCapacity -> 0x00c1 (1.93Wh)

Read RemainingCapacity -> 0x0146 (3.26Wh)

Read FullChargeCapacity -> 0x00c1 (1.93Wh) (5)

Read FullChargeCapacity -> 0x1215 (46.29Wh)

Read Current -> 0x0000 (0)

Read Current -> 0x0000 (0)

Read RunTimeToEmpty -> 0xffff (N/A)

Read RunTimeToEmpty -> 0xffff (N/A)

Read AverageCurrent -> 0x0000 (0)

Read AverageCurrent -> 0x0000 (0)

Read AverageTimeToFull -> 0xffff (N/A)

Read AverageTimeToFull -> 0xffff (N/A)

Read AverageTimeToEmpty -> 0xffff (N/A)

Read AverageTimeToEmpty -> 0xffff (N/A)

Read CycleCount -> 0x05d5 (1493)

Read CycleCount -> 0x0001 (1)

Read MaxError -> 0x0000 (0%)

Read MaxError -> 0x0001 (1%)

Read RelativeStateOfCharge -> 0x0064 (100%)

Read RelativeStateOfCharge -> 0x0007 (7%)

Read ManufacturerData -> 0x03 0x32 0x01 0x32 0x00 0x00 0x16 0x10 0x59 0x10 0x8D 0x10 0x08 0x10

Read unknown 0x30 -> 0x0A 0x7F 0xAE 0x59 0x50 0x5E 0x27 0x00 0x00 0x00 0x00

(authentication sequence 2)

Write unknown 0x27: 0x44 0x91 0x11 0x45 0xB2 0x77 0xFC 0x5C 0x5D 0x00 0xCF 0xE9 0x7B 0x72 0xE1 0x2E 0x03

Read unknown 0x28 -> 0xA6 0xCB 0x36 0x12 0xEF 0x36 0xF6 0x41 0x9B 0xB7 0xB7 0xDC 0xD5 0x9F 0xD1 0x36 0x5C 0xA0 0x07 0x3F 0xDF 0x4A 0xC6 0x2E 0x00

First, some minor differences:

The replacement battery has a bug / feature where it generates the wrong packet checksum (PEC) on the first access (1) . However, the laptop is happy enough trying again.

. However, the laptop is happy enough trying again. The replacement battery reports that it has an integrated charge controller whereas the original battery doesn’t (2) .

. The replacement battery reports a manufacturer of SANYO whereas the original battery reports a manufacturer of LGC (3) . In fact, Lenovo batteries are made by both Sanyo and LG Chem; I suspect that whoever made my replacement battery just happened to be cloning a Sanyo-made battery.

. In fact, Lenovo batteries are made by both Sanyo and LG Chem; I suspect that whoever made my replacement battery just happened to be cloning a Sanyo-made battery. The original battery responds to the manufacturer-specific function OptionalMfgFunction5 with something that looks like a serial number (4) .

It turns out that all of these minor differences can be ignored. The interesting parts are the authentication sequences:

Authentication sequence 1 : The laptop writes 4 bytes using the manufacturer-specific command OptionalMfgFunction4 (0x3c) and then reads 12+4 bytes using the same command. In this case, the outgoing message sends a four byte challenge and the response is the string “Lenovo Japan” followed by a four byte response that depends on the challenge (this is a form of challenge-response authentication).

: The laptop writes 4 bytes using the manufacturer-specific command OptionalMfgFunction4 (0x3c) and then reads 12+4 bytes using the same command. In this case, the outgoing message sends a four byte challenge and the response is the string “Lenovo Japan” followed by a four byte response that depends on the challenge (this is a form of challenge-response authentication). Authentication sequence 2 : The laptop writes 17 bytes with command 0x27 and reads 8+17 bytes with command 0x28. These commands are not defined in the Smart Battery Specification. Again, it will turn out that this is a sort of challenge-response authentication.

It is not clear why there are two different authentication steps, but perhaps the first one has previously been broken: this is, after all, a cat-and-mouse game between Lenovo and those who have an interest in circumventing their scheme. Neither authentication scheme is implemented in my replacement battery.

(The line marked (5) is just to draw attention to the embarrassing situation where my 62Wh original battery, after two years, now has a full capacity of just under 2Wh.)

Attacking the problem

Now that we understand approximately how the battery is authenticated, the next question is how we might circumvent this authentication. There are a number of possible options.

Option 1: Replace just the used Li-Ion cells in the original battery, preserving the original controller

This is a common approach to battery reconditioning. However, there are a number of pitfalls. Getting the Lenovo batteries apart necessarily involves damaging the plastic to some extent, as the seams have both latches and glue. Also, replacing cells needs to be done carefully as some controllers can brick themselves if the battery voltage is disconnected (I do not know whether this is the case for the Lenovo batteries). The problematic corollary is that the controller will remember data about the old cells, and I’m not sure what effect this might have.

I decided not to go down this path except as a last resort. I did – after getting my replacement battery working – pull apart the original battery for my own edification, and I might comment more on this in a later post.

Option 2: Modify the firmware on the non-genuine battery to emulate the genuine battery

“Modify the firmware on a battery?”, you say incredulously. Yes, in fact smart batteries run firmware, and usually this firmware is updateable. For example, the TI BQ20Z80 battery fuel gauge chip is known to have an embedded ARC microprocessor and flash memory, and has been reverse engineered sufficiently that it is in fact practical to download/upload/modify the firmware (see Charlie Millers’s Blackhat talk on Battery Firmware Hacking).

There are two angles to this. Firstly, if we could download the firmware from the genuine battery, we would perhaps be able to work out the authentication algorithm. Secondly, if we could upload new firmware to a non-genuine battery, we could implement the authentication algorithm on the new battery.

At this point I hooked up the SMBus lines of both batteries to a microcontroller so I could send commands to them independent of the laptop. I used an Arduino just because it’s what I had readily at hand, although any microcontroller (preferably 3.3V) would work fine.

Unfortunately, I got nowhere with this: while all the standard SBS commands worked, neither battery responded to any TI vendor-specific commands that I tried. This makes me think that neither uses a TI chip or they are well locked down. The non-genuine battery responded to ManufacturerAccess command 0x0001 with 0x2168 (some sort of device ID perhaps?), while the genuine battery did not appear to respond to ManufacturerAccess in any useful way.

Given that I had no idea what chip was inside either battery – and even if I knew that, I might not have been able to get enough information to hack the firmware – I decided to shelve this option temporarily.

Option 3: Interpose between the laptop and the battery

Another option would be to add a small embedded microcontroller in between the laptop and battery which would answer the authentication commands if the battery cannot. (In fact it may not be necessary to interpose in the strict sense, it is probably sufficient just to sit on that bus.)

There is some spare space in the Thinkpad’s mini-PCIe slot so it would be possible to run wires from the SMBus lines down to that bay. However, adding extra electronics to the laptop is non-ideal, and at this point we still don’t know how to actually calculate the responses to the authentication challenges.

Option 4: Modify the ThinkPad embedded controller firmware

Clearly if we could modify the laptop to skip the battery authentication, that would solve all of our problems. Since the battery authentication happens even when the laptop is switched off, it cannot be BIOS that is doing the authentication (BIOS runs on the main system processors, and does not run when the system is switched off). Therefore the authentication must be being performed by another part of the system: the embedded controller.

We turn to the embedded controller in part 2 of this series…