`
lvjun106
  • 浏览: 429199 次
  • 性别: Icon_minigender_1
  • 来自: 芜湖
社区版块
存档分类
最新评论

获取微信聊天记录 通过HTTP协议实时 第三方apk实时获取

阅读更多
第一步:登陆
1、get访问微信首页https://wx.qq.com
提供session、headers
用途:获取cookie
后续访问必须带session、headers、cookie这三个参数,并保持不变
get参数分别是
appid:值为自定义,格式为wx782c26e4c19acffb
fun:值为new
lang:值为en_us
_:值为当前时间戳
用途:获取二维码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表示没有新消息
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号,
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
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这段代码贴出来 
计算密码调用与这几段代码进行配合
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秒获取一次。 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics