امنیت در کد نویسی
این قسمت و همچنین امنیت فایل
اجرایی کاملآ به نحوه استفاده برنامه نویس از قفل و استفاده بهینه او از امکانات
باالقوه قفل بستگی دارد. چرا که حتی اگر قفلی از نظر لایه های امنیتی بسیار محکم و
غیر قابل نفوذ باشد اما اگر بصورت ساده و ابتدایی مورد استفاده قرار گیرد به راحتی
توسط Cracker قابل دور زدن خواهد
بود. با رعایت نکات زیر میتوان تا حدی امنیت فایل اجرایی را تامین نمود.
روشهای بهینه کردن قفل گذاری
- چک کردن قفل در درون کد نرم
افزار بدون Timer
برای بالاتر رفتن امنیت نرم افزار سعی کنید از Timer Component برای چک کردن قفل
استفاده نکنید. زیرا یافتن رویداد (Event) OnTimer
برای یک Cracker
کار بسیار ساده ای بوده، و قادر هستند آنها را به راحتی حذف
کنند. پیشنهاد میشود برای انجام روند قفل گذاری در قسمتهای مختلف نرم افزار دستورات
ارتباط با قفل تایپ شده و از Function
یا Procedure
استفاده نگردد. در صورتیکه متدی برای چک قفل نوشته میشود نام آن
طوری انتخاب شود که Cracker را
گمراه کند.
توجه
: در صورتیکه میخواهید در روند قفل
گذاری پیغام خاصی را نمایش دهید متن پیغام را طوری انتخاب نمایید که به هیچ عنوان
نشان دهنده روند قفل گذاری نباشد.
- چک کردن قفل به صورت تصادفی
میتوانید به جای استفاده از Timer
از این روش استفاده نمایید، به این ترتیب که در لابه لای
کدهای یک متد یک عدد تصادفی را تولید نموده (بین 1 تا 10) و اگر این عدد مساوی عدد
مورد نظر شما بود چک قفل انجام شود. این عمل به این دلیل است که با هر بار اجرای
متد قفل چک نشده و چک کردن قفل به صورت کاملا تصادفی انجام میگیرد و همچنین سرعت
سیستم کاهش نمی یابد :
Function1()
{
… //function1 Instructions
If (GenerateRandom() = 7)
{
… //Checking Software Lock
}
… //function1 Instructions
}
توجه داشته باشید که این روش باید در قسمتهای مختلف
برنامه مورد استفاده قرار گیرد. برای مثال هنگام باز شدن فرمهای اصلی و کلیک کردن button ها.
- ذخیره کردن داده های مهم نرم
افزار در قفل
در اغلب نرم افزارها مقادیری (Database
Connection String و Database
Password) وجود دارد که برای نرم افزار بسیار مهم و
حیاتی بوده و نرم افزار بدون دسترسی به آن مقدار قابل اجرا نخواهد بود. برای بالا بردن امنیت نرم افزار خود،
پیشنهاد میگردد مقادیر مهم را در قفل ذخیره نموده و هنگام اجرای برنامه در زمان چک
کردن قفل آن مقدار را از قفل خوانده و استفاده نمایید. در این حالت حتی اگر Cracker موفق شود روتین های چک قفل را از
درون فایل اجرایی حذف نماید باز هم نرم افزار بدون آن مقدار قابل اجرا نخواهد بود.
- چک کردن قفل درون یک Thread
این روش، روشی بسیار مفید در زمانیکه برنامه نویس بخواهد قفل
نرم افزاری را به صورت مداوم چک کند بدون اینکه در کار User
Interface نرم افزار خللی وارد شود. در این روش توصیه
میشود برای امنیت بالاتر از دو یا چند Thread
استفاده گردد و هر کدام از آنها با روشی متفاوت عملیات چک کردن
قفل را در یک حلقه انجام دهد. البته در نظر داشته باشیدکه از کار انداختن Thread ها ی برنامه کاری بسیار ساده می
باشد پس ، برای چک قفل برنامه هرگز فقط به Thread
ها اکتفا نکنید و در صورت استفاده از آنها سعی کنید بین Thread و exe
اصلی به نوعی وابستگی ایجاد کنید. به نحوی که در صورت از
کار انداختن Thread ها ، برنامه
شما به نوعی Crash کرده و روند
اجرای exe مختل شود. یا قسمتی از
کد اصلی برنامه را داخل thread
قراردهید. در این صورت اگر Thread ، Suspend
شود برنامه به صورت کامل اجرا نخواهد شد. لازم به ذکر است که Trace کدی که داخل Thread نوشته شده به نسبت مشکل تر است و
استفاده از Thread هایی که شامل
تکه کد های اصلی برنامه هستند بسیار مفید خواهد بود.
جلوگیری از Debug کردن نرم افزار
- چک کردن مقدار
CRC-32 برای DLL
یا Activ
(Clyclic R edunda ncy Checksum) CRC چيست؟
معمول ترین آن CRC32
بوده که یک عدد 32 بیتی است و برای هر داده ای قابل محاسبه می
باشد از فایل گرفته تا یک مقدار رشته ای یا حتی یک قسمت از حافظه. همانطور که می
دانید داده ها به صورت رشته ای از بایتها قابل نمایش بوده که مقدار CRC هر بایت قابل محاسبه میباشد.
الگوریتمهای مختلفی برای محاسبه CRC
وجود دارد ولی نکته قابل توجه اینست که تمام آنها برای یک داده
ثابت مقدار یکسانی را تولید میکنند. توجه داشته باشید که اگر برای یک داده مقدار CRC چند بار محاسبه شود مقادیر به دست
آمده یکسان هستند و هر داده ای مقدار CRC
مختص به خود را دارد به عبارت دیگر مقدار CRC هیچ دو داده ای با هم یکسان نیست.
به طور معمول برای استفاده از قفل های سخت افزاری یا نرم
افزاری برای ارتباط با قفل باید از DLL
یا ActiveX
استفاده نمود. به دلیل ماهیت خاص این نوع Object
ها، آنها قابل جایگزین شدن بوده و با این کار میتوان اداره نرم
افزار را در دست گرفت. پیشنهاد میشود در صورت امکان با استفاده از روشهای مختلف
محاسبه CRC قبل از استفاده از
متدهای یک DLL یا ActiveX مشخص نمایید که آیا این Object، Object
اصلی است یا خیر؟
//Before using object
If (CalculateCRC(object) = MainCRC)
{
//you can use methods of object
{
- استفاده از کدهای نا معلوم و نا
مشخص
استفاده از کدهای نا معلوم و نا مشخص،
Debug نرم افزار را مشکل ساخته و زمان بیشتری برای Crack کردن نرم افزار باید صرف شود. در
واقع این کدها در نرم افزار عمل خاصی را انجام نداده و فقط در بخشهایی از نرم افزار
که قرار است کلمه عبور یا شماره سریال وارد شود قرار گرفته و کار Debug را مشکل تر میکند. برای مثال
دستوراتی به زبان اسمبلی وجود دارد (Macro)
که با قرار دادن آنها در بین دستورات برنامه باعث میشود بعضی از Debugger ها را با مشکل مواجه شوند :
__asm{
mov eax, offset jump
push eax
//this can include any code, but it must not change
//the stack or, if it does, it must clear it.
pop eax
jmp eax
jump:
//any code
}
- غیر فعال نکردن منوها و دکمه ها
در نرم افزارهای Trial
اگر قرار است یک نرم افزار را به صورت
Trial منتشر نمایید، منوها و دکمه ها را غیر فعال نکرده
و کد اصلی را به طور مستقیم در نرم افزار قرار ندهید بعضی از ابزارهای نرم افزار
سازی امکاناتی را در اختیار قرار داده تا بتوانید فقط کد های مورد نظر را Compile نمایید :
{$DEFINE Trial}
{$IFDEF Trial}
//No Action
{$ELSE}
//Operation (No Compile)
{$ENDIF}
- استفاده نکردن از رویدادها به
طور مستقیم در Borland Delphi
ه علت ماهیت خاص VCL(Visual Component
Library) در Delphi یافتن
Event ها از طریق Decompile
کردن فایل EXE
کا ر ساده ای میباشد بنابراین پیشنهاد میشود که برنامه نویسان
دلفی از رویداد های آن به طور مستقیم استفاده نکرده و به روش زیر عمل کنند :
Button1.OnClick := Method1
Code Checksum - CRC Calculate
محاسبه CRC
برای بخشی از کد یا تمام فایل در زمان اجرا، راه مناسبی برای
جلوگیری از Debug میباشد. زیرا Debugger ها به منظور قرار دادن Breakpoint در برنامه باید در کد تغییر
ایجاد کنند. در این لحظه با محاسبه مجدد CRC
برای متدهای درون برنامه میتوان مشخص نمود که کد تغییر کرده است
یا خیر. این روش نتنها در مقابل Debugger ها موثر است بلکه میتوان در
مقابل Code Patching نیز از آن
استفاده نمود.
//Calculate CRC in memory
if (Func1CRC != MainCRC)
{
//Do an action to stop your progra
}
Debugger:
Debugger برنامه ای است که به توسعه دهنده (Developer) اجازه می دهد برنامه را در
حال اجرا مشاهده نماید. دو ویژگی مهم آنها قرار دادن
Breakpoint و همچنین Trace
کردن برنامه ها میباشد.
این ویژگی ها به توسعه دهنده اجازه میدهد خطاهای برنامه
را یافته و در جهت اصلاح آنها اقدام کند. Debugger
یکی از مهمترین ابزارهای مهندسی معکوس بوده که از یک Disassembler برای برگرداندن کدها به
زبان اسمبلی استفاده مینماید.