始まりは、ヤフオクで落札した1台の IBM ThinkPad X30 だった。
出品ページには「スーパーバイザーパスワード(SVP)が設定されています」と最初から明記されていた。電源を入れればパスワードのプロンプトで弾かれ、OSが起動しないどころかBIOS設定画面にすら入れない ―― それを承知のうえで入札した。
理由は単純で、「EEPROMのピンをショートさせればSVPは解除できる」という手法をネットで見つけていたからだ。この世代のSVPはCMOS電池を抜いても消えず、基板上のEEPROMに保存されている。だから電池リセットは効かないが、逆にそのチップさえ相手にすれば解除できる ―― そう読んで、解除できる前提でジャンク機を買ったわけである。
そして実際にその手法を試し ―― 機体を完全な文鎮(起動不能)にしてしまった。
この記事は、そこからEEPROMを直接読み書きして救出するまでの記録だ。読み物としてだけでなく、同じ症状に出くわしたときの手順書として使えるように、解析の過程をそのまま書く。結論から言えば、壊れていたのはたった2バイトだった。


⚠️ 本記事は自分で所有する機体に対する修復・調査の記録です。他人の機器のロック解除を推奨するものではありません。またEEPROMのダンプにはシリアル番号・UUID・MACアドレスが含まれるため、公開にあたっては伏せています。
| 対象機 | IBM ThinkPad X30 |
| 症状 | 0175: Bad CRC1, stop POST task → 起動不能 |
| 原因 | SDA/SCLショート法の誤用によるEEPROM破損 |
| 主な道具 | Arduino Uno(専用プログラマ不要) |
| 壊れた量 | 2バイト |
| 結果 | 全エラー解消・完動。元のパスワードまで判明 |
前提:なぜCMOSクリアでBIOSパスワードが消えないのか
X30世代のスーパーバイザーパスワードは、CMOSバックアップ電池を抜いても消えない。マザーボード上の ATMEL 24RF08 ―― I2C接続のEEPROM に保存されているからだ。電池で保持しているCMOS RAMとは別の、電源が落ちても中身が残る不揮発メモリである。
だからCMOSクリアは無力で、このチップへ直接アクセスするしかない。

U37 の位置にある8ピンSOIC、ATMEL 24RF08CN(中央左)。そして右下の黄色い包みが CMOSバックアップ電池(Panasonic製リチウム電池)だ。この電池を抜いてもパスワードは消えない ―― 保存先が電池ではなく左のチップだから、というのがこの1枚に写っている。手前の 32.768k はRTC用のクリスタル。そして、そのチップを相手にする定番の攻略法が「起動中に SDA/SCL ピンを一瞬ショートさせ、パスワード照合の読み出しを化けさせて検証を素通りさせる」というものだ。私がこの機体を落札した決め手も、この手法の存在だった。さっそく試した ―― これが地獄の入口だった。
事故:ショート法で文鎮化する(0175 Bad CRC1)
I2Cは SDA(データ)と SCL(クロック)が独立して上下することで通信が成立する。ところが「ショートし続ければいい」と誤解し、両ピンを繋ぎっぱなしにしてしまった。両線が同電位に固定されればバスは死に、BIOSはEEPROMの応答を待って固まる。何度かリトライした末に――
0175: Bad CRC1, stop POST task
POSTが完全停止した。OSはおろかBIOS設定にもフロッピー起動にも進めない。ショートのタイミング次第でEEPROMが「読み出し」ではなく「書き込み」と誤動作し、中身の一部が破壊されてしまったのだ。完全な文鎮化である。
ハマりどころ その1 ―― ショート法は「一瞬だけ触れて即離す」技。繋ぎっぱなしはバスを殺すうえ、EEPROMそのものを壊す。そして着手前にダンプを取っておけば、この後の苦労はまるごとゼロだった。
方針転換:Arduino UnoでEEPROMを吸い出す
壊れたのがEEPROMのデータなら、中身を直接読んで何が壊れたかを特定し、直せばいい。24RF08はI2Cなので、Arduino Uno一枚で読み書きできる。チップの3本のピンに極細線をはんだ付けし、Arduinoへ繋ぐ。
| チップ側ピン | 役割 | Arduino Uno |
|---|---|---|
| 5 | SDA(データ) | A4 |
| 6 | SCL(クロック) | A5 |
| 4 | GND | GND |
VCCは繋がない。チップの電源はThinkPad本体側から供給させる。

ANALOG IN の A4(SDA)と A5(SCL)へ差し込む。Unoでは I2C がこの2本に固定されている。
ハマりどころ その2:チップに電源が来ていない
最初は全アドレスがタイムアウトして焦った。原因は単純で、チップに電源が来ていなかっただけ。24RF08は本体側から給電されるので、ThinkPadを「0175で固まった状態」のまま維持する必要がある。テスターで実測すると、電源OFF時は 0.2V、起動後は 3.3V。これで解決した。
まずI2Cアドレスをスキャンする
#include <Wire.h>
void setup(){
Wire.begin();
Wire.setWireTimeout(25000, true); // 応答が無いとき固まらず timeout させる
Serial.begin(9600); while(!Serial);
for(uint8_t a=0x50; a<=0x57; a++){
Wire.beginTransmission(a);
if(Wire.endTransmission()==0){
Serial.print("Found 0x"); Serial.println(a,HEX);
}
}
}
void loop(){}
応答したのは 0x54 0x55 0x56 0x57 の4アドレス。24RF08は256バイト×4ブロックが別々のI2Cアドレスに割り当たる構造だ。オフセットを指定して各ブロックを読み出すと――データは全消去されておらず、シリアル番号やMACアドレスらしき情報も生きていた。希望が見えた。
解析:壊れたのは、たった2バイトだった
ここが今回の山場。ダンプを眺めていると、ブロック 0x57 によく似た8バイトのレコードが2つ並んでいるのに気づく。
0x57 offset 56: 2E 16 1F 14 18 32 00 C1
0x57 offset 64: 2E 16 1F AE 42 32 00 C1
~~~~~ ここだけ違う
この世代のThinkPadのパスワードは「7バイトのデータ + 1バイトのチェックサム(単純な8ビット合計)」形式で、同じ値を2箇所に持っている。そこで各レコードの合計を計算して、格納されたチェックサムと突き合わせた。
| スロット | データ(7バイト) | 格納CS | 正しい合計 | 判定 |
|---|---|---|---|---|
| A(offset 56) | 2E 16 1F 14 18 32 00 |
C1 |
C1 |
✅ 一致 |
| B(offset 64) | 2E 16 1F AE 42 32 00 |
C1 |
85 |
❌ 不一致 |
決定的だったのは、スロットBに格納されたチェックサム C1 が、スロットAのデータの合計と一致すること。つまり本来AとBは同じ値で、ショートによってBの4〜5バイト目だけが 14 18 → AE 42 に化けたのだ。データだけが書き換わりチェックサムは更新されずに残ったから、0175 Bad CRC1 が出ていた。犯人はこの2バイト。
手計算だけでは不安なので、Pythonで機械的に検証した。
def s8(b): return sum(b) & 0xFF # 8ビット合計
recA = bytes.fromhex("2E161F14183200C1")
recB = bytes.fromhex("2E161FAE423200C1")
for name, r in [("A", recA), ("B", recB)]:
calc, stored = s8(r[:7]), r[7]
print(name, f"calc={calc:02X} stored={stored:02X}",
"OK" if calc == stored else "BAD")
# A calc=C1 stored=C1 OK
# B calc=85 stored=C1 BAD <- 破損確定
ついでに全ブロックを走査し、他に整合の取れないレコードが無いかも確認した。破損はこの1レコードだけ。元の値へ戻せばEEPROMは破壊前の状態へ完全に復元される――それはすなわち、上位のCRCも自動的に整合するということだ。
発見:ついでに元のパスワードまで判明する
修復対象のバイト列 2E 16 1F 14 18 32 00 は、実はパスワードそのものだ。ThinkPadはパスワードをキーボードのスキャンコードで保存している。デコードしてみると――
2E=C 16=U 1F=S 14=T 18=O 32=M 00=(終端)
↓
PASSWORD = "CUSTOM"
ランダムなバイト列ではなく実在する単語になった。デコードの解釈が正しいことの、これ以上ない裏付けだ。「解除」を目指していたのに、元のパスワードまで復元してしまった。解析とはこういう瞬間が楽しい。
修復:安全弁を付けて2バイトを書き戻す
あとは 0x57 のオフセット67・68を、化けた AE 42 から元の 14 18 に書き戻すだけ。事故を繰り返さないよう、「対象が想定どおり壊れた値であること」を確認してから書く安全弁を入れた。
// 書き込み前に現在値を確認 → AE,42 でなければ中止する
if (readByte(0x57,67)!=0xAE || readByte(0x57,68)!=0x42){
Serial.println("想定外の値。中止。"); return;
}
writeByte(0x57, 67, 0x14); // AE -> 14
writeByte(0x57, 68, 0x18); // 42 -> 18
// 書き込み後は必ず読み戻してチェックサムを検証する
// writeByte: beginTransmission → write(offset) → write(val) → endTransmission、
// 直後に 15ms 待つ(EEPROMの書き込みサイクル待ち)
実行結果――
=== AFTER ===
2E 16 1F 14 18 32 00 C1
checksum: computed 0xC1 stored 0xC1 MATCH - repaired!

復活:0175が消え、残りのエラーも自然に片づいた
電源を入れ直すと 0175 は消え、BIOS画面に入れるようになった。Securityを見るとスーパーバイザーパスワードは Disabled。当初の目的も達成である。
ただし今度は別のエラーが顔を出した。
0271: Check date and time settings
0251: System CMOS checksum bad - Default configuration used
0188: Invalid RFID Serialization Information Area or Bad CRC2

0271・0251 はCMOSがリセットされただけの無害なもの。問題は 0188 で、これは別の領域(シリアライゼーション情報)のCRC2を指す。ショートはCRC1だけでなくCRC2領域も巻き込んでいて、CRC1を直してPOSTが先へ進めるようになったことで、それまで隠れていたエラーが表面化した形だ。
同じ手順でCRC2も解析するつもりで身構えたが、結末はあっけなかった。BIOSで日付と時刻を設定して保存(F10)したら、3つとも消えた。設定保存の過程でBIOS自身がCMOSとシリアライゼーション領域のCRC2を書き直したものと思われる。再起動しても再発しない。手動でのCRC2修復は不要だった。
再現手順:もう一度やるための8ステップ
同じ症状(0175 Bad CRC1 でのブリック、あるいは未知のSVP)に出くわしたとき、この順番でやれば最短で済む。
- 何をおいても、先にダンプを取る。壊す前の正常な中身が最強の保険になる。
- チップの
SDA / SCL / GNDに極細線を直付けする。隣ピンとのブリッジをテスターで確認。 - ThinkPadを起動した状態で、
SDA→A4 / SCL→A5 / GND→GNDでArduinoへ接続。VCCは繋がず本体側給電を使う(実測で 3.3V を確認)。 - I2Cアドレスをスキャンする。24RF08は
0x54–0x57に応答。各ブロック256バイトをオフセット指定で読む。 - ダンプ内の「7バイト+1バイト合計チェックサム」レコードを探し、合計が合わないものが破損箇所。
- 同じ値を持つ健全なコピーがあれば、そこから正しいバイトを復元して書き戻す。書き込み前に現在値を確認する安全弁を必ず入れる。
- パスワード文字列が要るなら、バイト列をキーボードのスキャンコードとしてデコードする。
- 復活後に
0271 / 0251 / 0188が出たら、まずBIOSで日時を設定して保存。多くはこれで消える。
まとめ:教訓
- ショート法は最終手段、しかも一瞬だけ。繋ぎっぱなしはI2Cバスを殺すだけでなく、EEPROMの破壊行為に等しい。
- 着手前のダンプが全て。正常な中身さえ手元にあれば、今回の救出は数十秒で終わっていた。
- データは意外と生きている。ダンプさえ取れれば、チェックサムの整合を追うことで多くは復元できる。壊れるのは往々にして、ほんの数バイトだ。
- ThinkPadのパスワードはスキャンコードで保存されている。読めれば元の文字列まで戻せる。
破損2バイト、復元完了。0175 / 0251 / 0271 / 0188 いずれも解消。IBM ThinkPad X30 · ATMEL 24RF08 · Arduino Uno。