Netscan pro 3.3 註冊演算法分析全過程

看雪資料發表於2015-11-15

Netscan pro 3.3 註冊演算法分析全過程
                               文 / 風雨無阻【CCG】
Netscan pro 簡介:
    一旦連結上網路,其實你就是與成千上萬的電腦為鄰,當你在瀏覽網頁、ICQ、聊天時,你知道有哪些主機正在對你的電腦做些什麼事情嗎?這是一套在TCP/IP通訊協定下的監視工具,幫助你監控你的電腦所有的網路連結活動,確保你在Internet或是TCP/IP架構下的網路正常的安全運作,免於被外界侵犯。透過Netscan Pro可以見到所有對你的電腦的連結清單,所有由外而入的活動都一覽無遺。

一、破解專用工具及基礎知識
1.  使用工具:
fi3.1,Ollydbg 1.09c漢化版,W32DSM10(這幾個軟體可以到http://www.pediy.com下載), 
Netscan pro 3.3下載地址:http://www.skycn.com/soft/7990.html

2.  共享軟體常見的幾種註冊過程:
(1)使用者名稱+註冊碼:這種型別最常用,一般軟體會要求你輸入使用者名稱和註冊碼,然後讀取你輸入的內容,呼叫程式中的某個函式根據使用者名稱計算出註冊碼,或者根據註冊碼計算出使用者名稱。這種方式現在使用的較少,適合破解初學者練習使用,因為一旦在網上公佈使用者名稱和註冊碼,每個人都可以使用了,本文涉及的Netscan Pro就是這種。
(2)機器碼+註冊碼:這種方式也叫作一機一碼,其實和上面差不多,只不過把使用者名稱換成了硬碟序列號。每個硬碟的序列號不一樣,這樣的序列號和註冊碼不能通用,在一定程度上起到了軟體保護的作用。
(3)使用註冊檔案:這種方式相對於上面兩種方式保護強度就更進一層了,當你註冊軟體的時候,就會讓你選擇你的註冊檔案,然後讀出註冊檔案的內容,在進行註冊認證。比如國內使用很廣泛的《流光》就是這種註冊認證方式。
(4)網路線上認證:這種方式目前來講保護方式最強,比如現在用的各種遊戲的外掛註冊就採用的是這種方式,一般這種情況在網上有一臺伺服器的資料庫裡面有已經註冊的使用者列表,當你使用軟體的時候,軟體就會把你輸入的使用者名稱和密碼和伺服器資料庫裡面的使用者名稱密碼進行比較,如果資料庫中沒有,就會註冊失敗。
以上只是共享軟體眾多註冊方法裡面幾種常見的方法,通用的方法就是暴力破解(暴破),這種方法是初學者使用的方法,沒有多少技術含量,當你寫出某個軟體的序號產生器的時候,標誌者你的破解水平達到了中等。
當軟體讀入使用者名稱以後就會呼叫一個函式進行註冊碼的計算,我們把這個CALL叫做關鍵CALL,如果你的目的是寫出序號產生器就要認真讀這個關鍵CALL裡面的彙編語句,弄明白軟體作者的註冊演算法,然後把演算法用你自己熟悉的程式語言寫出來,就一切OK了!軟體計算出正確的註冊碼以後就會和輸入的註冊碼進行比較,如果是一樣的,就會註冊成功,一般是將真,假兩個註冊碼地址放在暫存器裡面,緊接著對這兩個地址的資料進行比較,緊跟一個跳轉指令。這個跳轉就是控制程式下一步顯示註冊成功或者註冊失敗。下文我會就本文的軟體進行詳細的解釋。

二、本軟體的破解步驟
1.使用偵測軟體fi3.1 檢測軟體是否加殼,這是破解每個軟體的第一步。當然如果你使用TRW2000或者是softice動態跟蹤的話就不必了,如果使用ollydbg進行除錯就必須保證軟體無殼。方法很簡單,我們將可執行檔案圖示拖到fi3.1的圖示上放開即可,經過檢測Netscan Pro 3.3沒有加殼,下面就可以繼續我們的序號產生器之路了。

2.執行程式,進行註冊,隨便輸入使用者名稱和註冊碼,這時程式會彈出註冊失敗的對話方塊。我們記下對話方塊的內容“註冊名或註冊碼錯誤!”,然後開啟W32DSM10對可執行檔案Netscanpro.exe進行反彙編,再點選“參考->串式資料參考”在彈出的視窗中找到剛剛記下的註冊失敗對話誆的內容。我們用滑鼠雙擊就會轉到程式中,程式碼如下:

:00407160 E803080000              call 00407968
:00407165 83C408                  add esp, 00000008
:00407168 85C0                    test eaxeax
:0040716A 7454                    je 004071C0
:0040716C 6A00                    push 00000000
* Possible StringData Ref from Data Obj ->"你已經註冊 !"
:0040716E BABE174C00              mov edx, 004C17BE
:00407173 66C745DC2000            mov [ebp-24], 0020
。。。。。。。。。。。。
:004071BE EB4D                    jmp 0040720D
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040716A(C)
:004071C0 6A00                    push 00000000
* Possible StringData Ref from Data Obj ->"註冊名或註冊碼錯誤 !"
:004071C2 BADC174C00              mov edx, 004C17DC
:004071C7 66C745DC2C00            mov [ebp-24], 002C
。。。。。。。。。。。。

程式碼分析:
程式執行到00407160時呼叫了一個函式,這個函式很有可能就是我們要找的關鍵CALL,當讓也可能是還上面的某個CALl,一般關鍵CALL就在0040716C的跳轉指令的前面不遠處,00407160的CALL結果影響EAX暫存器的值,如果註冊碼正確0040716A處就不會跳轉從而出現註冊成功的對話方塊,如果不正確就會跳轉到004071C0 出現註冊失敗的對話方塊。

下面就要進行動態跟蹤具體分析了。
3.用ollydbg開啟可執行檔案,使用快捷鍵“Ctrl+G”出現轉到對話方塊輸入407160,在按F2 在407160處設定斷點,再按F9執行程式,點選註冊按鈕,輸入使用者名稱和註冊碼,點選“註冊”程式會被ollydbg攔截,我們按F8單步跟蹤(如果遇到CALL按F8就不會跟進,一般第一遍跟蹤的時候不用跟進,只是弄明白每個CALL的具體作用是什麼,找到我們所需要的關鍵CALL)執行完407160處的語句以後我們就會在右上的暫存器視窗發現正確的註冊碼儲存在EDX中,這就說明此處的CALL是關鍵CALL,下面就要對這個CALL進行詳細研究了,按 F9 就會回到程式,再次點選註冊,程式被中斷,我們這次按F7跟進這CALL
然後在按F8 單步跟蹤,注意觀察右上的暫存器視窗,程式執行到下面:

004079C3  |>PUSH netscanp.00510548           ;  ASCII "abcdefgh"
004079C8  |>CALL netscanp.00499DA4
004079CD  |>POP ECX
004079CE  |>CMP EAX,8
004079D1  |>JNB SHORT netscanp.004079D7
004079D3  |>XOR EAX,EAX
004079D5  |>JMP SHORT netscanp.00407A2A
004079D7  |>PUSH netscanp.00510548           ; /Arg1 = 00510548 ASCII "abcdefgh"
004079DC  |>CALL netscanp.0040789C          ; 
etscanp.0040789C
004079E1  |>POP ECX
004079E2  |>MOV EDX,EAX
004079E4  |>MOV EAX,netscanp.005105AC      ;  ASCII "987654321"
004079E9  |>/MOV CL,BYTE PTR DS:[EAX]
004079EB  |>|CMP CL,BYTE PTR DS:[EDX]
。。。。。。。。。。。。
00407A03  |>JNZ SHORT netscanp.004079E9
00407A05  |>JE SHORT netscanp.00407A0B
00407A07  |>XOR EAX,EAX
00407A09  |>JMP SHORT netscanp.00407A2A
。。。。。。。。。。。。
00407A25  |>MOV EAX,1
。。。。。。。。。。。。

程式碼分析:
004079C3 處送入使用者名稱,接下來的一個CALL是計算使用者名稱的長度,這一點一定要注意,一般的軟體對使用者名稱的長度都有要求,從004079CE處的語句可以看出,這個軟體對使用者名稱長度的要求是大於等於8位,接著執行,執行完4079DC的語句以後就會在右上視窗發現正確的註冊碼,那就說明4079DC處的CALL就是就算註冊碼的函式呼叫,接下來的一個迴圈就是真假兩個註冊碼的比較,比較完後,根據比較結果決定是否執行 xor eax,eax 語句,從而設定EAX暫存器的值,下面我們跟進4079DC的CALL:
004078B3  |>MOV ESI,netscanp.004C207C   ;下面的字串就是原始字串           
; ASCII "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
004078B8  |>LEA EBX,DWORD PTR SS:[EBP-C]
。。。。。。。。。。。。
004078E7  |>JGE SHORT netscanp.0040795C        ;下面的迴圈開始進行註冊碼的計算
004078E9  |>/PUSH EDI                         
004078EA  |>|MOV ECX,DWORD PTR SS:[EBP-10]  
004078ED  |>|MOVSX EAX,BYTE PTR DS:[ECX]   ;| EAX = F(i)
004078F0  |>|PUSH EAX                         ;下面的CALL含有浮點操作
004078F1  |>|CALL netscanp.0040760C             ; k = (int)((cos( F[i]*7.5+i*4.5)+1)*52)
。。。。。。。。。。。。。
00407933  |>||POP ESI               
00407934  |>||MOVSX EDX,BYTE PTR SS:[EBP+EAX-50]  ;將字串表查出來的字元取出
00407939  |>||PUSH EDX                              ;EAX = k%0x3e; EBP是固定值
。。。。。。。。。。。。。
0040794D  |>| MOV EAX,DWORD PTR DS:[EBX]        ;EAX = k / 0x3e
0040794F  |>||TEST EAX,EAX                ;如果EAX = 0 則讀取使用者名稱的下一位 
00407951  |>|JNZ SHORT netscanp.004078FD   ;否則跳轉到 4078FD 繼續
00407953  |>|INC EDI                       ;EDX = EDX +1
00407954  |>|INC DWORD PTR SS:[EBP-10]
00407957  |>|CMP EDI,DWORD PTR SS:[EBP-4]
0040795A  |>JL SHORT netscanp.004078E9    ;跳轉到4078E9 讀取下一位使用者名稱

程式碼分析:
跟進以後馬上就發現了一個字串,就會聯想到很有可能使用的查表的方法計算出註冊碼,以後的分析證明了這個想法,分析程式碼的時候有個技巧:要特別注意程式碼中的迴圈,一般軟體註冊演算法就是根據使用者名稱的每一位計算出註冊碼,這樣的話必定會用到迴圈。這個軟體在4078ED處每次迴圈傳人使用者名稱的一位,計算出部分註冊碼然後組合起來就是整個註冊碼了,4078FE處函式用到了浮點命令,這對於初學者來說有一定的難度,在此就不具體分析了給出函式的作用,用F(i)表示使用者名稱的第i位,函式返回的結果是: 
k = (int)((cos( F[i]*7.5+i*4.5)+1)*52);
從4078FD處開始根據上面的結果在字串中的對應位置找出字元作為註冊碼。在上面的程式碼中有註釋,有些使用者名稱字元可能會計算好幾位註冊碼。在00407934將字串表查出來的字元取出,儲存起來這樣就組成了整個註冊碼。給大家一個測試用的使用者名稱和註冊碼:
使用者名稱:abcdefgh     註冊碼:11YA5PrJ1
後記:總的來說,這個軟體註冊演算法不是很複雜,適合想從“暴破”專家轉行到註冊碼方面的朋友練習,這算是單表查詢類軟體中較簡單的了,就是有一個CALL使用了浮點指令,可能有一點點麻煩,不過我想既然大家都是搞這一行的就知道“破解不能怕麻煩”,破解本來就是麻煩的事。還有,搞破解不能只看不練,你練習的多了自然就有自己的技巧,相信你很快就能熟練的讀懂大部分軟體的註冊演算法從而寫出序號產生器了。祝大家早日 成功!!! 

附8位使用者名稱的序號產生器C語言原始碼:
#include <math.h>
#include <stdio.h>
main()
{
  double m;
  int i,j=0,k,a,b,flag = 1;;
  char y[9],s[63]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    char sn[30];
  printf("Please Inpout UserName(8 byte):
");
  scanf("%s",y);
  for(i=0;i<8;i++)
  {
    m = y[i]*7.5+i*4.5;
    k = (int)((cos(m)+1)*52);
    while(flag==1 && k!=0)
    {
      a = k/0x3e;
      b = k%0x3e;
      sn[j++] = s[b];
      k = a;
      if(a == 0)    flag = 0;
    }
    flag = 1;
  }
  sn[j] = NULL;
  printf("User Name: %s
Your SN  : %s
",y,sn);
}

相關文章