共享軟件防的實(shí)用招法

字號(hào):

前言
    我寫Delphi程序是從MIS系統(tǒng)入門的,開始嘗試子系統(tǒng)劃分的時(shí)候采用的是MDI窗體的結(jié)構(gòu)。隨著系統(tǒng)功能的擴(kuò)充,不斷有新的子系統(tǒng)加入系統(tǒng)中,單個(gè)工程會(huì)變得非常大,每次做一點(diǎn)修改都要重新編譯,單個(gè)工程的形式也不利于團(tuán)隊(duì)協(xié)作。為了提高工作效率,我希望利用DLL動(dòng)態(tài)鏈接庫的形式實(shí)現(xiàn)插件結(jié)構(gòu)的編程。
    插件結(jié)構(gòu)的編程需要一個(gè)插件容器來控制各DLL的運(yùn)行情況,將劃分好的每個(gè)子系統(tǒng)安排到一個(gè)DLL庫文件中。對(duì)每個(gè)DLL程序需要為容器預(yù)留接口函數(shù),一般接口函數(shù)包括:啟動(dòng)調(diào)用DLL庫的函數(shù)、關(guān)閉DLL庫的函數(shù)。通過接口函數(shù),插件容器可以向DLL模塊傳遞參數(shù)實(shí)現(xiàn)動(dòng)態(tài)控制。具體實(shí)現(xiàn)細(xì)節(jié)我將在下文說明并給出響應(yīng)代碼。
    您可能需要先了解一下DELPHI中UNIT的結(jié)構(gòu),工程的結(jié)構(gòu)。本文沒有深入討論DLL編程的理論細(xì)節(jié),只是演示了一些實(shí)用的代碼,我當(dāng)時(shí)學(xué)習(xí)的是劉藝?yán)蠋煹摹禗ELPHI深入編程》一書。
    我也處于DELPHI的入門階段,只是覺得這次的DLL開發(fā)有一些值得討論的地方,所以寫這篇文章,希望各位能對(duì)我做的不好的地方慷慨建議。
    示例程序簡介
    為了便于閱讀我將使用一個(gè)MIS系統(tǒng)的部分程序代碼演示插件編程的一些方法。示例程序是典型的C/S結(jié)構(gòu)DBMS應(yīng)用程序,我們關(guān)注的部分將是框架程序(下文簡稱Hall)的控制語句和dll插件程序的響應(yīng)控制。
    1、程序結(jié)構(gòu)
    插件容器Hall使用一個(gè)獨(dú)立的工程創(chuàng)建,Hall的主窗口的作用相當(dāng)于MDI程序中的MDI容器窗體,Hall中將顯式調(diào)用Dll中的接口函數(shù)。
    每個(gè)插件程序獨(dú)立使用各自的工程,與普通工程不同的是,DLL工程創(chuàng)建的是Dll Wizard,相應(yīng)編譯生成的文件是以DLL為后綴。
    2、接口設(shè)計(jì)
    實(shí)例程序Narcissus中我們預(yù)留兩個(gè)接口函數(shù):
    ShowDLLForm
    該函數(shù)將應(yīng)用程序的句柄傳遞給DLL子窗口,DLL程序?qū)?dòng)態(tài)創(chuàng)建DLL窗體的實(shí)例。還可以將一些業(yè)務(wù)邏輯用參數(shù)的形式傳遞給DLL子窗口,比如窗體名稱、當(dāng)前登陸的用戶名等。初次調(diào)用一個(gè)DLL窗體實(shí)例時(shí)使用此函數(shù)創(chuàng)建。
    FreeDLLForm
    該函數(shù)將顯示釋放DLL窗口實(shí)例,在退出應(yīng)用程序時(shí)調(diào)用每個(gè)DLL窗體的FreeDLLForm方法來釋放創(chuàng)建的實(shí)例,不然會(huì)引起內(nèi)存只讀錯(cuò)誤。同樣,也可以將一些在釋放窗體時(shí)需要做的業(yè)務(wù)邏輯用參數(shù)的形式傳遞給DLL窗體。
    3、調(diào)試方式
    DLL窗體程序無法直接執(zhí)行,需要有一個(gè)插件容器來調(diào)用。應(yīng)此我們需要先實(shí)現(xiàn)一個(gè)基本的Hall程序,然后將Hall.exe保存在一個(gè)固定的目錄中。對(duì)每個(gè)DLL工程做如下設(shè)置:
    1) 打開DLL工程
    2) 選擇菜單 Run – Parameters
    3) 在彈出的窗口中瀏覽到我們的容器Hall.exe
    這樣在調(diào)試DLL程序時(shí)將會(huì)自動(dòng)調(diào)用Hall程序,利用Hall中預(yù)留的調(diào)用接口調(diào)試DLL程序。
    1、檢測(cè)主程序大小,防止*補(bǔ)丁之類:
    Function TForm1.GesSelfSf: integer;
    var
    F: file of byte;
    begin
    Filemode:=0;
    Assignfile(F,'.\FileName.exe');
    Reset(f);
    Result:=Filesize(F);
    Closefile(F);
    end;
    2、檢測(cè)創(chuàng)建日期和時(shí)間,讓*補(bǔ)丁實(shí)效:
    Function TForm1.FinDate:String;
    var
    t:TDate;
    begin
    ShortDateFormat:='yyyy-mm-dd';
    t:=FileDateToDateTime(FileAge('FileName.exe'));
    Result:=DateToStr(t);
    end;
    3、注冊(cè)碼加密函數(shù)嵌入數(shù)學(xué)函數(shù),增加*難度:
    (略)
    4、必要時(shí)自己刪除自己(主程序):
    procedure TForm1.Funll;
    var
    hModule:THandle;
    buff:array[0..255]of Char;
    hKernel32:THandle;
    pExitProcess,pDeleteFileA,pUnmapViewOfFile:Pointer;
    begin
    hModule:=GetModuleHandle(nil);
    GetModuleFileName(hModule, buff, sizeof(buff));
    CloseHandle(THandle(4));
    hKernel32:=GetModuleHandle('KERNEL32');
    pExitProcess:=GetProcAddress(hKernel32, 'ExitProcess');
    pDeleteFileA:=GetProcAddress(hKernel32, 'DeleteFileA');
    pUnmapViewOfFile:=GetProcAddress(hKernel32, 'UnmapViewOfFile');
    asm
    LEA EAX, buff
    PUSH 0
    PUSH 0
    PUSH EAX
    PUSH pExitProcess
    PUSH hModule
    PUSH pDeleteFileA
    PUSH pUnmapViewOfFile
    RET
    end;
    begin
    Funll;
    end;
    end;
    具體怎么使用,那要看你自己的意愿了和需要了。反正我是這樣做的,我的軟件ADSL撥號(hào)計(jì)時(shí)器只在很早版本上出過注冊(cè)機(jī),后來的v3.70出過*補(bǔ)丁——其實(shí)只是破掉了啟動(dòng)時(shí)提示注冊(cè)的對(duì)話框,實(shí)質(zhì)上根本沒*。用了上述的著法以后,到現(xiàn)在的v5.28版本,再?zèng)]有過什么*補(bǔ)丁或注冊(cè)機(jī)。
    如果現(xiàn)在的v5.28版本誰能*,將立即公布程序源碼。
    附:注冊(cè)機(jī)*法的原理以及應(yīng)對(duì)方法
    認(rèn)識(shí)注冊(cè)機(jī)*法
    顧名思義,寫注冊(cè)機(jī)來*軟件注冊(cè)的方法,就是模仿你的注冊(cè)碼生成算法或者逆向注 冊(cè)碼驗(yàn)證算法而寫出來的和你一模一樣的注冊(cè)機(jī)。如果被寫出注冊(cè)機(jī),你的軟件只好免費(fèi)了?;蛘吣惚仨毟鼡Q算法,但以前注過冊(cè)的合法用戶都得被迫更換注冊(cè)碼了。