程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 於破解過招,保護你的共享軟件

於破解過招,保護你的共享軟件

編輯:Delphi
——此文曾作為連載刊登於《電腦報》2003年41、42期,如要轉載,請注明出自《電腦報》

  ——本人僅是一名初學者,如有疏漏之處,還請列位前輩們指教,謝謝![email protected]

   共享軟件是軟件業目前世界上比較熱門的話題,國內更是如此。成千上萬的中國程序員以極大的熱情投入到這個領域來,都憧憬著用辛勤的勞動來獲得豐厚的回報;但,實際並非如此,絕大多數的人都弑羽而歸。值得注意的是:除了選題和技術上的原因外,最大的原因就是共享軟件被破解(Crack)了。
    
     破解見得多了,不免有些麻木。大部分作者都是新軟件發布一個星期左右甚至一天之內就會在網上發現注冊機或者被修改過的軟件(行話稱之為“爆破”)。破解者制作了英文、中文、俄文、德文等語種的注冊機大肆發散不說,還常常給作者寄一份,外加一封挖苦辱罵的信。唉!我們得罪了誰?沒日沒夜地熬夜編碼,難道得到的就是這連綿的挖苦和不盡的羞辱嗎?
    
     不!決不!我們有理由也有能力保護自己的勞動成果!但問題是:如何保護?關注國內,網上關於破解資料和教程俯拾皆是,而關於軟件保護方面的資料則是鳳毛麟角(大多都關系到什麼技術壟斷),這種畸形現狀就導致了相當一部分朋友的加密非常脆弱甚至可以稱得上是“弱智” !要知道,你要面對的是已經形成團伙的眾多破解高手呀,國內的什麼CCG、BCG,國外的eGis、King、Core、TNT、DAMN和TMG,皆為水平一流的破解組織。全球盜版軟件不少於80%都是由他們的破解的,技術實力連大軟件公司都不可小視。
    
     看到這裡,你是否已經已經灰心了?別怕,雖然我們理論上無法完全避免被破解,但如果能夠有效地拖延被破解的時間,並充分打擊破解者的自信心,是可以讓破解者無法忍受這種折磨從而最終放棄的。
    
     破解,通常的做法有兩種——暴力破解(爆破)和寫注冊機。下面我就來依次講解每種破解方法的原理和應對方法,這些都是鄙人積累的一些共享軟件保護經驗,某些關鍵地方還有例程講解(Delphi代碼,使用C++和VB的朋友可以自己稍微修改一下),希望能對新手們有些幫助,能夠更有效地保護自己的勞動成果。
    
     §暴力破解(爆破)
    
     這是最常見,也是最簡單的破解的方法。該法最適合於對付沒有CRC效驗的軟件,破解新手樂於采用。
    
     大凡共享軟件,驗證是否注冊大多數要采用if條件語句來進行判斷,即使你采用了什麼RSA或ECC等強力加密算法,也免不了使用if條件語句。呵呵,這裡就是共享軟件最為危險的地方哦,當然也是爆破手孜孜不倦所尋求的目標呀!
    
     例如,你的注冊函數類似如下:
  
   { 利用RSA進行注冊碼的數字簽名驗證 }

    if RSAVerify(MD5(Key), MD5(Code), e, n) then
      ShowMessage('注冊成功!')
    else
      ShowMessage('注冊失敗!');

   { 這裡Key是用戶輸入的注冊碼,是由你發送給注冊用戶的 }
   { Code是根據用戶輸入的用戶名自動計算出來的注冊碼    }
   { e是RSA算法的公匙,而n是RSA算法的模數。            }
   這個注冊函數即使使用了強勁的RSA算法進行注冊碼驗證,可是依然很容易被破解,我們只要把這裡修改為:
  
    { 將邏輯判斷改為否 } 

    if not RSAVerify(MD5(Key), MD5(Code), e, n) then
      ShowMessage('注冊成功!')
    else
      ShowMessage('注冊失敗!');
  就可以了。這時戲劇性的結果會產生:隨便輸入任何注冊碼都可以注冊通過,相反輸入正確的注冊碼卻無法通過注冊。:) 其具體操作是先反匯編或者跟蹤你的程序,找到判斷注冊碼的cmp、test等匯編指令後的關鍵跳轉指令處,通常是je、jz之類的匯編指令,把它們修改為jne或jnz即可,這樣常常只需要修改一個字節就可以完美破解之。:)
    
     令人遺憾的是,目前大部分共享軟件都是這樣進行判斷的,這也是為什麼網上被破解的軟件鋪天蓋地的主要原因。因為這樣破解實在是太簡單了...
    
     難道沒有什麼可以防止的方法嗎?當然有啊!只要把軟件的關鍵代碼嵌入到注冊碼或者注冊文件中就可以充分防止破解。但現在問題是,怎麼嵌入呢?
    
     最簡單的方法就是把關鍵代碼(你的軟件功能限制部分最關鍵而且最簡單的一個函數)做成一個小Dll(動態鏈接庫),用強力對稱算法加密(密匙可以是主程序某一固定不變的部分或殼的特征Hash值)後生成一個注冊文件(License文件,呵呵,格式只有你知道哦!),或者Base64編碼後生成一個注冊表文件,用戶可以雙擊導入注冊表內。
    
     效驗流程如下:已注冊用戶驗證注冊碼時,先驗證有沒有文件,沒有文件者自然受限制的功能無法使用。如果有注冊文件,解密之即生成一個小臨時文件。如果主程序被脫殼或者被修改(爆破),自然Hash值密碼不符,解密出來的肯定都是垃圾碼,沒有一點用處。只有沒有被修改的主程序才能正確的解碼,而且當然只有解密正確的文件才是一個真正的Dll文件,才能被GetProcAddress函數找到欲調用的關鍵函數地址。這樣只有已注冊用戶才可以享受到你軟件的全部功能了。
    
     如此一來,Cracker破解你的軟件就變得很困難了:
    
     首先,他如果沒有注冊文件,即使他把主程序脫殼了,由於受限制的部分和注冊文件是關聯的,他也根本無法修補完整。
    
     第二,即使他得到了你的注冊文件,由於是加密文件,他也無法直接利用之,這樣就逼迫他去拆解你的算法,這可是他們最不願意的碰到的事情哦!如果到了這一步,我想99%的Cracker都是會放棄的,呵呵,只有真正對加密算法有研究的Cracker高手才會繼續破解下去。
    
     第三,你是可以用些小技巧來使他的生活更加痛苦一些的,呵呵。這裡我推薦大家使用DSA公開密匙加密算法,它和RSA一樣,可以進行數字簽名(RSA還可以加密,DSA則只能進行數字簽名)。我這裡選用它的原因就是它有一項非常實用的特性:隨機數填充機制。即DSA每次簽名都要使用一個隨機數K,正因為有這個K的存在,即使是相同的用戶名和機器識別碼,由DSA加密過的每份注冊文件都不會相同。這對Cracker拆解你的注冊文件來說是一個極大的障礙。
    
     第四,即使他得到了解密後的Dll文件,他也需要大幅度地修改主程序或者把你的Dll部分的關鍵代碼拆出來填到主可執行文件中。呵呵,這就看他對PE文件格式理解得如何了。即使這樣,如果你的程序中有大量的Hash效驗和死機代碼,呵呵,你就耐心等著我們可愛的Cracker同志吐血吧……:)
    
     所以記住啊:用完這個Dll臨時文件後立即從內存中卸載此Dll並刪掉,而且注意在解密之前探測一下,系統中有沒有FileMon這個威脅極大的探測器呀!
  
    { 探測FileMon }

    function DetectFileMon: Boolean;
    begin
      if CreateFile(PChar('.FILEVXD'), 
                    GENERIC_READ or GENERIC_WRITE,
                    FILE_SHARE_READ or FILE_SHARE_WRITE, 
                    nil,
                    OPEN_EXISTING, 
                    FILE_ATTRIBUTE_NORMAL, 
                    0) <> INVALID_HANDLE_VALUE then
        Result := True //如果有,就Down機!
      else 
        Result := False;
    end;  
   當然,你可以保護得更好一些:可以不采用臨時Dll,而把解密後的關鍵代碼用WriteProcessMemory這個API函數寫入到主可執行文件自己進程被提交(Committed)的內存頁面的指定位置去。這樣由於磁盤上沒有解密後的臨時文件,破解更加困難。事實上,目前世界上最強勁的專業保護軟件Armadillo就是用的這種方法。而且這種方法可以充分防止被調試器Dump。但實現起來比較困難,尤其是在WinNT 5以後的操作系統中。
    
     由於這種方法將注冊文件和受限制代碼唯一關聯,爆破手拿到你的軟件也只有干瞪眼。建議大家都給共享軟件加上功能限制,這樣比時間和次數限制更加安全。
    
     §寫注冊機
    
     顧名思義,這種方法就是模仿你的注冊碼生成算法或者逆向注冊碼驗證算法而寫出來的和你一模一樣的注冊機。這玩意威脅極大,被爆破了還可以升級。如果被寫出注冊機,呵呵,你的軟件只好免費了。或者你必須更換算法,但以前注冊過的合法用戶都得被迫更換注冊碼了,累死你!呵呵...
    
     上面的方法雖然可以避免爆破,但注冊機的威脅還是存在的。Cracker要寫注冊機必須詳細研究你軟件的驗證模塊,這必須先將你的軟件脫殼,再反匯編或者用調試器跟蹤。市面上許多加殼和保護軟件都吹噓不可能被脫殼,令人可惜的是到目前為止沒有一個軟件兌現了它們的諾言。由於CPU最終執行的都是有效指令,所以等你的程序自解壓完成後再從內存中Dump出來就可以實現脫殼。因此不要在殼上面花很多功夫,因為沒有這個必要。
    
     反匯編是和調試器跟蹤也都是不可能防止的,因為所有的Win32程序都是必須通過API來調用Windows系統中的關鍵Dll的(如Kernel32.dll、GDI32.dll等),然而API是可以Hook的。我們只能從自己的代碼著手來保護我們的勞動果實了。
    
     為了自己調試和以後維護的方便,我們一般采用有意義的名字給我們的函數命名,可這給了Cracker可乘之機。例如這樣的函數是什麼意思大家應該是一目了然吧?IsRegistered(), IsLicensed(), LicenseVerify(), CheckReg()...這樣Cracker就可以輕松地從數千個函數中找到他的目標---你的注冊碼效驗函數!而且破解Delphi編寫的軟件還有一件TMG小組的破解利器---DeDe,它可以輕松看到你軟件裡的Form、Unit和函數名,還可以反匯編一部分代碼,更是可以和Win32DASM合作反匯編更多的代碼,對Delphi軟件威脅極大。
    
     為了不給Cracker創造溫馨舒適的破解環境,我們要混亂(Obfuscate)我們的代碼,將軟件中所有的函數名全部替換成隨機生成的函數名。例如Func_3dfsa_fs32zlfv()這個函數是什麼意思?恐怕只有天知道了。網上有現成的代碼混亂器,你按你使用的編程語言的種類可以找到一些。但注意,只有當你要發布軟件時才使用之,而且一定注意備份源代碼。否則當你看不懂你自己的代碼時可別怪我呀!:)
    
     另外一定要使用公開密匙算法保護你的軟件,RSA、DSA和El Gamal之類的算法都可以從網上找到。但注意:將你算法單元中的所有涉及到算法名稱的字符串全部改名。避免被Cracker發現你用的算法而模仿寫出注冊機來!你還可以張冠李戴,明明用的DSA,將名字全部替換成RSA,呵呵,讓他模仿去吧!:)
    
     其它算法如對稱算法和Hash算法都也要注意改名,否則這樣:
  
    EncryptedCode = Blowfish(MD5(UserName), MD5(Key)); 

    //你的加密算法,使用了Blowfish(對稱算法)和MD5(Hash算法) 
   雖然我不了解Blowfish和MD5算法的原理,也不會逆向它們,但我了解你的效驗算法的流程和算法名,我馬上就可以從網上找到類似的Blowfish和MD5算法包,從而模擬你的軟件仿造出注冊機,啊?!真是……$&*&($#%@!
    
     如果你用的什麼其它不常見的算法(如Skipjack (NASA美國航天局標准算法), LOKI, 3-WAY, Safer之類不出名但強度很高的算法),並且全部改名,就讓他們去研究軟件中成堆的如下代碼是什麼加密算法吧!:)
  
    0167:005B9F70  MOV     EAX,[EBP-10]
    0167:005B9F73  CALL    00404000
    0167:005B9F78  PUSH    EAX
    0167:005B9F79  MOV     EAX,[EBP-10]
    0167:005B9F7C  CALL    004041C4
    0167:005B9F81  LEA     ECX,[EBP-14]
    0167:005B9F84  POP     EDX 
    0167:005B9F85  CALL    004B860C
   當然,最好把Hash算法也全部改名,給會給他們制造更多的困難。但注意,MD5和SHA之類的Hash的初始值會被Cracker從內存中找到,這樣他就知道了你用的Hash了。所有建議同時使用MD5的變形算法Ripe-MD(RMD)128或160和其它的Hash,如Tiger, Haval等算法。
    
     另外,請注意要經常效驗你的程序是否被修改(Hash效驗),如果被修改則退出。但請注意,有些病毒會修改進程的句柄表和它指向的內核對象,這樣病毒就可以直接修改運行中的PE文件而感染之了,另外還有網絡傳輸錯誤的問題也會導致軟件CRC出錯。因此請不要認為可執行文件的CRC不符而此時程序已被脫殼了。
    
     其實,程序被脫殼最明顯的標志是其大小明顯大於脫殼前。1M的PE文件被UPX、ASPack之類的軟件壓縮後通常只有400左右。如果你的軟件在運行中發現自己的大小大於800K,我想你應該知道如何做了吧?呵呵... :)
    
     還有一點,調試器對我們的威脅很大,我們不會肯定讓Cracker們舒舒服服地使用SoftICE、TRW和OllyDbg來調試我們的程序。除了常用的MeItICE方法外,這裡我給一個我寫的方法:
  
  { 檢查自己的進程的父進程是否為Explorer.exe,否則是被調試器加載了 }
  { 不過注意,控制台程序的父進程在WinNT下是Cmd.exe哦!}
  { 注意加載TlHelp32.pas單元 }
  
  procedure CheckParentProc;
  var //檢查自己的進程的父進程
    Pn: TProcesseNtry32;
    sHandle: THandle;
    H, ExplProc, ParentProc: Hwnd;
    Found: Boolean;
    Buffer: array[0..1023] of Char;
    Path: string;
    begin
      H := 0;
      ExplProc := 0;
      ParentProc := 0;
      //得到Windows的目錄
      SetString(Path,
                Buffer,
                GetWindowsDirectory(Buffer, Sizeof(Buffer) - 1));
      Path := UpperCase(Path) + 'EXPLORER.EXE'; //得到Explorer的路徑
      //得到所有進程的列表快照
      sHandle := CreateToolHelp32SnapShot(TH32CS_SNAPALL, 0);
      Found := Process32First(sHandle, Pn); //查找進程
      while Found do //遍歷所有進程
      begin
        if Pn.szExeFile = ParamStr(0) then //自己的進程
        begin
          ParentProc := Pn.th32ParentProcessID; //得到父進程的進程ID
          //父進程的句柄
          H := OpenProcess(PROCESS_ALL_Access, True, Pn.th32ParentProcessID);
        end
        else if UpperCase(Pn.szExeFile) = Path then
          ExplProc := Pn.th32ProcessID;      //Explorer的PID
        Found := Process32Next(sHandle, Pn); //查找下一個
      end;
      //嗯,父進程不是Explorer,是調試器……
      if ParentProc <> ExplProc then
      begin
        TerminateProcess(H, 0); //殺之!除之而後快耶! :)
        //你還可以加上其它什麼死機代碼來消遣消遣這位可愛的Cracker :)
      end;
    end;
   你可以在Delphi或者VC中試試,呵呵,是不是把Delphi和VC殺掉了,因為你現在用的是Delphi和VC的內置調試器來運行你的程序的,當然它會六親不認了,呵呵!調試的時候你還是把它注釋掉吧,發布時別忘記激活喲!
    
     最後一個問題,這也是一個非常重要的問題:保護你的字符串!!!字符串在注冊模塊中非常重要!當一個富有經驗的Cracker破解你的軟件時,首先做的就是攝取你的字符串。比如他會輸入錯誤的注冊碼,得到你關於錯誤注冊碼的提示,通常是“無效的注冊碼,請重新輸入!”或者“Invalid key, please input again!”等等,然後用OllyDbg下斷點調試或者用WinDASM、IDA Pro等靜態分析工具在被他脫殼後的程序中查找那個字符串,找到後進行分析。因此,請一定加密你的字符串!!!一定!!! 使用時再臨時解密出來,而且要盡量少使用消息提示框 ,避免被Cracker找到漏洞。加密字符串不需要太復雜的算法,隨便找一個快速的對稱算法就可以了。
    
     最後提醒你一句,不要在加密上花太多的功夫!你應該把更多的時間和精力都用來完善你的軟件,這樣會更合算。借用一位前輩的話來忠告大家吧:花點時間考慮你自己的軟件,看看它是否值得保護?如果沒人用你的軟件,保護也就沒有意義了,不要過高估計你的軟件“對世界的重要性”!
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved