你應該明白的是,并不是所有的軟件作者都像你想象并希望的那笨 沒有人愿意自己的軟件被別人在調試器中用一條d指令就能找到正確的注冊碼...要是那樣的話還出來搞什么?
前 邊兒我們講的查找軟件注冊碼的方法是有針對性的,必須保證的是該軟件使用的是明碼比較,這樣的話,我們只需找對地方,一個d指令就成了。那既然有明碼比較 這個詞,就不難猜出還有相應的非明碼比較...非明碼比較也比較容易理解,就是軟件比較兩個注冊碼的方法不同而以,并不是計算出正確的注冊碼后就與用戶輸 入的進行比較,它可能會采用每計算出一位就與注碼中的相應位比較一次,一但發現與用戶輸入的不同,就提示出錯等等等等...
遇到這樣的軟件,我 們其實也可以找到其相應的注冊碼,但有點兒慘,要一位一位的計下來...但是如果人家不給你面子,一但計算出某位不正確就跳走的話,那你怎么辦?所以,國 民想致富,種樹是根本...NG!所以遇到這種軟件,我們就只有對其算法進行分析,并做出注冊機才是唯一的方法(如果你想寫注冊機的話)...
你 要明白,就算我們能找到那些采用明碼比較的軟件的注冊碼,原因也僅僅是因為其采用的是明碼比較,所以我們沒有什么值的高興的地方,我們真正要做的,并不是 找到一個注冊碼而以...當然如果你剛入門,那對你的提高還是很有幫助的。我們Crack一個軟件的最終目的,是對其進行相應的分析,搞懂它的注冊算法并 寫出注冊機,這樣才算是成功的Crack了一個軟件,成功后的心情是難以表達的!就像你便秘了多天后一下子排了出來一樣 ^_^,呵呵這個比喻雖然粗俗,但是你可以想象一下,對一個軟件進行仔細的分析,最后一下把它的算法給搞明白了,那種感覺...我深信不疑的認為有一天你 也能體會的到,偶等你
相信你以前看過那些高人大蝦的關于軟件注冊算法分析的文章,同時也相信你有過試圖跟蹤分析某軟件的舉動,雖然后來的結果另人不太滿意
其 實分析一個軟件的注冊算法,這其中包括了一些技巧性方面的東西以及必要的經驗,很難想象一個連調試器的使用都還沒掌握的人試圖去分析一個軟件會是怎樣一個 場面...嘿嘿,偶是見過的 使用調試器并不難,但那并不意味著你就能去分析一個軟件了,見CALL就追這樣的舉動可不是偶一個人有過的經歷,本章我盡量給你說明適當的分析方法。
相信大家都有不在父母陪同下獨自使用調試器的能力以及看懂大部分匯編指令的能力了吧,那就夠了!我們開始...
正式開始今天的正題,我來舉兩個例子,相信這兩個例子都有足夠的表達能力,最起碼比我們家樓下那個賣油條的表達能力要強多了...
好的,首先,我們還是請出我們的那位老朋友吧 嘿嘿,在此,偶向CHINAZIP(中華壓縮)v7.0的作者表示我內心最真誠的歉意!相信我用這個老版本的中華壓縮不會給您帶來經濟上的麻煩...
通過前邊兒兩章的講解,我們已經把這個軟件大體上給搞明白了,并且也追出了其相應的注冊碼。而我們今天的目的是對其注冊算法進行分析,并寫出注冊機!這個軟件的注冊算法其實也比較簡(并且存在Bug)用它來當例子,很能說明情況...
好 的,我們開始,前邊兒追注冊碼的時候我們就已經知道了其用于計算正確注冊碼的關鍵CALL的所在位置為004f4dde,我們用TRW2000來對其進行 分析!(鑒于目前大部分教程中仍以TRW2000為主,而且這個是大多數菜鳥都會用的調試器,偶就用這個調試器來做具體講解)
先啟動CHINAZIP,幫助--注冊(所以我才說這個軟件非常適合寫教程用嘛,注冊后仍然中以再次注冊)輸入注冊名Suunb[CCG],注冊碼19870219。之看按Ctrl+N呼出TRW2000,下斷點bpx 004f4dde,F5返回。
接 著就按確定吧,呵呵,被TRW2000攔到了。通過前邊兩章的分析,我們以經知道了004f4dde處的這個CALL用于計算正確的注冊碼,所以我們直接 按F8跟進吧!注冊碼的算法,就包涵在這個CALL中,把它給分析透了,我們也就能弄明白軟件的注冊碼是怎樣生成的了。但是要怎么分析呢?這是一個比較嚴 肅的問題,面對那一堆堆的指令,我不知道你是怎么想的,反正我第一次時是覺的找不著北,我怎么哪些重要哪些不重要呢?再說了,里面又包涵了那么多 CALL,我還要一個一個地追進去看看?
呵呵,這就是我說的技巧所在了。其實也沒什么可怕的,只要你匯編不是問題,就行了。我們首先可以先把這 個計算注冊碼的CALL從頭到尾執行一遍,搞明白其中大概的跳轉以及其中某些CALL的作用,hehe~~你可以執行過一個CALL后就看一下各個寄存器 的變化情況(如果寄存器中的值改變了,顏色就會變)如果某寄存器的值在CALL過之后改變了,我們就可以看一下其包含的值是何類型,如是內存地址就用d指 令看一下,如是數值就看一下是不是得到你輸入注冊名或注冊碼的位數等等,這樣的話就可以淘汰下來一大部分的CALL,因為有許多CALL的作用只是把注冊 名或注冊碼裝入到內存中的某個地址或者得到注冊名(注冊碼)的位數或注冊碼某一位的ASCII碼,對與這些,我們不必深究。還是推薦你用Ollydbg, 執行過一條指令后很多信息都可以看到 好的,我接著說,按F8追入CALL之后先大概走一遍...我給出追入后的反匯編代碼,并給出注釋,相應的分析看后面...
0167:004f4fac push ebp <--F8跟入后的第一條指令
0167:004f4fad mov ebp,esp
0167:004f4faf push byte +00
0167:004f4fb1 push byte +00
0167:004f4fb3 push byte +00
0167:004f4fb5 push byte +00
0167:004f4fb7 push byte +00
0167:004f4fb9 push byte +00
0167:004f4fbb push byte +00
0167:004f4fbd push ebx
0167:004f4fbe push esi
0167:004f4fbf push edi
0167:004f4fc0 mov [ebp-08],ecx
0167:004f4fc3 mov [ebp-04],edx
0167:004f4fc6 mov eax,[ebp-04]
0167:004f4fc9 call 004041c0
0167:004f4fce xor eax,eax
0167:004f4fd0 push ebp
0167:004f4fd1 push dword 004f5097
0167:004f4fd6 push dword [fs:eax]
0167:004f4fd9 mov [fs:eax],esp
0167:004f4fdc xor esi,esi
0167:004f4fde lea eax,[ebp-0c]
0167:004f4fe1 mov edx,[ebp-04]
0167:004f4fe4 call 00403e24 <--此CALL過后用于得到用戶輸入的注冊名
0167:004f4fe9 mov eax,[ebp-0c] <--將得到的注冊名的地址裝用eax寄存器
0167:004f4fec call 0040400c <--此CALL用于得到用戶輸入的注冊名的位數,并將其放入eax中
0167:004f4ff1 mov edi,eax <--將注冊名的位數裝入edi中
0167:004f4ff3 test edi,edi <--對edi進行測試
0167:004f4ff5 jng 004f5051 <--如果edi中的值為0就會跳走
0167:004f4ff7 mov ebx,01 <--ebx置1,用于后面的運算
0167:004f4ffc mov eax,[ebp-0c] <--ebp-0c中裝的是注冊名的內存地址,此時將其付于eax
0167:004f4fff mov al,[eax+ebx-01] <--eax中此時裝的是注冊名的內存地址,加上ebx中的值再減去01,用于得到注冊碼中的相應位的字符,比如說我們第一次執行到這里的時候 ebx中裝入的是01,再減去01后得到的值其實還是eax本身,這樣就能得到注冊名中的第一個字符了,而執行到后邊再跳回來時ebx會加上1,所以就能 得到下一個字符了...
0167:004f5003 call 004f4f60 <--這個CALL很重要,后面會說明我們是怎樣知道它很重要的
0167:004f5008 test al,al <--在這里我們會發現一個測試運算,對象是al,而al在前邊CALL之前剛裝入了注冊名中的某一個字符,所以我們可以斷定上面的那個CALL會對得到的字符做上一些手腳,待會兒我們再跟入...
0167:004f500a jz 004f5031 <--如果al中裝的是0就跳到004f5031處,而al中的值會被004f5003處的那個CALL所改變
0167:004f500c lea eax,[ebp-18]
0167:004f500f mov edx,[ebp-0c] <--ebp-0c中裝的是注冊名的內存地址,此時裝入edx中
0167:004f5012 mov dl,[edx+ebx-01] <--跟前邊兒004f4fff處的指令道理相同,得到注冊碼中的當前參加運算的字符
0167:004f5016 call 00403f34 <--不重要!!
0167:004f501b mov eax,[ebp-18]
0167:004f501e lea edx,[ebp-14]
0167:004f5021 call 004088ac <--不重要!!
0167:004f5026 mov edx,[ebp-14]
0167:004f5029 lea eax,[ebp-10]
0167:004f502c call 00404014 <--該CALL同樣比較重要,其作用是這樣的,如果當前參加運算的字符在前邊004f5003的CALL里進行運算之后符合了要求(符合要求后 al會被置非0值)那么在004f500a處的跳轉將會失去作用,而執行到這里后該CALL會將當前的這個符合要求的字符保存到00D3B3C4處(內 存)!!后邊兒會再詳細說明
0167:004f5031 cmp ebx,byte +01 <--用此時ebx中裝的值減去1
0167:004f5034 jz 004f5040 <--如果為零,也就是說此時計算的是注冊名中的第一個字符的話就跳到004f5040處
0167:004f5036 mov eax,[ebp-0c] <--ebp-0c中裝的是注冊名的內存地址,該指令將注冊名的內存地址裝入eax中
0167:004f5039 movzx eax,byte [eax+ebx-02] <--用于得到上一個參加運算的字符
0167:004f503e jmp short 004f5046 <--無條件跳轉到004f5046處
0167:004f5040 mov eax,[ebp-0c] <--ebp-0c中裝的是注冊名的內存地址
0167:004f5043 movzx eax,byte [eax] <--得到注冊名的第一個字符
0167:004f5046 lea esi,[esi+eax*4+a8] <--!!!這一條指令就是關鍵所在,后面會說明的!!!此指令先得到本輪參加運算的字符的ASCII碼,然后乘以6,之后再加上a8(即十進制數 168,呵呵,可以理解)同時再將這個字符計算得到的值與前面已經運算過的字符的值的和相加!
0167:004f504d inc ebx <--ebx加1,用于得到注冊碼的下一個字符
0167:004f504e dec edi <--edi減1,edi中裝的是注冊碼的位數
0167:004f504f jnz 004f4ffc <--不為零就跳到004f4ffc處開始對下一個字符進行運算...也就是說每計算完一個字符就將edi減去1,直到其為0也就是所有的字符全參加過運算為止。
0167:004f5051 lea edx,[ebp-1c] <--把裝注冊碼后半部分的地址裝入edx,傳給下面的CALL
0167:004f5054 mov eax,esi <--將前面計算的注冊碼的后半部分的值裝入eax中
0167:004f5056 call 00408c70 <--將前面計算得到的注冊碼后半部分的值轉換為十進制,并裝入ebp-1c中
0167:004f505b mov ecx,[ebp-1c] <--epb-1c中裝的是注冊碼的后半部分
0167:004f505e lea eax,[ebp-0c]
0167:004f5061 mov edx,[ebp-10] <--ebp-10中裝的是注冊碼的前半部分
0167:004f5064 call 00404058 <--該CALL用于將前后兩部分注冊碼合并置一起,合并后的注冊碼會存放置ebp-0c處
0167:004f5069 mov eax,[ebp-08]
0167:004f506c mov edx,[ebp-0c]
0167:004f506f call 00403de0
0167:004f5074 xor eax,eax
0167:004f5076 pop edx
0167:004f5077 pop ecx
0167:004f5078 pop ecx
0167:004f5079 mov [fs:eax],edx
0167:004f507c push dword 004f509e
0167:004f5081 lea eax,[ebp-1c]
0167:004f5084 mov edx,05
0167:004f5089 call 00403db0
0167:004f508e lea eax,[ebp-04]
0167:004f5091 call 00403d8c
呵 呵,看了我加了注釋后的代碼是不是好理解多了?你也許會問,你怎么知道那些CALL是做什么的?我前邊兒不是說過方法了嗎?我們先大概地過上一遍,看一下 各個跳轉,然后再大大概的看一下各個CALL的作用...你以為上面這些注釋是我過一遍之后就能寫出來的?你多過幾遍,心中就會有了個大概...
你現在在想些什么?眾人:站著說話不腰痛...我暈~~
呵呵,我盡量說的仔細一些:
其 實很好理解的,我們追了進來,之后大概的看一下那些個CALL,其中一些稍有經驗的一看就知道是用來得到注冊名或長度什么的...之后我們再從頭跟一 遍...跟到004f4ff3處,會發現其會對注冊名的位數進行一個比較,看用戶是否輸入了注冊名...(也就是說如果edi中裝的注冊名的位數不大于 0,即沒輸入就跳走,一會兒我會在后面說一下關于這點兒的Bug)而后在004f4ffc處我們會發現軟件會得到注冊名的內存地址,接下來的一條指令一看 就知道是用來得到注冊名中的各個字符的,嘿嘿,見到這類指令,馬上就向下看吧,找一下下邊兒哪條指令會再跳回到004f4ffc處...呵呵,我們會在 004f504f處發現目標,好了,現在我們就知道了從004f4ffc到004f504f之間的那些個指令會對注冊名中的每一個字符進行計算...
呵 呵,也就是說軟件從004f4ffc處開始先是得到注冊名中的第N位字符,然后進行一系列的運算,之后執行到了004f504e處時把先前先到的注冊名的 位數減去1然后看其是否為0,不為0就再跳到004f4ffc處,然后得以注冊名的N+1位再來進行計算。此舉的目的就是為了看注冊名的各位是否都被計算 過了,如果不為0就說明還沒有計算完,呵呵,很簡單的道理嘛,edi中裝的是注冊名的位數,第計算過一位后就將其減1,減完了,注冊名的各位也就都參加了 運算...
好的,我們再來看具體的算法部分:
在004f4ff5的跳轉,如果你輸入了注冊名,其就不會跳走...偶輸入的是 Suunb[CCG],好的,此時會繼續執行到004f4ff7處,該指令對ebx進行初始化...給它付1,然后在004f4ffc處時會將ebp- 0c中裝的注冊名的內存地址裝入eax中,接著的004f4fff處用于得到注冊名的第一個字符,并將其裝入al。想象一下,eax中裝的是注冊名的內存 地址,從該地址開始連續10個內存單元是我們輸入的注冊名S u u n b [ C C G ] 呵呵,明白了嗎?eax中裝的內存地址就是注冊名在內存中的首地址,第一次執行到這里時ebx中裝的是1,eax+ebx-01后得到的還是注冊名的首地 址,也就是S。而等到后面004f504f處的跳轉指令跳轉回來之前,會在004f504d處有一條inc指令會給ebx加1,這樣的話再執行到這里時就 會得到注冊名中的第2個字符u了,嘿嘿,第三次來之前會再給ebx加上1,明白了嗎?總知你可以把ebx中的值理解為當前參加運算的字符在注冊名中的位 數,即ebx是1就是得到注冊名的第一位(S),如果ebx是2就是得到注冊名的第2位(u).
而后緊接著在004f5003處會有一個 CALL等著我們,呵呵,這個CALL比較關鍵,注冊碼的一部份由它來決定,要發現它的重要性并不難,因為在004f5003處下面會有一個跳轉,跳轉之 前會對al進行測試,嘿嘿,而al在CALL之前裝入的是當前參與運算的字符...并且你用調試器過一下這個CALL就會發現其對al進行了修改,呵呵, 這個CALL會對al做一些處理,而處理的結果直接影響了后面部分的流程,所以,對于它,我們一定要跟進...最好能派出兩個人在邊路對其進行防守,并找 專門的后位對其盯梢...
我們待會兒再跟進它,現在還是要先搞明白軟件大體上的算法。好的,我接著說,在004f5008處對al進行了測試之 后會有一個跳轉,即如果al中此時裝的值為0就跳到004f5031處去...你可以理解為這個CALL會對字符進行一些運算,如果符合了要求,al就會 被置0或1什么的,出來后的測試用來判斷當前字符是否符合要求,如果符合就跳或不符合就跳...
繼續,由于我輸入的注冊名的第一個字符是S,而 S剛好能通過004f5003處的那個CALL的計算 所以就沒有跳走,我繼續按F10進行單步執行...接下來的004f500c、004f500f、004f5012這三條指令跟前邊兒的得到注冊碼第N位 字符的指令道理是一樣的,你看注釋好了...而后面從004f5016到004f5029處的這幾條指令也沒什么好講的,對中間的兩個CALL好奇的話可 以進去大概看一下。得不到什么實質性的東西...而004f502c處的這個CALL嘛,就很重要了,呵呵,它的作用是什么呢?還記的我剛才說過的 004f5003處的那個CALL吧,它執行過后會使al發生變化,它下面的跳轉指令會根據al的值做相應跳轉,即如果al為0,就跳到004f5031 處,剛好就跳過了004f502c處的這個CALL...而我輸入的第一個字符是S,剛好符合了004f5003處那個CALL的要求,所以沒有跳走,于 是就執行到了這里,你可以追進去看一下,里面并不復雜,只是將當前參加運算的字符裝入內存的00D3B3C4處(如果當前參加運算的字符在 004f5003處沒有通過,就不會執行到這里,呵呵,明白過來了吧,這個CALL用于收集注冊名中所有符合004f5003處那個CALL要求的字符)
HOHOHO~~(請模仿周星星式的笑聲...)現在我們已經明白了一半了...好的,我們繼續...
不管你是從004f500a處 跳到004f5031處的,還是一步步執行到這里的,總知,不管你輸入的注冊名中參加當前運算的那一個字符符不符合004f5003處的那個CALL的要 求,總知都會執行到這里...這條指令用來干什么呢?還記的ebx中裝的是參加運算的字符在注冊名中的相應的位數嗎?cmp ebx,byte +01 就是用ebx減去1,該條指令的用途也就是看一下當前參加運算的字符是不是注冊名中的第一個字符,如果是就跳到 004f5040處,否則繼續... 我們先看004f5040處,當執行到此處時,ebp-0c中裝的其實是注冊名的內存地址(前邊就已經說過了)在這里將其裝入eax中,而后面 004f5043處的指令的用途就是得到注冊名的第一個字符...好了,我們再拐回來看004f5036處,如果當前參加運算的字符不是注冊名中的第一個 字符,就不會跳走,而執行到這里時同樣將ebp-0c中裝的注冊名的內存地址放入eax中,而004f5039處的eax,byte [eax+ebx-02]嘛,呵呵,很好理解,eax+ebx-01得到的是當前參加運算的字符的內存地址,而這里的eax+ebx-02得到的就是當前 參加運算的字符的前面的那個字符,了解?
我們接著看004f5046處的那條指令吧,這個同樣非常重要,它的作用是計算注冊碼的后半部分!
我 相信你很容易就能理解它的意思了,當執行到這里時,eax中裝的或者是注冊碼中的第一個字符,或者是當前參加運算的字符的前一個字符(注:字符在內存或寄 存器中是以ASCII碼來表示的,如S在eax中會顯示為00000053,而S的ASCII碼便是53,十進制為83)...我們第一次執行到這里 時,esi中的值為0(即00000000)eax*4+a8的意思就是用當前參加運算的字符的ASCII碼乘以4,再用積加上a8(也就是十進制數 168,一路發?)再用這個和與esi相加,我已經說過了,第一次執行到這里時esi中的值為0...而當第二次執行到這里時,esi中裝的便是注冊名的 第一個字符的ASCII碼乘以4再加一路發的和...
你會問你為什么知道它是計算注冊碼的后半部分的?猜的!!呵呵,當然不是,我們可以看到, 在004f5054處,程序會將前面計算的結果裝用eax中,后邊兒緊接著就是一個CALL,嘿嘿,光天化日之下,這也太明顯了吧,我們追進去大概看一下 就知道它的作用是將十六進制的數轉換為十進制的...并將轉換后的結果裝入edx中裝的內存地址處,在CALL之前我們會看到edx中的值以由 004f5051處裝入,即ebp-1c,呵呵,CALL過之后你用d ebp-1c看一下,就會看到你注冊碼的后半部分了...
而后程序會在 004f505b將注冊碼后半部分裝入ecx中,在004f505e處時會將一個內存地址ebp-0c裝入eax處(它的作用就是起一個傳遞參數的作用, 在待會兒的CALL中會用eax中裝入的值來存放結果)之后的004f5061處會將ebp-10裝入edx中,ebp-10處裝的是什么呢?我們用d ebp-10指令看一下就會知道它的地址為00D3B3C4,嘿嘿,你的嗅覺敏感嗎?不敏感的話我就再說一遍,還記的004f502c處的那個CALL 嗎?它的作用就是收集符合004f5003處的那個CALL的要求的字符...
嘿嘿,你明白過來了嗎?
這個軟件的注冊算法是這樣的: 首先得到注冊碼的位數,看其是否大于0,不大于0就跳到004f5051處...好的,我們輸入了Suunb[CCG]這個注冊名,此時的注冊碼位數就是 10,所以不會跳走,之后我們會來到004f4fff處,第一次執行到這里時會將注冊名的第一個字符S裝入al中,第二次來時會將注冊名中的第二個字符 (即u)裝入al中,它的作用就是將當前參加運算的字符裝入al中,之后緊接著就是一個CALL,這個CALL會對當前參加運算的字符進行計算...接著 出來會有一個跳轉,看al中裝的是不是0,如果是就跳到004f5031處,如果不是非0值就說明當前這個字符符合了要求,那么就會執行到 004f502c處,這里的CALL會將其存放置內存的00D3B3C4處...而后到了004f5031處會有一個比較,作用是看當前參加運算的字符是 不是注冊名中的第一個字符,是的話就跳到004f5040處,在此將注冊名的第一個字符裝入eax,用來參加004f5046處的計算。如果當前參加運算 的不是注冊名的第一個字符,那么就會在執行到004f5039處時得到當前參加運算的字符前面的那個字符,將其裝入eax后就無條件跳到004f5046 處來參加運算。了解?也就是說你輸入的注冊名的第一個字符會參加兩次計算,而最后一個字符不會參加計算(想想看,如果當前參加運算的字符是注冊名中的第一 個字符,它會參加計算,如果是第二個,就取前邊的一個,即第一個又會參加一次計算,到了第三個的時候取第二個,到了第四個的時候取第三個...而當最后一 個字符來到這里時會取前邊的那個字符來參加運算,而這之后就循環就結束了,所以,最后一個不會被計算入內)等到注冊名中的所有字符都參加過了運算,就會來 到004f5056處,在這里將前面004f5046處的計算結果轉換為十進制...而后會在后面的004f5064處的那個CALL里,將其與先前裝入 00D3B3C4處的所有符合004f5003處的CALL要求的字符合并到一起,這個結果,嘿嘿,就是真正的注冊碼了!
也就是說,真正的注冊碼是由以下部分組成的:
你輸入的注冊名中的所有符合004f5003處的那個CALL要求的字符+((注冊名中的第一個字符的ASCII碼*4+168)*2+(除第一位和最后一位外的所有字符)*4+168的和的和)
現在我們也知道注冊碼是怎樣煉成了的 那么我們要寫注冊機,就一定要把004f5003處的那個CALL給搞明白,這樣的話,我們才能對注冊名中的字符進行篩選...
好的,我們再來:
Ctrl+N呼出TRW2000,下bpx 004f5003,F5退出點確定被攔后便按F8跟進,呵呵,這個就沒什么好說的了,看我給出的注釋吧:
0167:004f4f60 push ebp
0167:004f4f61 mov ebp,esp
0167:004f4f63 push ecx
0167:004f4f64 push ebx
0167:004f4f65 push esi
0167:004f4f66 mov [ebp-01],al <--將字符裝入內存ebp-01處
0167:004f4f69 mov byte [ebp-03],02 <--ebp-03處裝入02
0167:004f4f6d mov byte [ebp-02],01 <--ebp-02處裝入01
0167:004f4f71 mov cl,[ebp-01] <--將參加運算的字符裝入cl
0167:004f4f74 dec ecx <--cl減1
0167:004f4f75 sub cl,02 <--cl再減去2
0167:004f4f78 jc 004f4fa4 <--有進位就跳轉,你不用擔心,一般都不會跳走的啦
0167:004f4f7a inc ecx <--ecx加1,也就是cl加1
0167:004f4f7b mov bl,02 <--bl裝入02
0167:004f4f7d xor eax,eax <--eax做異或運算,即將eax置0
0167:004f4f7f mov al,[ebp-01] <--al裝入參加運算的字符
0167:004f4f82 xor edx,edx <--edx置0
0167:004f4f84 mov dl,bl <--將bl中的值付給dl
0167:004f4f86 mov esi,edx <--再付給esi
0167:004f4f88 xor edx,edx <--edx置0
0167:004f4f8a div esi <--用eax中裝的參加運算的字符的ASCII碼除以當前esi的值
0167:004f4f8c test edx,edx <--測試edx,edx中裝的是上剛才除法計算后的余數
0167:004f4f8e jnz 004f4f93 <--不為0就跳到004f4f93處
0167:004f4f90 inc byte [ebp-03] <--ebp-03處的值加1
0167:004f4f93 cmp byte [ebp-03],02 <--用ebp-03處的值減去02
0167:004f4f97 jna 004f4f9f <--不大于就跳到004f4f9f處
0167:004f4f99 mov byte [ebp-02],00 <--ebp-02裝入00
0167:004f4f9d jmp short 004f4fa4 <--無條件跳轉到004f4fa4處
0167:004f4f9f inc ebx <--ebx加1
0167:004f4fa0 dec cl <--cl減1
0167:004f4fa2 jnz 004f4f7d <--不為0就跳到004f4f7d處再來一遍
0167:004f4fa4 mov al,[ebp-02] <--將ebp-02處的值裝入al后返回
0167:004f4fa7 pop esi
0167:004f4fa8 pop ebx
0167:004f4fa9 pop ecx
0167:004f4faa pop ebp
不知道你看不看的明白,我大概給你說明一下,大體上就是這樣的:
先 得到這個字符的ASCII碼,然后用其減去2,并將bl置值02...然后用eax中裝的減了2的ASCII碼除以esi中裝的bl中的值,之后就看 edx中裝的余數是否為0,如果為0就將ebp-03處的值加1,你應該知道ebp-03處在初始化的時候被付值為2,如果其被加了1,那就大于了2,這 樣的話后面在004f4f97處就不會跳走,如果不跳走,在004f4f99處,ebp-02就會被裝入00,之后就會無條件跳轉到004f4fa4處, 在那里就會把ebp-02的值,也就是00裝入al,明白過來了吧...但如果edx中裝的余數不為0,那么在004f4f8e處就會跳走,到了 004f4f93處時由于ebp-03中裝的是02,所以條件就會成立,從而可以跳到004f4f9f處去繼續執行程序...而到了004f4f9f處后 ebx(也就是bl)會被加上1,而cl在后面會被減去1,如果cl不為0的話就再跳到004f4f7d處,再來一遍,直到cl變為零為止...
上面這個過程在Delphi中可以這樣表示:
(變量S中裝的是當前參加運算的字符,Code變量用來收集符合要求的字符,變量的聲明我沒有寫明)
N:=Ord(S);
for i:=2 to N-1 do
begin
modz:=(N-i) mod i;
if modz=0 then Break;
end;
if modz<>0 then
Code:=Code+S;
呵呵,這就起到了與前面匯編代碼相同的作用了
下面我給你寫一個函數,該函數可以用來得到注冊碼中的所有符合要求的字符:
function GetKeyChar(Name: String): String;
var
i,ASC,sh,modz:integer;
Code:String;
begin
for i:=1 to Length(Name) do
begin
ASC:=Ord(Name);
for sh:=2 to ASC-1 do
begin
modz:=(ASC-sh) mod sh;
if modz=0 then Break;
end;
if modz<>0 then
Code:=Code+Name;
end;
Result:=Code;
end;
你可以這樣來用:
var
S1,S2:String;
begin
S1:=Edit1.text; //Edit1用來輸入注冊名;
S2:=GetKeyChar(S1); //此時S2中得到的便是注冊名中所有符合要求的字符了;
end;
嘿 嘿,你現在是不是很想知道都有哪些字符能符合要求?嘿嘿,其實CHINAZIP自己就可以告訴我們,你只要在輸入注冊名的時候把所有的可用字符全填上,然 后在用d指令看一下注冊碼的前半部(即非數字部分),就可以知道了,當然你也可以自己寫一個程序來試試,用Delphi的話可以直接用我上面給的函 數...告訴你好了,在所有的ASCII字符中,只有以下幾個符合要求:
CGIOSYaegkmq5=%)+;/
不信的話你可以試試,正確的注冊碼中的字符只能是這幾個...嘿嘿,看來還是比較尊重我們CCG的嘛
好了,我又廢話了這么多,把完整的注冊機給你貼出來吧,這并不難,只要加上后半部分注冊碼的計算就OK了:
你可以在Delphi中聲明以下函數,便可直接使用:
function GetKey(Name: String): String;
var
N:String;
i,sh,ci:integer;
Si:integer;
ASC,modz:integer;
begin
i:=Length(Name);
if i=0 then Result:='請輸入注冊名...'
else
begin
for sh:=1 to i do
begin
ASC:=Ord(Name[sh]);
for ci:=2 to ASC-1 do
begin
modz:=(ASC-ci) mod ci;
if modz=0 then Break;
end;
if modz<>0 then N:=N+Name[sh]
end;
Si:=Ord(Name[1])*4+168;
for sh:=1 to i-1 do
begin
Si:=Si+Ord(Name[sh])*4+168;
end;
N:=UpperCase(N+inttostr(Si));
Result:=N;
end;
end;
最 后順便說一下,如果你稍微留意一下,就會發現,在剛開始的004f4ff3處會對注冊名的位數進行測試,即如果位數為0就會在下一條指令時跳至 004f5051處,如果你稍微有一點兒經驗,就會發覺,通常如果程序發現注冊名的位數為0,就會直接跳到失敗處,而這個軟件卻沒有!004f5051處 是什么呢?暈~~具然是要得到注冊碼的后半部分,呵呵,我們以到00D3B3C4處看看,會看到一堆0,你現在在想什么呢?既然我們沒有輸入注冊名,那么 就不可能有注冊碼的前半部分,而后邊便會有CALL來合并前后兩部分的注冊碼 你重新啟動一下軟件,注冊名不填,把注冊碼填為0注冊一下看看...HOHO~~(請仍舊模仿周星星式的笑聲)這個粗心的作者啊,造成這樣的原因很簡單, 軟件根本就沒有判斷注名是否為空并在軟件初始化的時候把用于計算注冊碼后半部分的integer變量付了初始值0,否則的話00D3B3C4處的內存應該 為空值..(難不成到時連0都不用輸就能注冊?)
所以說,Crack并不是一件壞事,像這種情況你完全可以告訴作者的嘛,到時不但交了一個朋友 而且說不準還會得到個免費的注冊碼....(不知道有沒有白帽子Cracker?嘿嘿,CCC剛好也可以是注冊碼的前半部分哦~~)我希望你明白,對于這 種注冊算法簡單且存在Bug的軟件(通常也說明其作者還沒什么經驗 ^_^),我們不應該為能提供它注冊機而感到高興,如果能幫助其作者改善算法或去掉Bug,又何嘗不是一件好事呢?畢竟軟件上面加的有中華兩個字,你忍 心???
我不知道上面給你講的中華壓縮注冊分析你是否看懂了,我個人認為我講的還是比較詳細的了(幾乎每條指令都加了注釋且又再三在后面說明)但如果你仍然看不懂的話,請務必相信是本人寫的文章不好,不要放棄啊哥們兒~~!
好了,我再來給你舉另外一個例子...通過它來給你講一下另外一種比較常見的注冊碼計算方法,即將運算的結果與一個表中的字符進行轉換,也就是常說的密碼表啦^_^
本 來是想用網際快車FlashGet的,可是在看雪已經有人貼了最新的1.40版的破文&注冊機,正好前些天的時候網友啥也不是在后面跟貼說要幫他 看一下語音界面2.0這個軟件,down下來后大概看了一下,呵呵,發現這個正是我想要的,注冊碼計算的過程中采用了密碼表并且也不難...hehe~~ 后來HMILY老哥看到了啥也不是的另一個貼子,也寫個注冊機和破文,你可以參考一下,嘿嘿,HMILY跟偶是自己人,所以偶不怕他...
我們開始吧...
首先運算一下這個軟件,其會自動生成機器碼,在我這邊兒是xn2urkeUMwpNv5xZ。
這一章,我會用調試器Ollydbg來作講解,它真的很好用...我們開始吧:
偶 不知道你是否喜歡Ollydbg的下斷方式,總知偶是不喜歡,從那么多API里面先(字好小),再說了,偶還是喜歡用Hmemcpy來斷,除非斷不到或在 2K/XP下,否則偶才不要去跑API呢,往往要三四次才斷到,多累啊 我們還是先請臨時演員TRW2000出出一下場吧(把你的MP3先暫停一下 ),下bpx hmemcpy點確定后會被攔,pmodule后返回到0040432f處,我們就從這里開始吧 導演:"Stop!換吳孟達出場" 嘿嘿,繼續你的MP3,請同時打開Ollydbg
我們用Ollydbg載入,在反匯編代碼處(即左上方那個子窗口中)按Ctrl+G,輸入0040432f,回車后便來到了這里。我們大概看看,有經驗的話很容易就會看出此地便是了(這次運氣比較好,一下就能找到軟件計算注冊碼的地方 ^_^)
好吧,我們按F2在這里下斷,接著按F9來運行程序,在注冊處輸入CHINA Cracking Group Suunb后按確定會被Ollydbg斷下來,大概跑跑看看吧 我貼出反匯編代碼:
0040432F |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404333 |. 6A 05 PUSH 5
00404335 |. 51 PUSH ECX
00404336 |. 68 E8030000 PUSH 3E8
0040433B |. 8BCE MOV ECX,ESI
0040433D |. E8 10050000 CALL <JMP.&MFC42.#3092>
00404342 |. 8BC8 MOV ECX,EAX
00404344 |. E8 7D060000 CALL <JMP.&MFC42.#3873>
00404349 |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040434D |. 6A 05 PUSH 5
0040434F |. 52 PUSH EDX
00404350 |. 68 E9030000 PUSH 3E9
00404355 |. 8BCE MOV ECX,ESI
00404357 |. E8 F6040000 CALL <JMP.&MFC42.#3092>
0040435C |. 8BC8 MOV ECX,EAX
0040435E |. E8 63060000 CALL <JMP.&MFC42.#3873>
00404363 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
00404367 |. 6A 05 PUSH 5
00404369 |. 50 PUSH EAX
0040436A |. 68 EA030000 PUSH 3EA
0040436F |. 8BCE MOV ECX,ESI
00404371 |. E8 DC040000 CALL <JMP.&MFC42.#3092>
00404376 |. 8BC8 MOV ECX,EAX
00404378 |. E8 49060000 CALL <JMP.&MFC42.#3873>
0040437D |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
00404381 |. 6A 05 PUSH 5
00404383 |. 51 PUSH ECX
00404384 |. 68 EB030000 PUSH 3EB
00404389 |. 8BCE MOV ECX,ESI
0040438B |. E8 C2040000 CALL <JMP.&MFC42.#3092>
00404390 |. 8BC8 MOV ECX,EAX
00404392 |. E8 2F060000 CALL <JMP.&MFC42.#3873>
00404397 |. 8B7C24 68 MOV EDI,DWORD PTR SS:[ESP+68]
0040439B |. 33DB XOR EBX,EBX
0040439D |. 33C9 XOR ECX,ECX
0040439F |. 8D04BF LEA EAX,DWORD PTR DS:[EDI+EDI*4]
004043A2 |. 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4]
004043A5 |. 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] <--上面的這些你都不需要管,因為它們幫不了我們什么忙
004043A8 |. C1E6 02 SHL ESI,2 <--執行后ESI的值為5DC,及十進制數1500
004043AB |> 0FBE440C 50 /MOVSX EAX,BYTE PTR SS:[ESP+ECX+50] <--ESP+ECX+50就是機器碼在內存中的地址(首次執行到這里明ECX為0,得到的就是機器碼的第一位,第二次到這里時ECX會加上1,得 到的是第二位...)
004043B0 |. 03C6 |ADD EAX,ESI <--與ESI相加,也就是加上1500
004043B2 |. BD 3E000000 |MOV EBP,3E <--EBP置3E,及十進制數62
004043B7 |. 99 |CDQ <--擴展...
004043B8 |. F7FD |IDIV EBP <--EAX中裝的機器碼的第1 or 2 or 3 or 4位與1500的和與62相除
004043BA |. 0FBE440C 54 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+54] <--ESP+ECX+54得到機器碼的第5位(還記的ESP+ECX+50中裝的是第一位嗎?首次執行到這里明ECX為0,得到的就是機器碼的第 5位,第二次到這里時ECX會加上1,得到的是第6位...)
004043BF |. 03C6 |ADD EAX,ESI <--同樣加上1500
004043C1 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--重要的地方來了!此時EDX中裝的是前面的第1 or 2 or 3 or 4位機器碼加上1500后再除以62的余數,那4070E4處是什么呢?如果在TRW2000中,我們用d 004070E4就可以看到,在Ollydbg中,我們可以在左下角處按Ctrl+G來輸入相應的內存地址,這樣的話就可以看到了。我們會發現從 4070E4開始,裝的是一串字符,依次是 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ。呵呵明白什么意思了 嗎?4070E4處指的是0,那4070E4加上edx中裝的余數后的內存地址中裝的便是當前機器碼所對應的注冊碼。(如余數為5,那么從4070E4處 的0開始往后數第5個字符就是了)該條指令執行過后就會把相應的注冊碼裝入dl中
004043C7 |. 88540C 30 |MOV BYTE PTR SS:[ESP+ECX+30],DL <--將機器碼的第1 or 2 or 3 or 4所對應的注冊碼裝入ESP+ECX+30處
004043CB |. 99 |CDQ <--前邊兒得到的第5 or 6 or 7 or 8位機器碼擴展
004043CC |. F7FD |IDIV EBP <--同樣除以62
004043CE |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--與004043C1處作用相同,不用我多說了吧,就是得到當前機器碼的對應注冊碼
004043D4 |. 88440C 38 |MOV BYTE PTR SS:[ESP+ECX+38],AL <--裝入ESP+ECX+38處
004043D8 |. 0FBE440C 58 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+58] <--得到機器碼的第9 or 10 or 11 or 12位
004043DD |. 03C6 |ADD EAX,ESI <--與1500相加
004043DF |. 99 |CDQ <--擴展....
004043E0 |. F7FD |IDIV EBP <--除以62
004043E2 |. 0FBE440C 5C |MOVSX EAX,BYTE PTR SS:[ESP+ECX+5C] <--得到機器碼的第13 or 14 or 15 or 16位
004043E7 |. 03C6 |ADD EAX,ESI <--與1500相加
004043E9 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--前邊的第9 or 10 or 11 or 12位所對應的注冊碼
004043EF |. 88540C 40 |MOV BYTE PTR SS:[ESP+ECX+40],DL <--裝入ESP+ECX+40處
004043F3 |. 99 |CDQ <--擴展
004043F4 |. F7FD |IDIV EBP <--除以62
004043F6 |. 41 |INC ECX <--ECX加1
004043F7 |. 83F9 04 |CMP ECX,4 <--看ECX是否為4(前邊兒是一次計算四位的嘛,第一次計算第1、5、9、13位,第二次是2、6、10、14...)
004043FA |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--得到前邊的第13 or 14 or 15 or 16位機器碼所對應的注冊碼
00404400 |. 88440C 47 |MOV BYTE PTR SS:[ESP+ECX+47],AL <--裝入ESP+ECX+47處
00404404 |.^7C A5 \JL SHORT LIAOCACH.004043AB <--ECX小于4就從頭再來一遍(直到16位機器碼都計算完為止)
00404406 |. 8B35 AC524000 MOV ESI,DWORD PTR DS:[<&MSVCRT.atoi>] ; MSVCRT.atoi
0040440C |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404410 |. 51 PUSH ECX ; /s
00404411 |. 885C24 38 MOV BYTE PTR SS:[ESP+38],BL ; |
00404415 |. 885C24 40 MOV BYTE PTR SS:[ESP+40],BL ; |
00404419 |. 885C24 48 MOV BYTE PTR SS:[ESP+48],BL ; |
0040441D |. 885C24 50 MOV BYTE PTR SS:[ESP+50],BL ; |
00404421 |. FFD6 CALL ESI ; \atoi
00404423 |. 83C4 04 ADD ESP,4
00404426 |. 83F8 01 CMP EAX,1
00404429 |. 75 3C JNZ SHORT LIAOCACH.00404467
0040442B |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040442F |. 52 PUSH EDX
00404430 |. FFD6 CALL ESI
00404432 |. 83C4 04 ADD ESP,4
00404435 |. 83F8 01 CMP EAX,1
00404438 |. 75 2D JNZ SHORT LIAOCACH.00404467
0040443A |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
0040443E |. 50 PUSH EAX
0040443F |. FFD6 CALL ESI
00404441 |. 83C4 04 ADD ESP,4
00404444 |. 83F8 01 CMP EAX,1
00404447 |. 75 1E JNZ SHORT LIAOCACH.00404467
00404449 |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
0040444D |. 51 PUSH ECX
0040444E |. FFD6 CALL ESI
00404450 |. 83C4 04 ADD ESP,4
00404453 |. 83F8 01 CMP EAX,1
00404456 |. 75 0F JNZ SHORT LIAOCACH.00404467
00404458 |. 5F POP EDI
00404459 |. 5E POP ESI
0040445A |. 5D POP EBP
0040445B |. B8 FEFFFFFF MOV EAX,-2
00404460 |. 5B POP EBX
00404461 |. 83C4 54 ADD ESP,54
00404464 |. C2 0400 RETN 4
00404467 |> 8D7424 30 LEA ESI,DWORD PTR SS:[ESP+30] <--正確的注冊碼的前4位的地址裝入ESI中,嘿嘿,執行到這里時我們就可以看到正確的注冊的前4位了,在TRW2000 or SoftICE中可以用d ESP+30來查看而在Ollydbg中什么都不用做就可以在左上方反匯編代碼區與左下內存區中間的那個小窗體中看見 在TRW2000中下過D指令后按Alt+上下鍵翻幾下,就可以看到所有的注冊碼了(它們并沒有分太開嘛0063F5E0-0063F5E3是前4 位,0063F5E8-0063F5EB是5-8位,0063F5F0-0063F5F3是9-12位,0063F5F8-0063F5FB是最后4位, 而你輸入的注冊碼的內存地址:0063F5C0-0063F5C3是前4位,0063F5C8-0063F5CB是5-8 位,0063F5D0-0063F5D3是9-12位,0063F5D8-0063F5DB是最后4位,機器碼存放的地址是 0063F6000-0063F60F)
0040446B |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10] <--你輸入的注冊碼的前4位的地址裝入EAX中
0040446F |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的注冊碼的第1位或第3位(EAX中的值到了后邊兒會加上2,這樣再執行到這里時得到的就是第3位了)
00404471 |. 8ACA |MOV CL,DL <--傳入cl中
00404473 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的注冊碼的第1位或第3位比較(ESI中的值會與EAX中的值一起改變)
00404475 |. 75 1C |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404477 |. 3ACB |CMP CL,BL <--CL與BL比較,BL中的值為00000000(也就是空啦),這條指令有什么用呢?其實很簡單了,每4位注冊碼的后面都會再跟一個空值,也 就是如你在內存中可以看到1234.那個.就是空值,明白過來了吧,等到前4位都被測試過了,cl中就會裝入.也就是00000000,到時就可以在后面 跳走了
00404479 |. 74 14 |JE SHORT LIAOCACH.0040448F <--如果CL中的值為零(即4位已經全部比較過了),就跳走
0040447B |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--EAX+1后得到的會是你輸入的注冊碼的第2位或者第4位(視EAX的值而定)
0040447E |. 8ACA |MOV CL,DL <--傳入CL
00404480 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的注冊碼的第2位或第4位比較(ESI的值會與EAX一起改變)
00404483 |. 75 0E |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404485 |. 83C0 02 |ADD EAX,2 <--EAX加上2,這樣的話待會兒再跳到0040446F處時,再得到的就是你輸入的注冊碼的第3位了
00404488 |. 83C6 02 |ADD ESI,2 <--同上,ESI加上2后再跳到0040446F處重新再來一遍時就會得到正確的注冊碼的第3位
0040448B |. 3ACB |CMP CL,BL <--再次比較CL是否為空
0040448D |.^75 E0 \JNZ SHORT LIAOCACH.0040446F <--不為空就再跳到0040446F處,來繼續比較前4位中的1、3兩位
0040448F |> 33C0 XOR EAX,EAX <--1-4位全部比較完后會跳到這里
00404491 |. EB 05 JMP SHORT LIAOCACH.00404498
00404493 |> 1BC0 SBB EAX,EAX
00404495 |. 83D8 FF SBB EAX,-1
00404498 |> 3BC3 CMP EAX,EBX
0040449A |. 0F85 AB000000 JNZ LIAOCACH.0040454B
004044A0 |. 8D7424 38 LEA ESI,DWORD PTR SS:[ESP+38] <--與上面的大體相同嘛,將正確注冊碼的5-8位的內存地址裝入ESI中
004044A4 |. 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+18] <--你輸入的注冊碼的5-8位的內存地址裝入EAX中
004044A8 |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的注冊碼的第5 or 7位(道理我相信你一定已經明白了)
004044AA |. 8ACA |MOV CL,DL <--再裝入CL中
004044AC |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的注冊碼的第5 or 7位比較
004044AE |. 75 1C |JNZ SHORT LIAOCACH.004044CC <--不相等就跳走
004044B0 |. 3ACB |CMP CL,BL <--與BL中的00000000比較,看5-8位是否已經全部比較完畢
004044B2 |. 74 14 |JE SHORT LIAOCACH.004044C8 <--是的話就跳走
004044B4 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你輸入的注冊碼的第6 or 8位
004044B7 |. 8ACA |MOV CL,DL <--裝入CL
004044B9 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的注冊碼的第6 or 8位比較
004044BC |. 75 0E |JNZ SHORT LIAOCACH.004044CC <--不正確就跳走
004044BE |. 83C0 02 |ADD EAX,2 <--EAX加2,這樣做的目的相信你已經知道了吧
004044C1 |. 83C6 02 |ADD ESI,2 <--ESI也加上2
004044C4 |. 3ACB |CMP CL,BL <--比較CL是否為空
004044C6 |.^75 E0 \JNZ SHORT LIAOCACH.004044A8 <--不是就跳回去再來一遍
004044C8 |> 33C0 XOR EAX,EAX <--5-8位全部比較完后全跳到這里
004044CA |. EB 05 JMP SHORT LIAOCACH.004044D1
004044CC |> 1BC0 SBB EAX,EAX
004044CE |. 83D8 FF SBB EAX,-1
004044D1 |> 3BC3 CMP EAX,EBX
004044D3 |. 75 76 JNZ SHORT LIAOCACH.0040454B
004044D5 |. 8D7424 40 LEA ESI,DWORD PTR SS:[ESP+40] <--將正確的注冊碼的9-12位的內存地址裝入ESI
004044D9 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20] <--你輸入的
004044DD |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你輸入的注冊碼的第9 or 11位
004044DF |. 8ACA |MOV CL,DL <--裝入CL
004044E1 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--與正確的注冊碼的第9 or 11位比較
004044E3 |. 75 1C |JNZ SHORT LIAOCACH.00404501 <--不對便跳走
004044E5 |. 3ACB |CMP CL,BL <--看CL是否為空,即看9-12位是否全部比較完畢
004044E7 |. 74 14 |JE SHORT LIAOCACH.004044FD <--是的話跳走
004044E9 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你輸入的注冊碼的第10 or 12位
004044EC |. 8ACA |MOV CL,DL <--裝入CL
004044EE |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--與正確的注冊碼的第10 or 12位比較
004044F1 |. 75 0E |JNZ SHORT LIAOCACH.00404501 <--不相等就跳走
004044F3 |. 83C0 02 |ADD EAX,2 <--EAX加2
004044F6 |. 83C6 02 |ADD ESI,2 <--ESI加2
004044F9 |. 3ACB |CMP CL,BL <--看是否全部比較完畢
004044FB |.^75 E0 \JNZ SHORT LIAOCACH.004044DD <--沒有就跳回去再來一遍
004044FD |> 33C0 XOR EAX,EAX <--9-12位全部比較完畢后會跳到這里
004044FF |. EB 05 JMP SHORT LIAOCACH.00404506
00404501 |> 1BC0 SBB EAX,EAX
00404503 |. 83D8 FF SBB EAX,-1
00404506 |> 3BC3 CMP EAX,EBX
00404508 |. 75 41 JNZ SHORT LIAOCACH.0040454B
0040450A |. 8D7424 48 LEA ESI,DWORD PTR SS:[ESP+48] <--嘿嘿,都講了三遍了,你一定明白了吧,所以我把這最后一段留給你自己來看吧,從這里到00404530處我相信你猜也猜的出來 (眾人:鄙視你!)
0040450E |. 8D4424 28 LEA EAX,DWORD PTR SS:[ESP+28]
00404512 |> 8A10 /MOV DL,BYTE PTR DS:[EAX]
00404514 |. 8ACA |MOV CL,DL
00404516 |. 3A16 |CMP DL,BYTE PTR DS:[ESI]
00404518 |. 75 1C |JNZ SHORT LIAOCACH.00404536
0040451A |. 3ACB |CMP CL,BL
0040451C |. 74 14 |JE SHORT LIAOCACH.00404532
0040451E |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1]
00404521 |. 8ACA |MOV CL,DL
00404523 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1]
00404526 |. 75 0E |JNZ SHORT LIAOCACH.00404536
00404528 |. 83C0 02 |ADD EAX,2
0040452B |. 83C6 02 |ADD ESI,2
0040452E |. 3ACB |CMP CL,BL
00404530 |.^75 E0 \JNZ SHORT LIAOCACH.00404512
00404532 |> 33C0 XOR EAX,EAX <--全部通過后來到這里
00404534 |. EB 05 JMP SHORT LIAOCACH.0040453B
00404536 |> 1BC0 SBB EAX,EAX
00404538 |. 83D8 FF SBB EAX,-1
0040453B |> 3BC3 CMP EAX,EBX
0040453D |. 75 0C JNZ SHORT LIAOCACH.0040454B
0040453F |. 8BC7 MOV EAX,EDI
00404541 |. 5F POP EDI
00404542 |. 5E POP ESI
00404543 |. 5D POP EBP
00404544 |. 5B POP EBX
00404545 |. 83C4 54 ADD ESP,54
00404548 |. C2 0400 RETN 4
我的注釋寫的還算清楚吧 ^_^,我再大概給你講解一下:
軟件的注冊碼是這樣計算出來的,機器碼中的各個字符的ASCII碼加上1500后除以62的余數在密碼表中對應的字符,就是相應的注冊碼。
比 如說我這里的機器碼為xn2urkeUMwpNv5xZ,x的ASCII碼為78(十進制120) 78+5DC的值為654(即1620) 接著用1620除以3E(62)得商26余8,好的,我們從“密碼 表”0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ的開始處向后數8 下,就會到了8這個字符,嘿嘿,這就是x對應的注冊碼。
好的,我給出Delphi的注冊機(我仍將其寫為函數的形式):
function KeyGen(Name: String): String;
var
S:String[16];
P:String;
Key:String;
i,N,Z:integer;
begin
P:='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
if Length(Name)<16 then
Result:='機器碼必須為16位...'
else
begin
S:=Name;
for i:=1 to 16 do
begin
N:=Ord(S);
N:=N+1500;
Z:= N mod 62;
Z:=Z+1;
Key:=Key+P[Z];
end;
Result:=Key;
end;
end;
呵呵,這一章就是最后一章了,現在也寫完了.... 打個Kiss~~
<本章完>
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
