PC版微信數據庫解密詳細教程


原文出自:https://bbs.pediy.com/thread-251303-1.htm

在電子取證過程中,也會遇到提取PC版微信數據的情況,看雪、52破解和CSDN等網上的PC版微信數據庫破解文章實在是太簡略了,大多數只有結果沒有過程。經過反復試驗終於成功解密了數據庫,現在把詳細過程記錄下來,希望大家不要繼續在已經解決的問題上過度浪費時間,以便更投入地研究尚未解決的問題。

通過查閱資料得知,與安卓手機版微信的7位密碼不同,PC版微信的密碼是32字節(64位),加密算法沒有說明,但是可以通過OllyDbg工具從內存中獲取到這個密碼,然后通過一段C++代碼進行解密。

       首先下載OllyDbg 2.01漢化版,我用的版本如下圖所示:

 

       運行OllyDbg,然后運行PC版微信(需要下載客戶端的,不是網頁版)。先不要點擊登錄按鈕。

 

       切換到Ollydbg界面:

 

       點擊文件菜單,選擇“附加”,在彈出的對話框中找到名稱為WeChat的進程,其窗口名稱為“登錄”。然后點擊“附加”。

 

       附加成功后OllyDbg開始加載,成功加載后可以看到最上面OllyDbg后面有WeChat.exe的字樣:

 

       在查看菜單中選擇“可執行模塊”:

 

       找到名稱為WeChatWin的模塊,雙擊選中。為了方便觀察,在窗口菜單中選擇水平平鋪。在CPU窗口標題欄可以看到“模塊WeChatWin”字樣。

 

       在插件中選擇“StrFinder字符查找”中的“查找ASCII字符串”(注意如果下載的OllyDbg版本不對,可能沒有相關插件,因此一定要找對版本),要稍微等一會兒,會出現搜索結果的窗口。

 

       在此窗口點擊鼠標右鍵,選擇“Find”,在搜索框中輸入“DBFactory::encryptDB”。

 

       會自動定位在第一處,但我們需要的是第二處,即“encryptDB %s DBKey can’t be null”下面這一處。可以用鼠標點擊滾動條向下,找到第二處,用鼠標雙擊此處。

 

       在CPU窗口中可以看到已經定位到了相應的位置。用鼠標點擊滾動條向下翻。

 

       下面第六行應該是TEST EDX,EDX,就是用來比對密碼的匯編語言代碼。在最前面地址位置(本文中是0F9712BA)雙擊設置斷點(設置斷點成功則地址會被標紅,而且可以在斷點窗口中看到設置成功的斷點)

       點擊“運行”按鈕(或者在調試菜單中選擇“運行”),這時寄存器窗口中的EDX的值應該是00000000。

       切換到微信登錄頁面,點擊登錄,然后到手機端確認登錄。這是OllyDbg界面中的數據不斷滾動,直到EDX不再為全0並且各個窗口內容停止滾動為止。

 

       在EDX的值上面點擊鼠標右鍵,在彈出的菜單里面選擇“數據窗口中跟隨”,則數據窗口中顯示的就是EDX的內容。

 

       圖示中從0B946A80(這個數值是變化的,不但每台電腦不同,每次調試也可能完全不同)到0B946A9F共32個字節就是微信的加密密碼,本圖中就是:

“53E9BFB23B724195A2BC6EB5BFEB0610DC2164756B9B4279BA32157639A40BB1”

       一共32個字節,共64位。

       得到這個之后,就可以關閉OllyDbg了,微信也會自動被關閉。

 

       接下來就是解密過程。在看雪、52破解等多個論壇中都有相關的C++源碼,開始企圖使用Dev-C++或者C-Free等輕量級IDE進行編譯,也使用過Visual C++ 6.0綠色精簡版,結果多次嘗試出現各種錯誤,反復失敗,最終不得已使用Visual Studio,並對代碼進行了一定的修正,終於調試成功。

正好Visual Studio 2019剛剛發布直接到官方網站下載了社區版。

       根據查到的資料,需要先安裝openssl,為了省事直接下載了最新的Win64OpenSSL-1_1_1b,安裝后發現各種報錯,繼續查找資料發現原來sqlcipher使用的是低版本的openssl,之后找到了一個Win64OpenSSL-1_0_2r也報錯,最后發現還是官方這個直接解壓縮的版本靠譜:

https://www.openssl.org/source/openssl-1.0.2r.tar.gz

       把壓縮包直接解壓到任意目錄,比如c:\openssl-1.0.2r

       啟動Visual Studio 2019社區版(估計Visual Studio 2008以后的都應該可以,懶得找就直接官網下載最新的吧)

       在啟動界面右下方選擇“創建新項目”

 

       滾動下拉條,在窗口中選擇C++控制台應用:

 

       給項目隨便起個名字,選擇保存位置:

 

       然后點擊“創建”,即可完成新項目創建。生成默認的Hello World代碼:

 

       先要做好項目的基礎配置,之前調試失敗主要問題就出在這里了。

       在項目菜單中最下面選擇項目屬性“dewechat屬性”(這個跟設置的項目名稱一致)

 

       對話框最左上角的配置后面,可以選擇配置的是Debug模式還是Release模式(Release模式不包含調試信息,編譯完成的exe文件更小一些,但如果是自己用,這兩個模式沒有區別,配置了哪個,后面就要用哪個模式編譯,否則會報錯)

       先選擇C/C++下面的“常規”選項:

 

       右邊第一條是“附加包含目錄”,點擊右側空白處。在下拉框里選擇“編輯…”,在對話框中點擊四個圖標按鈕最左側的“新行”按鈕,會生成一個空白行,點擊右側的“…”:

       在彈出的對話框里選擇剛剛安裝的openssl目錄(本文是c:\openssl-1.0.2r)中的include目錄。 

 

       設置完成后如下:

 

       然后選擇左側“鏈接器”下面的“常規”:

 

       在中間位置,有一個“附加庫目錄”,點擊右側空白處,選擇openssl目錄下的lib目錄,設置完成后如下:

 

       最后點擊鏈接器下面的“輸入”:

 

       右側最上面有“附加依賴項”,默認已經有一些系統庫,點擊右側內容,選擇“編輯…”

 

       這個沒有增加新行的按鈕,只能手工錄入或者拷貝文件名進去,需要增加上圖所示的兩個庫名稱。

       設置完成后如下:

 

       現在所有的設置都OK了,可以把代碼放進來編譯了。

       由於太多網站轉載,而且很多有錯漏,已經搞不清原始代碼是哪位大神寫的了,其中有一些已經被廢棄的代碼,根據系統報錯提示進行了替換,另外做了一個主要的變化就是之前的代碼是把數據庫名寫在變量中,但由於需要解密很多庫,為了靈活,改為輸入參數的方法,即在運行時帶參數運行或者根據提示輸入需要解密的數據庫文件名。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using  namespace  std;
#include <Windows.h>
#include <iostream>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/hmac.h>
 
#undef _UNICODE
#define SQLITE_FILE_HEADER "SQLite format 3" 
#define IV_SIZE 16   //16或者改成 0x30
#define HMAC_SHA1_SIZE 20
#define KEY_SIZE 32
 
#define SL3SIGNLEN 20
 
#ifndef ANDROID_WECHAT
#define DEFAULT_PAGESIZE 4096       //4048數據 + 16IV + 20 HMAC + 12
#define DEFAULT_ITER 64000
#else
#define NO_USE_HMAC_SHA1
#define DEFAULT_PAGESIZE 1024
#define DEFAULT_ITER 4000
#endif
//pc端密碼是經過OllyDbg得到的32位pass。
unsigned  char  pass[] = { 0x53,0xE9,0xBF,0xB2,0x3B,0x72,0x41,0x95,0xA2,0xBC,0x6E,0xB5,0xBF,0xEB,0x06,0x10,0xDC,0x21,0x64,0x75,0x6B,0x9B,0x42,0x79,0xBA,0x32,0x15,0x76,0x39,0xA4,0x0B,0xB1 };
char  dbfilename[50];
int  Decryptdb();
int  CheckKey();
int  CheckAESKey();
int  main( int  argc,  char * argv[])
{
     if  (argc >= 2)     //第二個參數argv[1]是文件名
         strcpy_s(dbfilename, argv[1]);   //復制    
            //沒有提供文件名,則提示用戶輸入
     else  {
         cout <<  "請輸入文件名:"  << endl;
         cin >> dbfilename;
     }
     Decryptdb();
     return  0;
}
 
int  Decryptdb()
{
     FILE * fpdb;
     fopen_s(&fpdb, dbfilename,  "rb+" );
     if  (!fpdb)
     {
         printf ( "打開文件錯!" );
         getchar ();
         return  0;
     }
     fseek (fpdb, 0, SEEK_END);
     long  nFileSize =  ftell (fpdb);
     fseek (fpdb, 0, SEEK_SET);
     unsigned  char * pDbBuffer =  new  unsigned  char [nFileSize];
     fread (pDbBuffer, 1, nFileSize, fpdb);
     fclose (fpdb);
 
     unsigned  char  salt[16] = { 0 };
     memcpy (salt, pDbBuffer, 16);
 
#ifndef NO_USE_HMAC_SHA1
     unsigned  char  mac_salt[16] = { 0 };
     memcpy (mac_salt, salt, 16);
     for  ( int  i = 0; i <  sizeof (salt); i++)
     {
         mac_salt[i] ^= 0x3a;
     }
#endif
 
     int  reserve = IV_SIZE;       //校驗碼長度,PC端每4096字節有48字節
#ifndef NO_USE_HMAC_SHA1
     reserve += HMAC_SHA1_SIZE;
#endif
     reserve = ((reserve % AES_BLOCK_SIZE) == 0) ? reserve : ((reserve / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
 
     unsigned  char  key[KEY_SIZE] = { 0 };
     unsigned  char  mac_key[KEY_SIZE] = { 0 };
 
     OpenSSL_add_all_algorithms();
     PKCS5_PBKDF2_HMAC_SHA1(( const  char *)pass,  sizeof (pass), salt,  sizeof (salt), DEFAULT_ITER,  sizeof (key), key);
#ifndef NO_USE_HMAC_SHA1
     PKCS5_PBKDF2_HMAC_SHA1(( const  char *)key,  sizeof (key), mac_salt,  sizeof (mac_salt), 2,  sizeof (mac_key), mac_key);
#endif
 
     unsigned  char * pTemp = pDbBuffer;
     unsigned  char  pDecryptPerPageBuffer[DEFAULT_PAGESIZE];
     int  nPage = 1;
     int  offset = 16;
     while  (pTemp < pDbBuffer + nFileSize)
     {
         printf ( "解密數據頁:%d/%d \n" , nPage, nFileSize / DEFAULT_PAGESIZE);
 
#ifndef NO_USE_HMAC_SHA1
         unsigned  char  hash_mac[HMAC_SHA1_SIZE] = { 0 };
         unsigned  int  hash_len = 0;
         HMAC_CTX hctx;
         HMAC_CTX_init(&hctx);
         HMAC_Init_ex(&hctx, mac_key,  sizeof (mac_key), EVP_sha1(), NULL);
         HMAC_Update(&hctx, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset + IV_SIZE);
         HMAC_Update(&hctx, ( const  unsigned  char *)& nPage,  sizeof (nPage));
         HMAC_Final(&hctx, hash_mac, &hash_len);
         HMAC_CTX_cleanup(&hctx);
         if  (0 !=  memcmp (hash_mac, pTemp + DEFAULT_PAGESIZE - reserve + IV_SIZE,  sizeof (hash_mac)))
         {
             printf ( "\n 哈希值錯誤! \n" );
             getchar ();
             return  0;
         }
#endif
         //
         if  (nPage == 1)
         {
             memcpy (pDecryptPerPageBuffer, SQLITE_FILE_HEADER, offset);
         }
 
         EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new();
         EVP_CipherInit_ex(ectx, EVP_get_cipherbyname( "aes-256-cbc" ), NULL, NULL, NULL, 0);
         EVP_CIPHER_CTX_set_padding(ectx, 0);
         EVP_CipherInit_ex(ectx, NULL, NULL, key, pTemp + (DEFAULT_PAGESIZE - reserve), 0);
 
         int  nDecryptLen = 0;
         int  nTotal = 0;
         EVP_CipherUpdate(ectx, pDecryptPerPageBuffer + offset, &nDecryptLen, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset);
         nTotal = nDecryptLen;
         EVP_CipherFinal_ex(ectx, pDecryptPerPageBuffer + offset + nDecryptLen, &nDecryptLen);
         nTotal += nDecryptLen;
         EVP_CIPHER_CTX_free(ectx);
 
         memcpy (pDecryptPerPageBuffer + DEFAULT_PAGESIZE - reserve, pTemp + DEFAULT_PAGESIZE - reserve, reserve);
         char  decFile[1024] = { 0 };
         sprintf_s(decFile,  "dec_%s" , dbfilename);
         FILE  * fp;
         fopen_s(&fp, decFile,  "ab+" );
         {
             fwrite (pDecryptPerPageBuffer, 1, DEFAULT_PAGESIZE, fp);
             fclose (fp);
         }
 
         nPage++;
         offset = 0;
         pTemp += DEFAULT_PAGESIZE;
     }
     printf ( "\n 解密成功! \n" );
     return  0;
}

       將之前默認的代碼全部清除,將以上代碼拷貝進去,保存。然后在工具條欄中選擇是Debug還是Release模式,是x86還是x64(需要跟之前配置匹配,如果選了沒配置的模式會報錯。測試發現幾個選項沒有太大區別,建議默認),之后點擊“本地windows調試器”(或者按F5鍵),如果前面的步驟操作都正確,應該可以完成編譯並自動運行,彈出一個命令行窗口,提示需要輸入文件名:

 

       最下方顯示了生成的exe文件路徑,將這個文件拷貝到微信數據庫所在的目錄,一般是:

C:\Users\Administrator\Documents\WeChat Files\********\Msg

       其中********位置為需要解密的微信id,目錄內容如下:

 

       如果要解密ChatMsg.db,則在命令行窗口輸入指令dewechat ChatMsg.db回車即可。

 

       解密成功后,會在目錄中生成de_ChatMsg.db,用sqlite數據庫管理軟件打開即可。

 

       本文主要是個驗證過程,沒有做什么突破工作,目前的解密只能算是半自動過程,密碼算法部分的獲得是下一步需要研究的內容,希望大家共同努力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM