ترفندی برای کم اثر کردن شکستن قفل میکروکنترلر

اگر می‌خواهید محصول خود را که مبنی بر میکروکنترلر است، به بازار یا به هر دلیلی به افرادی، ارائه دهید و در عین حال نگران کپی شدن آن و هدررفتن زحماتتان هستید. احتمالاً خواندن این مطلب برای شما خالی از لطف نباشد. در ادامه می‌خواهم برای شما از روشی صحبت کنم که حتی اگر کسی با شکستن قفل میکروکنترلر به کد هگز برنامه شما دسترسی پیدا کرد، نتواند به راحتی از آن استفاده کند. اگرچه توضیحاتی که در ادامه خواهید دید به طور خاص برای میکروکنترلر STM32 نوشته شده است، اما اصل روش برای سایر میکروکنترلرها نیز کاربردی است.

کافی است چند روز از آشنایی شما با برنامه نویسی -خاصه در اینجا نوع میکروکنترلری‌اش- گذشته باشد تا پی ببرید که کار چندان ساده‌ای هم نیست. این ساده نبودن باعث شده تا عده‌ای به این فکر بیفتند که چه کاری است این همه زحمت. زحمت برنامه‎نویسی اش ارزانی طراحان. ما بیاییم کپی کنیم و بفروشیم و حالش را ببریم.

ماجرا از آنجا شروع شد که قرار شد نسخه‌ای از یکی از بردهای شرکت برای نمونه، به یک شرکت خارجی داده شود تا فیدبک لازم گرفته شود. انصافاً شرکت برای به اینجا رساندن برد هزینه مادی و زمانی زیادی صرف کرده بود. برای من به شخصه تصور این موضوع بسیار دردناک می‌نمود که در آن ینگه دنیا سر ایرانی جماعت کلاه گذاشته شود و قرار باشد این زحمات دوستان و همکاران به باد فنا رود. از این رو در پس خاطراتم به این صرافت افتادم که راهکاری برای این موضوع سرچ کنم. بارها خوانده بودم که شکستن قفل میکروکنترلر، شکستن شاخ غول نیست. کافی است که به برادران کشور دوست و همسایه برد را به همراه حق الزحمه، ارائه دهید تا برایتان تا فیها خالدون مدار آمار دربیاورند. بگذریم که بعضی شنیده‌ها حاکی از این است کار از این ساده‌تر هم شده و لازم نیست بردتان را به آن سر دنیا بفرستید و … .

قبل از اینکه ادامه روش را توضیح دهم، لازم و واجب است تشکر کنم از شخصی که اصل این روش را از او آموختم. کسی که سال‌هاست برای افزایش بار علمی کسانی که در حوزه الکترونیک کار می‌کنند، تلاش می‌کند و هزینه می‌دهد. کسی که علاوه بر مهندسی، سال‎هاست معلمی می‌کند و بر گردن بسیاری از ماها حق دارد.

از این صفحه می‌تونید مقاله “استفاده از شماره شناسایی میکروکنترلر برای جلوگیری از کپی کردن برنامه” استاد کی نژاد رو دانلود کنید. خوبه که قبل از خوندن ادامه مطلب، نگاهی بهش بندازید.

برای سادگی کار از همان پیش فرض‌هایی استفاده می‌کنم که در مقاله ذکر شده.

بعضی از میکروکنترلرها به صورت داخلی حافظه EEPROM دارند. شرکت ST اما راه دیگری برای طراحان در نظر گرفته و آن هم استفاده از حافظه فلش خود میکروکنترلر است. -البته معدود سری‌هایی هم تولید شده‌اند که حاوی EEPROM داخلی‌اند- اگر فرصتی باقی بود، انشالله در پستی جداگانه در این خصوص مفصل توضیح خواهم داد.

با این حساب به جای EEPROM گفته شده در مقاله، ما از فلش میکروکنترلر استفاده خواهیم کرد. به این ترتیب باید بتوانیم به صورت مستقیم، آدرسی از حافظه فلش میکرو را مقدار دهی کنیم. این کار با استفاده از نرم افزار ST-LINK یا STM32CubeProgrammer امکان پذیر است.

ابتدا باید read out protection  و write protection حافظه فلش میکرو را غیرفعال کنیم. در این صورت بعد از پروگرام کردن میکروکنترلر، امکان مشاهده و تغییر حافظه فلش را به صورت مستقیم خواهیم داشت. حال باید آدرسی از حافظه فلش میکروکنترلر را در نظر بگیریم که تغییر آن موجب اختلال در عملکرد برنامه اصلی ما نشود.

قبل از اینکه کاری انجام دهید، لازم هست حتماً نگاهی به memory map میکروکنترلر نگاهی انداخته باشید. main flash memory ناحیه‌ای است که برنامه ما در آن قرار می‌گیرد و البته در اینجا قرار است علاوه بر این، قسمتی از آن را برای ذخیره کد اختصاصی تراشه – UID – استفاده کنیم. بطور کلی حافظه‌های فلش در میکروهای STM32 از بانک تشکیل شده‌اند. اغلب میکروها دارای یک بانک هستند، اما بعضی سری‌های رده بالا هستند که بیش از یک بانک حافظه دارند. بانک‌ها هم از واحدهای کوچکتری به نام sector تشکیل شده‌اند. این واحدها در بعضی سری‌ها خود از واحدهای باز کوچکتری به اسم page می‌توانند تشکیل شده‌ باشند. مثل همین میکرویی که قرار است در ادامه بر روی آن کار را پیش ببریم.

اگر به صفحه 47 رفرنس منول STM32F030F4P6 مراجعه کنید، – فایل پی دی اف DM00091010.pdf – خواهید دید که این میکروی ساده تنها از 4 sector تشکیل شده. هر سکتور هم به 4 page یک کیلوبایتی تقسیم می‌شود. هر چند که UID تنها 96 بیت جا می‌گیرد اما از آنجا که ما نمی‌توانیم عملیات پاک کردن فلش را برای کمتر از حداقل یک page انجام دهیم، لازم است تا فضایی به مراتب بیش از 96 بیت را – یک page – برای این کار اختصاص دهیم. پس باید مراقب بود تا حجم کد برنامه ما تا قبل از page نهایی به اتمام برسد. به عبارتی از 15 کیلوبایت بیشتر نشود.

حالا نوبت به بحث شیرین کدنویسی می‌رسد.

قبل از شروع حلقه اصلی برنامه یا هر جای دیگری که می‌خواهید صحت برنامه را چک کنید، باید کدهای چک کردن اصل بودن برنامه را قرار دهید یا به کمک یک تابع فراخوانی کنید. مثلاً من تابع زیر را برای اینکار در نظر گرفتم و در فایل main.c خودم اون رو ایجاد کرد. در نتیجه قبل از ورود به حلقه اصلی این تابع را فراخوانی کردم.

اولین کاری که باید بکنیم این است که مقدار خوانده شده در جایی از حافظه فلش را با رمزی که از پیش تعیین کرده‌ایم، چک کنیم. در اینجا همانند مقاله رمز را 0x50 قرار دادم و همانطور که از خط 5 مشخص است، تساوی آن را با مقداری که در صدبایت مانده به انتهای فلش، چک کرده‌ام.

اگر رمز صحیح باشد و ما وارد بلاک شرطمان شویم، پس از اینکه حافظه را از حالت قفل درآوردیم باید آخرین page را پاک کنیم. در اینصورت برای دفعات بعد لازم نیست وارد این بلاک شویم. البته کارمان هنوز در این بلاک تمام نشده و باید قبل از خارج شدن UID میکرو را بخوانیم و در آدرسی مجزا از رمز ذکر شده، این 96 بیت را ثبت کنیم. همانطور که مشاهده می‌کنید در تابع زیر من UID را در 200 بایت مانده به انتهای فلش، جایی که هنوز در page آخری قرار دارد، قرار دادم.

پس از قفل مجدد فلش به جهت اطمینان و جلوگیری از نوشتن ناخواسته بر روی آن و خارج شدن از این بلاک شرطی، نوبت به چک کردن UID با آدرس تعیین شده ما برای ثبت UID در حافظه فلش، می‌رسد. اگر این چک کردن جواب نداد، یعنی اینکه جایی از کار می‌لنگد و احتمالاً کسی به امیدی واهی قصد سرقت برنامه ما را داشته. از این رو ما هم او را آنقدر در (1)while نگه می‌داریم، تا بفهمد که یک من ماست چقدر کره دارد:)

void verify_unique_id(void)
{
	uint32_t unique_id[3] = {0, 0, 0};

	if(*(__IO uint32_t*)(FLASH_BANK1_END + 1 - 100) == 0x50)
	{
		FLASH_EraseInitTypeDef erase_page;
		uint32_t page_error = 0;


		erase_page.TypeErase   = FLASH_TYPEERASE_PAGES;
		erase_page.PageAddress = FLASH_BANK1_END + 1 - FLASH_PAGE_SIZE;
		erase_page.NbPages     = 1;

		/* Unlock the Flash Program Erase controller */
		HAL_FLASH_Unlock();

		HAL_FLASHEx_Erase(&erase_page, &page_error);

		unique_id[0] = *(__IO uint32_t*)(UID_BASE);
		unique_id[1] = *(__IO uint32_t*)(UID_BASE + 4);
		unique_id[2] = *(__IO uint32_t*)(UID_BASE + 8);

		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_BANK1_END + 1 - 200, unique_id[0]);
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_BANK1_END + 1 - 200 + 4, unique_id[1]);
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_BANK1_END + 1 - 200 + 8, unique_id[2]);

		HAL_FLASH_Lock();
	}

	unique_id[0] = *(__IO uint32_t*)(UID_BASE);
	unique_id[1] = *(__IO uint32_t*)(UID_BASE + 4);
	unique_id[2] = *(__IO uint32_t*)(UID_BASE + 8);

	if(unique_id[0] != (*(__IO uint32_t*)(FLASH_BANK1_END + 1 - 200)) ||
			unique_id[1] != (*(__IO uint32_t*)(FLASH_BANK1_END + 1 - 200 + 4)) ||
			unique_id[2] != (*(__IO uint32_t*)(FLASH_BANK1_END + 1 - 200 + 8)) )
	{
		while(1);
	}

}

مقدار دهی به آدرسی خاص از حافظه فلش، بدون کدنویسی:

تا اینجا اغلب کاری که باید انجام دهید را توضیح دادم. اما هنوز یک مورد دیگر هم مانده که شاید سوال شما هم باشد.

چگونه رمزمان را در حافظه فلش ثبت کنیم تا عملیات ثبت UID انجام شود و بعداً خودمان در لوپ بی‌نهایت خودمان! گیر نکنیم؟

به عبارتی بر طبق مثال اینجا، قصد داریم پس از اینکه کدنویسی را انجام دادیم و میکروکنترلر را پروگرام کردیم، عدد 0X50 را در آدرس صد بایت مانده به انتهای فلش یعنی FLASH_BANK1_END + 1 – 100 = 0x8003F9C بریزیم.

برای اینکار نرم افزار STM32 ST-LINK Utility را نیاز داریم. اینکار را با نرم افزار جدید ST با نام CubeProgram هم می‌توان انجام داد.

پس از کانکت شدن موفقیت آمیز این برنامه با میکروکنترلر، ابتدا بایست هم Read و هم Write پروتکشن میکرو را برداشت. برای اینکار به target->option bytes بروید. ممکن است در میکروهای مختلف این قسمت کمی متفاوت باشد. برای میکرو مورد بررسی ما باید read out protection را بر روی level 0 قرار داد و همچنین در پایین صفحه، قسمت flash sectors protections تیک همه‌ی گزینه‌ها را با unselect all برداشت. پس از apply کردن، میکروکنترلر ما آماده است تا فلشش مقداردهی شود.

در صفحه اصلی نرم‌افزار در قسمت address شما می‌توانید آدرس مد نظرتان را وارد کنید. پس در اینجا ما 0x8003F9C  را میزنیم. در صورتی که data width هم روی 32 بیت قرار دارد، در اولین خونه 0x50 یا همان رمز خودتان را وارد کنید و اینتر را بزنید تا پروگرام شود.

پروگرام در آدرس خاصی از حافظه فلش
پروگرام در آدرس خاصی از حافظه فلش

اگر همه‌ی مراحل را به درستی طی کرده باشید پس از یک بار ریست باید برنامه شما به درستی کار کند وگرنه در آن لوپ کذایی باقی خواهد ماند.

پی‌نوشت 1:

بد نیست که آدرس 200 بایت مانده به انتهای فلش را -راستی مقدارش چیه؟- در همین نرم افزار بزنید تا هم UID میکروتون رو ببینید و هم درک بهتری از آنچه اتفاق افتاده پیدا کنید.

پی‌نوشت 2: (مهم)

دیفاین‌های بالا مثل FLASH_BANK1_END  برنامه را خواناتر و قابل حمل‌تر می‌کند. این تعاریف بطور پیشفرض در پروژه تولید شده توسط کیوب هستند. اما بهتر است که چکی هم با دیتاشیت انجام بدهید. متاسفانه در پروژه ایجاد شده برای من، این عدد اشتباه بود و لازم شد تا دیفاین را به مقدار صحیحش تغییر دهم. این مشکل احتمالاً ناشی از باگ برنامه است و ممکن است برای شما وجود نداشته باشد. اما لازم بود که به این نکته اشاره کنم.

 

۱۷ دیدگاه‌ها

  1. استاد
    پاینده باشی

      • استاد عالی بود
        ولی کاره یک ساعته بای پس این روش
        کسی که کپی میکنه اینارو به راحتی دور میزنه.uid میکرویی که کار میکنه رو بر میداره و برنامه تغییر میده که به جای خوندن uid
        متغییرها رو مقدار میده.
        فقط یک روش هست داره قفل نشکنه
        که پایه پروگرام clock و…بسوزونی که اونم روش خاص خودشو داره.

        • ممنون از نظرت. از میزان آسونی کار اطلاعی ندارم، اما بله درست میگید، به هر حال امکان شکستن قفل وجود داره. و این رو هم باید توجه کنیم که اینها سختی کار و هزینه رو برای فرد کپی کننده زیاد میکنه. همونطور که بالاخره میشه گاو صندوق رو باز کرد، اما دلیل نمیشه ما توش وسایل باارزشمون رو نذاریم با این توجیه که یکی هست که این رو بتونه باز کنه:)

        • چه جوری میخوان فایل هگزو تغییر بدن؟

          • سلام، متوجه نشدم. کی تغییر بده؟

          • ۲ بار تا الان من میکرو از برد آلمانی دادم چین خوندنش و کد هگز رو به من دادند بعد هم من اونو تبدیل به کد C کردم و تغییرات رو دادم.
            در عوض یک شرکت نامرد چینی هم میکروی یک برد من رو خونده و کپی کرده.

  2. مهدی شفیع

    این روش برای میکرو کنترلر های ۸ بیتی هم امکان پذیر است

  3. داود خردمند

    هر چند مدتی از این پست گذشته اما باید بگم این کار بیفایده ست و فقط زحمت کدنویسی رو بیشتر میکنه. طرف که کد رو کپی کرده با تریس کردن یا دیباگ میتونه به اون حلقه وایل برسه و خیلی راحت یک دستور اسکیپ یا نوپ و غیره جاش بذاره تا از لوپ بپره بیرون. روشهای دیگری هست که بهتر از اینه. اما همین روش هم بخواهیم درستش کنیم کافیه بجای اون حلقه وایل (در صورت کپی بودن فایل) از یک تاخیر با مقدار رندم (مثلن یک دقیقه تا یک ساعت) یک اشکالی در کارکرد برنامه ایجاد کنیم. ردیابی این اشکال فوق العاده سختتر هست چون برنامه کامل از کار نمیوفته فقط در حدی خراب کار میکنه که قابل اطمینان نخواهد بود و بدرد تولید یا استفاده توسط فرد کپی کننده نمیخوره، طرف سردرگم میمونه اشکال از کجاست و بعید هست که به فکرش برسه ایراد از کد هست

    • سلام، ممنون از نظرت. بله باهات موافقم و ایده‌ات رو دوست داشتم. بحث امنیت خیلی گسترده است و هرقدر هم که راه‌های نفوذ بسته بشه باز راهی برای شکستنش پیدا میشه 🙂 ما فقط می‌تونیم سعی کنیم این احتمال رو کاهش بدیم.

  4. سلام . من برای امنیت نرم افزارم از روش یونیک ای دی استفاده میکنم . مراحل کار بدین شکل است.
    ابتدا ۴ تا کد UID میکرو خونده میشه بعد توی یک تابع این ۴ عدد با هم مخلوط میشه و ۴ تا کد دیگه استخراج میشهEncrypt میشه (این کار جوری اجام میشه که کاملا یک طرفه است و از کد های ثانویه نمیشه به کد های اولیه رسید) کد های ثانویه در قسمتی از eeprom خارجی ذخیره میشه . در خلال برنامه و در بعضی از روتین ها این کد ها به روش خاصی مقایسه و چندین خروجی تابع ایجاد میشود. خروجی این توابع کیفیت اجرای برنامه را دستکاری میکند . در صورتی که ای توابع خروجی های غیر مجاز تولید کنند میکرو بدون هیچ علامتی دستورات را اشتباه انجام میدهد.

  5. محمد نامدارصحت

    با سلام من می خواهم مقداری را در یک خانه از حافظه فلش ذخیره کنم و بعد آن را آپدیت کنم ولی این عمل انجام نمی شود و باید حتما آن صفحه را کامل پاک کنم در صورتی که در برنامه st link utility می شود مقدار را ویرایش کرد ولی در برنامه آیا راهی وجود دارد که فقط همان خانه را پاک کنیم و دوباره مقدار دهی کنیم ؟؟؟
    باتشکر

    • با سلام، باید به رفرنس منوال میکروکنترلرتون مراجعه کنید. اصولاً به این صورت هست که شما باید کل page را پاک کنید و بعد میتونید به یک بایت از اون پیج مقدار جدید بدین. با برنامه st link utility من دقیق خاطرم نیست، اما حتی اگر امکان پذیر باشه، میتونه این پروسه رو به صورت ضمنی و بدون اینکه شما متوجه بشید انجام بده. یعنی هر بار که میخواهید یک بایت رو تغییر بدین، از پیج بک آپ بگیره و باید مدنظر شما رو تغییر بده. بعد کل پیج رو پاک کنه و با مقادیر جدید جایگزین کنه.

      • محمد نامدارصحت

        سلام احتمالا به اینصورت کار میکنه که گفتید چون خیلی گشتم چنین چیزی پیدا نکردم که فقظ یک خانه را بشود ویرایش کرد بدون پاک کردن یک صفحه از پاسخ شما ممنونم .

  6. سلام و احترام،
    خطاب به دوستان کپی کار! باید بگم که خیلی مواقع اینکه خودمون یه کد‌برای میکروکنترلر بنویسیم راحت تر و ارزون تر از کپی کردن میکروکنترلر هست!
    مثلا یکبار با شخصی مکاتبه کردم که‌ چند روز تلاش برای شکستن قفل و بازگشایی کد میکروکنترلر PIC12F679 انجام داده بود و به نتیجه هم نرسیده بود.
    در حالی که در همون مدت میتونست زبان C و رجیستر های میکروکنترلر رو یاد بگیره و خودش کد برای میکروکنترلر بنویسیه!
    خلاصه کلامم اینه که به اینکه خودتون هم یه کد بنویسید فکر کنید…

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *