1. 程式人生 > >一個簡單的網路驗證程式的分析

一個簡單的網路驗證程式的分析

一個關於網路賬戶認證的小程式的分析,包含兩種情況可能分析:一:只有客戶端EXE而無伺服器端的EXE。二:客戶端和伺服器都具有EXE檔案可供分析。 首先看第一種情況:只有客戶端EXE的分析步奏: 首先,開啟客戶端試看情況:

圖片1.png (7.54 KB, 下載次數: 0)

下載附件

23 分鐘前 上傳

伺服器端開啟後,隨便輸入一個使用者資訊後,單擊Register,顯示如下:

圖片2.png (13.25 KB, 下載次數: 0)

下載附件

23 分鐘前 上傳

使用OD開啟客戶端EXE之後,有多種方法可以找到程式的功能主體——以SOCKET程式設計為主體的與伺服器的互動部分和賬戶的驗證部分。主要常用的可以使用兩種方法較為簡單。第一種為查詢當前模組中的標籤,然後對所有的
GetDlgItemTextA函式下斷點,因為既然客戶端有賬戶的使用者名稱和密碼的輸入輸出,那麼可以推測一下程式可能會用該函式以獲取輸入的文字資訊,如果有該函式且確實實現了獲取使用者輸入資訊的獲取功能則可在斷下來之後自然找到傳送伺服器端驗證的函式Send()函式以及接收函式Recv()函式,以及再接下來的響應分析判斷登陸是否成功。第二種方法,直接在中文搜尋引擎中搜索ASCII碼並在反彙編視窗中跟蹤顯示成功的資訊ASCII碼,則亦自然能找到相應所需的功能函式。(若以上兩種皆無法成功,可以嘗試下訊息斷點,亦或者直接找Socket程式設計時所需的幾個必要函式的呼叫位置)。以下分析使用第二種方法: 跟蹤後: 00401380  |. /75 17         jnz Xcrackmen.00401399 00401382  |. |50            push eax                                 ; /Style 00401383  |. |A1 FC864000   mov eax,dword ptr ds:[0x4086FC]          ; | 00401388  |. |68 6C604000   push crackmen.0040606C                   ; |Crackne net-1 0040138D  |. |68 50604000   push crackmen.00406050                   ; |Registration successful ! 00401392  |. |50            push eax                                 ; |hOwner => NULL 00401393  |. |FF15 A4504000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA 00401399  |> \5E            pop esi 0040139A  |.  83C4 58       add esp,0x58 0040139D  \.  C3            retn 往上看會發現Socket(),connect(),send(),recv(),GetDlgItemText
()函式的呼叫,我將斷點下在了呼叫第一個GetDlgItemText()函式之前的那一行程式碼的位置,執行程式,輸入賬號資訊單擊Register,程式斷在斷點處,往下單步走分析: 004012B1  |.  8D5424 30     lea edx,dword ptr ss:[esp+0x30] 004012B5  |.  6A 15         push 0x15                                ; /Count = 15 (21.) 004012B7  |.  52            push edx                                 ; |Buffer 004012B8  |.  68 E8030000   push 0x3E8                               ; |ControlID = 3E8 (1000.) 004012BD  |.  50            push eax                                 ; |hWnd => NULL 004012BE  |.  FFD7          call edi                                 ; \GetDlgItemTextA 004012C0  |.  8B15 00874000 mov edx,dword ptr ds:[0x408700] 004012C6  |.  8D4C24 48     lea ecx,dword ptr ss:[esp+0x48] 004012CA  |.  6A 15         push 0x15                                ; /Count = 15 (21.) 004012CC  |.  51            push ecx                                 ; |Buffer 004012CD  |.  68 E9030000   push 0x3E9                               ; |ControlID = 3E9 (1001.) 004012D2  |.  52            push edx                                 ; |hWnd => NULL 004012D3  |.  FFD7          call edi                                 ; \GetDlgItemTextA 004012D5  |.  6A 00         push 0x0                                 ; /Flags = 0 004012D7  |.  8D4424 34     lea eax,dword ptr ss:[esp+0x34]          ; | 004012DB  |.  6A 14         push 0x14                                ; |DataSize = 14 (20.) 004012DD  |.  50            push eax                                 ; |Data 004012DE  |.  56            push esi                                 ; |Socket 004012DF  |.  E8 CE000000   call <jmp.&WS2_32.#19>                   ; \send 004012E4  |.  6A 00         push 0x0                                 ; /Flags = 0 004012E6  |.  8D4C24 4C     lea ecx,dword ptr ss:[esp+0x4C]          ; | 004012EA  |.  6A 14         push 0x14                                ; |DataSize = 14 (20.) 004012EC  |.  51            push ecx                                 ; |Data 004012ED  |.  56            push esi                                 ; |Socket 004012EE  |.  E8 BF000000   call <jmp.&WS2_32.#19>                   ; \send 004012F3  |.  68 F4010000   push 0x1F4                               ; /Timeout = 500. ms 004012F8  |.  FF15 88504000 call dword ptr ds:[<&KERNEL32.Sleep>]    ; \Sleep 004012FE  |.  6A 00         push 0x0                                 ; /Flags = 0 00401300  |.  8D5424 1C     lea edx,dword ptr ss:[esp+0x1C]          ; | 00401304  |.  6A 14         push 0x14                                ; |BufSize = 14 (20.) 00401306  |.  52            push edx                                 ; |Buffer 00401307  |.  56            push esi                                 ; |Socket 00401308  |.  E8 9F000000   call <jmp.&WS2_32.#16>                   ; \recv 值得令人注意的地方有獲取的使用者名稱和密碼的存放地址和接收的伺服器回傳資訊的內容和存放地址。再往下跟,即可發現以下內容: 00401323  |> /0FBE7404 2C   /movsx esi,byte ptr ss:[esp+eax+0x2C] 00401328  |. |03D6          |add edx,esi 0040132A  |. |40            |inc eax 0040132B  |. |3BC1          |cmp eax,ecx 0040132D  |.^\72 F4         \jb Xcrackmen.00401323 0040132F  |>  33C0          xor eax,eax 00401331  |>  8A4C04 14     /mov cl,byte ptr ss:[esp+eax+0x14] 00401335  |.  32CA          |xor cl,dl 00401337  |.  884C04 14     |mov byte ptr ss:[esp+eax+0x14],cl 0040133B  |.  40            |inc eax 0040133C  |.  83F8 06       |cmp eax,0x6 0040133F  |.^ 72 F0         \jb Xcrackmen.00401331 大概跟過分析後可知,功能為將輸入1+使用者名稱第一位的ASCII碼值+第二位+....+最後一位=》儲存至EDX。然後將接收到的伺服器回傳的資訊每次取一個位元組與dl相異或,最終得到一個末位為00的字串CHKFL。而後出現以下程式碼:
00401347  |.  BE 7C604000   mov esi,crackmen.0040607C                ;  CHKPS
0040134C  |.  8D4424 18     lea eax,dword ptr ss:[esp+0x18]
00401350  |>  8A10          /mov dl,byte ptr ds:[eax]
00401352  |.  8A1E          |mov bl,byte ptr ds:[esi]
00401354  |.  8ACA          |mov cl,dl
00401356  |.  3AD3          |cmp dl,bl
00401358  |.  75 1E         |jnz Xcrackmen.00401378
0040135A  |.  84C9          |test cl,cl
0040135C  |.  74 16         |je Xcrackmen.00401374
0040135E  |.  8A50 01       |mov dl,byte ptr ds:[eax+0x1]
00401361  |.  8A5E 01       |mov bl,byte ptr ds:[esi+0x1]
00401364  |.  8ACA          |mov cl,dl
00401366  |.  3AD3          |cmp dl,bl
00401368  |.  75 0E         |jnz Xcrackmen.00401378
0040136A  |.  83C0 02       |add eax,0x2
0040136D  |.  83C6 02       |add esi,0x2
00401370  |.  84C9          |test cl,cl
00401372  |.^ 75 DC         \jnz Xcrackmen.00401350
00401374  |>  33C0          xor eax,eax
00401376  |.  EB 05         jmp Xcrackmen.0040137D
00401378  |>  1BC0          sbb eax,eax
0040137A  |.  83D8 FF       sbb eax,-0x1
0040137D  |>  85C0          test eax,eax
0040137F  |.  5B            pop ebx
00401380  |.  75 17         jnz Xcrackmen.00401399
00401382  |.  50            push eax                                 ; /Style
00401383  |.  A1 FC864000   mov eax,dword ptr ds:[0x4086FC]          ; |
00401388  |.  68 6C604000   push crackmen.0040606C                   ; |Crackne net-1
0040138D  |.  68 50604000   push crackmen.00406050                   ; |Registration successful !
00401392  |.  50            push eax                                 ; |hOwner => 004306FC ('Riijj - Crackme net-1 20060915',class='myWindowClass')
00401393  |.  FF15 A4504000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
00401399  |>  5E            pop esi
0040139A  |.  83C4 58       add esp,0x58
0040139D  \.  C3            retn

可知,是將運算得來的字串與一給點的字串CHKPS相比較,若相等則輸出成功。
根據以上在若無伺服器的EXE檔案具體分析時,可得知:以下兩點即為重要,一:最終驗證的資訊與接收的伺服器的傳送資訊有極大的關係。二:重新換一組使用者資訊後發現計算得到的仍是CHKFL字串,因此可以推斷出伺服器端傳過來的資訊是根據每次輸入使用者名稱的而不同的,而確定的是若使用者輸入正確傳過來的資訊可以與使用者名稱計算後得到CHKPS,否則永遠為CHKFL。根據此,若是無伺服器可以自己寫一下伺服器了。
那麼在有伺服器時,可進行雙端除錯,根據以上原理下斷點後,可以看到以下程式碼:
00401133      8D7C24 2C     lea edi,dword ptr ss:[esp+0x2C]
00401137  |.  83C9 FF       or ecx,0xFFFFFFFF
0040113A  |.  33C0          xor eax,eax
0040113C  |.  F2:AE         repne scas byte ptr es:[edi]
0040113E  |.  F7D1          not ecx
00401140  |.  49            dec ecx
00401141  |.  74 0C         je Xserver_c.0040114F
00401143  |>  0FBE5404 2C   /movsx edx,byte ptr ss:[esp+eax+0x2C]
00401148  |.  03DA          |add ebx,edx
0040114A  |.  40            |inc eax
0040114B  |.  3BC1          |cmp eax,ecx
0040114D  |.^ 72 F4         \jb Xserver_c.00401143
0040114F  |>  8D4424 40     lea eax,dword ptr ss:[esp+0x40]
00401153  |.  8D4C24 2C     lea ecx,dword ptr ss:[esp+0x2C]
00401157  |.  50            push eax
00401158  |.  51            push ecx
00401159  |.  E8 A2000000   call server_c.00401200
0040115E  |.  83C4 08       add esp,0x8
00401161  |.  83F8 01       cmp eax,0x1
00401164  |.  75 3B         jnz Xserver_c.004011A1
00401166  |.  66:A1 5870400>mov ax,word ptr ds:[0x407058]
0040116C  |.  8B15 54704000 mov edx,dword ptr ds:[0x407054]
00401172  |.  66:894424 14  mov word ptr ss:[esp+0x14],ax
00401177  |.  895424 10     mov dword ptr ss:[esp+0x10],edx
0040117B  |.  33C0          xor eax,eax
0040117D  |>  8A4C04 10     /mov cl,byte ptr ss:[esp+eax+0x10]
00401181  |.  32CB          |xor cl,bl
00401183  |.  884C04 10     |mov byte ptr ss:[esp+eax+0x10],cl
00401187  |.  40            |inc eax
00401188  |.  83F8 06       |cmp eax,0x6
0040118B  |.^ 72 F0         \jb Xserver_c.0040117D
0040118D  |>  6A 00         push 0x0                                 ; /Flags = 0
0040118F  |.  8D4C24 14     lea ecx,dword ptr ss:[esp+0x14]          ; |
00401193  |.  6A 06         push 0x6                                 ; |DataSize = 6
00401195  |.  51            push ecx                                 ; |Data
00401196  |.  56            push esi                                 ; |Socket
00401197  |.  E8 04010000   call <jmp.&WS2_32.#19>                   ; \send 在這些反彙編程式碼中發現了和客戶端中一樣的計算1+使用者名稱第一位+第二位+.......+最後一位。並發現了我們最感興趣的Send函式,那麼其上的程式碼必為計算髮送資訊的程式碼,執行至401159處的CALL時,不跳過而跟進函式,可發現以下程式碼:
00401200  /$  8B5424 04     mov edx,dword ptr ss:[esp+0x4]
00401204  |.  83EC 18       sub esp,0x18
00401207  |.  83C9 FF       or ecx,0xFFFFFFFF
0040120A  |.  33C0          xor eax,eax
0040120C  |.  56            push esi
0040120D  |.  57            push edi
0040120E  |.  8BFA          mov edi,edx
00401210  |.  BE 01000000   mov esi,0x1
00401215  |.  F2:AE         repne scas byte ptr es:[edi]
00401217  |.  F7D1          not ecx
00401219  |.  49            dec ecx
0040121A  |.  74 14         je Xserver_c.00401230
0040121C  |>  0FBE3C10      /movsx edi,byte ptr ds:[eax+edx]
00401220  |.  A8 01         |test al,0x1
00401222  |.  75 05         |jnz Xserver_c.00401229
00401224  |.  0FAFF7        |imul esi,edi
00401227  |.  EB 02         |jmp Xserver_c.0040122B
00401229  |>  03F7          |add esi,edi
0040122B  |>  40            |inc eax
0040122C  |.  3BC1          |cmp eax,ecx
0040122E  |.^ 72 EC         \jb Xserver_c.0040121C
00401230  |>  33C9          xor ecx,ecx
00401232  |>  69F6 19990100 /imul esi,esi,0x19919
00401238  |.  8BC6          |mov eax,esi
0040123A  |.  33D2          |xor edx,edx
0040123C  |.  BF 1A000000   |mov edi,0x1A
00401241  |.  F7F7          |div edi
00401243  |.  80C2 41       |add dl,0x41
00401246  |.  88540C 08     |mov byte ptr ss:[esp+ecx+0x8],dl
0040124A  |.  41            |inc ecx
0040124B  |.  83F9 14       |cmp ecx,0x14
0040124E  |.^ 72 E2         \jb Xserver_c.00401232
00401250  |.  8B7C24 28     mov edi,dword ptr ss:[esp+0x28]
00401254  |.  B9 05000000   mov ecx,0x5
00401259  |.  8D7424 08     lea esi,dword ptr ss:[esp+0x8]
0040125D  |.  33C0          xor eax,eax
0040125F  |.  C64424 1C 00  mov byte ptr ss:[esp+0x1C],0x0
00401264  |.  F3:A7         repe cmps dword ptr es:[edi],dword ptr d>
00401266  |.  5F            pop edi
00401267  |.  5E            pop esi
00401268  |.  75 16         jnz Xserver_c.00401280
0040126A  |.  68 1C714000   push server_c.0040711C                   ;  Key check correct\n
0040126F  |.  E8 74000000   call server_c.004012E8
00401274  |.  83C4 04       add esp,0x4
00401277  |.  B8 01000000   mov eax,0x1
0040127C  |.  83C4 18       add esp,0x18
0040127F  |.  C3            retn

跟蹤執行分析後可知:根據使用者名稱資訊以及相關計算得到了一長度為20的字串,將其與輸入密碼比較一個雙字,若匹配則輸出認證成功,並將EAX置為1,否則輸出認證失敗,並將EAX置為0。跳出函式後,可以看到以下內容:
0040115E  |.  83C4 08       add esp,0x8
00401161  |.  83F8 01       cmp eax,0x1
00401164  |.  75 3B         jnz Xserver_c.004011A1
00401166  |.  66:A1 5870400>mov ax,word ptr ds:[0x407058]
0040116C  |.  8B15 54704000 mov edx,dword ptr ds:[0x407054]
00401172  |.  66:894424 14  mov word ptr ss:[esp+0x14],ax
00401177  |.  895424 10     mov dword ptr ss:[esp+0x10],edx
0040117B  |.  33C0          xor eax,eax
0040117D  |>  8A4C04 10     /mov cl,byte ptr ss:[esp+eax+0x10]
00401181  |.  32CB          |xor cl,bl
00401183  |.  884C04 10     |mov byte ptr ss:[esp+eax+0x10],cl
00401187  |.  40            |inc eax
00401188  |.  83F8 06       |cmp eax,0x6
0040118B  |.^ 72 F0         \jb Xserver_c.0040117D
0040118D  |>  6A 00         push 0x0                                 ; /Flags = 0
0040118F  |.  8D4C24 14     lea ecx,dword ptr ss:[esp+0x14]          ; |
00401193  |.  6A 06         push 0x6                                 ; |DataSize = 6
00401195  |.  51            push ecx                                 ; |Data
00401196  |.  56            push esi                                 ; |Socket
00401197  |.  E8 04010000   call <jmp.&WS2_32.#19>                   ; \send
0040119C  |.^ E9 1AFFFFFF   jmp server_c.004010BB
004011A1  |>  66:A1 5070400>mov ax,word ptr ds:[0x407050]
004011A7  |.  8B15 4C704000 mov edx,dword ptr ds:[0x40704C]
004011AD  |.  66:894424 14  mov word ptr ss:[esp+0x14],ax
004011B2  |.  895424 10     mov dword ptr ss:[esp+0x10],edx
004011B6  |.  33C0          xor eax,eax
004011B8  |>  8A4C04 10     /mov cl,byte ptr ss:[esp+eax+0x10]
004011BC  |.  32CB          |xor cl,bl
004011BE  |.  884C04 10     |mov byte ptr ss:[esp+eax+0x10],cl
004011C2  |.  40            |inc eax
004011C3  |.  83F8 06       |cmp eax,0x6
004011C6  |.^ 72 F0         \jb Xserver_c.004011B8
004011C8  |.^ EB C3         jmp Xserver_c.0040118D

這就是傳送資訊的計算程式碼,其中若前面匹配成功則會根據CHKPS去計算髮送資訊,否則則根據CHKFL去計算。這也就是為何無論輸入是什麼,只要錯誤就一定會在客戶端中計算得到CHKFL,而唯一正確的方能算出CHKPS。
附:伺服器程式碼:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace SocketServer
{
    class Class1
    {
        //定義埠號
        private const int porNum = 8877;
        [STAThread]
        static void Main(string[] args)
        {
            bool done = false;
            TcpListener listener = null;
            //定義監聽主機IP地址
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");
            listener = new TcpListener(localAddr,porNum);
            listener.Start();
            while (!done)
            {
                Console.Write("******虛擬riijj服?器******\n正在偵聽8877埠...");
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine("\n接收使用者名稱及密碼...");
                NetworkStream ns = client.GetStream();
                byte[] bytes = new byte[256];
                int bytesRead = ns.Read(bytes, 0, bytes.Length);
                Console.WriteLine("使用者名稱:");
                //獲取輸入使用者名稱
                int i=0;
                String data1 = null;
                while (bytes[i] != 0)
                {
                    data1 = null;
                    data1 = System.Text.Encoding.ASCII.GetString(bytes, 0, i+1);
                    i++;
                }
                //處理使用者名稱,ASCII碼之Sum值+1,然後取低位跟CHKPS做異或運算
                //char[] w = new char[20];
                char[] b = new char[20];
                data1.CopyTo(0,b,0,data1.Length);
                Int32 data3 = 0;
                for (int y = 0; y < data1.Length; y++)
                {
                    data3 += (int)b[y];
                }
                data3 = data3 + 1;
                string m;
                m=Convert.ToString(data3, 16).Substring((Convert.ToString(data3, 16).Length - 2), 2);
                Console.WriteLine(data1);
                char[] a = new char[5] {'C','H','K','P','S'};
                byte[] m1 = new byte[5];
                for(int x=0;x<5;x++)
                {
                    m1[x] = (byte)(int.Parse(m, System.Globalization.NumberStyles.HexNumber) ^ (int)a[x]);
                    Console.WriteLine("打印出與CHKPS分別異或後的值...");
                    Console.WriteLine(m1[x]);
                }
                Console.WriteLine("打印出(SUM+1)的值(AL)...");
                Console.WriteLine(m);
                Console.WriteLine("打印出(SUM+1)的值...");
                Console.WriteLine(Convert.ToString(data3,16));
                Console.WriteLine("打印出加密字串(超出ASCII碼範圍的為問號)...");
                Console.WriteLine(System.Text.Encoding.ASCII.GetChars(m1));
                Console.WriteLine("密碼:");
                //獲取輸入密碼
                int j = 20;
                String data2 = null;
                while (bytes[j] != 0)
                {
                    data2 = null;
                    data2 = System.Text.Encoding.ASCII.GetString(bytes, 20, i);
                    j++;
                }
                Console.WriteLine(data2);
                byte[] sendData = m1;
                byte[] sendData1 = new byte[1];
                sendData1[0]=(byte)int.Parse(m, System.Globalization.NumberStyles.HexNumber);
                //向buffer寫入異或的值+(SUM值+1)
                try
                {
                    ns.Write(sendData,0,sendData.Length);
                    ns.Write(sendData1, 0, 1);
                    ns.Close();
                    client.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
            listener.Stop();
        }
    }
}