- 單片機一般作為一個產品的邏輯中心,工作時一直在進行著邏輯判斷與執行操作,相當于人類的大腦。單片機可以通過修改程序來控制外圍電路的工作狀態,從而改變產品的功能。如果一個產品具有單片機,那么它往往是這個產品的核心。而開發一款功能復雜且成熟的產品所需要的費用不低并且研發周期較長,一些個人為了某些原因,為了快速仿造競爭對手的產品,一般就原樣照抄對方的硬件電路,但由于單片機內部有針對產品功能單獨設計的程序,不能直接再市面上購買,所以就只能想辦法通過特殊的解密方法將單片機內部的程序讀取出來。將解密后讀取出來的程序燒錄在新的芯片中從而完成產品功能的抄襲。通過專用的設備和工具,利用單片機芯片設計上的硬件漏洞或軟件缺陷,通過多種技術手段獲取單片機內程序這叫做單片機解密或單片機破解。以不正當方式獲得其他公司或個人的商業秘密,是一種常見的不正當競爭行為。單片機加密技術是對產品知識產權保護的一種技術手段。
Unique ID加密原理
當前市面上的單片機資源中大多數具有Unique ID,即每一個芯片內部均具有唯一的芯片ID號。不同的芯片廠家實現該功能的方式不同,一些用生產線的流水號,另一些是用晶圓的特性來生成ID。 利用Unique ID加密的方法流程如下: - A. 將單片機燒錄程序A后上電運行程序,完成加密操作。
- B. 將單片機燒錄程序最終程序B,該程序包含ID識別部分。 加密流程圖如下:
 - 程序A負責讀取芯片內部的Unique ID,并進行加密運算。將運算的結果寫入芯片內部的EEPROM。
- 程序B是實現產品最終功能的程序,該程序其中加入了產品身份識別功能。該程序首次上電后將首先讀取EEPROM數據,并進行與加密算法相同的逆向算法,將加密后的數據進行解密獲得芯片Unique ID。將解密后的ID與當前芯片的ID進行比對,如果數據不同則判斷為非法程序。
加密算法 為了防止破解者通過仿真的方式找到芯片ID信息,一般不宜直接存放ID號。而是經過相應的加密處理后寫入存儲器中。校驗方式也不宜采用常見、簡單的校驗規則,應盡可能采用某些特殊的檢驗方式,使破解者不能迅速確定校驗算法。
加密的算法類似數學公式 Y=F(X)
其中Y為加密以后的數據,X為原始數據,而F則為加密算法。
加密算法有很多種,本實驗中采用簡單的位移后取反的方式來進行數據加密。 
最后將加密的數據存入EEPROM中,由于芯片的ID廠家不允許更改,這大大增加了破解難度,加強了對產品的保護。 詳細設計過程
使用Unique ID對單片機程序加密功能的實現需要以下幾點功能:
① 實驗板1用于對程序加密實驗。
② 將從實驗板1中讀取到的數據燒錄到實驗板2中,通過實驗板上的LED狀態驗證程序加密是否成功。
基于Unique ID的單片機程序加密分為2個程序,這2個程序是不同的。
程序A:用于讀取芯片硬件ID并進行算法處理,將處理后的數據保存到EEPROM中。
程序B:用于將EEPROM中的數據讀取出來并使用與加密相同的算法進行解密獲得芯片ID,將解密后獲得的ID與芯片本身的ID進行比較。比較結果只有2種可能,即為相同與不同,程序根據結果從而執行不同的操作如圖所示; 
實驗中使用STM8S單片機作為硬件載體 軟件設計 系統初始化函數選擇了內部16MHz時鐘源,1分頻后系統總線時鐘頻率為16MHz。
將GPIO與EEPROM擦寫均進行了初始化配置。 函數代碼如下: void SystemInit(void){ CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); //總時鐘源 使用內部時鐘源1分頻 16M/1分頻 =16MHz Gpio_Init(); //單片機系統GPIO初始化 FLASH_DeInit();//存儲數據的EEPROM初始化 FLASH_SetProgrammingTime(FLASH_PROGRAMTIME_STANDARD);// 默認的EEPROM寫入時間}LED驅動程序 根據硬件的電路原理圖可以看出,LED連接到PE5引腳。采用灌電流驅動,當引腳為低電平時LED點亮,引腳為高電平時LED熄滅。上電后LED默認為熄滅狀態,所以引腳初始化配置為高速輸出模式高電平。函數代碼如下: /*描述:MCU外圍GPIO初始化*//*輸入:無*/void Gpio_Init(vod){ //功能引腳上電初始化,引腳初始化狀態根據外部功能而定義// GPIO_Init(GPIOE, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_FAST); //LED}LED的狀態采用宏定義方式,直接控制IO管腳電平狀態。 操作代碼如下: /*LED操作宏定義*/#define LED_ON GPIO_WriteLow(GPIOE, GPIO_PIN_5); //點亮LED#define LED_OFF GPIO_WriteHigh(GPIOE, GPIO_PIN_5); //熄滅LED獲取Unique ID程序 STM8S105K4T6數據手冊顯示該芯片具有96bit Unique ID,96bit/8=12byte。存放ID的起始地址為0x48CD,從該地址讀取12次,將芯片ID獲取并存入數組。 函數代碼如下: /*描述:獲取芯片ID*//*輸入:存放芯片ID的數組*//*輸出:無*//*參數:無*/void Get_ChipID(unsigned char *p)//Get chip ID{ unsigned char i=0; for(i=0; i<12; i++) //讀取12次 { *p = *(u8*)(0X48CD+i); //將數據讀出后進行存放 p++; }}加密算法程序 編碼函數執行對Unique ID進行數據算法處理。實驗中采用的算法為將存放Unique ID的數組進行左移三個元素后逐位取反。將編碼后的數據存入另一個數組。
編碼函數代碼如下: /*描述:對芯片ID進行編碼處理*//*輸入:存放芯片ID的數組;存放芯片ID編碼后的數組*//*輸出:無*/ /*參數:對輸入數組左移三次后逐位取反*/void Encode(unsigned char *DataIn,unsigned char *DataOut)//對數據進行編碼加密{ unsigned char i;//數組數據左移次數 unsigned char j;//數據位移臨時局部變量 unsigned char DataSwap;//搬運數據用的臨時變量 unsigned char TempData[12];//數據處理交換變量數組 for(i=0;i<12;i++)//數據數組交換 { TempData = *DataIn; DataIn++; } for(i=0;i<3;i++) { //數組元素左移操作 DataSwap = TempData[0]; for(j=0;j<11;j++) //數組左移搬運 { TempData[j] = TempData[j+1]; } TempData[11] = DataSwap; } for(i=0;i<12;i++) { TempData = ~ TempData ; //對數組數據進行按位取反 *DataOut = TempData ; DataOut++; }}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
解密算法程序 解碼函數負責對從EEPROM中讀取的數據進行解碼,是編碼函數的一個逆向算法處理。算法為將輸入的數組(EEPROM數據)進行右移三個元素后按位取反。示意圖如下: 
函數如下 /*描述:對從EEPROM讀出的數據進行解碼*//*輸入:存放編碼數據的數組,存放解碼數據的數組*//*輸出:無*//*參數:數據右移三位后按位取反(算法)*/void Decode(unsigned char *DataIn,unsigned char *DataOut){ unsigned char i;//數組數據左移次數 unsigned char j;//數據位移臨時局部變量 unsigned char DataSwap;//搬運數據用的臨時變量 unsigned char TempData[12];//數據處理交換變量數組 for(i=0;i<12;i++)//數據數組交換 { TempData = *DataIn; DataIn++; } for(i=0;i<3;i++)//數據右移三次 { //數組元素右移操作 DataSwap = TempData[11]; for(j=10;j>0;j--)//數組右移搬運 { TempData[j+1] = TempData[j]; } TempData[1] = TempData[0]; TempData[0] = DataSwap;//最后一次搬運 } for(i=0;i<12;i++)//對數組數據進行諸位取反 { TempData = ~ TempData ; *DataOut = TempData; DataOut ++; }}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
數據寫入EEPROM程序 將編碼處理后的數據寫入EEPROM,存儲器起始地址為0x000040A0,通過單字節寫入。將12個字節全部寫入EEPROM。函數代碼如下: /*描述:EEPROM數據寫入*//*輸入:數據源的數組*//*輸出:無*//*參數:無*/void EEPROMWrite(unsigned char *p){ unsigned char i;//數據交換用局部臨時變量 FLASH_Unlock(FLASH_MEMTYPE_DATA); //解鎖EEPROM,允許寫入數據 while( !(FLASH_GetFlagStatus(FLASH_FLAG_DUL)) );//等待EEPROM解鎖完成 for(i=0;i<12;i++) { FLASH_ProgramByte(0x000040A0+i, *p);//將數據寫入EEPROM,寫入存儲起始地址為0x000040A0 while( !( FLASH_GetFlagStatus(FLASH_FLAG_EOP)) );//等待EEPROM單次寫入完成 p++; } FLASH_Lock(FLASH_MEMTYPE_DATA); //鎖定EEPROM}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
從EEPROM讀出數據程序 程序首先需要從EEPROM中讀取加密后的數據,將數據保存到數組EncodeData中。程序代碼如下: /*描述:讀取芯片EEPROM數據*//*輸入:用于存放EEPROM的數組*//*輸出:無*//*參數:*/void Get_EEPROMData(unsigned char *p){ unsigned char i=0; for(i=0; i<12; i++) { *p = FLASH_ReadByte(0x40A0 + i);//從EEPROM讀取數據 p++; }}數據比對程序 數據比對函數負責將解密后數據與Unique ID兩個數組的數據進行比對。該函數只有兩個返回結果,數據完全相同與不同,返回不同的值。根據不同的返回值從而判斷程序是否合法。函數代碼如下: /*描述:將兩個數據進行比對*//*輸入:要比對的數據A,要比對的數據B*//*輸出:比對結果 1:數據相同 0:數據不同*//*參數:對數組進行按字節比對*/unsigned char IDCheck(unsigned char *a,unsigned char *b){ unsigned char i; unsigned char Flag;//比對結果標志 1:數據相同 0:數據不同 Flag = 1; for(i=0;i<12;i++) { if( (*a) != (*b) ) { Flag = 0; } a++; b++; } return Flag;//返回值:比對結果標志 1:數據相同 0:數據不同}數據數組
使用數組的存儲芯片ID與編碼加密后的數據。代碼如下: /*數組定義*/unsigned char Chip_IDData[12]; //存放芯片Unique ID unsigned char EnCodeData[12]; //對芯片Unique ID編碼加密后的數據unsigned char DecodeData[12]; //對芯片EEPROM解密后的數據A程序主函數代碼
本程序主要作用是將數據進行加密編碼處理并將結果寫入EEPROM, 程序的主函數調用上方的單元功能函數。 /*描述:主函數*//*輸入:無*//*輸出:無*//*參數:無*/void main(void){ SystemInit(); //系統初始化 Get_ChipID(Chip_IDData); //獲取芯片 ID并存儲在數組 Encode(Chip_IDData,EnCodeData); //編碼運算處理 EEPROMWrite(EnCodeData); //將編碼數據寫入 EEPROM LED_ON; //寫入完成后點亮LED while(1); //軟件循環}運行該程序后,將讀取到的芯片ID數據存入數組。通過內部編碼算法函數將ID進行處理并寫入EEPROM。程序運行流程圖如圖所示:
 B程序主函數代碼
程序B作用為對EEPROM數據讀取并進行解碼處理后判斷程序合法性。主函數代碼如下: /*描述:Main主函數*//*輸入:無*//*輸出:無*//*參數:無*/void main(void){ SystemInit(); //系統初始化 Get_ChipID(Chip_IDData);//獲取芯片自身ID Get_EEPROMData(EncodeData); //從EEPROM讀取編碼后的數據 Decode(EncodeData,DecodeData);//將編碼數據進行解碼 //將解碼后的數據與當前芯片ID進行比對 if(IDCheck(Chip_IDData,DecodeData)) { //返回1 比對通過,芯片與ID身份識別通過 LED_ON; //點亮LED } else {//比對失敗 程序非法 LED_OFF; //熄滅LED While(1); //死循環 } while(1) { ; //程序正常執行循環 }}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
解碼程序為最終燒錄到芯片中的程序,產品功能也是在次此結構下開發的。程序執行后會讀取當前芯片自身的Unique ID與存儲在EEPROM中的數據。將EEPROM中的數據進行解碼后與Unique ID進行比對,當數據完全相同時判斷程序合法并執行正常程序功能,當數據比對不相同后判斷程序非法并執行相應操作。解碼程序運行流程圖如圖所示: 
系統測試
 將運行B程序后的實驗板1通過通過仿真器與電腦連接,將STVP切換到“Program Memory”選項卡后點擊“Read curren tab of active sectors”按鈕后讀取數據。在菜單File->save中選擇保存程序B。(模擬程序被解密) 將讀取的程序B燒錄到實驗板2中,觀察LED的現象。
燒錄程序B后實驗板1測試LED亮起,實驗板2測試LED未亮起。 
測試結果
從實驗中可以看到,實驗板二雖然燒錄了程序B,但是之前并沒有運行過程序A的加密算法,所以程序識別出EEPROM解密后數據與芯片自身ID不匹配從而熄滅LED。
本文所設計的系統已經通過上述過程的測試,可以滿足對單片機程序加密的功能需求。 當前加密系統中需要完善的部分
程序中僅僅有1處判斷程序合法性,一旦遭到反編譯后容易被破解
程序被判斷為非法后沒有保護操作,應加入強制擦除 Flash進行保護
程序需要燒錄2次來完成整個加密系統的流程. 應在程序中加入啟動加密條件,實現一次程序燒錄即可完成加
|