【轉】逆向小試 ―― QQ 自動登錄器原理分析

原文: http://bbs.pediy.com/showthread.php?t=31642

 

標 題: 【原創】逆向小試 ―― QQ 自動登錄器原理分析
作 者: figo
時 間: 2006-09-07,19:38:02
鏈 接: http://bbs.pediy.com/showthread.php?t=31642

【文章標題】 : 逆向小試 ―― QQ 自動登錄器原理分析
【調試環境】 : WINXP OllyDbg1.10  
【軟件名稱】 : QQ 自動登錄器
【調試目的】 : 學習逆向
【文章作者】 : figo
【作者聲明】 : 純技術交流,沒有其他目的,不針對任何軟件。
         失誤之處懇請批評指正,或有更好的方法或者技巧,歡迎互相交流。  

【分析過程】 ;

QQ 自動登錄器是一款很不錯的操縱多QQ 帳號的軟件。
分析這款軟件是因爲整理以前的光盤是無意發現的,剛好有同學要我幫他寫個QQ 木馬,
於是順便把它調試了一下,不多說了,進入正題吧:





我們先運行一下程序,添加一個自動登錄的 QQ 帳號。設 QQ 號爲 382174647,密碼爲 123123。

再將所有的 WinExec 函數置斷點,出現了 QQ 自動登錄器的主界面,啓動帳號 382174647,這時程序中斷在如下代碼:

00404B20   .  6A 00         PUSH 0                                      ; /ShowState = SW_HIDE
00404B22   .  52            PUSH EDX                                    ; |CmdLine 
00404B23   .  FF15 38804000 CALL DWORD PTR DS:[<&KERNEL32.WinExec>]     ; /WinExec

這時 EDX 指向的字符串內容爲: 
"C:/Program Files/Tencent/QQ/qq.exe /START QQUIN:382174647 PWDHASH:Qpf0SxOVUjUkWySXOZ16kw== /STAT:41"

上面的代碼還是比較好理解的,唯一需要講解的是 EDX 所指向的字符串是怎樣生成的。

將代碼頁上翻,可以發現不遠出有調用 CString.Format 函數的代碼,入棧參數爲字符串:
 
"%s /START QQUIN:%d PWDHASH:%s /STAT:%d"

不難分析出:
第一個 %s 爲 QQ.EXE  的路徑.
第一個 %d 爲 QQ 號碼.
第二個 %s 爲 QQ 密碼的密文
第二個 %d 爲 QQ 登陸狀態 , 41是代表正常登陸,40是隱藏登陸


下面分析一下密文的生成:

程序目錄下有個 LoginData.dat,用於保存QQ 自動登錄數據的 ,由於程序必會讀取該文件的,所以
先把所有的 CFile.Read 置斷點,然後運行程序,程序中斷在如下代碼:



00402731  |.  6A 10         PUSH 10
00402733  |.  50            PUSH EAX
00402734  |.  8D4C24 20     LEA ECX,DWORD PTR SS:[ESP+20]
00402738  |.  E8 1F3E0000   CALL <JMP.&MFC42.#5442_?Read@CFile@@UAEI>;  讀文件頭, LoginData.dat 的前 10H 個字節
0040273D  |>  817C24 08 000>CMP DWORD PTR SS:[ESP+8],10400           ;  校驗 LoginData.dat 文件的有效性
00402745  |.  74 3C         JE SHORT QAL1_4_1.00402783         ;   偏移量 0 的值固定爲 10400H



0040468A  |.  6A 10         PUSH 10
0040468C  |.  50            PUSH EAX
0040468D  |.  8D4C24 1C     LEA ECX,DWORD PTR SS:[ESP+1C]
00404691  |.  E8 C61E0000   CALL <JMP.&MFC42.#5442_?Read@CFile@@UAEIPAXI@Z>    ;  讀文件頭
00404696  |.  396C24 2C     CMP DWORD PTR SS:[ESP+2C],EBP                      ;  已記錄的 QQ 數,位於偏移量 08H
0040469A  |.  75 34         JNZ SHORT QAL1_4_1.004046D0


00404759  |.  6A 40         |PUSH 40
0040475B  |.  50            |PUSH EAX
0040475C  |.  8D4C24 1C     |LEA ECX,DWORD PTR SS:[ESP+1C]
00404760  |.  E8 F71D0000   |CALL <JMP.&MFC42.#5442_?Read@CFile@@UAEIPAXI@Z>   ;  讀取每個 QQ 的登錄數據
00404765  |.  83F8 40       |CMP EAX,40                                        ;  QQ 登錄數據塊固定尺寸爲 40H
00404768  |.  0F85 E8000000 |JNZ QAL1_4_1.00404856

這時候 D ESP + 34 來查看 QQ 登錄數據塊的內容,數據如下:

0012F6D8  E8 8A 40 00 01 00 00 00 D6 07 09 00 01 00 12 00  ?@....?....
0012F6E8  17 00 1A 00 09 00 00 00 29 00 00 00 42 97 F4 4B  ......)...B?K
0012F6F8  13 95 52 35 24 5B 24 97 39 9D 7A 93 B7 85 C7 16  ?5$[$????
0012F708  33 38 32 31 37 34 36 34 37 00 00 00 00 00 00 00  382174647.......

大家注意到了沒? 地址 0012F6F4 處的內容 :

42 97 F4 4B 13 95 52 35 24 5B 24 97 39 9D 7A 93

這正是密碼 “123123” 的標準 MD5 碼。

我們把 0012F6F4 處的內容設硬件斷點,再運行程序,程序中斷在如下代碼:

0040478B  |.  8B4C24 50     |MOV ECX,DWORD PTR SS:[ESP+50]           ;  ESP + 50 值爲 0012F6F4
0040478F  |.  8D442A 1C     |LEA EAX,DWORD PTR DS:[EDX+EBP+1C]       ;  [EDX + EBP + 1C] 指向的值爲 0038F2C8
00404793  |.  894C2A 1C     |MOV DWORD PTR DS:[EDX+EBP+1C],ECX
00404797  |.  8B5424 54     |MOV EDX,DWORD PTR SS:[ESP+54]
0040479B  |.  8950 04       |MOV DWORD PTR DS:[EAX+4],EDX
0040479E  |.  8B4C24 58     |MOV ECX,DWORD PTR SS:[ESP+58]
004047A2  |.  8948 08       |MOV DWORD PTR DS:[EAX+8],ECX
004047A5  |.  8B5424 5C     |MOV EDX,DWORD PTR SS:[ESP+5C]
004047A9  |.  83C9 FF       |OR ECX,FFFFFFFF
004047AC  |.  8950 0C       |MOV DWORD PTR DS:[EAX+C],EDX
004047AF  |.  8B43 60       |MOV EAX,DWORD PTR DS:[EBX+60]
004047B2  |.  8D5428 30     |LEA EDX,DWORD PTR DS:[EAX+EBP+30]
004047B6  |.  33C0          |XOR EAX,EAX
004047B8  |.  F2:AE         |REPNE SCAS BYTE PTR ES:[EDI]

上面代碼的功能是將 0012F6F4 處的內容複製到 0038F2C8。
這時要把 0012F6F4 處的硬件斷點刪除,改設 0038F2C8 。
大家一定對此感疑惑吧?這是因爲 0012F6F4 是屬於棧地址,保存的是臨時變量。
很多人對數據進行置硬件斷點時,程序被莫名其妙的中斷再 7CXXXXXX 處,那可能是把硬件斷點置在臨時變量上了。
一般程序都會把一些重要的臨時變量複製到全局變量或固定的內存空間。所以在一般的調試過程中要儘量避免在棧地址
中置硬件斷點,除非情況特殊(比如 脫殼或分析算法之類的)。

繼續運行程序,程序主界面出現了,啓動帳號,程序中斷在以下代碼:



00401AF0  |.  8B5C24 1C     MOV EBX,DWORD PTR SS:[ESP+1C]
00401AF4  |>  8A043B        /MOV AL,BYTE PTR DS:[EBX+EDI]            ;  EBX 指向密碼的 MD5 碼
00401AF7  |.  8D6F 01       |LEA EBP,DWORD PTR DS:[EDI+1]            ;  EBP 爲臨時計數變量
00401AFA  |.  884424 10     |MOV BYTE PTR SS:[ESP+10],AL
00401AFE  |.  8B4C24 10     |MOV ECX,DWORD PTR SS:[ESP+10]
00401B02  |.  81E1 FF000000 |AND ECX,0FF
00401B08  |.  8BC1          |MOV EAX,ECX
00401B0A  |.  C1E8 02       |SHR EAX,2
00401B0D  |.  8A80 F8864000 |MOV AL,BYTE PTR DS:[EAX+4086F8]
00401B13  |.  880432        |MOV BYTE PTR DS:[EDX+ESI],AL
00401B16  |.  8B4424 20     |MOV EAX,DWORD PTR SS:[ESP+20]
00401B1A  |.  3BE8          |CMP EBP,EAX
00401B1C  |.  0F8D A0000000 |JGE QAL1_4_1.00401BC2                   ;  判斷是否將 MD5 碼的最後一個字節加密完
00401B22  |.  8A441F 01     |MOV AL,BYTE PTR DS:[EDI+EBX+1]
00401B26  |.  83E1 03       |AND ECX,3
00401B29  |.  884424 11     |MOV BYTE PTR SS:[ESP+11],AL
00401B2D  |.  8B4424 11     |MOV EAX,DWORD PTR SS:[ESP+11]
00401B31  |.  25 FF000000   |AND EAX,0FF
00401B36  |.  8BD8          |MOV EBX,EAX
00401B38  |.  C1EB 04       |SHR EBX,4
00401B3B  |.  C1E1 04       |SHL ECX,4
00401B3E  |.  0BD9          |OR EBX,ECX
00401B40  |.  45            |INC EBP
00401B41  |.  8A8B F8864000 |MOV CL,BYTE PTR DS:[EBX+4086F8]
00401B47  |.  884C32 01     |MOV BYTE PTR DS:[EDX+ESI+1],CL
00401B4B  |.  8B4C24 20     |MOV ECX,DWORD PTR SS:[ESP+20]
00401B4F  |.  3BE9          |CMP EBP,ECX
00401B51  |.  7D 3C         |JGE SHORT QAL1_4_1.00401B8F             ;  不會產生的跳轉
00401B53  |.  8B5C24 1C     |MOV EBX,DWORD PTR SS:[ESP+1C]
00401B57  |.  83E0 0F       |AND EAX,0F
00401B5A  |.  C1E0 02       |SHL EAX,2
00401B5D  |.  8A4C1F 02     |MOV CL,BYTE PTR DS:[EDI+EBX+2]
00401B61  |.  884C24 12     |MOV BYTE PTR SS:[ESP+12],CL
00401B65  |.  8B4C24 12     |MOV ECX,DWORD PTR SS:[ESP+12]
00401B69  |.  81E1 FF000000 |AND ECX,0FF
00401B6F  |.  8BE9          |MOV EBP,ECX
00401B71  |.  C1ED 06       |SHR EBP,6
00401B74  |.  0BE8          |OR EBP,EAX
00401B76  |.  83E1 3F       |AND ECX,3F
00401B79  |.  8A85 F8864000 |MOV AL,BYTE PTR SS:[EBP+4086F8]
00401B7F  |.  884432 02     |MOV BYTE PTR DS:[EDX+ESI+2],AL
00401B83  |.  8A89 F8864000 |MOV CL,BYTE PTR DS:[ECX+4086F8]
00401B89  |.  884C32 03     |MOV BYTE PTR DS:[EDX+ESI+3],CL
00401B8D  |.  EB 17         |JMP SHORT QAL1_4_1.00401BA6
00401B8F  |>  8B5C24 1C     |MOV EBX,DWORD PTR SS:[ESP+1C]
00401B93  |.  83E0 0F       |AND EAX,0F
00401B96  |.  8A0485 F88640>|MOV AL,BYTE PTR DS:[EAX*4+4086F8]
00401B9D  |.  884432 02     |MOV BYTE PTR DS:[EDX+ESI+2],AL
00401BA1  |.  C64432 03 3D  |MOV BYTE PTR DS:[EDX+ESI+3],3D
00401BA6  |>  8B4424 20     |MOV EAX,DWORD PTR SS:[ESP+20]
00401BAA  |.  83C7 03       |ADD EDI,3                               ;  被加密的 MD5 碼計數變量
00401BAD  |.  83C2 04       |ADD EDX,4                               ;  加密後的 MD5 碼計數變量
00401BB0  |.  3BF8          |CMP EDI,EAX                             ;  判斷是否加密完成
00401BB2  |.^ 0F8C 3CFFFFFF /JL QAL1_4_1.00401AF4
00401BB8  |.  8BC6          MOV EAX,ESI
00401BBA  |.  5F            POP EDI
00401BBB  |.  5E            POP ESI
00401BBC  |.  5D            POP EBP
00401BBD  |.  5B            POP EBX
00401BBE  |.  83C4 08       ADD ESP,8
00401BC1  |.  C3            RETN



00401BC2  |>  8A4424 10     MOV AL,BYTE PTR SS:[ESP+10]
00401BC6  |.  83E0 03       AND EAX,3
00401BC9  |.  C1E0 04       SHL EAX,4
00401BCC  |.  8A88 F8864000 MOV CL,BYTE PTR DS:[EAX+4086F8]
00401BD2  |.  B0 3D         MOV AL,3D
00401BD4  |.  884C32 01     MOV BYTE PTR DS:[EDX+ESI+1],CL
00401BD8  |.  884432 02     MOV BYTE PTR DS:[EDX+ESI+2],AL
00401BDC  |.  884432 03     MOV BYTE PTR DS:[EDX+ESI+3],AL           ;  最後兩個字符固定爲 '='
00401BE0  |>  8BC6          MOV EAX,ESI



上面的的代碼就是將 MD5 碼加密成密文,代碼看起來有點亂
爲了便於理解,我用 C 代碼寫了一個登錄QQ 的演示程序:

#include "windows.h"
#include "stdio.h"
#include "md5.h"

char szTable[100] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

void login(char * szNum,char * szPwd, char * szPath,int iPwdlen);
void QQHash(unsigned char* szMDin,unsigned char* szQQout);

void main()
{
  
  int icnt;
  char szQQnum[20];
  char szQQpwd[20];
  char szQQpath[255] = "" ;
  char szPathfmt[10] = "%s %s";
  char szTmp[255] = "";
  unsigned long lpPath;

  printf("請輸入 QQ.exe 所在的路徑/n (例如 c://program files//tencent//qq//) : /n");
  
  do
  {
    scanf("%s",szTmp);
    wsprintf(szQQpath,szPathfmt,szQQpath,szTmp);

  
  }while(getchar() != 0x0a);
  
  lpPath = (unsigned  long )(szQQpath) + 1;


  
  printf("請輸入 QQ 號碼 :/n");
  scanf("%s",szQQnum);
    
  printf("請輸入 QQ 密碼 : /n");

  scanf("%s",szQQpwd);
  icnt = strlen(szQQpwd);
  
    login(szQQnum,szQQpwd,(char*)(lpPath),icnt);


}

void login(char * szNum,char * szPwd, char * szPath, int iPwdlen)
{  
  
  char szCmd[255] = "";
  char szMDHash[30] = "";
  char szQQHash[30] = "";
  MD5 MD;  
  
  MD.Hash ((unsigned char*)szMDHash,(unsigned char*)szPwd,(unsigned long) iPwdlen);
  QQHash((unsigned char*)szMDHash,(unsigned char*)szQQHash);

  
  wsprintf((LPSTR)szCmd,"%sQQ.exe /START QQUIN:%s PWDHASH:%s /STAT:41", 
  szPath,szNum,szQQHash);
  WinExec((LPCSTR)szCmd,SW_SHOWNORMAL);



}





void QQHash(unsigned char* szMDin,unsigned char* szQQout)
{

  int i = 0 , j=0;
  unsigned char c1,c2,c3,t1;
  c1=0;
  c2=0;
  c3=0;
  t1=0;


  while(true)
  {
    c1 = szMDin[j];
    t1 = c1;
    c1 = c1 >> 2;
    szQQout[i] =szTable[c1];
    j++;
    i++;
    
    if(j >= 16)
    {
      c1 = szMDin[j];
      c1 = c1 & 0x03;
      c1 = c1 << 4;
      szQQout[i] = szTable[c1];
      i++;
      szQQout[i] = 0x3d;
      i++;
      szQQout[i] = 0x3d;
      break;


    }
    
    c2 = szMDin[j];
    c1 = t1 & 0x03;
    c1 = c1 << 4;
    t1 = c2;
    c2 = c2 >> 4;
    c2 = c2 | c1;
    szQQout[i] =szTable[c2];
    
    j++;
    i++;

    c1 = t1 & 0x0f;
    c1 = c1 << 2;
    c3 = szMDin[j];
    t1 = c3 >> 6;
    t1 = c1 | t1;
    c3 = c3 & 0x3f;
    szQQout[i] =szTable[t1];
    i++;
    szQQout[i] =szTable[c3];
    
    j++;
    i++;


  

  
  }




由於代碼寫的比較倉促,註釋也懶的加,所以上面代碼也不好理解,建議讀代碼之前,自己跟蹤一下關鍵代碼。

當然,上面的代碼只是 QQ自動登錄器的一個簡單演示,功能並不完善。
有興趣編寫 QQ 自動登錄器的,可以參考參考上面代碼(詳細代碼 見附件)。

QQ 自動登錄器很方便,但最好不要在別人電腦上使用,因爲它保存的密碼只用 MD5 加密一次,容易被爆破。
在我的 Athlon XP 2500+ 上大約花了兩個小時就把一個 8位的純數字密碼爆破完成。
而QQ 的本地保存密碼就相對安全多了,多次 MD5 循環加密,很難爆破的。



特別聲明:
  
關於代碼中的 MD5.H 非本人編寫的,而是摘自 《看雪論壇精華7》中的 “QQ本地密碼文件暴力破解” 一文。
感謝原作者 ―― lotusroots。
沒辦法,人懶啊,自己去寫個 MD5 代碼很費時的。
轉載時請保持文章的完整性。

 

上傳的附件

文件類型: rar qqlogin.rar (0, 322 次下載) 下載此附件需要消耗2Kx,下載中會自動扣除。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章