-
[原创]破解微信数据库 并查询好友总人数
- 世界美景 2017-3-30 10:19 2971
由于工作需求破解了微信的数据库 并获取想要的信息上传服务器 都是内部手机 网上大神反编译了微信 发现微信的数据库是通过
手机的IMEI(唯一识别码) + UIN 大写的IMEI + UIN 进行MD5加密 取32位小写 的前7位就是破解数据库的密码
这是核心原理 根据这个 可以手动获取IMEI 加上UIN 通过 http://tool.chinaz.com/Tools/MD5.aspx线上加密
手动破解不用写代码这种
IMEI 应该不用我说 设置里自己去看 下载三方软件也可以看
至于 UIN 是在 data data
com.tencent.mm shared_prefs 文件里的
aunt_info_key_prefs.xml 在这个文件里的
value 值就是了 我们要找到Uin
还有一个问题 网上提及的很少 微信的数据库加密是通过getDeviceID 这个函数会随机获取 IMEI 1 IMEI2 和MEID 进行加密 我已经确认了
肯定不会获取 IMEI2 只会通过默认卡1 IMEI1 和MEID进行 加密 如果破解失败可以用MEID 尝试进行破解
我在下面的连接数据库那做了这个机制
失败就走 MEID试一遍 MEID无法直接获取 我们都是手动获取的把MEID放在文件里 进行读取
接下来就是写代码了 微信数据库也是比较火 网上资料不少的 我也是参考了好多
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/" ; // U ID 文件路径 private static final String WX_SP_UIN_PATH = WX_ROOT_PATH + "shared_prefs/auth_info_key_prefs.xml" ; private String mDbPassword; // 提交参数 private int count= 0 ; private String IMEI; private String Uin; private static final String WX_DB_DIR_PATH = WX_ROOT_PATH + "MicroMsg" ; private List<File> mWxDbPathList = new ArrayList<>(); private static final String WX_DB_FILE_NAME = "EnMicroMsg.db" ; private String mCurrApkPath = "/data/data/" + MyApplication.getContextObject().getPackageName() + "/" ; private static final String COPY_WX_DATA_DB = "wx_data.db" ; // 我是放在了服务里 做了延迟每隔一个小时破解一次 可以去掉 public void onCreate() { super .onCreate(); // 获取root权限 execRootCmd( "chmod -R 777 " + WX_ROOT_PATH); execRootCmd( "chmod 777 /data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml" ); Toast.makeText( this , "开启服务" ,Toast.LENGTH_LONG).show(); Timer timer = new Timer(); timer.schedule( new TimerTask() { @Override public void run() { // 获取微信的U id initCurrWxUin(); //sqlt= (EditText) findViewById(sqlt); // 获取 IMEI 唯一识别码 TelephonyManager phone = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); IMEI = phone.getDeviceId(); System.out.println( "IMEI" +IMEI); // 根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母) initDbPassword(IMEI, Uin); System.out.println(mDbPassword + "数据库的密码" ); System.out.println( "开始统计好友数量" ); // 递归查询微信本地数据库文件 File wxDataDir = new File(WX_DB_DIR_PATH); mWxDbPathList.clear(); searchFile(wxDataDir, WX_DB_FILE_NAME); System.out.println( "查询数据库文件" ); System.out.println(mWxDbPathList+ "读取到的数据库" ); try { //处理多账号登陆情况 // 这里要注意 微信登陆多个账号会产生 多个数据库文件 // 所有我们需要把每个数据库文件都尝试破解一下 总有一个密码正确连接成功 // 有需要可以把注释解开 // 现在的写法是取集合遍历出来的数据库最后一个 我发现遍历的函数很大几率从老文件开是遍历 // 最后遍历的db 数据库很可能是新的 也就是当前账号的 但并不稳定 因为这个函数并没有解释 // 是从老的开始遍历 两个数据库的情况下 最后一个数据库确实是新的 // for (int i = 0; i < mWxDbPathList.size(); i++) { File file = mWxDbPathList.get(mWxDbPathList.size()- 1 ); File file = mWxDbPathList.get(i); // 正常写法 String copyFilePath = mCurrApkPath + COPY_WX_DATA_DB; //将微信数据库拷贝出来,因为直接连接微信的db,会导致微信崩溃 当前数据库只能连接一个 copyFile(file.getAbsolutePath(), copyFilePath); File copyWxDataDb = new File(copyFilePath); openWxDb(copyWxDataDb); // } } catch (Exception e){ // Toast.makeText(getApplicationContext(),"解析失败",Toast.LENGTH_LONG).show(); } // 上传统计数据 login(); } }, 1000 , 3600000 ); //0是延时;100是间隔 相关工具类 直接复制就行 环境复制就 ------------------------------------------------------------------------------------------ /** * 执行linux指令 * * @param paramString */ public void execRootCmd(String paramString) { try { Process localProcess = Runtime.getRuntime().exec( "su" ); Object localObject = localProcess.getOutputStream(); DataOutputStream localDataOutputStream = new DataOutputStream((OutputStream) localObject); String str = String.valueOf(paramString); localObject = str + "\n" ; localDataOutputStream.writeBytes((String) localObject); localDataOutputStream.flush(); localDataOutputStream.writeBytes( "exit\n" ); localDataOutputStream.flush(); localProcess.waitFor(); localObject = localProcess.exitValue(); } catch (Exception localException) { localException.printStackTrace(); } } /** * 获取微信的uid * 微信的uid存储在SharedPreferences里面 * 存储位置\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml */ private void initCurrWxUin() { Uin = null ; File file = new File(WX_SP_UIN_PATH); try { FileInputStream in = new FileInputStream(file); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(in); Element root = document.getRootElement(); List<Element> elements = root.elements(); for (Element element : elements) { if ( "_auth_uin" .equals(element.attributeValue( "name" ))) { Uin = element.attributeValue( "value" ); } } } catch (Exception e) { e.printStackTrace(); LogUtil.e( "获取微信uid失败,请检查auth_info_key_prefs文件权限" ); } } /** * 根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母) * * @param imei * @param uin * @return */ private void initDbPassword(String imei, String uin) { if (TextUtils.isEmpty(imei) || TextUtils.isEmpty(uin)) { LogUtil.e( "初始化数据库密码失败:imei或uid为空" ); return ; } String md5 = getMD5(imei + uin); System.out.println(imei+uin+ "初始数值" ); System.out.println(md5+ "MD5" ); String password = md5.substring( 0 , 7 ).toLowerCase(); System.out.println( "加密后" +password); mDbPassword = password; } public String getMD5(String info) { try { MessageDigest md5 = MessageDigest.getInstance( "MD5" ); md5.update(info.getBytes( "UTF-8" )); byte [] encryption = md5.digest(); StringBuffer strBuf = new StringBuffer(); for ( int i = 0 ; i < encryption.length; i++) { if (Integer.toHexString( 0xff & encryption[i]).length() == 1 ) { strBuf.append( "0" ).append(Integer.toHexString( 0xff & encryption[i])); } else { strBuf.append(Integer.toHexString( 0xff & encryption[i])); } } return strBuf.toString(); } catch (NoSuchAlgorithmException e) { return "" ; } catch (UnsupportedEncodingException e) { return "" ; } } /** * md5加密 * * @param content * @return */ private String md5(String content) { MessageDigest md5 = null ; try { md5 = MessageDigest.getInstance( "MD5" ); md5.update(content.getBytes( "UTF-8" )); byte [] encryption = md5.digest(); //加密 StringBuffer sb = new StringBuffer(); for ( int i = 0 ; i < encryption.length; i++) { if (Integer.toHexString( 0xff & encryption[i]).length() == 1 ) { sb.append( "0" ).append(Integer.toHexString( 0xff & encryption[i])); } else { sb.append(Integer.toHexString( 0xff & encryption[i])); } } return sb.toString(); } catch (Exception e) { e.printStackTrace(); return null ; } } /** * 递归查询微信本地数据库文件 * * @param file 目录 * @param fileName 需要查找的文件名称 * /private void searchFile(File file, String fileName) { // 判断是否是文件夹 if (file.isDirectory()) { // 遍历文件夹里所有数据 File[] files = file.listFiles(); if (files != null) { for (File childFile : files) { searchFile(childFile, fileName); } } } else { if (fileName.equals(file.getName())) { mWxDbPathList.add(file); } } }/** * 复制单个文件 * * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public void copyFile(String oldPath, String newPath) { try { int byteRead = 0 ; File oldFile = new File(oldPath); if (oldFile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte [] buffer = new byte [ 1444 ]; while ((byteRead = inStream.read(buffer)) != - 1 ) fs.write(buffer, 0 , byteRead); } inStream.close(); } } catch (Exception e) { System.out.println( "复制单个文件操作出错" ); e.printStackTrace(); } } ------------------------------------------------------------------------------------------------------------ // 重点到了 我们之前的操作 遍历文件获取的db 数据库 以及密码 现在就可以用 数据库和密码进行连接 密码正确 成功进入数据库 获取表信息 我下面获取的是好友的总人数 误差在 + - 2 直接大部分这个 这个需要自己观察表 进行查询 /** * 连接数据库 * * @param dbFile */ private void openWxDb(File dbFile) { Context context = MyApplication.getContextObject(); SQLiteDatabase.loadLibs(context); SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { public void preKey(SQLiteDatabase database) { } public void postKey(SQLiteDatabase database) { database.rawExecSQL( "PRAGMA cipher_migrate;" ); //兼容2.0的数据库 } }; try { //打开数据库连接 SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, mDbPassword, null , hook); //查询所有好友总数(verifyFlag!=0:公众号等类型,群里面非好友的类型为4,未知类型2) // Cursor c1 = db.rawQuery("select * from rcontact where verifyFlag = 0 and type != 4 and type != 2 and type !=33 limit 20, 9999", null); Cursor c1 = db.rawQuery( "select * from rcontact where username not like \'gh_%\' and verifyFlag<>24 and verifyFlag<>29 and verifyFlag<>56 and type<>33 and type<>70 and verifyFlag=0 and type<>4 and type<>0 and showHead<>43 and type<>65536" , null ); while (c1.moveToNext()) { String type = c1.getString(c1.getColumnIndex( "type" )); System.out.println(type+ "参数" ); count++; } // sqlt.setText("好友总数"+count); System.out.println( "总共参数" +count); // Toast.makeText(getApplicationContext(),"好友总数"+count,Toast.LENGTH_SHORT).show(); c1.close(); db.close(); } catch (Exception e) { LogUtil.e( "读取数据库信息失败" ); // e.printStackTrace(); //打开数据库连接 // 根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母) /// initDbPassword("A100004AF6C883", Uin); String MEID=readFileSdcard( "/mnt/sdcard/meid.txt" ); String dMEID=MEID.replace( "\r\n" , "" ); initDbPassword(dMEID.toUpperCase(),Uin); System.out.println(mDbPassword+ "MEID---密码" ); count= 0 ; SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, mDbPassword, null , hook); //查询所有联系人(verifyFlag!=0:公众号等类型,群里面非好友的类型为4,未知类型2) // 获取好友总数 // Cursor c1 = db.rawQuery("select * from rcontact where verifyFlag = 0 and type != 4 and type != 2 and type !=33 limit 20, 9999", null); Cursor c1 = db.rawQuery( "select * from rcontact where username not like \'gh_%\' and verifyFlag<>24 and verifyFlag<>29 and verifyFlag<>56 and type<>33 and type<>70 and verifyFlag=0 and type<>4 and type<>0 and showHead<>43 and type<>65536" , null ); while (c1.moveToNext()) { String type = c1.getString(c1.getColumnIndex( "type" )); System.out.println(type+ "参数" ); count++; //Toast.makeText(getApplicationContext(),type,Toast.LENGTH_SHORT).show(); } // sqlt.setText("好友总数"+count); // Toast.makeText(getApplicationContext(),"好友总数"+count,Toast.LENGTH_SHORT).show(); System.out.println( "总共参数" +count); c1.close(); db.close(); } } //*//*读在/mnt/sdcard/目录下面的文件 public String readFileSdcard(String fileName){ String res= "" ; try { FileInputStream fin = new FileInputStream(fileName); int length = fin.available(); byte [] buffer = new byte [length]; fin.read(buffer); res = EncodingUtils.getString(buffer, "UTF-8" ); fin.close(); } catch (Exception e){ e.printStackTrace(); } return res; } |
相关博客 我也是参考他完成的需求 http://m.blog.csdn.net/article/details?id=54024442 写的更详细 破解成功 LOG会打印相关信息 可以进行查询 也可以下载个 sqlcipher 数据库可视化工具 进行查看
排版 对付着看吧 我进行调整了 但复制过来还是 串行
http://blog.csdn.net/qq_35834055/article/details/67636801 我的博客
https://github.com/1998lixin/WeChat-database 源码地址