1. 程式人生 > >以色列Aladdin HASP SRM(AES-128)加密狗破解經驗分享

以色列Aladdin HASP SRM(AES-128)加密狗破解經驗分享

這個教程不想談太多的技術(主要是我對[脫殼破解]還是處在初級爆破階段),只想和大家分享一些破解過程,有些過程已經有些忘記,大家湊合看吧
因為自己是程式設計師,真是非常熱愛這個行業,正因為熱愛,所以對各方面的技術也都感興趣,黑客入侵、木馬、外掛、破解都幹過,主要是當初所謂的成就感和來自別人崇拜的目光吧,但是因為自己主要是平時沒事玩一玩,對一些軟體自己動手破解後用著舒服一些,也沒有加入過任何的組織(主要也是水平確實不夠), 所以在幾個方面始終都沒有步入很深入的層次,最近加入吾愛,主要也是因為破解這個加密狗的原因,同時也是想自己能夠在這個領域再提高一些,希望得到大家的指教。

好了,步入正題吧,朋友問我一個軟體能否破解去掉狗,因為我對狗實際的破解經驗不多,所以我只能說試試看吧,軟體名字這裡就不說了。



1:執行
拿到一個軟體肯定是先執行看看嘍(未插加密狗)

報錯:
 




缺少執行環境,從標題上面看可以確定加密狗的廠商,google搜尋之(遇到問題我很少會先問別人,基本都是先在網路上搜索,自己在尋找學習的過程也是自己進步的過程,畢竟自己經歷了才是最難忘的嘛,過程比結果更重要)。
在搜尋後發現,這個狗不簡單啊,自己信心受到了一些打擊,畢竟看介紹功能非常強大(其實目前我還不知道這個狗是哪種型別)

最終在官網下載了軟體和驅動:
http://www.aladdin.com.cn/hasp/support.asp

安裝完成後,再執行,哦,檢測到偵錯程式?我沒調戲它啊,想了一下肯定是因為我開著Visual Studio開發環境。


 


關掉VS再執行,果然沒有提示被調戲,但已經感覺到這個軟體有些不簡單啊,現在顯示找不到狗了,出現找不到加密狗了.
 


2:簡單分析
有了提示對話方塊,大都都懂的,Debugger工具該上場了,先選擇WinDbg吧,結果也不出所料一頓崩潰,算了用OD吧,虛擬機器上的WinXP有OD(話說OD在Win8.1 64bits上面支援的非常不好),隱藏偵錯程式,下斷點在MessageBoxA(W),執行後進程自動退出,看來對付調戲做足了檢測啊,那就直接執行吧,彈出無狗視窗.執行後F12暫停,切換到呼叫棧:

 


找到了呼叫來自哪裡了,經過一頓分析,沒什麼進展,反而隨著跟蹤和除錯,發現水越來越深,大量的花指令,大量的崩潰,深感自己有點力不從心了,還是繼續在網上搜索相關的東西吧。


3:深入分析
通過一系列的分析,知道這個狗是 Aladdin HASP SRM,而且有AES-128加密演算法的,本地安裝有服務程式:hasplms.exe,開放TCP埠:1947(SentinelSRM (hasplm), Aladdin HASP License Manager),狗是網路版,只不過是localhost,客戶端通過socket連線到localhost的hasplms去互動,同時對程式進行了查殼,殼為:

Signature: HASP HL Protection V1.X -> Aladdin
Matches: 42
Signature: HASP HL Protection V1.X -> Aladdin
Matches: 39



4:嘗試各種方案
    1).不帶狗脫殼:<失敗>
    經過一系列嘗試,確定不帶狗是不可能脫掉的,遂向朋友拿到了USB加密狗(開始主要還是低估了這個狗,以為不帶狗就可以脫殼,很傻很天真).

    2).帶狗脫殼:<失敗>
    有了加密狗,程式終於可以正確運行了,所以就嘗試脫殼,使用"兩次記憶體斷點法尋找OEP",對.IDAta和.text分別下斷點,還真定位到了,很興奮(佩服大牛們分享的經驗).

    Ctrl+A後代碼顯示為:
 

經查這是Borland C++的特徵碼。
    那就dump和IAT修復吧,分別是使用PETools和ImportREC,輸入OEP地址並且調整IAT的Size到合適的大小,結果居然有近200個不能定位,我的媽呀,這可如何是好,進行了最後一翻掙扎,深感自己功力不夠,無法確信自己可以手動修復IAT,即能找到每一個API的真正呼叫,但要把200個修復完也得把人累個半死啊,如果修復後還有其他檢驗就不好辦了,最終不得不放棄。

 


    3).模擬網路資料包/DeviceIoControl:<失敗>
    好吧,放棄了脫殼方案,就看看能不能模擬狗吧,因為是網路狗,所以可以先看看網路資料包。使用CommView截localhost:1947的資料包,經過帶狗連續多次的分析,發現每次都是不同的,初步判斷即使把網路資料包模擬返回估計也無法認證通過,最後簡單的寫程式測試了一下,確實是這樣的,如果沒有返回對應的資料包客戶端就會出錯,估計用HOOK DeviceIoControl也是一樣的,再次放棄。

    4).MultiKey等驅動級別模擬:<失敗>
    後來到網上又進行了一翻搜尋,發現老外已經開發出很多驅動級別的HASP狗模擬,也找到了很多的配套工具,比如:h5dmp/h4dmp,hasploger,Toro Dongles Monitor,HaspHLDumper等等,經常一翻測試,最終使用h5dmp dump資料了,此時感覺自己終於看到了曙光,就用MultiKey相關的工具安裝驅動,進行登錄檔檔案的轉換,匯入登錄檔,結果拔掉狗經常無法找到驅動,或者還是失敗,無法模擬,以下是登錄檔檔案.


 


    在模擬加密狗部分花費了大量的時間,最終在網上發現這種辦法是針對HASP HL,而且是不支援AES-128的,即使後來新的模擬狗驅動支援AES-128,也需要把加密狗每次傳送和返回的資料進行記錄,建立一個Q-A資料表才可以,但官方說AES-128演算法可是上萬種啊。
    "QTable"=hex:\
    42,84,... 84,AD,A4,\

    "ATable"=hex:\
    82,22,C2 ... 84,AD,A4,\
    如果程式總是隨機使用某種加密演算法,豈不哭死,直到現在才覺得模擬狗應該也走不下去了,號稱全球第一的加密狗果然不簡單,連國外的大牛們也不能破解其演算法,還得製作Q-A資料庫,對簡單的或許可以,複雜的肯定就不行了吧,此時心灰意冷,感到很無助,耗費了很大的精力,到頭來還是一場空,決定暫停破解,畢竟已經熬了幾個夜晚和週末了。



    5).尋找高人破解:
    通過加一些QQ群找到了一些相關的有經驗的大牛們,打算向他們討教,結果沒人理,說給錢才可以,我就問了一下多少錢?說是至少1w,因為這個太複雜,破解需要一週的時間,問如何破解,可否複製狗,回答只能爆破,無法複製狗,問了一下朋友可否接受這個價格,朋友說有點貴,如果沒辦法也可以接受。後繼續詢問對方,說要先付一半的錢,破解不了可以退一部分。我覺得不太可靠,和對方說我再考慮一下吧,就沒有再聯絡對方。後來自己也考慮了一下,自己之前花費了那麼大的心血,如果去找別人收費破解,真的不甘心。


5:最後的戰役:
    1).希望重現:
    經歷上述失敗後,幾天沒有再繼續,深感自己功力還是不夠,就繼續搜尋相關技術和破解這個狗的相關資訊,尋找更多的這個狗的相關工具,最後使用RTVIDTool2成功的將PW1和PW2從記憶體中dump出來,而且也儲存出來.hvc檔案,.hvc就是頒發給每個開發商的一個vendor code,裡面肯定是有密碼,AES Key和很多亂七八糟的資訊,有了這個東西,就可以自己寫個程式訪問加密狗了。
 




    2).程式測試:
    使用Sentinel_HASP_SDK裡面帶的開發工具VendorSuite\toolbox,指定vendor code,果然可以成功的login,encrypt/decrypt資料,但自己還是高興有點早了,雖然可以訪問加密狗,還是不知道如何利用這個去複製和破解,因為據說這個加密狗有兩種,一個是Master加密狗,另一個就是客戶端的狗,使用Master加密狗才可以複製客戶端加密狗的,也就是master才有寫狗的許可權。

 



    3).確定使用API HOOK方案:
    雖然知道了vendor code不能複製狗,但也不是完全沒用,應該是說幫上了大忙,也是最後成功的轉折點。有了這個就可以做很多的除錯分析,跟蹤HASP API的呼叫入口,經過一翻思考後,打算用API HOOK的方式截獲相關加密狗的API,查SDK文件有如下幾個:
    hasp_status_t HASP_CALLCONV hasp_login(hasp_feature_t feature_id, hasp_vendor_code_t vendor_code, hasp_handle_t *handle);
    hasp_status_t HASP_CALLCONV hasp_login_scope(hasp_feature_t feature_id, const char *scope, hasp_vendor_code_t vendor_code, hasp_handle_t *handle);
    hasp_status_t HASP_CALLCONV hasp_encrypt(hasp_handle_t handle, void *buffer, hasp_size_t length);
    hasp_status_t HASP_CALLCONV hasp_decrypt(hasp_handle_t handle, void *buffer, hasp_size_t length);
    還有hasp_logout, hasp_read,hasp_write,hasp_get_size,hasp_get_rtc,hasp_get_info等等


    4).DUMP解密的程式碼:
    在程式未執行時是無法找到相關的api呼叫的,畢竟是加密的,尋找程式碼解壓可以先在socket api位置下斷點,比如:connect,此時對當前進行了dump,用IDA Pro靜態分析,通過API特徵碼和相關的字串分析歷盡千辛終於找到了hasp_login,hasp_login_scope,hasp_encrypt,hasp_decrypt的位置.



    5).API HOOK程式:
        主要HOOK了幾個API,使用CFF修改IAT靜態匯入自己的DLL,真是萬幸,程式沒有做修改校驗。這部分可以參考我發的另一個文章,比較詳細:
http://www.52pojie.cn/thread-257140-1-1.html



            gHooks.Add(_T("KERNEL32.DLL"),  "IsDebuggerPresent",    my_IsDebuggerPresent);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetModuleHandleA",     my_GetModuleHandleA);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetModuleHandleW",     my_GetModuleHandleW);
            gHooks.Add(_T("KERNEL32.DLL"),  "Process32FirstW",      my_Process32FirstW);
            gHooks.Add(_T("KERNEL32.DLL"),  "Process32NextW",       my_Process32NextW);
            gHooks.Add(_T("KERNEL32.DLL"),  "CloseHandle",          my_CloseHandle);
            gHooks.Add(_T("KERNEL32.DLL"),  "EnterCriticalSection", my_EnterCriticalSection);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetProcAddress",       my_GetProcAddress);
            gHooks.BeginAll();

            對Process32NextW的hook是為了跳過對一些程序的檢查,之前提到的,如果我開著VS就提示有偵錯程式(沒辦法,工作要用VC),這樣灰常的不爽,所以HOOK之後有如下程式碼:            

BOOL WINAPI my_Process32NextW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe)
{
    CAdAutoHookApi autoHook(&gHooks, my_Process32NextW);
    BOOL ret = Process32Next(hSnapshot, lppe);
    if(ret)
    {
//        logOutput(formatString("Process32Next(0x%08X, Exe:%S)\r\n", (int)hSnapshot, lppe->szExeFile));
        if(_wcsicmp(lppe->szExeFile, L"devenv.exe") == 0)
        {
            wcscpy(lppe->szExeFile, L"crack.exe");
            logOutput("Process32Next, Rename \"devenv.exe\" to \"crack.exe\"\r\n");
        }
    }
    return ret;
}

同時寫了HASP API函式:

BOOL WINAPI my_Process32NextW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe)
{
    CAdAutoHookApi autoHook(&gHooks, my_Process32NextW);
    BOOL ret = Process32Next(hSnapshot, lppe);
    if(ret)
    {
//        logOutput(formatString("Process32Next(0x%08X, Exe:%S)\r\n", (int)hSnapshot, lppe->szExeFile));
        if(_wcsicmp(lppe->szExeFile, L"devenv.exe") == 0)
        {
            wcscpy(lppe->szExeFile, L"crack.exe");
            logOutput("Process32Next, Rename \"devenv.exe\" to \"crack.exe\"\r\n");
        }
    }
    return ret;
}

這是找到的每個API地址,這部分也是大費周折(開發的SDK API和這個程式使用的API版本不同,沒辦法完全匹配)

int HookHaspAPI_Begin()
{
    if(!g_is_hasp_login_scope)
    {
        void *pOldAddr = (void *)0x0053FCE0;
        const BYTE verifyData[] = { 0x81, 0xEC, 0x00, 0x04, 0x00, 0x00, 0x56 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_login_scope, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : hasp_login_scope hooked\r\n"));
            g_is_hasp_login_scope = true;
        }
    }
 
    if(!g_is_hasp_encrypt)
    {
        void *pOldAddr = (void *)0x0053EF60;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_encrypt, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_encrypt hooked\r\n"));
            g_is_hasp_encrypt = true;
        }
    }
 
    if(!g_is_hasp_decrypt)
    {
        void *pOldAddr = (void *)0x0053F000;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_decrypt, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_decrypt hooked\r\n"));
            g_is_hasp_decrypt = true;
        }
    }
 
    if(!g_is_hasp_get_info)
    {
        void *pOldAddr = (void *)0x0053FDE0;
        const BYTE verifyData[] = { 0x81, 0xEC, 0xB8, 0x09, 0x00, 0x00, 0x8B, 0x84 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_get_info, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_get_info hooked\r\n"));
            g_is_hasp_get_info = true;
        }
    }
 
    if(!g_is_hasp_logout)
    {
        void *pOldAddr = (void *)0x0053EED0;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x08, 0x6A, 0x00 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_logout, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_logout hooked\r\n"));
            g_is_hasp_logout = true;
        }
    }
}

 6).HOOK時機:
    上述已經找到了每個API的地址,但何時進行HOOK,這裡通過測試,發現程式在使用GetProcAddress呼叫某個API後而且沿未呼叫hasp_login時程式碼是解密的,因為我要hook某個地址時可以預先判斷程式碼是否匹配,所以最後索性在截獲的GetProcAddress中直接呼叫HookHaspAPI_Begin(),直接某個時刻成功為止:

int HookHaspAPI_Begin()
{
    if(!g_is_hasp_login_scope)
    {
        void *pOldAddr = (void *)0x0053FCE0;
        const BYTE verifyData[] = { 0x81, 0xEC, 0x00, 0x04, 0x00, 0x00, 0x56 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_login_scope, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : hasp_login_scope hooked\r\n"));
            g_is_hasp_login_scope = true;
        }
    }
 
    if(!g_is_hasp_encrypt)
    {
        void *pOldAddr = (void *)0x0053EF60;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_encrypt, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_encrypt hooked\r\n"));
            g_is_hasp_encrypt = true;
        }
    }
 
    if(!g_is_hasp_decrypt)
    {
        void *pOldAddr = (void *)0x0053F000;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_decrypt, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_decrypt hooked\r\n"));
            g_is_hasp_decrypt = true;
        }
    }
 
    if(!g_is_hasp_get_info)
    {
        void *pOldAddr = (void *)0x0053FDE0;
        const BYTE verifyData[] = { 0x81, 0xEC, 0xB8, 0x09, 0x00, 0x00, 0x8B, 0x84 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_get_info, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_get_info hooked\r\n"));
            g_is_hasp_get_info = true;
        }
    }
 
    if(!g_is_hasp_logout)
    {
        void *pOldAddr = (void *)0x0053EED0;
        const BYTE verifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x08, 0x6A, 0x00 };
        HANDLE hHook = gHooks.Add(pOldAddr, my_hasp_logout, verifyData, sizeof(verifyData));
        if(hHook != NULL)
        {
            gHooks.Begin(hHook);
            logOutput(formatString("HASP API : my_hasp_logout hooked\r\n"));
            g_is_hasp_logout = true;
        }
    }
}

7).分析截獲的資料:
    通過資料截獲分析,最終發現呼叫hasp_login_scope成功後,就會反覆的呼叫hasp_decrypt去解密資料,每次去解密的資料大小都是16,輸入的資料是隨機的,但有2次解密的資料大小是32768,而且輸入的資料是固定的內含,當然解密後的資料也是固定的,這是API log.
    [  1688]12:24:20.179 - ApiDebugger Loaded.
    [  1688]12:24:20.211 - Process32Next, Rename "devenv.exe" to "crack.exe"
    [  1688]12:24:20.215 - Process32Next, Rename "devenv.exe" to "crack.exe"
    [  1688]12:24:20.246 - HASP API : hasp_login_scope hooked
    [  1688]12:24:20.246 - HASP API : my_hasp_encrypt hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_decrypt hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_get_info hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_logout hooked
    [  1688]12:24:20.248 - HASP API : Patch Address 0x0052B45B
    [  1688]12:24:20.248 - HASP API : Patch Address 0x00591DFA
    [  1688]12:24:20.250 - HASP API : Call hasp_login_scope(5476)
    [  1688]12:24:20.252 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F998, 16)
    [  1688]12:24:20.255 - D : D67BF973E80CC4AF98D23FB8285FCB4A
    [  1688]12:24:20.256 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F998, 16)
    [  1688]12:24:20.259 - D : 083E3F19B6527EB976CF7D18BBB80890
    [  1688]12:24:20.263 - HASP API : Call hasp_decrypt(0x88888888, 0x003A0000, 32768)
    [  1688]12:24:20.267 - HASP API : Read data from "data.enc1.bin"
    [  1688]12:24:20.270 - HASP API : Call hasp_decrypt(0x88888888, 0x003A0000, 32768)
    [  1688]12:24:20.274 - HASP API : Read data from "data.enc2.bin"
    [  1688]12:24:20.330 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F91C, 16)
    [  1688]12:24:20.333 - D : 3CE5A8C8DCC197E5C426D78A3CBFA846
    [  1688]12:24:21.393 - HASP API : Call hasp_logout()
    [  1688]12:24:21.395 - ApiDebugger Unloaded.

    根據這些重要資料進行分析,可以認為:16個位元組的資料解密純粹是為了校驗加密狗的合法性,32768的資料解密應該是對主程式的內容解密,因此之前以為不帶狗就能去破解是多麼的天真,如果不帶狗就能破解,這狗還有啥意思?
8).確定判斷地址:
    現在我可以對hasp_decrypt函式在請求32768長度資料解密時返回固定的資料,但16位元組的校驗請求怎麼辦?這又是一個難題,因為你不知道演算法,如果你隨便返回資料程式就報錯.
    這部分不細說了,主要是設定引數記憶體地址的訪問斷點,最終確定了有2處hasp_decrypt呼叫後資料驗證地址:

ASM:

007040FB    8A5429 F0           MOV DL,BYTE PTR DS:[ECX+EBP-10]
007040FF    3BC2                CMP EAX,EDX      // 這裡是判斷,這是殼程式的檢查
00704101    68 3D457000         PUSH AAA.0070453D
00704106    C3                  RETN
00704107    D840 E9             FADD DWORD PTR DS:[EAX-17]
0070410A    FFF9                ???                                              ; Unknown command
0070410C    FFFF                ???                                              ; Unknown command
 
 
 
0052B45B    8A5429 F0           MOV DL,BYTE PTR DS:[ECX+EBP-10]
0052B45F    3BC2                CMP EAX,EDX   // 同樣的程式碼,也是判斷,這是主程式執行後的檢查
0052B461    68 9DB85200         PUSH AAA.0052B89D
0052B466    C3                  RETN

 9).爆破方法:
        嘗試了2種辦法,第一種是,我既然已經知道了返回資料與正確資料的判斷位置,完美的做法是,要檢查之前,把正確的資料複製到返回的資料地址:

ASM:

PUSH EAX
 
MOV EAX, DWORD PTR DS:[EDX+52C4E4+0]
MOV DWORD PTR DS:[EBP-10+0], EAX
 
MOV EAX, DWORD PTR DS:[EDX+52C4E4+4]
MOV DWORD PTR DS:[EBP-10+4], EAX
 
MOV EAX, DWORD PTR DS:[EDX+52C4E4+8]
MOV DWORD PTR DS:[EBP-10+8], EAX
 
MOV EAX, DWORD PTR DS:[EDX+52C4E4+0C]
MOV DWORD PTR DS:[EBP-10+0C], EAX
 
POP EAX
 
MOV DL,AL
CMP EAX,EDX
JMP 0052B461

 程式碼寫好了,也要選擇插入位置,最終在記憶體找到了大片的90 90 90 90記憶體,然後在程式執行中動態的將程式碼寫入到這個位置,同時在原始碼處做了一個JMP語句.
        完成後再執行程式,最終程式完美執行,這時候真的有一種想哭的感覺,心情豁然開朗!
        成功破解後,又試了一種簡單的方案,直接Patch Code "CMP EAX,EDX" 為  "CMP EAX,EAX/CMP EDX,EDX"也是成功的。所以也就驗證了對於隨機解密後的資料僅僅是為了加密狗的校驗。
    10).最終的my_hasp_decrypt函式:
        隨著一步步的深入,搞明白了,核心API就是hasp_decrypt,最終hook的函式程式碼為:

hasp_status_t HASP_CALLCONV my_hasp_decrypt(hasp_handle_t handle,
                                            void *buffer,
                                            hasp_size_t length)
{
    logOutput(formatString("HASP API : Call hasp_decrypt(0x%08X, 0x%08X, %d)\r\n", 
        (int)handle, (int)buffer, (int)length));
 
    if(length == 0x8000)
    {
        const BYTE data1[] = { 0x41, 0xC9, 0x2A, 0x77, 0x12, 0xA5, 0xB3, 0x6B };
        const BYTE data2[] = { 0x08, 0xB3, 0xC4, 0x2E, 0xC8, 0xAF, 0x78, 0x6E };
        if(memcmp(buffer, data1, sizeof(data1)) == 0)
        {
//            writeDataToFile("data.dec1.bin", buffer, length);
            if(readDataFromFile("data.enc1.bin", buffer, length) == length)
            {
                logOutput(formatString("HASP API : Read data from \"data.enc1.bin\"\r\n"));
            }
        }
        if(memcmp(buffer, data2, sizeof(data2)) == 0)
        {
//            writeDataToFile("data.dec2.bin", buffer, length);
            if(readDataFromFile("data.enc2.bin", buffer, length) == length)
            {
                logOutput(formatString("HASP API : Read data from \"data.enc2.bin\"\r\n"));
            }
        }
    }
    else if((length == 16) && (memcmp(buffer, gZeroInput, sizeof(gZeroInput)) == 0))
    {
        memcpy(buffer, gZeroOutput, length);
        logOutput(formatString("HASP API : Decrypt Zero Buffer.\r\n"));
        string hexData = string("E : ") + toHexString((const char *)buffer, length) + "\r\n";
        logOutput(hexData);
    }
    else
    {
        writeDataToFile("enc.bin", buffer, length);
        string hexData = string("D : ") + toHexString((const char *)buffer, length) + "\r\n";
        logOutput(hexData);
        memset(buffer, 0, length);
 
        if(!g_is_patch_code1)
        {
            // 0052B45B
            const BYTE verifyData[] = { 0x8A, 0x54, 0x29, 0xF0, 0x3B, 0xC2, 0x68 };
            const BYTE patchCode[]  = { 0x3B, 0xC0 };
 
            void *pVerifyAddr = (void *)0x0052B45B;
            void *pPatchAddr  = (void *)0x0052B45F;
 
            if(CAdHookApi::VerifyAddress(pVerifyAddr, verifyData, sizeof(verifyData)))
            {
                if(CAdHookApi::PatchCode(pPatchAddr, patchCode, sizeof(patchCode)))
                {
                    logOutput(formatString("HASP API : Patch Address 0x0052B45F, (CMP EAX, EAX)\r\n"));
                    g_is_patch_code1 = true;
                }
            }
        }
 
        if(!g_is_patch_code2)
        {
            // 007040FB
            const BYTE verifyData[] = { 0x8A, 0x54, 0x29, 0xF0, 0x3B, 0xC2, 0x68 };
            const BYTE patchCode[]  = { 0x3B, 0xC0 };
 
            void *pVerifyAddr = (void *)0x007040FB;
            void *pPatchAddr  = (void *)0x007040FF;
 
            if(CAdHookApi::VerifyAddress(pVerifyAddr, verifyData, sizeof(verifyData)))
            {
                if(CAdHookApi::PatchCode(pPatchAddr, patchCode, sizeof(patchCode)))
                {
                    logOutput(formatString("HASP API : Patch Address 0x007040FF, (CMP EAX, EAX)\r\n"));
                    g_is_patch_code2 = true;
 
                    ApiDebugferShutdown();
                }
            }
        }
    }
    return HASP_STATUS_OK;
}

最後經過多次驗證,100%確信自己真的把這個狗給破解了,過程中經歷的艱辛和不眠立馬化為無有,都被最後的成功所淹沒,這種感覺妙不可言。
對於這個加密狗破解,是自己花費了最大力氣的一個(總歷時應該有100小時),雖然最終是API HOOK+爆破,程式碼僅Patch 2個節,但為了這個2個位元組確實經歷了太多,每個破解思路都失敗的時候也想過放棄,慶幸自己最終還是堅持了下來,贏取了勝利.
 從這個過程中也發現自己在某些地方還非常的初級,尤其是演算法分析上面,只有能深入演算法分析,才能到達更高的層次,總是爆破不利於水平的提高,後面有時候會繼續對這個加密狗進行研究,爭取做到完美脫殼,將來再與大家分享。

PS:謝謝大家能看完,多提建議和意見,也希望熟悉這個加密狗的大牛和大神們也能分享這方面的經驗,能夠找到一種更簡單快速的破解方法。

轉:https://www.52pojie.cn/thread-258182-1-1.html

相關推薦

以色列Aladdin HASP SRM(AES-128)加密破解經驗分享

這個教程不想談太多的技術(主要是我對[脫殼破解]還是處在初級爆破階段),只想和大家分享一些破解過程,有些過程已經有些忘記,大家湊合看吧。因為自己是程式設計師,真是非常熱愛這個行業,正因為熱愛,所以對各方面的技術也都感興趣,黑客入侵、木馬、外掛、破解都幹過,主要是當初所謂的成就

Aladdin HASP SRM(AES-128)加密破解經驗分享

這個教程不想談太多的技術(主要是我對[脫殼破解]還是處在初級爆破階段),只想和大家分享一些破解過程,有些過程已經有些忘記,大家湊合看吧。 因為自己是程式設計師,真是非常熱愛這個行業,正因為熱愛,所以對各方面的技術也都感興趣,黑客入侵、木馬、外掛、破解都幹過,主要是當初所謂的成

低功耗藍芽BLE之AES-128加密演算法

低功耗藍芽中的所有加密和認證都基於同一個加密引擎,稱為高階加密系統(AES)。AES最初源自美國的一項政府計劃,試圖尋找未來可用的加密引擎。一直以來,AES被用於許多有線和無線標準,迄今為止安全研究人員還沒有找到其演算法的弱點。 AES可以有多種形式,取決於在給定的時間內能夠處理的資料塊以及金鑰的大小。

某軟體聖天諾加密破解過程

這個軟體是用聖天諾狗來加密的,當軟體沒有檢測到加密狗時,則需要註冊它。好,我們看看怎麼樣來破解它。 工具:trw2000     軟體在執行時,如果沒有狗,則會跳出一個需要註冊的對話方塊,而且也是用動態註冊的方法,如果註冊碼不正確register按鈕是灰色的。這類軟體一

老王教您怎麼做cass7.1 8.0 9.1所有系列的複製 加密 破解

cass7.1 8.0 9.1所有系列的複製狗 加密狗 破解狗本來是出於好奇看到網上有這樣的東西,學了下感覺挺簡單的,如果你是cass的初學者想僅僅是想學習這個軟體,不想花大價格購買正版的,這個是可以學習用的。 需要喜歡的版本cad,和cass(這裡我選擇cad2006和cass9.1,cad200

Hasp SRM 加密模擬解密免Hasp SRM

HASP SRM SaaS Pass輔助SaaS的供給商通過避免使用者的登入資訊取別人共享來最大限度地擴充套件其收進。由於SaaS Pass只容許使用者從受權功的盤算機去拜訪你的按需利用軟體,維護您的定閱發入並把持訪答敏感的終極使用者軟體。 HASP SRM SaaS Pas

java.. C# 使用AES加密互解 采用AES-128-ECB加密模式

convert odi summary for number als name apache base java需要下載外部包, commons codec.jar 1.6 較新的JAVA版本把Base64的方法改成靜態方法,可能會寫成Base64.encodeToStr

微信AES-128-CBC加密解密

brush sha 解密 method pre tcl int cipher ++ [TestClass] public class UnitTest1 { [TestMethod] public void TestMeth

PHP AES cbc模式 pkcs7 128加密解密

今天在對接一個第三方介面的時候,對方需要AES CBC模式下的加密。這裡簡單寫一個demo class Model_Junjingbao extends Model { private static $_partnerKey = '6d70a09e4d0f8095'; //

python AES CFB-128加密

1.傳統的pycrypto已經不更新了有很多bug 導致 CFB模式有問題,網上的99%都是錯的,要用pycryptodome包 2.包出現問題應該去github看wiki或者bug 搜關鍵詞 3.以下是程式碼 def AES_128_CFB(String):

Java程式使用HASP加密加密保護的操作步驟--手把手包你學會

轉自 http://www.cnblogs.com/hasp/archive/2011/12/15/2249393.html Hasp5.1的Envelope對java web的war包或者jar包中class檔案加密 Hasp 5.1能對java web工程中的c

java AES 128加密解密演算法

最近在做app後臺的伺服器,使用到AES加密解密演算法,無奈網上的都不符合要求,於是自己借鑑著寫了一個AES加密解密工具。 金鑰長度問題 預設 Java 中僅支援 128 位金鑰,當使用 256 位金鑰的時候,會報告金鑰長度錯誤 Invalid AES

java使用AES加密解密 AES-128-ECB加密

http://www.cnblogs.com/chen-lhx/p/5817161.html *************************************************** import javax.crypto.Cipher; import jav

HASP 4 加密模擬、解密硬複製HASP 4

hasp4有一組明碼 pass1 pass2 通常你能夠通功TORO的監督器或是hasphl 2008 在Attach your dongle 運言伏軟體的共時refresh 取得.DMP 為字尾的檔名.通常替432F672D.dmp 以16進製表現.Hasp系列的加密鎖可以

AES-128 ECB 加密有感

最近要搞一個加密。很是蛋疼。 原本微控制器和上位機發送資料就是非常簡單的。 這個專案相當複雜。每次資料命令都差不多1K,一次資料都要1K以上。是通過GPRS傳送的。 這些難纏的問題再前一階段已經解決了。 現在老師,還讓我們做AES加密。是對我們做控制的人來說還確實有點難度啊 。 寫的詳單詳細。 我再囉嗦一

Hasp HL加密模擬、解密硬複製Hasp HL

HASP HL掩護鎖取利用程式之間的保險通訊通講名現了對兩頭層攻打的強盛保護。輕鬆辨別運用軟體受權使用者:每一個HASP HL時光型保護鎖皆有一個獨一的ID號。 ISO認證:HASP HL維護鎖依照ISO9001:2000設計出產,確保。一貫的產品品德及機能表示。Hasp系列

nodejs中aes-128-cbc加密和解密

和java程式進行互動的時候,java那邊使用AES 128位填充模式:AES/CBC/PKCS5Padding加密方法,在nodejs中採用對應的aes-128-cbc加密方法就能對應上,因為有使用向量(iv),所以nodejs中要用createCiphe

AES 128-bit ecb cbc 模式 C語言加密演算法

原始碼檢視: aes.h #ifndef _AES_H_ #define _AES_H_ #include <stdint.h> // #define the macros below to 1/0 to enable/disable the mod

使用USB直接方式解決ESXi識別加密的問題

虛擬機 usb esxi 加密狗 VMware ESXi對USB外設的支持比較好,但這並不是說ESXi的虛擬機能支持所有的USB外設。例如一些財務軟件的加密狗,在物理機操作系統中使用沒有問題,但在ESXi中的虛擬機不能識別這些加密狗。對於這種問題,可以將主機的USB接口以"直連"的方式映射到虛

使用pyaes測試AES-ECB 加密解密示例

odi pytho 通信協議 文件 wid log this class there   最近在搞一個藍牙鎖,通信協議說是使用的標準AES-ECB加密、解密,無奈我測試的時候,加密後的數據和通信協議文檔給的數據不一致,懷疑文檔寫的aes-ecb傳參是否swap了or非標準A