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",對.
IDA
ta和.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之後有如下程式碼:              

[C++] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

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函式:  

[C++] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

hasp_status_t HASP_CALLCONV my_hasp_login(hasp_feature_t feature_id,

hasp_vendor_code_t vendor_code,

hasp_handle_t *handle)

{

logOutput(formatString("HASP API : Call hasp_login(%d)\r\n", (int)feature_id));

*handle = 0x88888888;

return HASP_STATUS_OK;

}

hasp_status_t HASP_CALLCONV my_hasp_login_scope(hasp_feature_t feature_id,

const char  *scope,

hasp_vendor_code_t vendor_code,

hasp_handle_t *handle)

{

logOutput(formatString("HASP API : Call hasp_login_scope(%d)\r\n", (int)feature_id));

*handle = 0x88888888;

return HASP_STATUS_OK;

}

hasp_status_t HASP_CALLCONV my_hasp_encrypt(hasp_handle_t handle,

void *buffer,

hasp_size_t length)

{

logOutput(formatString("HASP API : Call hasp_encrypt(0x%08X, 0x%08X, %d)\r\n",

(int)handle, (int)buffer, (int)length));

return HASP_STATUS_OK;

}

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));

這部分程式碼後面再說。。。

}

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

[C++] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

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(),直接某個時刻成功為止:  

[C++] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

FARPROC WINAPI my_GetProcAddress(HMODULE hModule, LPCSTR lpProcName)

{

CAdAutoHookApi autoHook(&gHooks, my_GetProcAddress);

FARPROC ret = GetProcAddress(hModule, lpProcName);

string moduleName = getFileName(getModuleNameA(hModule));

if(IsBadStringPtrA(lpProcName, 255))

{

//        logOutput(formatString("GetProcAddress(0x%08X(%s), 0x%08X) : 0x%08X\r\n", 

//            (int)hModule, moduleName.c_str(), (int)lpProcName, (int)ret));

}

else

{

//        logOutput(formatString("GetProcAddress(0x%08X(%s), %s) : 0x%08X\r\n",

//            (int)hModule, moduleName.c_str(), (lpProcName != NULL) ? lpProcName : "NULL", (int)ret));

/*

if(strcmp(lpProcName, "LoadLibraryA") == 0)

{

MessageBoxW(::GetDesktopWindow(), L"GetProcAddress(LoadLibraryA)", L"ApiDebugger", MB_OK);

}

*/

}

HookHaspAPI_Begin();

return ret;

}

    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] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

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] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

PUSH EAX

MOV EAXDWORD PTR  DS:[EDX+52C4E4+0]

MOV DWORD  PTR  DS:[EBP-10+0], EAX

MOV EAXDWORD PTR  DS:[EDX+52C4E4+4]

MOV DWORD  PTR  DS:[EBP-10+4], EAX

MOV EAXDWORD PTR  DS:[EDX+52C4E4+8]

MOV DWORD  PTR  DS:[EBP-10+8], EAX

MOV EAXDWORD 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的函式程式碼為:  

[C++] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

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