42a 「謎の自殺機能X」 TAMPER を試してみる (NVIC,RTC,BKP,GPIO,UART,LEDチカチカ)

謎の機能TAMPER

製品をハッキングしようとした人が、ネジを開けてしまったらその製品が自殺するという機能のことだそうです.

ソフトを動かすために必要なパスワードをbackup registerに記憶しておき、バッテリでbackupしておきます.

そしてTAMPERピンには、製品のフタに取りつけたSWを接続しておき、フタが開くとTAMPERがオンになって、パスワードを消してしまいます.

その結果、フタを開けてしまったその製品はもう動かなくなってしまいます.

そんな実装をするための機能です.こんな機能を知ってました? わたしは初めて知りました.

こんなケッタイな機能をコンスーマ製品に実装した例をわたしは寡聞にして知りませんが、軍事用飛行機は墜落する前にHDDを小型の爆弾で破壊すると聞いたことがあるので、軍事用製品には重宝される機能なんでしょうか?

TAMPERはBACKUP機能の一部である

BACKUP機能とは、バッテリでレジスタが保持されるという理解でだいたい合ってます.

バッテリでバックアップされたレジスタに、key wordを書いておいて、フタが開いたらkey wordを消してしまうのがTAMPER機能です.

なので、まずはBACKUP機能について一通り知っておくのがよいでしょう.

「41a RTCをバッテリバックアップで動かしてみる」にBACKUPの説明が書かれているのでそれを参照してください.

TAMPERピンはどれ?

こちらの表にピンアサインが書かれています. LQFP64の列に着目します.

PC13のalternate functionのdefaultがTAMPERピンであるとわかります.

回路づくり

Li電池+TAMPERスイッチ+UART をSTM32-DISCOVERYにとりつけます.

詳細解説は、「41a RTCをバッテリバックアップで動かしてみる」にLi電池とUSBシリアル変換基板の説明が書かれているのでそれを参照してください.

TAMPERピンPC13とGNDにプッシュSWを取りつけました.

下図の実体配線図に加えて、PA12のすぐそばのSB1にハンザ付けされているchip抵抗を外す必要がありますのでご注意のほど.

サンプルプログラムでどんな動作をさせるか?

最初に起動するとterminal softにこんなメッセージが出るはずです.

このメッセージが伝えるところは、backup register BKP_DR1がゼロなのでダメだししています.

===starting TAMPER test program

===BKP_DR1=0

===backup data is cleared (timer does not work)

このままでは動かないので、backup register BKP_DR1に適切な値を書きます.

まず、terminal softでenterを押してください.するとメニューが表示されます.

-----TAMPER TEST MENU-----

int 10 : LED flash 10 Hz LEDが10Hzで点滅する

off : LED off LEDオフ

on : LED on LED点滅オン

setbackup : set backup register A5A5 backup registerを適切に設定する

-----------------------

setbackupと打ちます.すると次のように表示されます.backup registerにA5A5を書いたというメッセージです.

command: (0)setbackup/

set BKP_DR1 = A5A5

それで、STM32-DISCOVERYをresetしてください.すると、次のように表示されTIM3割り込みと同時にLEDが点滅し正常に動作します.

===starting TAMPER test program

===BKP_DR1=a5a5

===backup data is correct (timer starts)

TIM3 interrupted 1

TIM3 interrupted 2

TIM3 interrupted 3

では、TAMPER機能を試してみます.TAMPER SWをオンしてみます.

するとこのようなメッセージが出て、TAPMERだからもはや動かなくなった!と言ってます.

LEDの点滅も停まってしまいます.

===TAMPER interrupt has occured. BKP_DR1=0

===Backup registers are reset.

===This equipment does not work any more.

この状態でresetしてももうダメです.

回復するには、復活の呪文 setbackup を唱えないと動かなくなってしまったわけです.

これがTAMPERによる自殺機能です.

サンプルプログラムでどんな動作をさせるか? (その2)

しかし、おかしいとおもいませんか?

TAMPERはこのように通電時に自殺する機能でしょうか?

それよりか、電源OFF時にフタが開けられたら人知れず自殺するのがTAMPER本来の死に様ですよね?

なので、オフ状態でTAMPER SWをオンしたら自殺してくれるのかを確かめなくてはなりません.

やってみましょう.

1)起動してterminal softから setbackup と打って、backup registerを正しく設定します.

2)つぎに電源をオフします.USBケーブルを引っこ抜けばいいでしょう.

3)TAMPER SWをオンする

4)USBケーブルを接続すると、もはやLEDは点滅しません

5)terminal softを起動してSTM32-DISCOVERYをresetすると、次のメッセージが出て、backup registerが消えたことがわかります.

電源オフ状態でもTAMPERが効いて、自殺したことがわかりました.

===starting TAMPER test program

===BKP_DR1=0

===backup data is cleared (timer does not work)

サンプルプログラム解説

サンプルプログラムはこのページの末尾からDLできます.projectの組み込み方法はこちらを参照してください.

ページ末尾には42a....と42b....の2つのprojectがあります.

以下で説明するのは42a....です.42bはそのあとで説明します.

重要な注意: 当初compile errorが出ました.エラー回避のために、syscalls.cというファイルを追加してあります.これはyagartからDLしたファイルですが、これがないとcompileできません.トラブルシュートの詳細はこちらのページを参照してください.

42aのmain.c

#include "stdio.h" このC標準関数3兄弟を使おうとするとsyscalls.cが必要になります.詳しくはこちらを参照

#include "string.h"

#include "stdlib.h"

#include "stm32f10x.h" STM32のhardwareに根ざした設定を多数やってます

#include "usrlib-uart3d.h" UART3を使う自作ライブラリ

#include "usrlib-gpio.h" GPIOを使う自作ライブラリ

char UARTbuf[100]; UART送信文字列バッファ

NVIC_InitTypeDef NVIC_InitStructure; 割込優先度コントローラNVIC設定用構造体

GPIO_InitTypeDef GPIO_InitStructure; GPIO設定用構造体

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM設定用構造体

void Menu(void); UARTのメニュー処理ルーチン

int main(void) メインルーチン始まり

{

uint16_t x;

↓つかうperipheralのclockをenableしています.

clock周波数を変更するようなことは特にしてないので24MHzが供給されます.

RTCはRTC専用の初期設定ルーチンでclockをenableしてるのでここにはないです.

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // power for RTC

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); // backup for RTC

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // USART1 TX/RX (default)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // LED(PC8,PC9), TAMPER(PC13)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

第一引数の選択肢は下記です.

RCC_APB1PeriphClockCmd()

RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7, RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2, RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC

RCC_APB2PeriphClockCmd()

RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB, RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE, RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1, RCC_APB2Periph_USART1, RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17

↓割込優先度コントローラの設定

NVIC割り込みコントローラは4bitの優先度を持っていて、つまり最大で16個の割り込み優先度設定が出来るようです、

割り込み優先度には、PreemptionPriority とSubPriority にカテゴライズされています.

前者が大分類、後者が小分類というような階層になっています.

大分類に3bitを割り当て、小分類に1bitを割り当てるような階層作りができます.

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

それをやるのがこの↑関数です.引数によって、大分類と小分類のbitアサインを決めます.

| PreemptionPriority | SubPriority |

---------------------------------------------------------

NVIC_PriorityGroup_0 | 0 (0bit) | 0-15 (4bit) |

NVIC_PriorityGroup_1 | 0-1 (1bit) | 0-7 (3bit) |

NVIC_PriorityGroup_2 | 0-3 (2bit) | 0-3 (2bit) |

NVIC_PriorityGroup_3 | 0-7 (3bit) | 0-1 (1bit) |

NVIC_PriorityGroup_4 | 0-15(4bit) | 0 (0bit) |

ここでは、TIM3とUSART3とTAMPERの3つの割り込みを使うので NVIC_PriorityGroup_2 にしました.

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; TIM3割込

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 最高優先度

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; USART3割込

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 2番目優先度

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = TAMPER_IRQn; TAMPER割込

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; 3番目優先度

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

↓UARTの設定

UART_Init(); 自作ライブラリの初期設定をコール

UART_PutString("===starting TAMPER test program\r\n"); 起動メッセージをUARTへ送信

↓BACKUP機能の設定

PWR_BackupAccessCmd(ENABLE); BACKUP機能をenable

BKP_TamperPinLevelConfig(BKP_TamperPinLevel_Low); TAMPERピンをLOW activeに設定.BKP_TamperPinLevel_HighならHIGH activeになる.

BKP_ClearFlag(); BUCKUP機能のフラグをクリア(なんのためかは知りません)

BKP_ITConfig(ENABLE); TAMPER割り込みをenable

BKP_TamperPinCmd(ENABLE); TAMPERピンをenable (TAMPERには特段のGPIO設定は不要)

x = BKP_ReadBackupRegister(BKP_DR1); backup register BKP_DR1を読む.BKP_DR1~BKP_DR10まで使えます.

sprintf(UARTbuf,"===BKP_DR1=%x\r\n",x); BKP_DR1をUARTに表示

UART_PutString(UARTbuf);

↓BKP_DR1=A5A5ならkeyが正しく設定され生きている そうでなければ自殺した死後の世界

if(x==0xA5A5) UART_PutString("===backup data is correct (timer starts)\r\n\n");

else UART_PutString("===backup data is cleared (timer does not work)\r\n\n");

↓LEDチカチカの設定

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; PC8とPC9がLEDのポート.そこを出力に設定する.

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; 出力ドライブ能力は最低でよい

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; フツーの出力ポートに設定

GPIO_Init(GPIOC, &GPIO_InitStructure);

↓タイマの設定 24MHz÷24000÷1000=1Hz

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = 24000; // 24MHz / 24000 / 1000 = 1Hz

TIM_TimeBaseStructure.TIM_Prescaler = 1000;

TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

if(x==0xA5A5) TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); BKP_DR1がA5A5ならTIM3スタート

if(x==0xA5A5) TIM_Cmd(TIM3, ENABLE); BKP_DR1がA5A5ならTIM3スタート

while(1) { Menu(); } メニューを無限ループする

} メインルーチンおしまい

↓debug用関数

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t* file, uint32_t line){ while (1) { } }

#endif

↓TIM3の割り込みルーチン

int times = 1; TIM3割り込みカウンタ

void TIM3_IRQHandler(void) TIM3でLEDを点滅させます

{

TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); 割り込みフラグをクリア.これを怠ると次回の割り込みがかかりません.

LedBlueToggle(); ブルーLEDを点滅させる自作関数 usrlib-gpio.h の中に定義されています

sprintf(UARTbuf,"TIM3 interrupted %d\r\n",times++); UARTへメッセージを表示

UART_PutString(UARTbuf); UARTへメッセージを表示

}

↓TAMPER割り込みルーチン

void TAMPER_IRQHandler(void)

{

uint16_t x;

if(BKP_GetITStatus() != RESET) TAMPER割り込みかどうかをチェック

{

TIM_Cmd(TIM3, DISABLE); TIM3を止める

x = BKP_ReadBackupRegister(BKP_DR1); BKP_DR1を読み、UARTへ表示する

sprintf(UARTbuf,"===TAMPER interrupt has occured. BKP_DR1=%x\r\n",x);

UART_PutString(UARTbuf);

↓自殺メッセージをUARTへ送信

UART_PutString("===Backup registers are reset.\r\n");

UART_PutString("===This equipment does not work any more.\r\n");

BKP_ClearITPendingBit(); TAMPER割り込みフラグクリア

BKP_ClearFlag(); よくわからんがクリア

BKP_TamperPinCmd(DISABLE); TAMPERピンをdisable

}

}

↓以下はメニュー処理ルーチン

void Menu(void)

{

int n,i;

char tmp[30];

if(UART_STR_EXIST==0) return; // there is not any command line

n=separate_line(command); // divide a line into words

// display command line

strcpy(UARTbuf,"\r\ncommand: ");

for(i=0;i<n;i++)

{

sprintf(tmp,"(%d)%s/ ",i,Field[i]);

strcat(UARTbuf,tmp);

}

strcat(UARTbuf,"\r\n");

UART_PutString(UARTbuf);

if(strcmp(Field[0],"int")==0)

{

float f;

f = atof(Field[1]);

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = 24000; // 24MHz / 24000 / 1000 = 1Hz

TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(1000.0/f);

TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

}

else if(strcmp(Field[0],"off")==0)

{

GPIO_ResetBits(GPIOC, GPIO_Pin_8);

TIM_Cmd(TIM3, DISABLE);

}

else if(strcmp(Field[0],"on")==0)

{

TIM_Cmd(TIM3, ENABLE);

}

else if(strcmp(Field[0],"setbackup")==0)

{

BKP_WriteBackupRegister(BKP_DR1,0xA5A5); setbackupコマンドでBKP_DR1=A5A5を書きます

UART_PutString("set BKP_DR1 = A5A5\r\n");

}

else UART_PutString("command error\r\n");

UART_PutString("\r\n");

UART_PutString("-----TAMPER TEST MENU-----\r\n");

UART_PutString("int 10 : LED flash 10 Hz\r\n");

UART_PutString("off : LED off\r\n");

UART_PutString("on : LED on\r\n");

UART_PutString("setbackup : set backup register A5A5\r\n");

UART_PutString("-----------------------\r\n\n");

UART_STR_EXIST=0;

return;

}

ページ末尾には42a....と42b....の2つのprojectがあります.

以下で説明するのは42b....です.

42aはTAMPER SWがactiveになったら割り込みがかかり、UARTにメッセージをだすような仕組みになっていました.

しかし、TAMPERのそもそもの機能は、電源オフ時にTAMPER検出したらbackup registerをチャラにすることです.

したがって、TAMPER割り込みなんか不要です.

TAMPER割り込みを削除して、電源オフ時にTAMPERを喰らったら静かに自殺することだけに機能を絞ってみたのが以下の42bです.

42aと違うところだけ赤で示します.

42bのmain.c

#include "stdio.h"

#include "string.h"

#include "stdlib.h"

#include "stm32f10x.h"

#include "usrlib-uart3d.h"

#include "usrlib-gpio.h"

char UARTbuf[100]; // UART transfer string buffer

NVIC_InitTypeDef NVIC_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

void Menu(void);

int main(void)

{

uint16_t x;

// clock initialization

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // power for RTC

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); // backup for RTC

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // USART1 TX/RX (default)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // LED(PC8,PC9), TAMPER(PC13)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

// interrupt initialization

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // LED flash timer

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = TAMPER_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

// USART port initialization

UART_Init();

UART_PutString("===starting TAMPER test program\r\n");

↓TAMPER機能を活かすには最低限ブルーの部分は必要です.やってみてそのようです.

PWR_BackupAccessCmd(ENABLE);

BKP_TamperPinLevelConfig(BKP_TamperPinLevel_Low);

BKP_ClearFlag();

//BKP_ITConfig(ENABLE); // Enable Tamper interrupt

BKP_TamperPinCmd(ENABLE); // Enable Tamper pin

x = BKP_ReadBackupRegister(BKP_DR1);

sprintf(UARTbuf,"===BKP_DR1=%x\r\n",x);

UART_PutString(UARTbuf);

if(x==0xA5A5) UART_PutString("===backup data is correct (timer starts)\r\n\n");

else UART_PutString("===backup data is cleared (timer does not work)\r\n\n");

// LED port initialization

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // LED port

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOC, &GPIO_InitStructure);

// LED PC8 flashing timer

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = 24000; // 24MHz / 24000 / 1000 = 1Hz

TIM_TimeBaseStructure.TIM_Prescaler = 1000;

TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

if(x==0xA5A5) TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

if(x==0xA5A5) TIM_Cmd(TIM3, ENABLE);

while(1) { Menu(); }

}

↓TAMPER割り込みを廃止

//void TAMPER_IRQHandler(void)

//{

// uint16_t x;

// if(BKP_GetITStatus() != RESET) // Tamper detection event occurred

// {

// TIM_Cmd(TIM3, DISABLE);

// x = BKP_ReadBackupRegister(BKP_DR1);

// sprintf(UARTbuf,"===TAMPER interrupt has occured. BKP_DR1=%x\r\n",x);

// UART_PutString(UARTbuf);

// UART_PutString("===Backup registers are reset.\r\n");

// UART_PutString("===This equipment does not work any more.\r\n");

// BKP_ClearITPendingBit(); // Clear Tamper pin interrupt pending bit

// BKP_ClearFlag(); // Clear Tamper pin Event(TE) pending flag

// BKP_TamperPinCmd(DISABLE); // Disable Tamper pin

// }

//}