Who secretly deleted your wechat? Don't panic! Python

Keywords: Python Database Android Mobile

I don't know if you have any experience. If you want to contact a friend who hasn't been contacted for a long time, you will find that the other party has deleted you a long time ago, but you still don't know.

 

It's believed that there are some "zombie fans" in everyone's wechat address book. They lie in the contact list silently. You think they are still friends. That's really too young and naive. In fact, the other party has already deleted them from the friends list. How can we screen these people?

 

 

 

There are a lot of tools for detecting zombie powder on the Internet. During the detection, a detection message will be sent to each friend in the wechat address book, which will seriously "disturb" each other. During the detection, some other software will implant some code viruses, which makes the operation in the dark box unsafe.

 

 

The purpose of this article is to automate the operation of wechat App, filter out all zombie powders by "simulating transfer to friends", and delete them with one click.

 

2

preparation

 

Before you start writing scripts, you need to do the following

  • A Android phone or simulator after Root. If there is no Root device, it is recommended to use Netease MuMu simulator

  • Android development environment, Android Studio

  • sqlcipher graphical tool

  • Automation tool: installing pocoui in Python virtual environment

 

3

Make up a copy

 

The whole operation is divided into three steps: Cracking the wechat database to filter out friends in the address book, simulating the transfer to friends to get zombie powder data, and deleting all zombie powder.

 

Step 1, we need to crack the wechat App database.

 

ps: here is just a brief description of the cracking process. If you want to crack wechat address book data with one click, you can skip this step and directly use the APK provided at the end of the article.

 

First, we use Android Studio to create a new project. When the project is initialized, we will grant the application administrator permission and modify the read-write permission of wechat directory.

 

//Directory of wechat App
public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";

/**
 * Executing linux instructions
 *
 * @param paramString
*/
public static 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 + "
";
        localDataOutputStream.writeBytes((String) localObject);
        localDataOutputStream.flush();
        localDataOutputStream.writeBytes("exit
");
        localDataOutputStream.flush();
        localProcess.waitFor();
        localObject = localProcess.exitValue();
    } catch (Exception localException)
    {
        localException.printStackTrace();
    }
}

//Get permission
RootUtils.execRootCmd("chmod 777 -R " + WX_ROOT_PATH);

 

Then, get the password of wechat database.

 

The password of wechat database is generated by the imei of device and the uid of wechat into md5 algorithm.

 

/**
 * According to the md5 code generated by imei and uin, obtain the database password (remove the first seven lowercase letters)
 *
 * @param imei
 * @param uin
 * @return
 */
public static String getDbPassword(String imei, String uin)
{
    if (TextUtils.isEmpty(imei) || TextUtils.isEmpty(uin))
    {
        Log.d("xag", "Failed to initialize database password: imei or uid Empty");
        return "Password error";
     }
     String md5 = MD5Utils.md5(imei + uin);
     assert md5 != null;
     return md5.substring(0, 7).toLowerCase();
}

 

Then, we can use SQLCipher dependency database to query wechat database. We need to add the following dependencies for the project to facilitate the operation of the database.

 

//We need to rely more on the project
implementation 'net.zetetic:android-database-sqlcipher:3.5.4@aar'

 

Use the password obtained above to open the encrypted database, and then query the "rcontact" table to obtain the wechat address book's wechat friends' wechat, nickname, user name and other data.

 

/**
 * Connect to database
 * <p>
 * Introduction to common library: [rcontact] contact table, [message] chat message table
 *
 * @param dbFile
 */
private void openWxDb(File dbFile, String db_pwd)
{
    //all contacts 
    List<Contact> contacts = new ArrayList<>();
    SQLiteDatabase.loadLibs(this);
    SQLiteDatabaseHook hook = new SQLiteDatabaseHook()
    {
        public void preKey(SQLiteDatabase database)
        {
        }

        public void postKey(SQLiteDatabase database)
        {
             atabase.rawExecSQL("PRAGMA cipher_migrate;"); //Database compatible with 2.0
        }
    };

    try
    {
        //Open database connection
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, db_pwd, null, hook);
         //Query all contacts
         //Filter out some contacts such as myself, group chat, public number, service number, etc
         //verifyFlag! = 0: public number, service number
         //Note blacklist users, I - Settings - Privacy - Address Book blacklist

         Cursor c1 = db.rawQuery(
                    "select * from rcontact where verifyFlag =0 and type not in (2,4,8,9,33,35,256,258,512,2051,32768,32770,32776,33024,65536,65792,98304) and username not like "%@app" and username not like "%@qqim" and username not like "%@chatroom" and encryptUsername!=""",
                    null);

         while (c1.moveToNext())
         {
             String userName = c1.getString(c1.getColumnIndex("username"));
             String alias = c1.getString(c1.getColumnIndex("alias"));
             String nickName = c1.getString(c1.getColumnIndex("nickname"));
             int type = c1.getInt(c1.getColumnIndex("type"));

             contacts.add(new Contact(userName, alias, nickName));
          }
          Log.d("xag", "Number of contacts in wechat address book:" + contacts.size() + "individual");
          for (int i = 0; i < contacts.size(); i++)
          {
             Log.d("xag", contacts.get(i).getNickName());
          }
          c1.close();
          db.close();
    } catch (Exception e)
    {
          Log.e("xag", "Failed to read database information" + e.toString());
          Toast.makeText(this, "Failed to read wechat address book!", Toast.LENGTH_SHORT).show();
    }

    Toast.makeText(this, "Read wechat address book successfully!", Toast.LENGTH_SHORT).show();
}

 

It should be noted that the data in the rcontact table in the database is quite messy. In addition to normal friend data, blacklist friend, deleted friend, public ID, wechat group and other data are also included. We need to filter through the type and verifyFlag fields.

 

 

In order to facilitate Python operation, finally write the queried friend data to the csv file.

 

/***
 * Write data to csv
 * @param output_path
 * @param contacts
 */
public static void writeCsvFile(String output_path, List<Contact> contacts)
{
    try
    {
        File file = new File(output_path);
        //Delete previously saved files
        if (file.exists())
        {
             file.delete();
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
        //Add header name
        bw.write("userName" + "," + "alias" + "," + "nickName");
        bw.newLine();
        for (int i = 0; i < contacts.size(); i++)
        {
            bw.write(contacts.get(i).getUserName() + "," + contacts.get(i).getAlias() + "," + contacts.get(i).getNickName());
            bw.newLine();
        }
        bw.close();
     } catch (IOException e)
     {
         e.printStackTrace();
     }
}

 

Step 2, we need to simulate a transfer to a friend to determine whether the friend relationship is normal.

 

First, we need to initialize Airtest, and then use the adb to export the data generated in step 1 from the mobile phone to the local.

 

def __init_airtest(self):
        """
        //Initialize 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__)

def export_wx_db_from_phone(target_path):
    """
    //Export address book data from mobile phone
    :param target_path:
    :return:
    """
    #Wechat address book data
    wx_db_source_path = "/data/data/com.xingag.crack_wx/wx_data.csv"

    #Export to local
    os.popen('adb pull %s %s' % (wx_db_source_path, target_path))

 

Then there is a series of automation operations.

 

Open wechat, traverse the list of friends, get each friend's wechat to search for friends, jump to the friend's chat interface.

 

def __to_friend_chat_page(self, weixin_id):
        """
        //Click to a friend's chat interface
        :param weixin_id:
        :param weixin_name:
        :return:
        """
        #1. Click Search
        element_search = self.__wait_for_element_exists(self.id_search)
        element_search.click()

        print('Click search')

        #2. Search box
        element_search_input = self.__wait_for_element_exists(self.id_search_input)
        element_search_input.set_text(weixin_id)

        #3. Search list
        element_search_result_list = self.__wait_for_element_exists(self.id_search_result_list)

        #3.1 is there a corresponding contact? If so, it is in the first sub View layout
        #Note: the most commonly used chat list may appear, which needs to be judged here
        index_tips = 0
        for index, element_search_result in enumerate(element_search_result_list.children()):
            #Tips for contacts
            # if element_search_result_list.children()[0].offspring(self.id_contact_tips).exists():

            if element_search_result.offspring(text=self.text_contact_tips).exists():
                index_tips = index
                break

        #4. Click the first contact to enter the chat interface
        element_search_result_list.children()[index_tips + 1].click()

 

Then try to transfer money to the other party. If the friend relationship is normal, a payment page will pop up to let you enter the password.

 

def __judge_is_friend(self, weixin_id, weixin_name):
        """
        //Judge whether you are a wechat friend
        :param weixin_id: wechat number
        :return:
        """
        #Try to transfer money to your friends and set a small amount to prevent direct payment
        #If the other party is your friend, then you will be asked to enter the password and close the page
        #If the other party is not your friend, you will be prompted that it is not your friend, and you cannot continue to operate
        #5. Click the + button in the friend interface
        self.poco(self.id_chat_more_button).click()

        #6. Click transfer button
        self.poco(self.id_chat_more_container).offspring(text=self.text_chat_transfer_account_text).click()

        #7. Input amount
        self.poco(self.id_transfer_account_input).set_text(self.money)

        #8. Click transfer button
        self.poco(self.id_transfer_account_container).offspring(text=self.text_chat_transfer_account_text).click()

 

If it's Zombie powder, the app will pop up a warning dialog box to remind you that you are not a friend of the payee and can't complete the transfer operation.

 

 

If the warning dialog box exists, you can judge whether the friend relationship is normal. Abnormal friend relationship, including: Zombie powder, abnormal account of the other party, etc.

 

#10. Warning dialog box pops up
#Pop up friend relationship is abnormal
if element_transfer_account_result_button:
     #Prompt content
     ransfer_account_result_tips = self.poco(self.id_transfer_account_result_tips).get_text()

     if self.text_friend_no_tips in transfer_account_result_tips:
         print('Be careful!%s You've been pulled black!!!' % 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))
     elif self.text_friend_limit_tips in transfer_account_result_tips:
         print('%s Account receiving restriction!!!' % weixin_name)
         write_to_file(self.path_account_limit, 'id:%s,nickName:%s' % (weixin_id, weixin_name))
     elif self.text_friend_is_norm in transfer_account_result_tips:
         print('%s Bad friends!!!' % weixin_name)
         write_to_file(self.path_relationship_unnormal, 'id:%s,nickName:%s' % (weixin_id, weixin_name))

     #Click the confirm button
     element_transfer_account_result_button.click()

     #Back to main page
     self.__back_to_home()

else:
     #Including the normal friend relationship and the other party's account restrictions
     print('Good friends')
     self.__back_to_home()

 

Finally, click the return key of the mobile phone to return to the wechat main interface.

 

def __back_to_home(self):
        """
        //Back to main interface
        :return:
        """
        print('Prepare to go back to the main interface')
        home_tips = ['WeChat', 'mail list', 'find', 'I']
        while True:
            keyevent('BACK')
            is_home = False

            #Judge whether it reaches the home page
            if self.poco(text=home_tips[0]).exists() and self.poco(text=home_tips[1]).exists() and self.poco(
                    text=home_tips[2]).exists() and self.poco(text=home_tips[3]).exists():
                is_home = True

            if is_home:
                print('Has returned to wechat Homepage~')
                break

 

By cycling the above operations, you can determine which are zombie fans, which friends' accounts are limited, and which are normal friend relationships.

 

Step 3, delete the list of zombie powder obtained above.

 

Get the above list of zombie powder data, you can use the above method to carry out a series of automatic UI operations, and delete these friends.

 

def del_friend_black(self, weixin_id):
        """
        //Delete blacklist friends
        :return:
        """
        #To friends chat interface
        self.__to_friend_chat_page(weixin_id)

        #Click the upper right corner of the chat interface to enter the friend details interface
        self.poco(self.id_person_msg_button).click()

        #Click friends' Avatar
        self.poco(self.id_person_head_url).click()

        #Click the top right corner of personal business card to pop up the friend operation menu
        self.poco(self.id_person_manage_menu).click()

        #Find delete action bar
        #Note: for current mainstream mobile phones, you need to slide to the bottom to display the [delete] operation column
        self.poco.swipe([0.5, 0.9], [0.5, 0.3], duration=0.2)

        #Click Delete to open the delete dialog box
        self.poco(self.id_person_del, text=self.text_person_del).click()

        #Confirm to delete friends [confirm to delete]
        #The interface will directly return to the main interface
        self.poco(self.id_person_del_sure, text=self.text_person_del).click()

 

4

Result conclusion

 

Compiling Android project or running APK directly can save the friend data of wechat address book to the project file directory.

 

Then running Python program will traverse the address book friend data, automatically operate wechat App, then write all the zombie powder to the local file, and finally you can choose to delete all the zombie powder.

Posted by Najjar on Mon, 23 Dec 2019 23:46:14 -0800