大家好!我叫肖亞平,從小熱愛上了電子技術,讀書時陰差陽錯的選擇了電子這方面的專業,學習電子技術、一直到今年畢業。對于我的理解來說,學校里面學到的技術不是全部實用,但是必須有用,所以一直奮斗在前線。我對學習總結出一句話“壓力不是有人努力,而是比你牛X N倍的人依然在努力”
STM32通過讀取芯片唯一ID號來實現程序的保護,防止被抄襲。STM32芯片通過STlink可以把程序讀出來。只要在相同的芯片上面,就可以跑起來,那么如何才能讓一個程序只能夠在一個芯片上正常運行呢? 經過一個下午的時間,總結出這個方法。通過讀取ID號,然后在修改HEX文件來保護自己的程序不被抄襲,相對于開發成本中等的產品,還是有用的。但是,有一點麻煩。我用的是STM32F103ZET6芯片。下面將介紹詳細的步驟和方法。
一.獲取ID碼
- 工欲善其事,必先利其器,準備好工具,需要一個STlink及下載軟件,一個可以正常運行的硬件。
- uint16_t temp[12]; //存放芯片ID的臨時變量
- uint16_t aa[12] = {0xee,0x01,0x02,0x03,0x04,
- 0x05,0x06,0x07,0x08,0x09,
- 0x10,0x11};//預置ID號,在HEX文中修改。
-
- aa這個數組用于存放預置ID號
復制代碼
- /******************************************************************************
- 函數名稱:讀取芯片唯一ID碼
- 創建時間:2015-08-11
- 修改時間:2015-08-11
- 備 注:
- ******************************************************************************/
-
- void Get_ChipID(void)
- {
- u32 temp0,temp1,temp2;
- temp0 = *(__IO u32*)(0x1FFFF7E8); //產品唯一身份標識寄存器(96位)
- temp1 = *(__IO u32*)(0x1FFFF7EC);
- temp2 = *(__IO u32*)(0x1FFFF7F0);
-
- //ID碼地址: 0x1FFFF7E8 0x1FFFF7EC 0x1FFFF7F0 ,只需要讀取這個地址中的數據就可以了。
-
- temp[0] = (u8)(temp0 & 0x000000FF);
- temp[1] = (u8)((temp0 & 0x0000FF00)>>8);
- temp[2] = (u8)((temp0 & 0x00FF0000)>>16);
- temp[3] = (u8)((temp0 & 0xFF000000)>>24);
- temp[4] = (u8)(temp1 & 0x000000FF);
- temp[5] = (u8)((temp1 & 0x0000FF00)>>8);
- temp[6] = (u8)((temp1 & 0x00FF0000)>>16);
- temp[7] = (u8)((temp1 & 0xFF000000)>>24);
- temp[8] = (u8)(temp2 & 0x000000FF);
- temp[9] = (u8)((temp2 & 0x0000FF00)>>8);
- temp[10] = (u8)((temp2 & 0x00FF0000)>>16);
- temp[11] = (u8)((temp2 & 0xFF000000)>>24);
- }
復制代碼
- 通過void Get_ChipID(void)就可以得到ID碼了,這個ID碼可以用串口輸出,也可以用STM32 ST-LINK Utility讀出來。這里,兩種方法我都會講到。
-
- 方法一:通過串口輸出得到ID碼。
-
- /****************************************************************************************************************************
- 函數名稱:串口2初始化配置
- 創建時間:2015-08-11
- 修改時間:2015-08-11
- 備 注:
- *****************************************************************************************************************************/
- void USART2_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; //定義GPIO類型結構體
- USART_InitTypeDef USART_InitStructure; //定義串口類型結構體
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //配置GPIOA時鐘,并使能時鐘。
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//配置USART2的時鐘,并使能時鐘
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //配置PA.02作為TXD
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //配置成推挽輸出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO時鐘為50MHZ
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //配置PA.03作為RXD
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置GPIO輸入浮空
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
- USART_InitStructure.USART_BaudRate = 115200; //配置串口波特率為115200
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位數據位
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
- USART_InitStructure.USART_Parity = USART_Parity_No; //
- USART_InitStructure.USART_HardwareFlowControl = T_HardwareFlowControl_None; //硬件流程控制
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //啟動發送和接收
- USART_Init(USART2, &USART_InitStructure); //初始化串口2
-
- USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能串口2接收中斷
- USART_Cmd(USART2, ENABLE); //使能外部中斷
- }
復制代碼
- /****************************************************************************************************************************
- 函數名稱:USART2發送數據函數
- 創建時間:2015-08-11
- 修改時間:2015-08-11
- 備 注:
- *****************************************************************************************************************************/
- void Usart2_SendData(uint16_t uiSendDataNumber,uint16_t * uiData)
- {
- static uint16_t uiTempData = 0; //發送數據臨時變量
- for(uiTempData = uiSendDataNumber;uiTempData > 0;uiTempData--) //數據的個數
- {
- USART_SendData(USART2, *uiData++); //調用發送函數
- // uiData++; //發送數據的地址加1,切換到下一個要發送數據的地址。
- while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);//發送緩沖區空狀態標志位。只有當緩沖區為空時,才發送下一個數據。
- }
- }
復制代碼
串口配置好后,就可以調用Usart2_SendData(12,temp);函數,就能在串口助手看到ID碼了。
- 芯片唯一ID碼:32 FF DA 05 43 41 38 36 30 71 02 43 96位
復制代碼
方法二:通過STlink得到ID碼。
1、打開STM32 ST-LINK Utility,確保STlink與硬件連接正常。 2、在Address中輸入“0x1FFFF7E8”芯片ID碼的首地址。輸入完成后,STlink自動讀取ID碼。
3、確定后,自動讀取ID碼
但是,用STlink讀出來的只有64位,少了32位,這個“0x1FFFF7F0”地址中讀取失敗,我也不知道是什么原因,可能是廠家不允許下載器訪問。
我們把上面ID碼整理一下:32 FF DA 05 43 41 38 36,由于在內存中是小端存儲方式。所以是這樣的。
通過兩種方法,把我們想要的ID碼得到后。就可以進行最關鍵的一步,修改HEX文件。
二.修改HEX文件加密
1、打開STM32 ST-LINK Utility,打開HEX文件。
- uint16_t aa[12] = {0xee,0x01,0x02,0x03,0x04,
- 0x05,0x06,0x07,0x08,0x09,
- 0x10,0x11};//預置ID號,在HEX文中修改。
復制代碼
現在就打HEX文件打開后。找到上面定義的數組,這個是關鍵所在,我花了一個下午時間,才搞清楚。
[size=9.0000pt]2、找到自定義ID在地址的位置后,開始修改。
3、把數組“aa[12]” 里面的內容改成與芯片的實際ID碼一致。如果程序被讀出來,在另外一片相同的芯片上面也運行不起來。因為設置的ID與芯片實際ID不一致。
4、現在如果下載,會提示你文件被修改,需要保存。我們保存成HEX文件到桌面。
5、重新打開剛才保存的HEX文件。現在可以看到,修改的ID已經在HEX文件里面了。
6、下載程序到單片機。
7、完成下載。到這兒就基本完成了。
8、看到這兒,有的人會問,為什么不在編譯時直接放在數組里,還要在HEX文件中修改,這樣做的目的是方便批量生產。
- 在程序中,我做了一個簡單的判斷。如下
- Get_ChipID(); //先讀出芯片實際ID
- for(i=0;i<12;i++)
- {
- if(temp == aa) //判斷設置的ID與實際ID是不是一致。如果一致。LED燈不亮
- {
- ;
- }
- else
- {
- GPIO_ResetBits(GPIOF, GPIO_Pin_6);
- }
- }
復制代碼
|