Featured post

Acer 3820TG BIOS mod for a replacement battery

I recently bought a replacement battery for my Acer (Aspire 3820TG-5454G50n to be exact) laptop from TradeShop in EBay.

img_20160902_002725

The new replacement battery at top and the dead one below

After receiving it and inserting it into the laptop nothing happens, the charging led starts immediately flashing like there were a problem. The problem was that BIOS did not recognize the battery, nothing was visible in BIOS nor in Linux. It was like there were no battery attached at all. Even though the laptop was able run on battery it did not receive any charge.

Searching for the net and various forums for a solution, one suggestion repeating itself was to update to the latest BIOS from Acer. Downloaded BIOS version 1.19 from Acer, created DOS rescue USB stick and reflashed the BIOS… Update was successful but no luck with the battery, still not recognized nor charging.

Browsing the forums led to the schematics of the laptop [1] which showed that the battery is handled by a separate microcontroller called KBC and it talks to battery over i2c bus.

kbc

kbc_i2c

bat-if

In the BIOS update package there were a sub-directory KBC with an executable and a bin-file. Looking at the bin file, named W07AC115.bin, it looked very much like it was the firmware image for KBC. It was not encrypted so it was easy to see what is was made of. It contained something very much like battery names:

% strings W07AC115.bin | grep AS10 
!AS10B3E
AS10B5E
!AS10B6E
!AS10B7E
!AS10E7E
!AS10E76
!AS10E36

The original battery type is AS10E7E and the replacement is AS10E76 and both names seem to be included in the firmware image. So things should work just fine.

Obviously the BIOS cannot read the sticker on the battery but gets the battery name, type and other information from KBC which in turn talks to the battery over i2c bus using SBS (Smart Battery Specification) [2] protocol. So maybe the battery identifies itself with some other, non-compatible, name? Or the replacement battery is just broken and should be replaced.

Arduino UNO i2c battery reader

The SBS protocol is very simple, writing command bytes and reading reply back. The battery connector pin order was in the schematics:

   1 2 3 4 5 6 7 8
++ X X X X X X X X --

pin
1 2     +12.6V / 1.5 - 2.0A
5       SCL
6       SDA
7 8     GND

All that needs to be done is wire i2c and GND (battery has its own power) from Arduino to the battery connector. The interface board consists just two 1kOhm pull-up resistors to 3.3V from Arduino I/O pins to battery connector pins. rig

Arduino              Battery
3v3 -----+--+
         |  |
         R  R 1kOhm
         |  |
SCL -----|--+------- SCL (5)
SDA -----+---------- SDA (6)
GND ---------------- GND (8)

Using Arduino software based i2c library SoftI2CMaster [3] gives freedom to use any available I/O pin for i2c instead of dedicated but fixed i2c pins.

Requesting and reading strings and integers from the battery is done by using the commands from SBS. Battery is the device 11 on the i2c bus, all other addresses return nothing.

#define SDA_PIN 3 
#define SDA_PORT PORTD 
#define SCL_PIN 2
#define SCL_PORT PORTD
#define I2C_VERYSLOWMODE 1
#define I2C_TIMEOUT 2000
#define I2C_NOINTERRUPT 1

#include "SoftI2CMaster/SoftI2CMaster.h"

#define VOLTAGE 0x09
#define TEMPERATURE 0x08
#define CURRENT 0x0a
#define CAPACITY 0x10
#define TIME_TO_FULL 0x13
#define CHARGE 0x0d
#define STATUS 0x16
#define CYCLE_COUNT 0x17
#define MANUFACTURE_DATE 0x1b
#define MANUFACTURER_NAME 0x20
#define DEVICE_NAME 0x21
#define DEVICE_CHEMISTRY 0x22

static byte deviceAddress = 11;
static byte buf[64];

static int fetchString(byte func) {
  i2c_start(deviceAddress<<1 | I2C_WRITE);
  i2c_write(func);
  i2c_rep_start(deviceAddress<<1 | I2C_READ);

  byte i;
  byte size;
  size = i2c_read(false);
  if (size > sizeof(buf)-1)
    size = sizeof(buf)-1;
  for (i = 0; i < size-1; i++) {
    byte b1 = i2c_read(false);
    buf[i] = b1;
  }
  byte b1 = i2c_read(true);
  buf[i] = b1;
  buf[i+1] = 0;

  i2c_stop();

  return size;
}  

static int fetchWord(byte func) {
  i2c_start(deviceAddress<<1 | I2C_WRITE);
  i2c_write(func);
  i2c_rep_start(deviceAddress<<1 | I2C_READ);

  byte b1 = i2c_read(false);
  byte b2 = i2c_read(true);
  i2c_stop();

  return (int)b1|((( int)b2)<<8);
}

Then it is just a matter of asking and printing replies back, like:

printString("Device name: ");
fetchString(DEVICE_NAME);
printString((const char *)buf); 
printNewline();

Source code for Arduino Uno is available in Github [4]. SoftI2C library had to be set running with slowest bitrate and even then there were occasional data transmission errors. A simple patch for enabling even slower speed resolved the issue:

diff --git a/SoftI2CMaster.h b/SoftI2CMaster.h
index 7393a8d..af70577 100644
--- a/SoftI2CMaster.h
+++ b/SoftI2CMaster.h
@@ -159,9 +159,13 @@ uint8_t __attribute__ ((noinline)) i2c_read(bool last);
 #if I2C_SLOWMODE
 #define I2C_DELAY_COUNTER (((I2C_CPUFREQ/25000L)/2-19)/3)
 #else
+#if I2C_VERYSLOWMODE
+#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/12000L)/2-19)/3)
+#else
 #define I2C_DELAY_COUNTER (((I2C_CPUFREQ/100000L)/2-19)/3)
 #endif
 #endif
+#endif
 
 // Table of I2C bus speed in kbit/sec:
 // CPU clock:           1MHz   2MHz    4MHz   8MHz   16MHz   20MHz

Using the above quickly whipped up rig revealed that the battery identifies itself as AS10B41, which is not in the above list of battery names supported by the KBC firmware.
screen

Patching the KBC

Changing one of the battery names in the KDC firmware image file would be an easy solution, unless the firmware is protected by a checksum or something. As the KBC controller is responsible of the keyboard, mousepad, fans, and about everything messing with it just might brick the whole laptop.

In some of the discussion forum thread had quite definite consensus that there is no checksum at all. So I changed one battery name (using emacs hexl-mode) to AS10B41 and copied it over to the DOS USB stick in KBC sub-directory as W07AC115.bin (first making backup of the original firmware file). After booting from the USB stick to DOS mode, reflashing only the KBC by running the KBC.BAT.

Instantly after hitting return, it was very obvious that things were not good… fans went to full blow, screen flickered, mousepad not working. Luckily keyboard was sort of still working and I had the original image in the USB stick. Renamed it back to W07AC115.bin and ran KBC.BAT again. Instantly things were back to normal. Maybe there is a checksum after all…

Going through the forums more another checksum candidate was coming up, a simple byte-wise modulo 256 sum of all bytes from range between 0x8000 – 0x20000 should add up to 92 (dec). At least for the original file that was correct:

% ruby -e 'puts IO.binread("W07AC115.bin", 0x20000-0x8000, 0x8000).each_byte.reduce(0, &:+) & 0xff'
92

So a change from AS10E76 to AS10B41 introduces a difference of 11 in the checksum and which must be compensated.

% echo -n E76 B41 | hexdump -C
00000000 45 37 36 20 42 34 31 |E76 B41|
% echo $(((0x45 + 0x37 + 0x36) - (0x42 + 0x34 + 0x31)))
11

In the same forum thread there were speculation of a certain address which contains the checksum byte, but instead of changing a random byte at random address, why not sacrifice one more battery name by having one of its letters to be incremented by 11. Changing e.g. AS10E36 to LS10E36 would do, as the difference between ‘A’ and ‘L’ is 11. Rechecking the checksum of the changed firmware binary using above Ruby script showed it was correct.

Then reflashing the modified KBC firmware again as above, and… battery was recognized and everything was ok with the laptop! And the battery gets charged! And Linux also knows about it:

% grep . /sys/class/power_supply/BAT0/{model*,manu*,charge*,tech*}
/sys/class/power_supply/BAT0/model_name:AS10B41
/sys/class/power_supply/BAT0/manufacturer:SONYCorp
/sys/class/power_supply/BAT0/charge_full:6394000
/sys/class/power_supply/BAT0/charge_full_design:6600000
/sys/class/power_supply/BAT0/charge_now:6161000
/sys/class/power_supply/BAT0/technology:Li-ion

References

If the links get broken try to google the filename.
[1] acer_aspire_3820_3820g_3820t_3820tg_wistron_jm31-cp_rev_-1_sch.pdf
[2] sbdat110.pdf
[3] SoftI2CMaster library
[4] Arduino code in Github

Reveal saved Mozilla Firefox passwords

Firefox stores website, user name and password information in encrypted form in the logins.json file located in ~/mozilla/firefox/*.default directory. This is how passwords can be revealed (assuming you know the password) quickly on command line if no Firefox happens to be around.

Pretty printed using jq (a handy Swiss knife of json), logins.json file looks like this:

% jq . < logins.json
{
 "version": 1,
 "nextId": 2,
 "logins": [
   {
     "id": 1,
     "hostname": "http://acme.dot.com",
     "httpRealm": null,
     "formSubmitURL": "http://acme.dot.com",
     "usernameField": "username",
     "passwordField": "password",
     "encryptedUsername": "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCGOHEBzuDyqBBCKuLsDGrpovzfTGSuzNLJu",
     "encryptedPassword": "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECHw9Rnls6h9IBBCjpngvOZX25Hbpvmne2qEr",
     "guid": "{e611000f-e3a3-49cb-b24e-c3c1f898398b}",
     "encType": 1,
     "timeCreated": 1479141437109,
     "timeLastUsed": 1479141437109,
     "timePasswordChanged": 1479141437109,
     "timesUsed": 1
   }
 ],
 "disabledHosts": []
}

The entries encryptedUsername and encryptedPassword are encrypted with keys in cert8.db and key3.db (both are encrypted Sqlite database files). Dealing with encrypted sqlite db on shell is sort of challenging, but fortunately there is an utility pwdecrypt (from libnss3-tools Debian package). It will dig out the keys from those db-files and decrypt encrypted lines, other lines are output in verbatim.

Relevant attributes should be extracted first from the json with jq

% jq -r -S '.logins[] | .hostname, .encryptedUsername, .encryptedPassword' logins.json
http://acme.dot.com
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCGOHEBzuDyqBBCKuLsDGrpovzfTGSuzNLJu
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECHw9Rnls6h9IBBCjpngvOZX25Hbpvmne2qEr

then each line can be fed to pwdecrypt (-d gives the directory where db-files are located). If passwords are stored with master password, that should be given with -p (here master password is foobar):

% jq -r -S '.logins[] | .hostname, .encryptedUsername, .encryptedPassword' logins.json | pwdecrypt -d . -p foobar
http://acme.dot.com
Decrypted: "JoeLoser"
Decrypted: "verysekret"

With little more shell around whole file can be decrypted and printed out in clear text:

% jq -r -S '.logins[] | .hostname, .encryptedUsername, .encryptedPassword' logins.json |
 pwdecrypt -d . -p foobar |
 while read H && read U && read P ; do
   printf "%40s %20s %s\n" "$H" "${${U#Decrypted: \"}:0:-1}" "${${P#Decrypted: \"}:0:-1}"
 done
                     http://acme.dot.com             JoeLoser verysekret

Update:
If you get a cryptic error message from like

pwdecrypt: NSS_Init failed: SEC_ERROR_LEGACY_DATABASE: The certificate/key database is in an old, unsupported format.

Then change all occurrences of option -d . to -d sql:. (note the dot at the end) in pwdecrypt command, i.e. as to

pwdecrypt -d sql:. -p foobar