一、共享==免費(fèi)?
隨著Internet大行其道,“共享+注冊(cè)”模式日漸成為程序員發(fā)布自己軟件的主要手段,但是隨之而來的*手段也越來越高明。如何保護(hù)自己的勞動(dòng)成果不被暴力*或修改?用MD5摘要值驗(yàn)證是一個(gè)很常用的方法。
MD5作為一個(gè)公開算法,實(shí)現(xiàn)的方法很多,比如開源軟件、Windows自帶的API等,C++ Builder和Delphi中集成的Indy里面也有現(xiàn)成的MD5控件。
用API實(shí)現(xiàn)MD5雖然效率高,但是畢竟太繁瑣,具體方法可以參閱MSDN上crypt開頭的那幾個(gè)函數(shù)說明。用C++ Builder的MD5控件雖然方便,但是速度較慢,而且只能處理以‘’結(jié)尾的字符串,所以要用這個(gè)控件還需要我們對(duì)待驗(yàn)證數(shù)據(jù)做一些預(yù)處理。預(yù)處理的關(guān)鍵在于把較重要的內(nèi)容挑出來計(jì)算,這樣一方面可以去除‘’,另一方面可以極大減少待驗(yàn)證內(nèi)容的長(zhǎng)度,只計(jì)算關(guān)鍵代碼的摘要值。
二、源程序祥解
先要說明的是網(wǎng)上和Borland Help里面對(duì)Indy MD5控件介紹很少,這段程序基本上是我自己摸索的結(jié)果,在C++ Biulder 6.0和Indy 6.0下運(yùn)行正常。
先在Indy misc中拖一個(gè)MD5控件,假設(shè)其name為cipher,程序如下:
/*
MD5計(jì)算函數(shù) by DayDreamer 2004-04
功能:對(duì)文件名為fname的文件計(jì)算16個(gè)字節(jié)長(zhǎng)的MD5值,放在result里面
輸入:指向文件名的指針char *fname
輸出:MD5值放在result指向的緩沖區(qū)里面
返回值:如果正確計(jì)算出MD5值則返回true,否則返回false
*/
bool MD5( char* fname, char *result)
{
unsigned int i=0; //用于記錄讀出文件的大小
bool flag=false; //正確計(jì)算md的話則置標(biāo)記
static AnsiString ss; //用于臨時(shí)存放得到的MD5值
char *buffer=new char[myFILE_SIZE]; //用于讀取目標(biāo)文件,文件大小不能超過myFILE_SIZE
TFileStream *tt=new TFileStream(fname,fmOpenRead); //用于讀取目標(biāo)文件的全部?jī)?nèi)容,使用此方法簡(jiǎn)單一點(diǎn),也可以用標(biāo)準(zhǔn)的
CreateFile()讀取
i=tt->Read(buffer,myFILE_SIZE); //i中保存實(shí)際讀取的字節(jié)數(shù)
if ((i)&&(i!=1)) //如果讀出字節(jié)數(shù)不為0或1,則進(jìn)行處理
{ i=i-1; //最后一個(gè)字節(jié)必須留出,供結(jié)束符使用
_asm{ //這一段匯編主要是用來對(duì)文件預(yù)處理
mov ecx,i; //置計(jì)數(shù)器
mov ebx,buffer; //源地址指針
mov edx,buffer; //目的地址指針
l1: mov al, [ebx]; //取一個(gè)字節(jié),因?yàn)槿绻募^大會(huì)極大降低速齲員匭胍越舷噶6裙嗽創(chuàng)?/p>
cmp al,0x70; //只對(duì)大于0x70和小于0x91的字節(jié)進(jìn)行摘要
jb l2 ; //因?yàn)橹饕霓D(zhuǎn)移指令(jz,jnz)、空操作指令(nop)等都大于0x70小于0x91
cmp al,0x91; //也可以換成更寬松的條件,但至少應(yīng)該把0x00濾掉,因?yàn)樗灰暈榻Y(jié)束符
jnb l2 ;
mov [edx],al ; //符合條件的字節(jié)保存起來等待驗(yàn)證
inc edx;
l2: inc ebx;
dec ecx;
jnz l1;
mov byte ptr [edx+1],0; //在最后置結(jié)束符‘’
}
cipher->Reset(); //每次使用該控件之前應(yīng)該重置其內(nèi)部的一些參數(shù)
cipher->AutoCompleteInput=false; // 只有調(diào)用CompletedInput() 時(shí)才得到結(jié)果
cipher->CodeString(buffer); //輸入預(yù)處理過的數(shù)據(jù)
ss=cipher->CompletedInput(); //輸入完畢,得到摘要碼
if(ss.Length()=MD5_LENGHTH) //如果摘要碼正常(即為16位),則保存之
{ flag=true; //置正確標(biāo)志
memcpy(result,ss.c_str(),16); //把結(jié)果放入result
} // end if
}// end if
delete tt;
delete[] buffer;
return flag;}
調(diào)用的時(shí)候可以用Application->ExeName.c_str()當(dāng)作fname傳入,注意在C++ Biulder中直接run的話這會(huì)引起異常,這沒關(guān)系,發(fā)布出來的程序不會(huì)有問題。此處為了簡(jiǎn)明,在匯編代碼中沒有保存和恢復(fù)寄存器,使用時(shí)可以加上,或者發(fā)布時(shí)把optionsèAdvanced Complier中的Register variables設(shè)為none即可。
三、使用方法
利用MD5值來保護(hù)共享軟件的方法有很多,最簡(jiǎn)單的比如先把正常的執(zhí)行模塊的MD5摘要值靜態(tài)保存在外部文件中(比如包含在注冊(cè)文件中),每次運(yùn)行時(shí)計(jì)算自身的MD5并與之比較,從而判斷是否被修改。高深一點(diǎn)的比如用正確的MD5來還原正確的入口地址,或者參與注冊(cè)碼/機(jī)器碼計(jì)算。
是加上自身MD5驗(yàn)證之后再用ASProtect或ASPack之類的壓縮軟件加上殼,這樣一旦cracker用脫殼軟件脫殼,軟件將不會(huì)正常執(zhí)行,對(duì)于很多crack newbie來說十有八九會(huì)懷疑脫殼失敗,進(jìn)而放棄。
其實(shí)軟件保護(hù)與*永遠(yuǎn)是魔道相長(zhǎng)的一對(duì)矛盾,無論怎么嚴(yán)密的防范,肯定有人能破。我們要做的只是把大量的菜鳥cracker擋在門外,如果不幸被高手盯上,那么恭喜你:這說明你的軟件真的很有價(jià)值。
隨著Internet大行其道,“共享+注冊(cè)”模式日漸成為程序員發(fā)布自己軟件的主要手段,但是隨之而來的*手段也越來越高明。如何保護(hù)自己的勞動(dòng)成果不被暴力*或修改?用MD5摘要值驗(yàn)證是一個(gè)很常用的方法。
MD5作為一個(gè)公開算法,實(shí)現(xiàn)的方法很多,比如開源軟件、Windows自帶的API等,C++ Builder和Delphi中集成的Indy里面也有現(xiàn)成的MD5控件。
用API實(shí)現(xiàn)MD5雖然效率高,但是畢竟太繁瑣,具體方法可以參閱MSDN上crypt開頭的那幾個(gè)函數(shù)說明。用C++ Builder的MD5控件雖然方便,但是速度較慢,而且只能處理以‘’結(jié)尾的字符串,所以要用這個(gè)控件還需要我們對(duì)待驗(yàn)證數(shù)據(jù)做一些預(yù)處理。預(yù)處理的關(guān)鍵在于把較重要的內(nèi)容挑出來計(jì)算,這樣一方面可以去除‘’,另一方面可以極大減少待驗(yàn)證內(nèi)容的長(zhǎng)度,只計(jì)算關(guān)鍵代碼的摘要值。
二、源程序祥解
先要說明的是網(wǎng)上和Borland Help里面對(duì)Indy MD5控件介紹很少,這段程序基本上是我自己摸索的結(jié)果,在C++ Biulder 6.0和Indy 6.0下運(yùn)行正常。
先在Indy misc中拖一個(gè)MD5控件,假設(shè)其name為cipher,程序如下:
/*
MD5計(jì)算函數(shù) by DayDreamer 2004-04
功能:對(duì)文件名為fname的文件計(jì)算16個(gè)字節(jié)長(zhǎng)的MD5值,放在result里面
輸入:指向文件名的指針char *fname
輸出:MD5值放在result指向的緩沖區(qū)里面
返回值:如果正確計(jì)算出MD5值則返回true,否則返回false
*/
bool MD5( char* fname, char *result)
{
unsigned int i=0; //用于記錄讀出文件的大小
bool flag=false; //正確計(jì)算md的話則置標(biāo)記
static AnsiString ss; //用于臨時(shí)存放得到的MD5值
char *buffer=new char[myFILE_SIZE]; //用于讀取目標(biāo)文件,文件大小不能超過myFILE_SIZE
TFileStream *tt=new TFileStream(fname,fmOpenRead); //用于讀取目標(biāo)文件的全部?jī)?nèi)容,使用此方法簡(jiǎn)單一點(diǎn),也可以用標(biāo)準(zhǔn)的
CreateFile()讀取
i=tt->Read(buffer,myFILE_SIZE); //i中保存實(shí)際讀取的字節(jié)數(shù)
if ((i)&&(i!=1)) //如果讀出字節(jié)數(shù)不為0或1,則進(jìn)行處理
{ i=i-1; //最后一個(gè)字節(jié)必須留出,供結(jié)束符使用
_asm{ //這一段匯編主要是用來對(duì)文件預(yù)處理
mov ecx,i; //置計(jì)數(shù)器
mov ebx,buffer; //源地址指針
mov edx,buffer; //目的地址指針
l1: mov al, [ebx]; //取一個(gè)字節(jié),因?yàn)槿绻募^大會(huì)極大降低速齲員匭胍越舷噶6裙嗽創(chuàng)?/p>
cmp al,0x70; //只對(duì)大于0x70和小于0x91的字節(jié)進(jìn)行摘要
jb l2 ; //因?yàn)橹饕霓D(zhuǎn)移指令(jz,jnz)、空操作指令(nop)等都大于0x70小于0x91
cmp al,0x91; //也可以換成更寬松的條件,但至少應(yīng)該把0x00濾掉,因?yàn)樗灰暈榻Y(jié)束符
jnb l2 ;
mov [edx],al ; //符合條件的字節(jié)保存起來等待驗(yàn)證
inc edx;
l2: inc ebx;
dec ecx;
jnz l1;
mov byte ptr [edx+1],0; //在最后置結(jié)束符‘’
}
cipher->Reset(); //每次使用該控件之前應(yīng)該重置其內(nèi)部的一些參數(shù)
cipher->AutoCompleteInput=false; // 只有調(diào)用CompletedInput() 時(shí)才得到結(jié)果
cipher->CodeString(buffer); //輸入預(yù)處理過的數(shù)據(jù)
ss=cipher->CompletedInput(); //輸入完畢,得到摘要碼
if(ss.Length()=MD5_LENGHTH) //如果摘要碼正常(即為16位),則保存之
{ flag=true; //置正確標(biāo)志
memcpy(result,ss.c_str(),16); //把結(jié)果放入result
} // end if
}// end if
delete tt;
delete[] buffer;
return flag;}
調(diào)用的時(shí)候可以用Application->ExeName.c_str()當(dāng)作fname傳入,注意在C++ Biulder中直接run的話這會(huì)引起異常,這沒關(guān)系,發(fā)布出來的程序不會(huì)有問題。此處為了簡(jiǎn)明,在匯編代碼中沒有保存和恢復(fù)寄存器,使用時(shí)可以加上,或者發(fā)布時(shí)把optionsèAdvanced Complier中的Register variables設(shè)為none即可。
三、使用方法
利用MD5值來保護(hù)共享軟件的方法有很多,最簡(jiǎn)單的比如先把正常的執(zhí)行模塊的MD5摘要值靜態(tài)保存在外部文件中(比如包含在注冊(cè)文件中),每次運(yùn)行時(shí)計(jì)算自身的MD5并與之比較,從而判斷是否被修改。高深一點(diǎn)的比如用正確的MD5來還原正確的入口地址,或者參與注冊(cè)碼/機(jī)器碼計(jì)算。
是加上自身MD5驗(yàn)證之后再用ASProtect或ASPack之類的壓縮軟件加上殼,這樣一旦cracker用脫殼軟件脫殼,軟件將不會(huì)正常執(zhí)行,對(duì)于很多crack newbie來說十有八九會(huì)懷疑脫殼失敗,進(jìn)而放棄。
其實(shí)軟件保護(hù)與*永遠(yuǎn)是魔道相長(zhǎng)的一對(duì)矛盾,無論怎么嚴(yán)密的防范,肯定有人能破。我們要做的只是把大量的菜鳥cracker擋在門外,如果不幸被高手盯上,那么恭喜你:這說明你的軟件真的很有價(jià)值。

