——此文曾作為連載刊登于《電腦報》2003年41、42期,如要轉載,請注明出自《電腦報》
——本人僅是一名初學者,如有疏漏之處,還請列位前輩們指教,謝謝!crossbow@citiz.net
共享軟件是軟件業目前世界上比較熱門的話題,國內更是如此。成千上萬的中國程序員以極大的熱情投入到這個領域來,都憧憬著用辛勤的勞動來獲得豐厚的回報;但,實際并非如此,絕大多數的人都弒羽而歸。值得注意的是:除了選題和技術上的原因外,最大的原因就是共享軟件被破解(Crack)了。 { 利用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即可,這樣常常只需要修改一個字節就可以完美破解之。:) { 探測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以后的操作系統中。 EncryptedCode = Blowfish(MD5(UserName), MD5(Key)); //你的加密算法,使用了Blowfish(對稱算法)和MD5(Hash算法)
雖然我不了解Blowfish和MD5算法的原理,也不會逆向它們,但我了解你的效驗算法的流程和算法名,我馬上就可以從網上找到類似的Blowfish和MD5算法包,從而模擬你的軟件仿造出注冊機,?。?!真是……$&*&($#%@! 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等算法。 { 檢查自己的進程的父進程是否為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的內置調試器來運行你的程序的,當然它會六親不認了,呵呵!調試的時候你還是把它注釋掉吧,發布時別忘記激活喲!新聞熱點
疑難解答