[Перевод] NANDкромантия: трансплантация флэш-памяти наживую
Нередко при анализе встроенной системы происходят преднамеренные, а иногда и нет, изменения, которые приводят к тому, что целевая система выходит из строя и впадает в так называемое состояние «кирпича». В некоторых случаях для ее реанимации достаточно выполнить сброс к заводским настройкам, в иных же приходится прошивать систему при помощи интерфейса отладки (JTAG/SWD/*) или вручную через внешнее устройство памяти (SPI/NOR/Nand/eMMC). В данной статье мы рассмотрим весьма креативный метод «раскирпичивания» системы после подобного сбоя.
Как-то в свободное время я возился с памятью маршрутизатора, где в качестве загрузчика использовалась прошивка CFE (Common Software Environment). В ходе моего взаимодействия с CFE в попытке определить аргументы, передаваемые при загрузке в ОС устройства, конфигурация системы была случайно повреждена:
CFE> b
Press: to use current value
'-' to go previous parameter
'.' to clear the current value
'x' to exit this command
94908AC5300R ------ 03
94906REF ------ 07
GT-AC2900 ------ 08
Board Id : 8 X <---- whoops
Number of MAC Addresses (1-64) : 10 ^C <---- more whoops
x
Memory Configuration Changed -- REBOOT NEEDED <---- whoops saved.
flow memory allocation (MB) : 14 ----
В тот момент я понял, что для сохранения этих случайных изменений осталось выполнить заключительную операцию сохранения/записи, поэтому решил просто физически перезапустить устройство, чтобы избежать внесения изменений. После перезапуска возникла ошибка:
Shmoo WR DM
WR DM
0000000000111111111122222222223333333333444444444455555555556666666666
0123456789012345678901234567890123456789012345678901234567890123456789
00 ------++++++++++++++++++++++++++X+++++++++++++++++++++++++++----------
01 --+++++++++++++++++++++++++X++++++++++++++++++++++++++----------------
02 X---------------------------------------------------------------------
03 X---------------------------------------------------------------------
MEMSYS init failed, return code 00000001
MEMC error: 0x00000000
PHY error: 0x00000000
SHMOO error: 0x10c00000
0x00000082
0x00000000
Когда маршрутизатор снова заработал, то тут же выдал вышеприведенную ошибку и в CFE не вошел. Без возможности обратиться к загрузчику конфигурацию изменить было нельзя, а процесс его восстановления тоже не представлялся возможным. Онлайн-поиск решения по этой ошибке ничем не помог и привел в тупик. Единственным выводом было, что при повреждении CFE подобным образом устройство впадает в состояние «кирпича». Тогда я переключился на работу с резервным девайсом, чтобы получить ответ на изначальный вопрос по интересовавшим меня аргументам. К слову говоря, установка kernp mfg_nvram_mode=1 mfg_nvram_url=BADURL была особенно интересна.
Позже я вернулся к своему «кирпичу», чтобы найти способ его восстановления. В нем используется Broadcom SoC, и, как выяснилось, доступ к JTAG обеспечивается через нераспаянные контакты на плате:
После перебора контактов JTAG с помощью JTagulator мне удалось подключиться, используя OpenOCD.
$ openocd -f ../interface/jlink.cfg -f bcm49.cfg
Open On-Chip Debugger 0.11.0-rc2+dev-gba0f382-dirty (2021-02-26-14:07)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
DEPRECATED! use 'adapter speed' not 'adapter_khz'
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V10 compiled Dec 11 2020 15:39:30
Info : Hardware version: 10.10
Info : VTarget = 3.323 V
Info : clock speed 1000 kHz
Info : JTAG tap: bcm490x.tap tap/device found: 0x5ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x5)
Info : JTAG tap: auto0.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x4)
Info : JTAG tap: auto1.tap tap/device found: 0x0490617f (mfg: 0x0bf (Broadcom), part: 0x4906, ver: 0x0)
Info : JTAG tap: auto2.tap tap/device found: 0x0490617f (mfg: 0x0bf (Broadcom), part: 0x4906, ver: 0x0)
Info : bcm490x.a53.0: hardware has 6 breakpoints, 4 watchpoints
Другой способ восстановления системы реализуется через флеш-память, а именно микросхему Macronix NAND:
И здесь я задумался. У меня ведь есть рабочее устройство, на котором я вполне могу запустить загрузчик. А что, если при этом попробовать заменить его рабочий чип NAND на поврежденный, чтобы перепрошить?
Прежде, чем что-либо пробовать, я спросил коллегу, что он думает по поводу столь дурацкой идеи. Оптимистичных прогнозов он не дал, да и я, честно говоря, на них не особо рассчитывал. В итоге мы заключили небольшое пари по результату моего эксперимента, и я вернулся к работе.
Первым делом предстояло выяснить, переживет ли система удаление NAND в работающем состоянии? Я понимал, что для ответа на этот вопрос мне потребуется более методический подход, нежели просто «обдать работающее устройство горячим воздухом и извлечь микросхему». Для начала нужно было выяснить, как NAND запитана. Судя по схеме, Vcc подключены к микросхеме в следующих местах:
Определив дорожки подключения Vcc, проще всего ответить на основной вопрос можно было отключив их от NAND при работающей системе. Чтобы это сделать, изначально я попробовал отсечь эти дорожки и добавить проводные перемычки (рекомендую 36 AWG Magnet Wire), которые можно будет разъединить после запуска загрузчика:
С правой стороны я отсек трассу питания подальше, решив, что это место будет более удачным, поскольку от него запитывается несколько контактов NAND. При установке первой перемычки трассу я отсек небольшим ножом и зачистил покрытие абразивным карандашом:
Получилось так себе, потому что карандаш оказался великоват, и в итоге я оголил чересчур большой участок. Лучше использовать остроконечный нож, чтобы не натворить бардак и получить что-то подобное:
Припаяв и соединив провода, я запустил маршрутизатор и, дождавшись старта загрузчика (CFE), с помощью команды
dn
(dump nand) убедился в доступности NAND, затем обесточил микросхему, разъединив провода.CFE> dn
------------------ block: 0, page: 0 ------------------
00000000: 00000000 00000000 00000000 00000000 ................
00000010: 00000000 00000000 00000000 00000000 ................
00000020: 00000000 00000000 00000000 00000000 ................
----------- spare area for block 0, page 0 -----------
00000800: ff851903 20000008 00fff645 c2b9bf55 .... ......E...U
00000810: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000820: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000830: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
*** command status = 1
CFE>
web info: Waiting for connection on socket 1.␛[J
CFE>
web info: Waiting for connection on socket 0.␛[J
CFE> ␀---- <----- VCC Removed (reboot)
После отключения питания (отмечено как
Vcc removed
), устройство перезагрузилось и не смогло запустить загрузчик, так как NAND была недоступна. Проблема оказалась в том, что точка отсечения питания справа отключала его подачу не только на NAND, но и на SoC. Чтобы не усложнять, я просто восстановил этот отрезок и проделал всю ту же процедуру с установкой перемычки в точке ближе к NAND: Вернув систему в строй и повторив предыдущий тест, я получил ответ на изначальный вопрос: когда питание отключается разъединением проводов перемычки, система продолжает работать, что подтверждается командой
dn
: <----- NAND VCC Removed
CFE> dn
------------------ block: 0, page: 2 ------------------
Status wait timeout: nandsts=0x30000000 mask=0x80000000, count=2000000
Error reading block 0
00001000: 00000000 00000000 00000000 00000000 ................
Status wait timeout: nandsts=0x30000000 mask=0x80000000, count=2000000
----------- spare area for block 0, page 2 -----------
00000800: 00000000 00000000 00000000 00000000 ................
00000810: 00000000 00000000 00000000 00000000 ................
00000820: 00000000 00000000 00000000 00000000 ................
00000830: 00000000 00000000 00000000 00000000 ................
Error reading block 0
*** command status = -1 <----- Expected error reading NAND
CFE>
CFE>
CFE>
<----- NAND VCC Enabled
CFE>
CFE> dn
------------------ block: 0, page: 3 ------------------
00001800: 00000000 00000000 00000000 00000000 ................
00001810: 00000000 00000000 00000000 00000000 ................
----------- spare area for block 0, page 3 -----------
00000800: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000810: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000820: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000830: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
*** command status = 1 <----- Successful NAND read
CFE>
После того, как я убедился в возможности отключения NAND на работающей системе без влияния на загрузчик, очередным шагом было попробовать физически извлечь отключенную NAND из платы.
Прогрев микросхему горячим воздухом, я поочередно приподнял пинцетом сначала правую, а затем левую стороны:
В результате этого процесса система перезапустилась и провалила попытку войти в загрузчик:
CFE> ␀---- <----- NAND Removed (reboot)
BTRM
V1.6
CPU0
L1CD
MMUI
MMU7
DATA
ZBBS
MAIN
OTP?
OTPP
USBT
NAND
IMG?
FAIL
␀---- <----- FAIL boot loop
Так как стороны микросхемы при ее отсоединении я поднимал поочередно, посматривая при этом в консоль, то стало очевидно, что перезагрузка произошла при подъеме именно левой стороны:
Наиболее вероятной причиной было изменение состояния контактов Read Enable (RE#) или Ready/Busy (R/B#). Чтобы это проверить, я добавил к обоим перемычки:
Тут NAND пришлось установить на место, чтобы вернуть систему в загрузчик. Затем я в очередной раз отключил ее питание, разъединив ведущие на Vcc провода, а дорожкиRE# и R/B# привязал к земле (заземлил?):
Затем я снова поочередно извлек правую-левую стороны микросхемы, поглядывая в консоль загрузчика:
На этот раз он остался активен, и система в перезагрузку не ушла. Закончив очередной этап головоломки, я перешел к следующему — установке поврежденной NAND в работающее устройство.
Для припаивания NAND снова использовался горячий воздух. Первая попытка оказалась безуспешной, так как некоторые контакты замкнуло при моей попытке выровнять чип по обеим сторонам. На этом этапе ввиду сбоя в очередной раз пришлось ставить обратно рабочую NAND.
Во второй попытке я уже использовал небольшой листок бумаги для изолирования одной стороны NAND на время выравнивания и закрепления другой:
После установки первой стороны бумажку я убрал и припаял вторую. Загрузчик остался активен. Следующим шагом нужно было восстановить контакты RE#, RB# удалением заземляющей перемычки, а также вновь соединить перемычку Vcc. После окончательного восстановления подключения я выполнил
dn
, чтобы убедиться в доступности NAND: CFE> dn
------------------ block: 0, page: 0 ------------------
00000000: 00000000 00000000 00000000 00000000 ................
00000010: 00000000 00000000 00000000 00000000 ................
00000020: 00000000 00000000 00000000 00000000 ................
----------- spare area for block 0, page 0 -----------
00000800: ff851903 20080000 00c2b822 c978ff97 .... ......".x..
00000810: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000820: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
00000830: ffffffff ffffffff ffee9423 4ba37819 ...........#K.x.
*** command status = 1 <----- Success!
CFE>
Тест чтения завершился успешно, и через веб-интерфейс загрузчика я прошил микросхему заводским образом:
web info: Waiting for connection on socket 1.␛[J
web info: Upload 70647828 bytes, flash image format.␛[J <----- Image Upload
CFE> ........
Setting JFFS2 sequence number to 13
Flashing root file system at address 0x06000000 (flash offset 0x06000000): <-----Image Write
.................................................................... .....................................................................
....................................................................
....................................................................
....................................................................
....................................................................
....................................................................
....................................................................
Resetting board in 0 seconds...�----
BTRM
V1.6
CPU0
L1CD
MMUI
MMU7
DATA
ZBBS
MAIN
OTP?
OTPP
USBT
NAND
IMG?
IMGL
UHD?
UHDP
RLO?
RLOP
UBI?
UBIP
PASS
----
CFE version 1.0.38-161.122 for BCM94908 (64bit,SP,LE)
Build Date: Mon May 13 08:23:21 CST 2019 (defjovi@ubuntu-eva02)
Copyright (C) 2000-2015 Broadcom Corporation.
Boot Strap Register: 0x6fc42
Chip ID: BCM4906_A0, Broadcom B53 Quad Core: 1800MHz
Total Memory: 536870912 bytes (512MB)
Status wait timeout: nandsts=0x50000000 mask=0x40000000, count=0
NAND ECC BCH-4, page size 0x800 bytes, spare size used 64 bytes
NAND flash device: , id 0xc2da block 128KB size 262144KB
Initalizing switch low level hardware.
pmc_switch_power_up: Rgmii Tx clock zone1 enable 1 zone2 enable 1.
Software Resetting Switch ... Done.
Waiting MAC port Rx/Tx to be enabled by hardware ...Done
Disable Switch All MAC port Rx/Tx
*** Press any key to stop auto run (1 seconds) ***
Auto run second count down: 0
Booting from only image (address 0x06000000, flash offset 0x06000000) ... <----- Success!!111!
Decompression LZMA Image OK!
Entry at 0x0000000000080000
Starting program at 0x0000000000080000
/memory = 0x20000000
Booting Linux on physical CPU 0x0
Linux version 4.1.27 (jenkins@asuswrt-build-server) (gcc version 5.3.0 (Buildroot 2016.02) ) #2 SMP PREEMPT Fri Jun 19 13:05:44 CST 2020
CPU: AArch64 Processor [420f1000] revision 0
Detected VIPT I-cache on CPU0
Как видно из вывода, прошивка прошла успешно, и система загрузила ОС устройства.
Уверен, некоторые читатели спросят: «Почему просто не использовать для перепрошивки NAND специализированный программатор?» Это абсолютно уместный вопрос, и, быть может, так даже правильнее, чем заниматься всей этой чепухой.
И все же считаю, что здесь будет уместна цитата персонажа из к/ф «Парк Юрского периода», которого играл Джефф Голдблюм:
Ваши ученые настолько озабочены вопросом о том, могут ли они что-то сделать, что забывают приостановиться и подумать, а надо ли вообще это делать.