محافظت از برنامه فايل اجرايي (براي استفاده عموم)
اين مطالبي را كه مي نويسم شامل تجربيات بنده در مورد نوشتن رويه هاي محافظت از نرم افزارهاي تحت ويندوز مي باشد :
{ممكن است خيلي ساده باشه ولي خيلي روش كار و فكر كردم}
1- اگر اسمبلي يك مقداري سردر بياريد بسيار مفيد است
2-برنامه هاي وجود دارد كه به راحتي فانكشنها و پروسيجر هاي شما را غير فعال و حتي مسير اجرا شدن آنها را تغيير مي دهند . حتي بدون يك خط كدنويسي مانند :PE Explorer و Resource Tuner كه توي اين كار بسيار حرفه اي عمل مي كنند . حتي مي تونند برنامه هاي نوشته شده با VB.NET را فرمهايشان را نيز نشان دهند .
گروهي از كركرهاي ساده (تازه كار)كه از اسمبلي خبر ندارند با استفاده از اين برنامه هاي برنامه هاي شما را حداقل مي توانيد تغيير ليبل دهند . پس براي جلوگيري از شناسايي توابع بايد چه راهي را اتخاذ كرد ؟
جواب : شما نبايد در زمان طراحي فرم از اشياء استفاده كنيد بايد آنها را در هنگام اجرا بسازيد (Run Time) . شما نبايد در رويداد OnCreate فرم توابع بررسي قفل يا اطلاعات نويسنده را بنويسيد . يك تابع تعريف مي نمائيد .
public { Public declarations } procedure FirstTestLock(Sender: TObject); end;
program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Form1.FirstTestLock(nil); Application.Run; end.
اين فرامين را در تابع تعريف شده مي نويسيم :
procedure TForm1.FirstTestLock(Sender: TObject); var Timer1: TTimer; begin Try Timer1 := TTimer.Create(self); Timer1.Interval:=60000; Timer1.Enabled:=True; Timer1.OnTimer:=OnTimerActive1; . . .
private { Private declarations } procedure OnTimerActive1(Sender: TObject); . . .
حالا در مكاني دلخواه كدهاي اين تايمر را مي نويسيم :
procedure TForm1.OnTimerActive1(Sender: TObject); begin
حجم فايل را درون يك فايل به صورت كد شده يا در رجيستري ذخيره نمائيد
Crc را نيز همين طور حالا بررسي كنيد كه آيا حجم يا سي آر سي فايل تغيير نموده يا خير كه اگر تغيير نموده بود برنامه را Halt و مي توانيد دهن ويندوز را هم
%20-%20برنامه%20نویس_files/102.gif)
%20-%20برنامه%20نویس_files/106.gif)
حالا اگر كركر از Loader استفاده كرده باشد كه دهن ما ... است . براي رفع تا حدودي اين مشكل مي توانيد از يكي از شماره سريالهاي منحصر بفرد سيستم مانند هارد ديسك استفاده كنيد كه به صورت كد شده در فايل بانك اطلاعاتي يا رجيستري ذخيره و با شماره فعلي سيستم مقايسه كنيد . چون اين فرامين در داخل يك تايمر هستند مي توانيد حتي اگر برنامه با لودر اجرا شده باشد (معمولا حجم و CRC تغيير نمي كند) باعث بسته شدن برنامه و ساير كارهاي
%20-%20برنامه%20نویس_files/129.gif)
نكته بسيار مهم رشته ها هستند شما تحت هيچ شرايطي رشته ها را لخت (
%20-%20برنامه%20نویس_files/115.gif)
يعني آنها را مستقيم در برنامه ننويسيد :
پيشنهاد : شما مي توانيد در بيرون برنامه رشته را كد كنيد (مثلا :استفاده از خاصيت AESEncrypt مربوط به IceLicense) حالا در برنامه آنها را دي كد كنيد (مثلا : با استفاده از AESDecrypt مربوط به IceLicense ) ولي يك مشكل وجود دارد آن هم استفاده از كليد براي دي كد كردن است كه شما مجبور هستيد آن را در برنامه به صورت يك رشته وارد كنيد .
راه حل : شما مي توانيد : كليد را به اين صورت بنويسيد
PChar(Char(72)+Char(104)+Char(125))
%20-%20برنامه%20نویس_files/127.gif)
حالا بهتره توي تايمر يه مقداري حال ديباگر ها را هم بگيريم البته تا حدي :
با استفاده از :
var Task:HWND; Task:=FindWindow(Pchar(IceLicense1.AESDecrypt('wdwUZkYwdwZawd',PChar(Char(51)+Char(95)+Char(50)),nil)); PostMessage(Task,WM_CLOSE,0,0);
PChar(Char(51)+Char(95)+Char(50))
كليد تبديل كد به OlyDbg است .
البته تايمري كه اين كار و كارهاي ديگر را مي كند با همان روش بالا ايجاد و نيم ثانيه به نيم ثانيه اجرا شود .
اين جوري هم يكم كركرهاي تازه كار را آره ...
كليه روشهاي فوق قابل دور زدن و روئيت هستند فقط براي بالا بردن امنيت استفاده مي شوند .
البته يه كاري ديگه هم مي شه كرد كه من انجام دادم هنوز كه هنوزه كسي نتونسته Crack Me من را بيشتر از 50 درصد كرك كنند . البته اون 50 درصد بعدي خيلي مهم تره چون مجوز اجراي برنامه رو در گام اخر ميده اگر نتونستند كرك كنند اون را هم مي گم .
%20-%20برنامه%20نویس_files/102.gif)
%20-%20برنامه%20نویس_files/123.gif)
شايد براي شما اين سوال پيش بيايد كه چگونه بايد CRC و حجم يك فايل را بدست آورد :
روش بدست آوردن CRC16 , CRC32 , Size با استفاده از كامپوننت زير
با استفاده از تابع زير مي توانيد موارد مورد نياز خود را بدست آوريد
Error32 : WORD; FileBytes: TInteger8; CRC32File : LongWord; CalcFileCRC32 (Application.ExeName, CRC32File, FileBytes, Error32);
function GetFSize(Handle: THandle): Int64; var i64: record LoDWord: LongWord; HiDWord: LongWord; end; begin i64.LoDWord := GetFileSize(Handle, @i64.HiDWord); if (i64.LoDWord = MAXDWORD) and (GetLastError <> 0) then Result := 0 else Result := PInt64(@i64)^; end; Stream:TFileStream; FSize2:int64; Stream := nil; try Stream := TFileStream.Create(Application.ExeName, fmOpenRead or fmShareDenyNone); Fsize2:=GetFSize(Stream.Handle); finally Stream.Free; end;
%20-%20برنامه%20نویس_files/118.gif)
با استفاده از تابع
Deleteexe;
بخش سوم امنيت نرم افزارهاي (پيشرفته):
اين روش بسيار كارآمد و تقريبا مي توان گفت تا وقتي كه كد اصلي (Decode) لو نرفته برنامه شما تا 99% ايمن و به راحتي قابل انتشار در بازار حتي بازار جهاني است .
گوش فرا دهيد :
در برنامه هاي تجاري مي توان گفت كه 100% وجود بانك اطلاعاتي الزامي است (Sql Sever,Oracle , MySql ,DBISAM) و نكته مهمي كه وجود دارد برقراي ارتباط با بانك اطلاعتي است (Connect To Database) پس يكي از گزينه هاي مهم برنامه اتصال به بانك اطلاعاتي است .
در صورتي كه شما بخشي از كدهاي مهم برنامه را كه براي اجراي برنامه وجودشان حياتي است را محافظت كنيد مي توانيد گام مؤثري را در مقابله با كركرها برداريد .
كركرها در حالت طبيعي و نرمال پس از UnPack برنامه مي توانيد شرطها و كيسها و حلقه ها را شناسايي و عملكرد آنها را تغيير يا غيرفعال كنند . يك تايمر يا يك تابع را با يك Nop يا عكس كردن عملش آن را غير فعال و اصطلاحاٌ آن را كرك كنند . اما اگر حلقه For يا Case يا Connect به بانك اطلاعاتي وجود داشته باشد .
بله مي خوام برم بعد از اين مقدمه كوتاه روي اصل ماجرا .
خوب اگر ما بتوانيم كدهاي برنامه را بعد از مرحله Design و توليد فايل اجرايي تغيير دهيم ديگر كسي نمي تواند كدهاي ما را تغيير و آنها را Crack كند . البته غيير ممكن نيست ولي بسيار مشكل و به شانس بسيار زيادي بستگي دارد .
خوب پس براي اجراي كدهاي تغيير پيدا كرده چه كاري مي توان انجام داد . بايد با استفاده از الگوريتم عكس عمل كد در زمان اجرا (Run Time) كدهاي Encrypt شده را در حافظه Decrypt نمود .
بسياري از پروتكتورهاي معرف اين امكان مهم را ارائه و پشتيباني مي كنند كه ما به راحتي از آن به دليل (شايد بلد نبود نحوه استفاده يا عدم مطالعه Help) ناشناخته استفاده نمي كنيم .
من براي مثال نحوه انجام اين كار را با Protector & Packer SVKP 1.44 توضيح مي دهم .
در پكيجهاي ابزارهاي Packer و Protector معمولا دو فايل با نامهاي مشخص وجود دارد كه براي اين مجموعه به نامهاي delphi_svkp_block_begin.inc و delphi_svkp_block_end.inc فايل اول حاوي اطلاعات و كدهاي اسمبلي زير است :
asm DB $EB,$7F,$EB,$00,$99,$AA,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $EB,$7F,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end;
و فايل دوم حاوي كدهاي زير است :
asm DB $EB,$7F,$EB,$00,$BB,$CC,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $EB,$7F,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end; asm DB $90,$90,$90,$90,$90,$90,$90,$90,$90,$90 end;
اين كدها قرار است قبل و بعد از فرامين كليدي و مهم ما كه در بالا به يكي از مهمترين آنها اشاره شد قرار مي گيرند .
با فرض اينكه اين دو فايل در پوشه include قرار داشته باشند به اين نحو استفاده مي شوند :
unit main; interface procedure Start; implementation uses Registry, Classes, Windows, SysUtils, special; var RegMessage : String; // This procedure will encrypted and it will decrypted only if user have correct registration key procedure ActionForRegisteredUsers; begin {$I include\delphi_svkp_block_begin.inc} RegMessage := 'Registered version!'; MessageBox(0,PChar (RegMessage),'',0); {$I include\delphi_svkp_block_end.inc} end; procedure Start; begin try // this code will decrypted before using and after use removed // tip: This type of blocks is very good use in initialize part of program // (load DLL(s), initialize data, fill some special variables and etc.) {$I include\delphi_svkp_block_type2_begin.inc} RegMessage := 'Encryption blocks example'; {$I include\delphi_svkp_block_type2_end.inc} // This code will decrypted before using and encrypted again after use {$I include\delphi_svkp_block_type1_begin.inc} MessageBox(0,PChar (RegMessage),'',0); ActionForRegisteredUsers; //this procedure is only for registered users {$I include\delphi_svkp_block_type1_end.inc} except end; end; end.
در كد بالا از يك Uses به نام special استفاده شده است كه ممكن است براي شما سوال پيش بيايد كه اين فايل چيست . معمولا Protectorها علاوه بر تغييراتي كه برروي فايل اجرايي انجام مي دهند يك سري كدهاي سودمند براي مقابله با Debugger ها و ساير سيستمهاي نفوذي مي نويسند كه با توجه به نوع زبان برنامه نويسي قابل استفاده است . كه در اينجا با استفاده از اين فايل كه در اصل براي استفاده از يك Dll كه همراه package پروتكتور عرضه شده استفاده شده است . عدم استفاده از اين فايل اشكالي در سيستمي حفاظتي از كدها نمي كند . فقط جهت افزايش امنيت است .
حالا براي اينكه بتوان كدها را حفاظت نمود پس از توليد فايل اجرايي شما بايد به وسيله پروتكتور فايل را محافظت كنيد . در هنگامي كه پروتكتور در حال تغيير موقعيت احضار توابع و رويه ها است . وقتي به كدهاي ما رسيد :
procedure ActionForRegisteredUsers; begin {$I include\delphi_svkp_block_begin.inc} RegMessage := 'Registered version!'; MessageBox(0,PChar (RegMessage),'',0); {$I include\delphi_svkp_block_end.inc} end;
اطلاعات موجود در فايل هاي مشخص شده را با اطلاعات كد هاي ما و يك سري كد كه با ااگوريتم RSA توليد مي شود تركيب مي نمايد . و باعث تغيير فيزيكي محتواي فايل اجرايي مي شود . يا اصطلاحاْ به نوعي محتواي فايل را خراب مي كند .
حالا براي اجراي برنامه به صورت صحيح و رجيستر شده حتما نياز به كد فعال سازي جهت Decrypt شدن اطلاعات خراب شده در فايل اجرايي برنامه است كه اين فايل توسط پروتكتور توليد مي شود .
كه در اين مثال SVKP فايل را به نام regkey.key توليد مي كند كه حاوي كد فعال سازي و بازسازي اطلاعات تخريب شده در زمان اجراست است.
به يك نمونه از محتواي اين فايل توجه كنيد :
-----BEGIN RSA PUBLIC KEY----- AAG/aAN0uqBYL7SxnMqOHJouq3Yhz2nV3KW6pfhDQ29qbIlFRJWrYni8fVTud9Gli5rJft jAYy/pf0Ee2aKcD2t+j6+y2pweECBTfOjVwBw9bmFWTOchMBG8b7xzh3wZ+6S/2/QeYILI 0Gmop/wuCrZzm3MkekGSRspC2v2R8dtgDl3ZGsHwMYpmOqWnNSrpxzeanGAS4D3iUyo50H tVKGOHqba96HD4yG5G+suc3tZObR1rJveXpCU9WLCFltVxkXhpDYQFr7XhEJTi3H9TEfJ1 coH8MQla3j1vwPLtyVD4hpeyyOHJ5qbTyLXmyjavIouaOBOSZBYlLi332qLiPI4DAQADA -----END RSA PUBLIC KEY-----
در صورتي كه اين فايل وجود داشته باشد برنامه اجرا و در غير اين صورت امكان اجراي برنامه وجود نخواهد داشت .
براي پيدا كردن اين كد بايد به روش سعي و خطا عمل كرد مانند روش پيدا كردن رمز فايلهاي RAR يا Zip يا از روش نقل قول (Fh_prg) : (Bruteforce) مي توان استفاده نمود كه كار بسيار وقت گير و مشكلي است .
البته من براي اين كار از پروتكتورها استفاده نمي كنم . من در برنامه ها از كامپوننت SecureCode استفاده مي كنم . به دليل كپي رايت از ارائه اين كامپوننت در اينجا شرمنده هستم
البته اگر فايل رجيستر را ارائه دهيد و سيستم امنيني برنامه شما پايين باشد به راحتي مي توان با مثلا OLLY آن را dump كرد و از آنجايي كه فايل به صورت صحيح در Ram لود شده است اين محدوديت از بين مي رود .
از دوستان تقاضا مي شود كه الگوريتمهاي antidebug خود دريغ نكنند . با همكاري و هم فكري هم ميشه سد بسيار بسيار بزرگي در مقابل كركرها ايجاد نمود .
از دوستان كركر كلاه سفبد تقاضا مي شود از الگوريتمهاي ضد ديباگ و مشكل ساز براي كركها بگذارند .
خب مي خوام ابزاري رو معرفي كنم كه رايگان و بسيار قدرتمند است براي كد كردن رشته ها در دلفي يا اصطلاحا مخفي كردن آنها اين ابزار بسيار مفيد براي كركرها تازه كار و كار كركرهاي حرفه اي را بسيار مشكل در پيدا كردن و شناسايي رشته هاي برنامه ها شما مي كند .
دقت كنيد : نام اين مجموعه Delphi Strings Killer (DSKiller) مي باشد . شما به صورت عادي برنامه را نوشته و كليه رشته ها را در برنامه تعريف مي كنيد سپس برنامه DSKiller را اجرا و فايل pas خود را مثلا اگر نامش unit1.pas باشد را open مي كنيد در فايل رشته ها را تعريف كرده ايد . حالا فايل شما باز شده و كليه رشته هاي شما ظاهر مي شود آنهايي را كه مي خواهيد كد كنيد را انتخاب و كليد كد شدن را بزنيد .
من يك مثال قبل از كد و يكي بعد از كد براي شما مي زنم
قبل از كد شدن :
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin if edit1.Text='HiProgrammer' then showmessage('Hoooooooooooooo') end; end.
بعد از كد شدن : (كركر)
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation const dbs: array [0..26] of Byte= ( $78,$EB,$94,$6C,$AC,$D9,$B6,$27,$F4,$A2,$CE,$21,$78,$ED,$0E,$29, $0D,$39,$6C,$6C,$80,$BC,$F4,$82,$D8,$80,$D4 ); function dcf(aBegPos, aLength: Integer): string; var tmp: string; i, endPos: Integer; MyKey: word; begin MyKey:= 12345; SetLength(Result, aLength); tmp:= ''; endPos:= aBegPos+ aLength -1; for i:= aBegPos to endPos do tmp:= tmp+ Chr(dbs[i]); for i:= 1 to Length(tmp) do begin Result[i]:= Char(Byte(tmp[i]) xor (MyKey shr 8)); MyKey:= (Byte(tmp[i])+ MyKey)* 52845+ 22719; end; end; {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin if edit1.Text=dcf(0 ,12) then showmessage(dcf(12 ,15)) end; end.
خوب اميد وارم كه استفاده كرده باشيد .
ما هرچه داريم رو مي كنيم . منتظر دارايهاي شما هم هستيم .