置RDP讀寫保護(hù)完整指南)
1. 固件被別人讀走很多時(shí)候就是從一個(gè)小疏忽開始的玩STM32的工程師應(yīng)該都有過這種體會(huì)好不容易把產(chǎn)品調(diào)穩(wěn)定了PCB打樣、貼片、燒錄、出貨忙完一陣子剛想歇口氣結(jié)果沒過多久市場(chǎng)上出現(xiàn)了一個(gè)功能幾乎一模一樣的東西。拆開一看主控是同一顆STM32板子走線風(fēng)格相似連固件行為都完全一致。這時(shí)候你大概率會(huì)意識(shí)到一個(gè)問題——固件被讀走了。很多人誤以為程序燒進(jìn)單片機(jī)就安全了實(shí)際上對(duì)于沒有做任何保護(hù)的STM32用ST-LINK連接SWD接口打開ST-LINK Utility或STM32CubeProgrammer點(diǎn)一下連接再點(diǎn)一下讀取整個(gè)Flash內(nèi)容分分鐘就能被導(dǎo)出來。拿到bin文件之后反匯編、逆向邏輯、移植到自己的板子上成本比你想象中低很多。做產(chǎn)品防抄設(shè)置讀寫保護(hù)RDP是最基礎(chǔ)、也最應(yīng)該養(yǎng)成習(xí)慣的一道防線。這篇文章就圍繞我自己在實(shí)際項(xiàng)目里用ST-LINK Utility給STM32設(shè)置讀寫保護(hù)的完整流程展開包括保護(hù)等級(jí)的選擇、具體操作步驟、解除方法以及我在幫同事處理芯片被鎖死問題時(shí)總結(jié)出的一些排查經(jīng)驗(yàn)。適合所有在做STM32產(chǎn)品開發(fā)的工程師尤其是畫完板子、寫完程序之后對(duì)固件安全還沒有特別重視的朋友。RDPReadout Protection讀保護(hù)是STM32內(nèi)部選項(xiàng)字節(jié)Option Bytes里的一個(gè)關(guān)鍵配置。它決定了一個(gè)核心問題別人通過調(diào)試接口能不能直接把你的Flash內(nèi)容讀出來。ST-LINK Utility是老牌官方工具雖然現(xiàn)在ST主推的是STM32CubeProgrammer但ST-LINK Utility在設(shè)置選項(xiàng)字節(jié)和批量處理時(shí)依然有大量工程師在用路徑熟悉、操作直觀。我接下來的實(shí)操會(huì)基于ST-LINK Utility 4.6.0版本新老項(xiàng)目都能參考。2. 固件泄露的路徑和RDP保護(hù)等級(jí)你得先知道自己防的是什么2.1 四種常見的固件泄露路徑在動(dòng)手設(shè)置保護(hù)之前先搞清楚威脅從哪里來這決定了你要防到什么程度。第一種是SWD/JTAG調(diào)試接口直接讀取。這是最粗暴也最常見的路徑只要芯片沒有設(shè)置讀保護(hù)任何一根ST-LINK連上SWDIO和SWCLK就能通過調(diào)試接口讀取Flash。整個(gè)讀取過程不需要知道你的程序邏輯也不需要破解什么完全是芯片默認(rèn)開放的權(quán)限。第二種是Boot模式引導(dǎo)攻擊。STM32支持從系統(tǒng)存儲(chǔ)器System Memory啟動(dòng)也就是內(nèi)置Bootloader。如果用戶沒有對(duì)這方面做限制攻擊者可以把BOOT0拉高讓芯片進(jìn)入系統(tǒng)Bootloader模式然后通過串口或USB嘗試讀取Flash。好在STM32的系統(tǒng)Bootloader本身對(duì)讀保護(hù)狀態(tài)是敏感的——當(dāng)RDP Level 1開啟之后系統(tǒng)Bootloader的讀操作也會(huì)被禁止所以RDP對(duì)這條路徑同樣有效。第三種是物理攻擊。比如用探針直接測(cè)量?jī)?nèi)部總線、用FIB聚焦離子束修改芯片結(jié)構(gòu)、用側(cè)信道分析等方式提取數(shù)據(jù)。這類手段成本極高通常只有專業(yè)機(jī)構(gòu)才會(huì)做普通產(chǎn)品遇到的概率不大。應(yīng)對(duì)這種威脅需要更高的防護(hù)等級(jí)也就是Level 2。第四種其實(shí)是自己人泄露比如離職工程師帶走固件、工廠端燒錄環(huán)節(jié)拷貝了hex文件。這種情況靠芯片層面的保護(hù)解決不了所有問題但設(shè)置好Level 1保護(hù)至少能增加一道門檻讓文件不能隨隨便便被讀出來二次分發(fā)。2.2 Level 0、Level 1、Level 2三檔保護(hù)的取舍邏輯RDP等級(jí)的語義在不同型號(hào)的STM32上略有差異但大框架是一樣的。Level 0就是沒有任何讀保護(hù)出廠默認(rèn)狀態(tài)。Flash內(nèi)容可以隨意通過調(diào)試接口讀寫這是大多數(shù)開發(fā)板和自制產(chǎn)品的默認(rèn)狀態(tài)。如果你做的是批量出貨的產(chǎn)品一直停在Level 0相當(dāng)于把固件裸奔在市場(chǎng)里。Level 1是最常用的保護(hù)等級(jí)也是我推薦絕大多數(shù)產(chǎn)品選擇的檔位。開啟之后調(diào)試接口SWD/JTAG無法再讀取Flash內(nèi)容也無法通過系統(tǒng)Bootloader的讀命令獲取固件。但芯片仍然允許通過調(diào)試接口進(jìn)行擦除和重新燒錄只不過每次從Level 1降回Level 0都需要先執(zhí)行一次全片擦除。這意味著攻擊者即使想暴力降級(jí)也只能得到一顆空片拿不到原有固件。Level 2是最高等級(jí)保護(hù)一旦設(shè)置芯片的調(diào)試接口會(huì)被永久禁用讀保護(hù)功能本身也無法再被關(guān)閉。更關(guān)鍵的是從Level 2無法再回到Level 1或Level 0相當(dāng)于把芯片變成了一個(gè)黑盒。程序再也無法通過調(diào)試器讀取或更新只能通過應(yīng)用程序自身的IAP升級(jí)邏輯來更新功能。這個(gè)等級(jí)適合固件價(jià)值極高、且確認(rèn)后續(xù)不需要調(diào)試的產(chǎn)品。如果貿(mào)然設(shè)置Level 2后期想改bug就得換芯片。我做產(chǎn)品時(shí)的默認(rèn)習(xí)慣是開發(fā)調(diào)試階段保持Level 0出樣機(jī)和量產(chǎn)前的最后一版固件切到Level 1。只有客戶明確要求最高防護(hù)、且軟件已經(jīng)凍結(jié)不再調(diào)試時(shí)才考慮Level 2。3. 解讀ST-LINK Utility在保護(hù)操作中的角色為什么這個(gè)老工具仍然好用3.1 工具選型ST-LINK Utility、STM32CubeProgrammer和J-Flash怎么選ST官方現(xiàn)在的通用編程工具是STM32CubeProgrammer功能確實(shí)更全支持OTA包生成、圖形化選項(xiàng)字節(jié)配置等。但ST-LINK Utility一直沒有完全退出大家的視野原因主要有幾個(gè)。第一ST-LINK Utility界面極度簡(jiǎn)潔核心功能一目了然。打開軟件、連接芯片、配置選項(xiàng)字節(jié)、保存hex所有高頻操作都在一級(jí)菜單里不需要在多級(jí)界面里翻找。第二選項(xiàng)字節(jié)的讀寫非常直接。在做RDP設(shè)置這種需要頻繁切換等級(jí)的場(chǎng)景下Utility的Option Bytes對(duì)話框里直接列出RDP級(jí)別選項(xiàng)和各個(gè)選項(xiàng)字節(jié)位改起來非常流暢。STM32CubeProgrammer雖然也支持但界面布局更復(fù)雜很多老工程師還是習(xí)慣Utility的操作節(jié)奏。第三對(duì)于ST-LINK/V2這種老款調(diào)試器ST-LINK Utility的兼容性反而更好。有些第三方ST-LINK或者老版本固件在CubeProgrammer下會(huì)報(bào)錯(cuò)但在Utility里依然能穩(wěn)定連接。當(dāng)然如果你用的是STM32H7系列或者新型號(hào)芯片ST-LINK Utility可能不支持部分新功能這時(shí)候建議用CubeProgrammer。我自己的習(xí)慣是老型號(hào)芯片、快速設(shè)置RDP用Utility新型號(hào)、需要配置復(fù)雜選項(xiàng)字節(jié)或做量產(chǎn)編程時(shí)用CubeProgrammer。3.2 連接前的準(zhǔn)備工作驅(qū)動(dòng)、硬件與狀態(tài)確認(rèn)工欲善其事必先利其器。在用ST-LINK Utility設(shè)置讀寫保護(hù)之前有幾個(gè)細(xì)節(jié)容易被忽略。驅(qū)動(dòng)是第一個(gè)坎。插上ST-LINK之后如果電腦設(shè)備管理器里顯示的是未知設(shè)備或者黃色感嘆號(hào)說明驅(qū)動(dòng)沒裝好。ST-LINK Utility安裝包自帶驅(qū)動(dòng)也可以在ST官網(wǎng)下載STSW-LINK009這個(gè)獨(dú)立驅(qū)動(dòng)包。我用過很多次裝完驅(qū)動(dòng)后務(wù)必重啟一下軟件否則工具可能仍然識(shí)別不到調(diào)試器。硬件連接上SWD接口只需要四根線SWDIO、SWCLK、GND、3.3V如果目標(biāo)板有獨(dú)立供電也可以不接VCC但必須共地。有些朋友只接了兩根數(shù)據(jù)線不接地結(jié)果連半天連不上其實(shí)就是共地問題。設(shè)置RDP過程中芯片供電一定要穩(wěn)定建議使用外部穩(wěn)壓電源或確保ST-LINK的3.3V輸出電流足夠避免在寫選項(xiàng)字節(jié)時(shí)電壓跌落導(dǎo)致寫入失敗。連接之前還要確認(rèn)目標(biāo)芯片沒有被其他軟件占用。比如Keil里打開了調(diào)試會(huì)話并占用著SWD口這時(shí)候再用ST-LINK Utility連接可能會(huì)提示無法建立連接。關(guān)閉Keil的調(diào)試界面拔插一次ST-LINK再重新連接基本都能解決。4. 設(shè)置讀寫保護(hù)實(shí)操?gòu)倪B接芯片到確認(rèn)狀態(tài)每步都不能省4.1 第一步連接目標(biāo)芯片并讀取當(dāng)前選項(xiàng)字節(jié)打開ST-LINK Utility點(diǎn)擊工具欄最左側(cè)的Connect圖標(biāo)或者通過菜單Target - Connect。軟件會(huì)彈出連接配置對(duì)話框這里有幾個(gè)關(guān)鍵配置需要看清。如果ST-LINK Utility能自動(dòng)識(shí)別出芯片型號(hào)Connection Mode選擇Normal即可。Mode下拉框里有Normal、Under Reset、Hot Plug等選項(xiàng)。Normal模式適用于芯片正常供電、且當(dāng)前沒有啟用Level 2保護(hù)的情況Under Reset模式會(huì)在連接過程中強(qiáng)制拉低復(fù)位引腳適合讀取保護(hù)已經(jīng)開啟、或者程序里關(guān)了SWD引腳的情況。我遇到過不少芯片程序初始化時(shí)把SWDIO和SWCLK引腳重映射成了普通GPIO導(dǎo)致Normal模式連不上。這時(shí)候選Under Reset模式在芯片上電后、程序運(yùn)行前的一小段時(shí)間窗口內(nèi)抓住調(diào)試接口就能連上。連接成功之后軟件右側(cè)會(huì)顯示芯片的Flash內(nèi)容左側(cè)的信息欄會(huì)列出芯片型號(hào)、Flash大小、UID等。此時(shí)先別急著操作點(diǎn)擊Target - Option Bytes查看當(dāng)前RDP級(jí)別。新出廠的芯片默認(rèn)是Level 0如果你拿到的是二手芯片或者別人調(diào)過的板子有可能是Level 1甚至Level 2這時(shí)候根據(jù)實(shí)際情況決定操作方向。4.2 第二步將RDP從Level 0切換為L(zhǎng)evel 1在Option Bytes對(duì)話框里找到Read Out Protection這個(gè)區(qū)域里面有一個(gè)下拉選擇框選項(xiàng)是AALevel 0和BBLevel 1之類的編碼。不同芯片顯示方式略有差別有些直接顯示Level 0/Level 1原理都一樣。選擇Level 1之后點(diǎn)擊OK或Apply按鈕ST-LINK Utility會(huì)提示即將寫入選項(xiàng)字節(jié)。確認(rèn)寫入后軟件會(huì)執(zhí)行一次擦除操作并寫入新的選項(xiàng)字節(jié)配置。注意一個(gè)細(xì)節(jié)從Level 0切換到Level 1Flash中的數(shù)據(jù)會(huì)保留不會(huì)自動(dòng)擦除。這也是為什么我們可以在程序燒錄完成之后再單獨(dú)設(shè)置讀保護(hù)不會(huì)影響已有固件。寫入完成后建議斷開連接然后重新上電再次連接芯片并查看Option Bytes確認(rèn)RDP已經(jīng)變成Level 1。養(yǎng)成這個(gè)回讀確認(rèn)的習(xí)慣非常重要我見過不少同事設(shè)置了保護(hù)但沒有驗(yàn)證結(jié)果后來發(fā)現(xiàn)根本沒寫進(jìn)去白忙一場(chǎng)。4.3 第三步驗(yàn)證保護(hù)是否真正生效驗(yàn)證保護(hù)生效最直觀的方法就是嘗試用ST-LINK Utility讀取Flash。連接上Level 1保護(hù)的芯片后軟件會(huì)彈出提示說明當(dāng)前芯片處于讀保護(hù)狀態(tài)無法讀取Flash內(nèi)容。如果你執(zhí)行Read操作讀取出來的數(shù)據(jù)會(huì)全是0xFF或者直接報(bào)錯(cuò)。這恰恰說明保護(hù)已經(jīng)生效了。還有一個(gè)驗(yàn)證維度是查看能不能擦除。Level 1狀態(tài)下調(diào)試接口仍然允許執(zhí)行全片擦除操作Erase但擦除之后RDP會(huì)自動(dòng)回到Level 0Flash內(nèi)容也全部清空。這也解釋了為什么很多破解行為最終只能得到一顆空片——攻擊者可以擦除你的芯片但拿不到你的固件。如果你的產(chǎn)品需要在現(xiàn)場(chǎng)升級(jí)固件那就要考慮另一種情況Level 1保護(hù)開啟后程序自身的IAPIn-Application Programming功能不受影響你可以在App里通過串口或網(wǎng)絡(luò)接收新固件并寫入內(nèi)部Flash。因?yàn)镮AP是CPU自己執(zhí)行程序去寫Flash跟調(diào)試接口無關(guān)所以RDP不會(huì)限制這部分功能。5. 解除讀寫保護(hù)的正確方法與不可逆風(fēng)險(xiǎn)5.1 解除Level 1保護(hù)的標(biāo)準(zhǔn)流程需要解除讀保護(hù)的場(chǎng)景也不少見。比如產(chǎn)品退回維修、舊板子要重新燒錄新固件或者你自己忘了芯片已經(jīng)上鎖想重新調(diào)試。解除Level 1保護(hù)的方法和設(shè)置過程類似。連接芯片后打開Option Bytes對(duì)話框在Read Out Protection下拉框里選擇Level 0點(diǎn)擊Apply。ST-LINK Utility會(huì)彈出一個(gè)警告框內(nèi)容大意是關(guān)閉讀保護(hù)將觸發(fā)Flash全片擦除所有數(shù)據(jù)都會(huì)丟失。確認(rèn)之后軟件先執(zhí)行擦除再寫入Level 0的選項(xiàng)字節(jié)配置。這個(gè)全片擦除行為是芯片硬件層面的設(shè)計(jì)不是軟件能繞過的。從安全角度看這是必須的如果允許不擦除就降級(jí)到Level 0那設(shè)置讀保護(hù)還有什么意義攻擊者只要降級(jí)就能讀出固件了。所以每次解除保護(hù)都意味著固件清空之后需要重新燒錄程序。解除完成后芯片恢復(fù)為完全開放狀態(tài)調(diào)試接口可以正常讀寫Flash。在開發(fā)過程中如果你需要反復(fù)修改固件一般不需要每次燒錄前都解除保護(hù)——直接使用編程功能即可ST-LINK Utility在檢測(cè)到Level 1保護(hù)時(shí)會(huì)自動(dòng)提示先擦除擦除后RDP自動(dòng)歸零再燒錄新程序整個(gè)過程說白了一次搞定。5.2 Level 2保護(hù)不存在后悔藥Level 2解除這個(gè)問題我必須非常明確地說從Level 2回到Level 1或Level 0是不可能的。這是芯片的硬件熔斷機(jī)制不是軟件沒設(shè)計(jì)好。具體表現(xiàn)是當(dāng)芯片處于Level 2狀態(tài)時(shí)調(diào)試接口所有功能都被禁用包括連接、讀取、擦除、選項(xiàng)字節(jié)修改。你在ST-LINK Utility里點(diǎn)連接軟件根本識(shí)別不到芯片更別說改RDP等級(jí)了??赡苡腥藭?huì)說通過串口系統(tǒng)Bootloader能解除嗎答案也是不能。Level 2狀態(tài)下系統(tǒng)Bootloader同樣被禁用。所以我的建議非常保守不要在生產(chǎn)過程中輕易設(shè)置Level 2。除非你有嚴(yán)格的軟件凍結(jié)流程、固件永久不再更新、且備貨量足夠。否則一旦產(chǎn)品上市后發(fā)現(xiàn)一個(gè)嚴(yán)重bug而芯片又是Level 2保護(hù)那你只能換芯片板子上的舊芯片等于報(bào)廢。這個(gè)代價(jià)有時(shí)候比固件泄露還大。5.3 解除保護(hù)之后必須做的兩件事第一件事是重新燒錄完整固件。因?yàn)榻导?jí)RDP必然擦除Flash包括選項(xiàng)字節(jié)里的其他配置比如獨(dú)立看門狗設(shè)置、硬件看門狗配置等也會(huì)被恢復(fù)成默認(rèn)值。燒錄程序之后建議把其他選項(xiàng)字節(jié)也一并檢查一遍確保和量產(chǎn)配置一致。第二件事是更新目標(biāo)文件記錄。我習(xí)慣在項(xiàng)目目錄里維護(hù)一個(gè)燒錄記錄表格記錄每塊板子的芯片UID、燒錄日期、固件版本號(hào)、RDP等級(jí)。解除保護(hù)這種操作尤其是返修板一定要在記錄里標(biāo)注清楚避免后續(xù)不知道這塊板子的保護(hù)狀態(tài)。6. 實(shí)戰(zhàn)避坑誤鎖、連不上、保護(hù)不起效的排查鏈路6.1 設(shè)置完保護(hù)后發(fā)現(xiàn)連不上芯片了別急著重裝驅(qū)動(dòng)這是一個(gè)高頻問題也是最容易讓人慌的場(chǎng)景。一次我給一塊STM32F103板子設(shè)置Level 1保護(hù)后重新上電結(jié)果ST-LINK Utility報(bào)錯(cuò)無法連接。我當(dāng)時(shí)第一反應(yīng)是芯片壞了后來排查了一圈才發(fā)現(xiàn)問題出在芯片的調(diào)試引腳復(fù)用配置上。具體背景是那個(gè)程序在初始化里把SWDIO和SWCLK兩個(gè)引腳重映射成了普通GPIO功能。在Level 0狀態(tài)下即使程序把SWD引腳復(fù)用掉了調(diào)試器依然可以搶占連接但某些STM32型號(hào)在設(shè)置RDP之后調(diào)試接口的連接時(shí)序變得更加嚴(yán)格程序跑起來之后很快就切斷了調(diào)試通道。解決辦法很簡(jiǎn)單連接模式改用Under Reset。ST-LINK Utility會(huì)在復(fù)位期間嘗試連接芯片在程序正式運(yùn)行之前抓住調(diào)試接口。具體操作是Connect對(duì)話框里把Mode改成Under Reset然后重新連接。一般情況下都能成功之后再按正常流程解除保護(hù)或進(jìn)行擦除。6.2 排查鏈路連接失敗的五個(gè)檢查點(diǎn)當(dāng)你在ST-LINK Utility里遇到連接失敗不要上來就懷疑芯片鎖死。我總結(jié)了一個(gè)固定排查順序基本能解決90%的問題。第一看ST-LINK的驅(qū)動(dòng)是否正常。設(shè)備管理器里如果能看到STMicroelectronics STLink dongle之類的設(shè)備驅(qū)動(dòng)就正常如果顯示未知設(shè)備先重裝驅(qū)動(dòng)。第二看硬件連接。SWDIO、SWCLK、GND三根線是不是都接好了地線虛焊是常見的隱蔽問題。用萬用表量一下ST-LINK和目標(biāo)板的GND是否導(dǎo)通。第三看目標(biāo)板供電。觸摸一下芯片表面有沒有明顯發(fā)熱用萬用表量VDD對(duì)GND電壓是否在正常范圍。芯片沒供電或者供電異常時(shí)連接必然失敗。第四看連接模式。Normal連不上就試Under Reset和Hot Plug。Hot Plug模式適合目標(biāo)板已經(jīng)上電的情況而Under Reset模式可以在芯片復(fù)位瞬間搶占連接。第五看是否被其他軟件占用。Keil、IAR、CubeProgrammer如果正在使用同一個(gè)ST-LINKUtility是搶不到設(shè)備的。關(guān)掉其他軟件、拔插一次ST-LINK再試。按照這個(gè)順序排查大部分死鎖其實(shí)都能救回來。6.3 保護(hù)層級(jí)與開發(fā)流程的配合最后說一個(gè)我踩過坑之后的體會(huì)讀保護(hù)一定要和項(xiàng)目的開發(fā)流程、生產(chǎn)流程綁定而不是當(dāng)成一個(gè)孤立的操作。開發(fā)階段我會(huì)把RDP保持Level 0方便反復(fù)調(diào)試和燒錄。出測(cè)試樣機(jī)時(shí)切換為L(zhǎng)evel 1同時(shí)完整驗(yàn)證一次IAP升級(jí)流程確保程序在保護(hù)狀態(tài)下能夠通過應(yīng)用層升級(jí)固件。量產(chǎn)階段工廠燒錄時(shí)默認(rèn)燒錄固件后再設(shè)置Level 1這一步可以寫進(jìn)生產(chǎn)文檔里讓產(chǎn)線測(cè)試人員按步驟執(zhí)行避免漏設(shè)。如果你用ST-LINK Utility做批量設(shè)置可以先把整個(gè)操作流程錄成操作視頻或者寫一個(gè)帶截圖的標(biāo)準(zhǔn)作業(yè)指導(dǎo)書。掌握幾百片板子逐一去設(shè)置RDP很枯燥也容易出錯(cuò)。實(shí)測(cè)下來用ST-LINK Utility連接-設(shè)置RDP Level 1-斷開每片板子大概20到30秒這個(gè)效率對(duì)于小批量生產(chǎn)完全能接受。大批量生產(chǎn)則可以考慮ST官方或第三方的離線燒錄器直接把保護(hù)配置寫進(jìn)燒錄腳本里。有一段經(jīng)歷讓我印象很深一個(gè)客戶把產(chǎn)品外包給我們做程序調(diào)通后他們沒有設(shè)置任何RDP保護(hù)就出貨了。三個(gè)月后市場(chǎng)出現(xiàn)了完全模仿的產(chǎn)品連串口打印信息都一樣。后來我們緊急幫他們產(chǎn)出一版開啟Level 1保護(hù)的固件重新燒錄出貨。但已經(jīng)流出去的舊設(shè)備是沒辦法追回的只能靠后續(xù)產(chǎn)品迭代來彌補(bǔ)。這就是我為什么反復(fù)強(qiáng)調(diào)——RDP保護(hù)應(yīng)該從第一版產(chǎn)品就要做而不是等出事之后才想起來。