第一步:登陆
1、get访问微信首页https://wx.qq.com
提供session、headers
用途:获取cookie
后续访问必须带session、headers、cookie这三个参数,并保持不变
2、get访问https://wx.qq.com/jslogin
get参数分别是
appid:值为自定义,格式为wx782c26e4c19acffb
fun:值为new
lang:值为en_us
redirect_uri:值为https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage
_:值为当前时间戳
用途:获取二维码uuid
3、get访问https://wx.qq.com/qrcode/{{uuid}}
用途:下载和展示二维码
4、扫码和确认,访问https://wx.qq.com/cgi-bin/mmwebwx-bin/login,
get参数
loginicon:值必须为true
uuid:值为{{uuid}}
r:值为当前时间戳/1524
_:值为当前时间戳
用途:返回登陆状态,登陆成功之后的redirect_uri
返回状态码说明如下:
200,扫码和确认成功
201,扫码,未确认
其他,未扫码或者其他原因
第二步、初始化页面和获取登陆信息
1、get访问{{redirect_uri}}
用途:返回登陆认证等信息,一个字典类型的json格式,下文用login_info表示
2、post访问https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=【当前时间戳】
post参数为
BaseRequest:通过1返回参数获取值
例子:{"BaseRequest": {"DeviceID": "uKUD8e%2Bp7iXqNpbOuPTntL7OdbsfxEv5JdQjKtb7Mc%2FVQK2leE%2BRrNVkI5fQZZjB", "Sid": "xkQE8IoFPjwXEf2W", "Uin": "575635712", "Skey": "@crypt_2b05caf0_2290c785d1bc5646d2ff0ff771ec3324", "isgrayscale": "1"}}
用途:返回微信用户信息、第一页好友信息、和BaseRequest、最新聊天信息等等
第三步、获取好友信息
get参数
r:值为当前时间戳
seq:值为0
skey:值为login_info[Skey]
用途:返回所有的好友信息,字典json格式
有用的好友信息字段说明:
Sex:1表示男,2表示女,0为其他【公众号、群、系统账号等等】
UserName,微信系统为每个微信号分配一个唯一号码,开头@@表示群、字母或者数字开头表示系统账号,其他【公众号、好友等】以单@开头
NickName,个人设置的昵称,重复可能性很大
Alias,微信号,如果没有设置为空,不会出现重复
3.2获取群组信息(如果不想获取群组内的人员昵称、头像等参数,可以先不用研究)
post访问 https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=xxx&lang=zh_CN&pass_ticket=xxx
参数:
参数:
BaseRequest:
{
DeviceID:”xxx”
Sid:”xxx”
Skey:”xxx”
Uin:xxx
}
Count:4
List:
[
0:{UserName: “xxx”, EncryChatRoomId: “”}
1:{UserName: “xxx”, ChatRoomId: “”}
…
]
参数说明:List>UserName就是对应群组的id,群组id是webwxgetcontact返回的id前缀是@@的。
注意:这个接口webwxgetcontact你可能是无法获取群组信息的,究其原因是因为你并没有将群聊加入通讯录,只要在微信上,点开你要监听的群组,然后开启将群组加入通讯录就可以了。
第四步、接受和发送新信息
1、接受信息
1)、定时检查是否有新信息
get参数是:
'r' : 当前时间戳*1000
'skey' : login_info[skey]
'sid' : login_info[sid]
'uin' : login_info[uin]
'deviceid' : login_info[deviceid]
'synckey' : login_info[synckey]
'_' : 当前时间戳*1000
用途:返回最新信息数,0表示没有新消息
2)、获取新信息内容,post访问https://wx.qq.com/webwxsync?sid=login_info[sid]&skey=login_info[skey]&lang=en_US&pass_ticket=login_info[pass_ticket]
post参数为
'BaseRequest' : login_info[BaseRequest]
'SyncKey' : login_info[SyncKey]
'rr' :~当前时间戳*1000
例子:{"rr": -1485065809, "BaseRequest": {"Ret": 0, "ErrMsg": ""}, "SyncKey": {"List": [{"Key": 1, "Val": 645531166}, {"Key": 2, "Val": 645531278}, {"Key": 3, "Val": 645531125}, {"Key": 11, "Val": 645531260}, {"Key": 13, "Val": 645524153}, {"Key": 201, "Val": 1485065810}, {"Key": 203, "Val": 1485064747}, {"Key": 1000, "Val": 1485058018}, {"Key": 1001, "Val": 1485057992}, {"Key": 1002, "Val": 1485058221}, {"Key": 1004, "Val": 1484911834}], "Count": 11}}
用途:返回最新信息列表
注意:群信息的发送者放在Content开头部分
2、发送信息
post参数
'BaseRequest': self.base_request,
'Msg': {
'Type': login_info[BaseRequest],
'Content': content,
'FromUserName': 自己的username,
'ToUserName': 发送的username,
'LocalID': login_info[msgid],
'ClientMsgId': login_info[msgid],
},
例子:{"Msg": {"Content": "啊啊啊啊", "ToUserName": "filehelper", "FromUserName": "@974141db55d51041ee1e0b8b6af5589776a5282910b9ac1e154693430a23f79f", "Type": "Test Message", "LocalID": 1485065800472, "ClientMsgId": 1485065800472}, "BaseRequest": {"Ret": 0, "ErrMsg": ""}}
返回发送结果json字典
-------------------------------------------------------------------
利用APK来获取聊天记录
第一步:安装apk的手机进行root
因为需要读取微信聊天记录信息表,所以手机需要root,这样给apk开启更大的权限,这里进行root的工具有好多比如360root大师等相关的root工具。
第二步:用Root Explorer把db文件赋予更高的权限
手机上安装Root ExPlorer软件,设置/data/data/com.tencent.mm/MicroMsg/b93e23895b9f5b4a8d781ba8d702cfe8/EnMicroMsg.db这些文件以及文件夹下的EnMicroMsg.db进行权限设定,赋予所有的权限(说明:这里的包名b93e23895b9f5b4a8d781ba8d702cfe8每个用户都是不一样的,需要用户你进行自己查看设置。)
第三步:开始写代码啦,
由于微信的数据db文件进行加密操作所以咱们这边需要解密
获取手机序列号IMEI号,
由于微信的数据db文件进行加密操作所以咱们这边需要解密
获取手机序列号IMEI号,
public static String getIMEI(Context context) { TelephonyManager manager = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE); if (manager.getDeviceId() != null && manager.getDeviceId().length() > 0) { return manager.getDeviceId(); } else { return ""; } }
获取微信用户信息号需要解析xml,这里解析xml用到的是XmlPullParser进行解析的
这里path路径是:/data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml
这里path路径是:/data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml
public static String getUin(String path) { try { FileInputStream inputStream = new FileInputStream(new File(path)); XmlPullParser parser = Xml.newPullParser(); parser.setInput(inputStream, "UTF-8"); int eventType = parser.getEventType();// 产生第一个事件 while (eventType != XmlPullParser.END_DOCUMENT) { //处理事件,不碰到文档结束就一直处理 switch (eventType) { case XmlPullParser.START_DOCUMENT: // 不做任何操作或初开始化数据 break; case XmlPullParser.START_TAG: // 解析XML节点数据 // 获取当前标签名字 String tagName = parser.getName(); if ("int".equals(parser.getName())) { String name = parser.getAttributeValue(0); String value = parser.getAttributeValue(1); Log.e("int", "name:" + name + ",value:" + value); return value; } break; case XmlPullParser.END_TAG: // 单节点完成,可往集合里边添加新的数据 break; case XmlPullParser.END_DOCUMENT: break; } // 别忘了进入下一个元素并触发相应事件 ,不然就会死循环 eventType = parser.next(); } } catch (FileNotFoundException e) { Log.e("FileNotFoundException:", e.toString()); } catch (XmlPullParserException e) { Log.e("XmlPullParserException:", e.toString()); } catch (IOException e) { e.printStackTrace(); } return ""; }
有上面两个字符串计算出打开db的密码,
这里的计算需要用到md5算法进行计算操作先把md5这段代码贴出来
计算密码调用与这几段代码进行配合
这里的计算需要用到md5算法进行计算操作先把md5这段代码贴出来
计算密码调用与这几段代码进行配合
String password= (MD5Util.md5(XmlUtil.getIMEI(context)+XmlUtil.getUin("/data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml"))).substring(0,7).toLowerCase();
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * author: * date: 2016/10/25 20:59 */ public class MD5Util { public static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; public static String toHexString(byte[] b) { //String to byte StringBuilder sb = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); sb.append(HEX_DIGITS[b[i] & 0x0f]); } return sb.toString(); } public static String md5(String s) { try { // Create MD5 Hash MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(s.getBytes()); byte messageDigest[] = digest.digest(); return toHexString(messageDigest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } }拿到密码后可以对数据库的读写进行操作
import android.content.Context; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import net.sqlcipher.Cursor; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * Created by zxf on 2016/10/25. */ public class DataHelp { /** * 操作数据库读取微信消息 * @param context * @param path */ public static void readWeChatDatabase(Context context,String path) { SQLiteDatabase.loadLibs(context); String password= (MD5Util.md5(XmlUtil.getIMEI(context)+XmlUtil.getUin(Constants.uinPath))).substring(0,7).toLowerCase(); Log.e("password", "password:"+password ); SQLiteDatabaseHook hook = new SQLiteDatabaseHook(){ public void preKey(SQLiteDatabase database){ } public void postKey(SQLiteDatabase database){ database.rawExecSQL("PRAGMA cipher_migrate;"); //最关键的一句!!! } }; try { long time=System.currentTimeMillis(); SQLiteDatabase db = SQLiteDatabase.openDatabase(path, password, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS, hook); long time2=System.currentTimeMillis(); long time_1=time2-time; Toast.makeText(context,"time_3:"+time_1, Toast.LENGTH_SHORT).show(); Log.e("readWeChatDatabase", "time_3:"+time_1 ); int count=0; Cursor c = db.rawQuery("select * from message" , null); while (c.moveToNext()) { int _id = c.getInt(c.getColumnIndex("msgId")); String content= c.getString(c.getColumnIndex("content")); count++; Log.e("readWeChatDatabase", "content:"+content ); } c.close(); db.close(); long time_2=System.currentTimeMillis()-time2; Toast.makeText(context,"time_4:"+time_2+",count:"+count , Toast.LENGTH_SHORT).show(); Log.e("readWeChatDatabase", "time_4:"+time_2+",count:"+count ); } catch (Exception e) { Log.e("e", "readWeChatDatabase: "+e.toString() ); } } /** * 复制单个文件 * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public static void copyFile(Context context,String oldPath, String newPath) { long time=System.currentTimeMillis(); deleteFolderFile(newPath,true); long time2=System.currentTimeMillis(); long time_1=time2-time; Log.e("copyFile", "time_1:"+time_1 ); InputStream inStream=null; FileOutputStream fs=null; try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在时 inStream = new FileInputStream(oldPath); //读入原文件 fs = new FileOutputStream(newPath); byte[] buffer = new byte[2048]; while ( (byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 fs.write(buffer, 0, byteread); } long time_2=System.currentTimeMillis()-time2; Log.e("copyFile", "time_2:"+time_2); readWeChatDatabase(context,newPath);//对copy出来的数据进行操作 } }catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); }finally { try { if (inStream!=null) { inStream.close(); } if (fs!=null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 删除指定目录下文件及目录 * * @param deleteThisPath * @return */ public static void deleteFolderFile(String filePath, boolean deleteThisPath) { if (!TextUtils.isEmpty(filePath)) { try { File file = new File(filePath); if (file.isDirectory()) {// 处理目录 File files[] = file.listFiles(); for (int i = 0; i < files.length; i++) { deleteFolderFile(files[i].getAbsolutePath(), true); } } if (deleteThisPath) { if (!file.isDirectory()) {// 如果是文件,删除 file.delete(); } else {// 目录 if (file.listFiles().length == 0) {// 目录下没有文件或者目录,删除 file.delete(); } } } } catch (Exception e) { e.printStackTrace(); } } } }
在初始activity调用
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import com.xilexuan.wechatcrack.util.DataHelp; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends AppCompatActivity { Timer timer; TimerTask mTimerTask; @Override protected void onCreate(Bundle savedInstanceState) { timer = new Timer(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final String old_path="/data/data/com.tencent.mm/MicroMsg/b93e23895b9f5b4a8d781ba8d702cfe8/EnMicroMsg.db"; final String new_path="/data/data/copy到你的文件夹下比如com...../EnMicroMsg.db"; DataHelp.copyFile(this,old_path,new_path); mTimerTask =new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { DataHelp.copyFile(MainActivity.this,old_path,new_path); } }); } }; timer.schedule(mTimerTask, 0, 10000);//10秒一获取 //Toast.makeText(this, pwd, Toast.LENGTH_SHORT).show(); } }最后在log内就能看到你截取到的微信聊天记录,这里设置的10秒获取一次。
相关推荐
然后,由于微信不是一个开放的平台,它并不提供公开的API供第三方应用直接读取消息。因此,常规的编程方法可能无法直接实现这个目标。一种可能的途径是利用Android系统的 AccessibilityService,这是一个允许应用在...
一种常见的方式是使用iTunes或iCloud备份手机数据,然后通过第三方工具如iTransor、Dr.Fone等解析备份文件获取微信聊天记录。这些工具通常提供导出为TXT或CSV格式的功能。具体步骤包括: 1. 使用iTunes或iCloud备份...
但是,由于安全和隐私原因,第三方应用无法直接访问其他应用的数据库,除非使用adb工具或者模拟器中的DDMS(Dalvik Debug Monitor Service)。 对于描述中提到的微信版本6.5.7,首先需要获取到该版本微信的资源ID。...
HOOK微信接口的目的可能包括获取未公开的API调用,如聊天记录、用户信息等。 2. **网络通信分析**:微信使用加密的通信协议,通过HOOK网络请求,可以分析其数据包结构,解密通信内容,理解微信的内部逻辑。 3. **...
而“Lib”目录可能包含项目依赖的第三方库,如Gson、Retrofit、OkHttp等,这些都是安卓开发中常用的工具库。 总的来说,这个压缩包为学习安卓微信聊天功能和图片上传的实现提供了实践材料,对于想深入理解这些功能...
7. **推送通知**:为了实现类似微信的消息推送功能,开发者需要集成第三方推送服务,如Firebase Cloud Messaging (FCM) 或极光推送等。这些服务会在后台接收服务器消息并推送到用户的设备上。 8. **多媒体支持**:...
6. **消息推送机制**:学习如何集成第三方推送服务(如Firebase Cloud Messaging),实现实时消息推送。 7. **社交功能实现**:包括好友添加、聊天、群组管理、朋友圈发布及评论等功能的实现,涉及到的数据结构设计...
6. **推送服务**:为了实现实时消息推送,源码可能集成微信自己的推送服务或者其他第三方推送服务,如Firebase Cloud Messaging (FCM)。 7. **图片和视频处理**:由于微信支持多媒体消息,源码可能包含图片和视频的...
- `libs`:库文件夹,存放项目依赖的第三方库JAR或AAR文件。 总结来说,这个项目提供了一种实现底部菜单的解决方案,利用Fragment构建各个页面,不使用滑屏切换,简化了实现过程,便于复用和维护。通过查看和学习这...
7. **网络通信**:微信使用HTTP或HTTPS协议与服务器进行通信,可能采用了诸如OkHttp或Retrofit等第三方库来简化网络请求的处理。 8. **数据库存储**:SQLite是安卓内置的轻量级数据库,微信可能使用它来存储用户...
13. **图片加载库**:考虑到图片加载和缓存,开发者可能使用了Glide或Picasso等第三方库来优化用户体验。 通过对这些知识点的学习和实践,开发者可以深入理解一个基于位置的聊天应用是如何运作的,并能为自己的项目...
这涉及到Android的权限管理、网络请求以及第三方库的集成。 5. **项目配置文件**: `qhinsProject.iml`是Android Studio的项目配置文件,它记录了项目模块的相关信息。`qhinsurance.jks`可能是一个签名文件,用于...
10. **推送通知**:实现类似微信的推送通知,需要集成第三方推送服务,如Google Firebase Cloud Messaging (FCM)。 11. **性能优化**:包括内存管理、减少冗余资源、加载优化等,以提供流畅的用户体验。 12. **...
10. **第三方库集成**:可能使用了如Glide、OkHttp等第三方库,学习如何有效利用社区资源优化开发。 通过这个项目,开发者不仅可以学习到具体的编程技巧,还能锻炼到整体应用的架构设计能力,对Android应用开发有更...
微信官方并不支持此类第三方插件,使用此类工具可能会存在封号风险,因此在享受便利的同时,用户需自行承担可能的风险。此外,为了保障个人信息安全,务必选择信誉良好的开发者和来源下载此类应用,避免下载含有恶意...
这一特性确保了数据的安全性,因为所有的通讯信息都将通过加密通道传输,使得用户能够牢牢掌控自己的通信数据,避免信息泄露或被第三方获取。 即时通信(IM)技术是现代互联网应用中的核心部分,它涵盖了从一对一的...
5. **支付功能**:微信支付是微信生态系统中的重要组成部分,它涉及到金融级的安全加密技术、第三方支付接口、交易处理和风险管理等复杂流程。 6. **大数据与人工智能**:微信积累了海量的用户数据,通过大数据分析...
首先,我们需要引入`FloatMenu`库,这是第三方开发者为实现微信长按菜单效果而创建的一个组件。可以通过在项目的`build.gradle`文件中添加以下依赖来引入: ```gradle dependencies { ... compile '...
对于第三方CameraAPK成像倒立的问题,文档建议修改vflip、hflip和camera.cfg三种组合来解决问题,但同时指出这种方法可能会引起其他APK方向转90度,因此需要具体问题具体分析。 对于摄像头添加闪光灯的功能,文档...