淺析MD5加密算法在軟件注冊中的應(yīng)用
我們已經(jīng)見識很多種MD5加密算法在軟件注冊中的應(yīng)用,同時也得到了一個結(jié)論:一味生搬硬套現(xiàn)成的算法而不加改變,即使算法再強大在破解者面前也是“不堪一擊”的。所以,我們就見識到了各種各樣的奇思妙想,將MD5算法使用得淋漓盡致。但是,算法上做足了功夫卻沒有注意注冊碼比較的過程,導(dǎo)致了注冊碼明碼在內(nèi)存中出現(xiàn),結(jié)果同樣會導(dǎo)致“功虧一簣”。所以,必須在算法的使用和注冊驗證這兩個方面同時下手,才可以讓一個優(yōu)秀的算法發(fā)揮出它最大的作用。

今天,我們選擇的這個MD5算法應(yīng)用的實例,卻從另一個角度啟發(fā)我們究竟什么才叫做靈活應(yīng)用。在注冊過程中,真正的注冊碼很容易就被發(fā)現(xiàn)了,但想要成功注冊?呵呵,門都沒有!在稍后分析的過程中,大家一定會看著注冊碼干著急,明明就是正確注冊碼了,卻怎么也用不了。這究竟是為什么呢?讓我們帶著這個問題開始下面的分析之路吧!
這次的實例是一個CrackMe,用PEiD檢查,確定這個小程序沒有加殼,接下來我們就可以搬出OD了。用OD打開這個CrackMe,載入之后打開“字符串查找功能”,很容易就發(fā)現(xiàn)了關(guān)鍵的注冊提示信息,如圖1所示,雙擊找到的注冊提示,就可以來到對應(yīng)的代碼處了,
現(xiàn)在我們看到的是“注冊提示”也就是來到了告訴我們注冊成功與否的地方,注冊碼計算和注冊驗證的代碼應(yīng)該就在前面了,一路向上來到以下代碼處。
00401C05 . 56 push esi ;在這里設(shè)置斷點
00401C06 . 8BD8 mov ebx, eax
00401C08 . FFD7 call edi ;取輸入的注冊碼
00401C0A . 8D4424 0C lea eax, [esp+C]
;將注冊碼保存在EAX中
00401C0E . 8D50 01 lea edx, [eax+1]
;對注冊碼作相同的檢查
00401C11 > 8A08 mov cl, [eax]
;取注冊碼的每一位
00401C13 . 40 inc eax
00401C14 . 84C9 test cl, cl
00401C16 .^ 75 F9 jnz short 00401C11 ;循環(huán)計算
00401C18 . 2BC2 sub eax, edx
;得到輸入的注冊碼的長度
00401C1A . 8BD0 mov edx, eax
;將注冊碼的長度保存在EDX中
00401C1C . 0F84 C5000000 je 00401CE7
程序首先將我們輸入的注冊碼進行了一個預(yù)處理,并把注冊碼的長度記錄下來。
00401C22 . 85DB test ebx, ebx
00401C24 . 0F84 BD000000 je 00401CE7
00401C2A . 33C9 xor ecx, ecx
00401C2C . 85D2 test edx, edx
;開始檢驗注冊碼的范圍
00401C2E . 7E 29 jle short 00401C59
00401C30 > 8A440C 0C mov al, [esp+ecx+C]
;取注冊碼的每一位
00401C34 . 3C 30 cmp al, 30
00401C36 . 7C 04 jl short 00401C3C
00401C38 . 3C 39 cmp al, 39
00401C3A . 7E 18 jle short 00401C54
00401C3C > 3C 41 cmp al, 41
00401C3E . 7C 0C jl short 00401C4C
00401C40 . 3C 47 cmp al, 47
00401C42 . 7F 08 jg short 00401C4C
00401C44 . 04 20 add al, 20
00401C46 . 88440C 0C mov [esp+ecx+C], al
00401C4A . EB 08 jmp short 00401C54
00401C4C > 3C 61 cmp al, 61
00401C4E . 7C 65 jl short 00401CB5
00401C50 . 3C 67 cmp al, 67
00401C52 . 7F 61 jg short 00401CB5
00401C54 > 41 inc ecx
00401C55 . 3BCA cmp ecx, edx
00401C57 .^ 7C D7 jl short 00401C30
00401C59 > 33C0 xor eax, eax
00401C5B . 85DB test ebx, ebx
00401C5D . 7E 0D jle short 00401C6C
緊接著就是對注冊碼的每一位進行驗證,主要目的就是檢驗注冊碼的每一位是否符合要求。簡單的說,就是注冊碼的每一位必須在數(shù)字和字母這個范圍內(nèi),超出這個范圍就會出錯了。大家可以注意到,上面的大段代碼都是用來檢驗注冊碼的,看來我們輸入的注冊碼雖然是“假碼”,但這個CrackMe卻對這個“假碼”很感興趣,一而再再而三地進行檢驗。下面又會對這個假碼進行怎樣的處理呢?我們繼續(xù)分析。
00401C5F . 8A4C14 0B mov cl, [esp+edx+B]
;取注冊碼的最后一位
00401C63 > 304C04 40 xor [esp+eax+40], cl
;與注冊名的每一位運算
00401C67 . 40 inc eax
;每計算一次EAX加1
00401C68 . 3BC3 cmp eax, ebx
;計算完了嗎
00401C6A .^ 7C F7 jl short 00401C63 ;循環(huán)計算
00401C6C > 8D4424 40 lea eax, [esp+40]
;保存計算的結(jié)果str1
00401C70 . 50 push eax ;參數(shù)入棧
這里程序?qū)⑽覀冚斎氲淖源a的最后一位單獨取了出來,并用它和注冊名一一計算。下面把這段代碼的詳細計算過程給大家說明一下。
第一步:取注冊碼的最后一位的HEX值;
第二步:取注冊名每一位的ASCII碼;
第三步:將HEX值與注冊名每一位的ASCII碼作XOR(異或)運算;
第四步:將計算的結(jié)果轉(zhuǎn)換成對應(yīng)的字母。
完整的過程就是這樣的,但感覺還是有些含糊,我們舉例來說明吧。在這里我輸入的注冊名是tcxb,注冊碼是12345,那么在這段代碼中的計算過程就是這樣的。
第一步:取注冊碼的最后一位,也就是這里的5,計算對應(yīng)的HEX值結(jié)果是35;
第二步:取注冊名每一位的ASCII碼,tcxb這四個字母對應(yīng)的ASCII碼分別是:74、63、78、62;
第三步:將35分別與74、63、78、62進行異或運算,XOR(35.74)=41,XOR(35.63)=56,XOR(35.78)=4D,XOR(35.62)=57;
第四步:因為在ASCII碼表中,字母A對應(yīng)的值就是41,所以注冊名第一位計算得到的最終結(jié)果就是A。
按照ASCII碼表中的對應(yīng)關(guān)系一一計算,可以得到每一個結(jié)果,分別是A、V、M、W。最后將這四個字母合并組成一個字符串,也就是我們所說的str1=AVMW。得到了這個字符串之后又要干什么呢?我們接著向下分析。
00401C71 . C64414 0F 00 mov byte ptr [esp+edx+F], 0
00401C76 . E8 75FEFFFF call 00401AF0
;將str1做MD5計算
00401C7B . 83C4 04 add esp, 4
00401C7E . 8D4C24 0C lea ecx, [esp+C]
;取輸入的注冊碼
00401C82 . 51 push ecx ; /String2
00401C83 . 50 push eax ; |String1
00401C84 . FF15 00704000 call [< &KERNEL32.lstrcmpA>]; lstrcmpA
00401C8A . 85C0 test eax, eax ;將兩者比較
00401C8C . 6A 00 push 0
00401C8E . 75 33 jnz short 00401CC3
00401C90 . 68 98714000 push 00407198 ;succeed!
00401C95 . 68 88714000 push 00407188 ;great,注冊成功!
00401C9A > 8B15 40974000 mov edx, [409740]; |
00401CA0 . 52 push edx ;|hOwner => NULL
00401CA1 . FF15 DC704000 call [< &USER32.MessageBoxA>]; MessageBoxA
這段代碼就簡單多了,主要作用是將上一步得到的str1即AVMW進行MD5計算,將計算得到的結(jié)果與我們輸入的注冊碼相比較,如果相等就注冊成功。通過各種方法很容易就可以計算出來MD5(AVMW)=c37985c0c4a3a9e4600d67c5d18af450。按照我們分析的結(jié)果,這個MD5值如果沒有問題就是注冊碼了。
現(xiàn)在我們重新運行程序依次輸入注冊名tcxb,注冊碼c37985c0c4a3a9e4600d67c5d18af450,很自信地按下“注冊”按鈕,什么?居然提示“注冊碼”不正確!怎么回事?我們分析的過程沒有錯呀!再一次打開OD重新分析一遍,輸入注冊名tcxb和注冊碼12345,跟蹤到最后,依然是上一步得到的那個計算結(jié)果。注冊碼就在眼前,可為什么不能成功注冊呢?
用OD分析的過程是不可能出錯的,問題是不是在我們,是不是分析的時候漏掉了什么?整理一下思路,這個CrackMe的計算過程很簡單,主要就是把一個字符串用MD5進行了一個加密處理。一般來說,MD5加密的都是注冊名或是注冊名加固定字符串等這樣一些東西,這里也沒有什么特別的地方。那么注冊碼驗證的地方就更不用懷疑了,很明顯是真假注冊碼比較,而且還是明碼比較,但為什么結(jié)果就是不對呢?
大家注意,我們所接觸的類似這樣將某一個字符串用MD5加密,然后將加密結(jié)果作為注冊碼的情況,被加密的字符串都是由注冊名直接變換得出的,比如加上一個固定字符串或者是和某一個數(shù)字做計算等,簡單的說就是被加密的信息與注冊碼之間是沒有關(guān)系的,從頭到尾都是對注冊名的計算。其實問題就是出在這里了。在這個CrackMe中,大家回憶一下被加密的那個str1是怎樣得來的?是用注冊碼的最后一位與注冊名的每一位分別計算后得出的,這樣在被加密的信息和輸入的注冊碼之間就有了一個動態(tài)關(guān)系了,只要輸入的注冊碼發(fā)生變化,就直接導(dǎo)致被加密的信息發(fā)生變化,被加密的信息都發(fā)生變化了,最后的結(jié)果肯定會出問題的。









