一. 目标场景

不知道你有没有经历过,想联系一位很长时间没有联系的朋友,发现对方很早以前已经把你删除了,而你还一无所知。

相信每个人的微信通信录里都存在一些「僵尸粉」,他们默默地躺在联系人列表中,你以为对方还是朋友,那就真是太年轻、太天真的;实际上,对方早就把从好友列表中删了,那如何来筛选出这群人呢?

我倒是知道一个方法,就是给对方转账,先输入金额,点确定,如果对方把你删了,会自动弹出一条,你不是收款方好友。。。这样就能知道对方是不是把你删了,但是,几百号甚至有的伙伴几千的微信好友,我们是不可能一个个去验证的。

网上的很大量检测僵尸粉的工具,检测的时候会给微信通信录内的每一个好友发送一条检测信息,严重「打扰」到对方;另外一部分软件在检测的时候,会植入一些代码病毒,暗箱操作显得很不安全。


这篇Python学习教程的目的是自动化操作微信 App,通过「模拟给好友转账」来筛选出所有的僵尸粉,并一键删除它们。

二 . 准 备 工 作

在正式的Python学习教程编写脚本之前,需要做好如下准备工作

一部 Root 后的 Android 手机或者模拟器,如果没有 Root 的设备,推荐使用网易 MuMu 模拟器

Android 开发环境、Android Studio

sqlcipher 图形化工具

自动化工具:Python 虚拟环境下安装 pocoui

三 . 编 写 脚 本

整个操作分为 3 步骤,分别是破解微信数据库筛选出通信录中的好友、模拟给好友转账得到僵尸粉数据、删除所有僵尸粉。

第 1 步,我们需要破解微信 App 的数据库。

ps:这里只是简单的说一下破解流程,想一键破解微信通信录数据,可以跳过这一步,直接使用文末提供的 APK。

首先,我们使用 Android Studio 新建一个项目,在项目初始化的时候,授予应用管理员权限以及修改微信目录的读写权限。

//微信App的目录publicstaticfinalStringWX_ROOT_PATH="/data/data/com.tencent.mm/";/***执行linux指令**@paramparamString*/publicstaticvoidexecRootCmd(StringparamString){try{ProcesslocalProcess=Runtime.getRuntime().exec("su");ObjectlocalObject=localProcess.getOutputStream();DataOutputStreamlocalDataOutputStream=newDataOutputStream((OutputStream)localObject);Stringstr=String.valueOf(paramString);localObject=str+"\n";localDataOutputStream.writeBytes((String)localObject);localDataOutputStream.flush();localDataOutputStream.writeBytes("exit\n");localDataOutputStream.flush();localProcess.waitFor();localObject=localProcess.exitValue();}catch(ExceptionlocalException){localException.printStackTrace();}}//获取权限RootUtils.execRootCmd("chmod777-R"+WX_ROOT_PATH);然后,获取微信数据库的密码。微信数据库的密码是由设备的imei和微信的uid进过md5算法生成的。/***根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母)**@paramimei*@paramuin*@return*/publicstaticStringgetDbPassword(Stringimei,Stringuin){if(TextUtils.isEmpty(imei)||TextUtils.isEmpty(uin)){Log.d("xag","初始化数据库密码失败:imei或uid为空");return"密码错误";}Stringmd5=MD5Utils.md5(imei+uin);assertmd5!=null;returnmd5.substring(0,7).toLowerCase();}

接着,就可以使用 SQLCipher 依赖库来对微信数据库进行查询,我们需要为项目添加如下依赖,方便操作数据库。

//我们需要对项目增加依赖implementation'net.zetetic:android-database-sqlcipher:3.5.4@aar'利用上面得到的密码打开加密数据库,然后查询「rcontact」表获取微信通讯录内所有的好友的微信号、昵称、用户名等数据。/***连接数据库*<p>*常用库介绍:【rcontact】联系人表,【message】聊天消息表**@paramdbFile*/privatevoidopenWxDb(FiledbFile,Stringdb_pwd){//所有联系人List<Contact>contacts=newArrayList<>();SQLiteDatabase.loadLibs(this);SQLiteDatabaseHookhook=newSQLiteDatabaseHook(){publicvoidpreKey(SQLiteDatabasedatabase){}publicvoidpostKey(SQLiteDatabasedatabase){atabase.rawExecSQL("PRAGMAcipher_migrate;");//兼容2.0的数据库}};try{//打开数据库连接SQLiteDatabasedb=SQLiteDatabase.openOrCreateDatabase(dbFile,db_pwd,null,hook);//查询所有联系人//过滤掉本人、群聊、公众号、服务号等一些联系人//verifyFlag!=0:公众号、服务号//注意黑名单用户,我-设置-隐私-通讯录黑名单Cursorc1=db.rawQuery("select*fromrcontactwhereverifyFlag=0andtypenotin(2,4,8,9,33,35,256,258,512,2051,32768,32770,32776,33024,65536,65792,98304)andusernamenotlike\"%@app\"andusernamenotlike\"%@qqim\"andusernamenotlike\"%@chatroom\"andencryptUsername!=\"\"",null);while(c1.moveToNext()){StringuserName=c1.getString(c1.getColumnIndex("username"));Stringalias=c1.getString(c1.getColumnIndex("alias"));StringnickName=c1.getString(c1.getColumnIndex("nickname"));inttype=c1.getInt(c1.getColumnIndex("type"));contacts.add(newContact(userName,alias,nickName));}Log.d("xag","微信通讯录中,联系人数目:"+contacts.size()+"个");for(inti=0;i<contacts.size();i++){Log.d("xag",contacts.get(i).getNickName());}c1.close();db.close();}catch(Exceptione){Log.e("xag","读取数据库信息失败"+e.toString());Toast.makeText(this,"读取微信通信录失败!",Toast.LENGTH_SHORT).show();}Toast.makeText(this,"读取微信通信录成功!",Toast.LENGTH_SHORT).show();}

需要注意的是,数据库中 rcontact 表的数据比较杂乱,除了正常的好友数据,黑名单好友、已删除好友、公众号、微信群等数据也包含在内,需要我们通过 type 和 verifyFlag 字段进行筛选。




为了便于 Python 操作,最后将查询的好友数据写入到 csv 文件中。

/****写入数据到csv中*@paramoutput_path*@paramcontacts*/publicstaticvoidwriteCsvFile(Stringoutput_path,List<Contact>contacts){try{Filefile=newFile(output_path);//删除之前保存的文件if(file.exists()){file.delete();}BufferedWriterbw=newBufferedWriter(newFileWriter(file,true));//添加头部名称bw.write("userName"+","+"alias"+","+"nickName");bw.newLine();for(inti=0;i<contacts.size();i++){bw.write(contacts.get(i).getUserName()+","+contacts.get(i).getAlias()+","+contacts.get(i).getNickName());bw.newLine();}bw.close();}catch(IOExceptione){e.printStackTrace();}}

第 2 步,我们需要模拟给好友转账,来判断这个好友关系是否正常。

首先,我们需要初始化 Airtest,然后利用 adb 把第 1 步生成的数据从手机里导出到本地。

def__init_airtest(self):"""初始化Airtest:return:"""device_1=Android('822QEDTL225T7')#device_1=Android('emulator-5554')connect_device("android:///")self.poco=AndroidUiautomationPoco(device_1,screenshot_each_action=False)auto_setup(__file__)defexport_wx_db_from_phone(target_path):"""从手机中导出通信录数据:paramtarget_path::return:"""#微信通信录数据wx_db_source_path="/data/data/com.xingag.crack_wx/wx_data.csv"#导出到本地os.popen('adbpull%s%s'%(wx_db_source_path,target_path))

然后就是一系列自动化操作。

打开微信,遍历好友列表,拿到每一个好友的微信号去搜索好友,跳转到好友的聊天界面。

def__to_friend_chat_page(self,weixin_id):"""点击到一个好友的聊天界面:paramweixin_id::paramweixin_name::return:"""#1、点击搜索element_search=self.__wait_for_element_exists(self.id_search)element_search.click()print('点击搜索')#2、搜索框element_search_input=self.__wait_for_element_exists(self.id_search_input)element_search_input.set_text(weixin_id)#3、搜索列表element_search_result_list=self.__wait_for_element_exists(self.id_search_result_list)#3.1是否存在对应的联系人,如果存在就在第一个子View布局下#注意:可能出现最常用的聊天列表,这里需要进行判断index_tips=0forindex,element_search_resultinenumerate(element_search_result_list.children()):#联系人的Tips#ifelement_search_result_list.children()[0].offspring(self.id_contact_tips).exists():ifelement_search_result.offspring(text=self.text_contact_tips).exists():index_tips=indexbreak#4、点击第一个联系人进入聊天界面element_search_result_list.children()[index_tips+1].click()

接着尝试着给对方转账,如果好友关系正常,就会跳出一个支付页面让输入密码。

def__judge_is_friend(self,weixin_id,weixin_name):"""判断是不是微信好友:paramweixin_id:微信号:return:"""#尝试给好友转账,设置一个小额度,以防止刷脸直接支付了#如果对方是你的好友,接下来会让你输入密码,关掉页面就行了#如果对方不是你的好友,会提示不是你的好友,不能继续操作了#5、点击好友界面的+按钮self.poco(self.id_chat_more_button).click()#6、点击转账按钮self.poco(self.id_chat_more_container).offspring(text=self.text_chat_transfer_account_text).click()#7、输入金额self.poco(self.id_transfer_account_input).set_text(self.money)#8、点击转账按钮self.poco(self.id_transfer_account_container).offspring(text=self.text_chat_transfer_account_text).click()

如果是僵尸粉,应用会弹出一个警告对话框,提示你不是收款方好友,没法完成转账的操作。




通过警告对话框是否存在,就可以判断好友关系是否正常。非正常的好友关系,包含:僵尸粉、对方账号异常等。

#10.弹出警告对话框#弹出好友关系不正常ifelement_transfer_account_result_button:#提示内容ransfer_account_result_tips=self.poco(self.id_transfer_account_result_tips).get_text()ifself.text_friend_no_tipsintransfer_account_result_tips:print('注意!%s已经把你拉黑了!!!'%weixin_name)self.friend_black_list.append({'id':weixin_id,'nickName':weixin_name})write_to_file(self.path_black_list,'id:%s,nickName:%s'%(weixin_id,weixin_name))elifself.text_friend_limit_tipsintransfer_account_result_tips:print('%s账号收到限制!!!'%weixin_name)write_to_file(self.path_account_limit,'id:%s,nickName:%s'%(weixin_id,weixin_name))elifself.text_friend_is_normintransfer_account_result_tips:print('%s好友关系不正常!!!'%weixin_name)write_to_file(self.path_relationship_unnormal,'id:%s,nickName:%s'%(weixin_id,weixin_name))#点击确认按钮element_transfer_account_result_button.click()#返回到主页面self.__back_to_home()else:#包含正常好友关系和对方账号限制的情况print('好友关系正常')self.__back_to_home()最后,模拟点击手机的返回键,一直回退到微信主界面。def__back_to_home(self):"""回退到主界面:return:"""print('准备回退到主界面')home_tips=['微信','通讯录','发现','我']whileTrue:keyevent('BACK')is_home=False#判断是否到达首页ifself.poco(text=home_tips[0]).exists()andself.poco(text=home_tips[1]).exists()andself.poco(text=home_tips[2]).exists()andself.poco(text=home_tips[3]).exists():is_home=Trueifis_home:print('已经回到微信首页~')break

循环上面的操作,就可以判断出哪些是僵尸粉,哪些好友的账号被限制,哪些是正常的好友关系。

第 3 步,删除上面获取到的僵尸粉列表。

拿到上面的僵尸粉数据列表,就可以利用上面的方式进行一系列自动化UI 操作,删除掉这些好友。

defdel_friend_black(self,weixin_id):"""删除黑名单好友:return:"""#到好友聊天界面self.__to_friend_chat_page(weixin_id)#点击聊天界面右上角,进入到好友的详细信息界面self.poco(self.id_person_msg_button).click()#点击好友头像self.poco(self.id_person_head_url).click()#点击个人名片的右上角,弹出好友操作菜单self.poco(self.id_person_manage_menu).click()#查找删除操作栏#注意:对于目前主流的手机,都需要滑动到最底部才能出现【删除】这一操作栏self.poco.swipe([0.5,0.9],[0.5,0.3],duration=0.2)#点击删除,弹出删除对话框self.poco(self.id_person_del,text=self.text_person_del).click()#确定删除好友【确定删除】#界面会直接回到主界面self.poco(self.id_person_del_sure,text=self.text_person_del).click()四 .结 论

编译 Android 项目或者直接运行 APK 就能将微信通信录的好友数据保存到项目文件目录下。

然后运行 Python 程序会遍历通讯录好友数据,自动化去操作微信 App,接着将所有的僵尸粉写入到本地文件中,最后可以选择将这些僵尸粉全部删除掉。

想试试你的微信好友通讯录有僵尸粉不,试一下呀!?更多的Python学习教程和一些小技巧会不定期为大家更新。大家可以用期待一下下呀!