天天看點

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

本文為看雪論壇精華文章 看雪論壇作者ID:落葉似秋 0x0 前言 最近一直在研究Windows逆向的東西,想着快要把Android給遺忘了。是以就想利用工作之餘來研究Android相關的技術,來保持對Android熱情。 調用微信代碼來發送朋友圈動态一直是自己想實作的東西,研究了一下,果然實作了,遂寫下本文當作記錄。本文主要分析發送純文字朋友圈動态和發送圖檔朋友圈動态。 0x1 朋友圈動态類型分析

本文用到的工具如下:

  • PC
  • 一台可以調試微信程序的Android手機
  • 微信7.0.11
  • ddms(用于跟蹤調用過程)
  • uiautomatorviewer(用于定位控件id)
  • jadx(用于對微信apk靜态分析)
  • frida(用于hook微信,獲得相關資訊,釋出朋友圈)

在分析代碼之前,首先要定位到與之相近的地方,我們首先想到的肯定是發朋友圈那個界面,如何檢視發朋友圈的界面是哪個Activity呢?很簡單,在手機上打開發送朋友圈的界面:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  把手機連接配接電腦,打開USB調試,在PC的cmd視窗中執行以下指令:

adb shell dumpsys activity top
           
android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  可以看到發朋友圈的那個Activity就是com.tencent.mm.plugin.sns.ui.SnsUploadUI   雖然找到了Activity,但還是不能高興太早,想要通過Activity知道哪部分是發朋友的代碼還是比較費力的。于是我們就想到從“發表”按鈕入手,找出發表朋友圈的相關代碼。 點選“發表”按鈕會發生什麼?發表是一個動态的行為,我們可以通過跟蹤點選“發表”按鈕時的調用過程,來找到有用的資訊。跟蹤調用過程,可以使用ddms工具來完成。 打開ddms,選中微信程序,在手機中打開發表朋友圈界面,然後在ddms中點選下圖圈出的圖示開始跟蹤:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  将朋友圈動态發出,再點一次上圖圈出的圖示停止跟蹤。 ddms會生成跟蹤結果,對于跟蹤結果,怎麼找到按鈕事件相關的資訊呢,學過Android的朋友就會想到onClick方法,那我們就在ddms的搜尋結果中搜尋這個名稱:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  成功的定位到了onClick的位置,但是比起這條onClick結果,更加令人引人注目的是它的上一條結果,因為它包含了我們剛才找到的Activity的類名:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  知道這個方法被調用,我們去看看com.tencent.mm.plugin.sns.ui.SnsUploadUI類裡的OnMemuItemClick究竟是什麼。   用jadx打開微信apk,定位到com.tencent.mm.plugin.sns.ui.SnsUploadUI類,在類中搜尋onMemuItemClick,結果不多,看起來比較像的就是這個onMemuItemClick了:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  在onMemuItemClick方法中看到了:

 String unused2 = SnsUploadUI.this.desc = SnsUploadUI.this.tQN.getText().toString();
           

這行代碼有什麼特别的呢,在我看來,有兩個特别的地方:

  • desc是描述(description)的英文單詞的縮寫
  • this.desc被賦予this.tQN.getText().toString()

我們發朋友圈動态時候,是要寫動态的描述的,是以這個desc可能就是發朋友圈動态的描述,如果是描述,我們就可以根據這個描述,順藤摸瓜找到發朋友圈動态的地方。 而且this.desc的值又來自于this.tQN.getText().toString(),即this.tQN很有可能就是我們填寫動态描述的文本框。我們來看看this.tQN指派的地方,它在onCreate方法被指派:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  可以知道它的id是d41,那麼d41是哪個控件?打開uiautomatorviewer,對發朋友圈界面截圖分析,點選截圖中的文本框,uiautomatorviewer右側跳轉到了相應的位置,果然,d41就是發動态時填寫描述文本框的id  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  好了,現在知道this.desc就是發表朋友圈動态時的描述,跟上他應該就可以找到發朋友圈動态的地方。 繼續往onMemuItemClick方法下部分析,可以看到this.desc被傳入了SnsUploadUI.this.tQO.a方法:

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  SnsUploadUI.this.tQO.a方法定義在接口com.tencent.mm.plugin.sns.ui.z 中:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态
android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  知道它定義在哪個接口并不能解決問題,畢竟接口沒有實質性代碼,要找還得找接口的的實作類,在com.tencent.mm.plugin.sns.ui.SnsUploadUI類中尋找this.tQO在哪裡會被指派。 最終,我們在com.tencent.mm.plugin.sns.ui.SnsUploadUI類的ag方法中看到了許多給this.tQO指派的地方:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  由此,可見this.tQO被賦予什麼值是根據this.tMY來決定的,this.tMY是一個int類型的資料,那我們hook com.tencent.mm.plugin.sns.ui.SnsUploadUI類的ag方法就可以知道this.tMY是什麼值。 在這裡,我用frida來hook,frida的javascript部分代碼如下:

var SnsUploadUI= Java.use('com.tencent.mm.plugin.sns.ui.SnsUploadUI');var ag = SnsUploadUI.ag.overload("android.os.Bundle");//get sns typeag.implementation=function(bundle){    var ret = ag.call(this,bundle);    send("sns type = " + this.tMY.value);    return ret;}
           

hook之後,每當我們在手機上打開釋出朋友圈動态的界面,ag方法被調用,控制台就會輸出相應的數字。 經過我的測試,這個數字是發表朋友圈動态的類型。朋友圈類型和其對應類如下:

  • 0 帶圖檔的動态,對應:com.tencent.mm.plugin.sns.ui.ai
  • 9 純文字動态,對應:com.tencent.mm.plugin.sns.ui.ae

這些類都直接或間接的實作了上面講到的com.tencent.mm.plugin.sns.ui.z接口。 這樣一來,就知道this.tQO會根據朋友圈的動态類型進行初始化,那麼,上面的SnsUploadUI.this.tQO.a方法很有可能就是發朋友圈動态的方法。 接下來,我們根據不同的朋友圈動态所對應的類來分别分析 0x2 文字動态分析 文字動态分析起來應該比圖檔動态來說簡單一些,我們就先來分析它。 上面講到,這類動态對應類是com.tencent.mm.plugin.sns.ui.ae,這個類裡我們主要看a方法,在看a方法之前,先看它傳入什麼參數,為了看清楚,這就要回看上文onMenuItemClick方法調用a方法的地方:

public final boolean onMenuItemClick(MenuItem menuItem) {    String unused2 = SnsUploadUI.this.desc = SnsUploadUI.this.tQN.getText().toString();    int pasterLen = SnsUploadUI.this.tQN.getPasterLen();    int privated = SnsUploadUI.this.tKm.getPrivated();    int syncFlag2 = SnsUploadUI.this.tKm.getSyncFlag();    ......    PInt pInt = new PInt();    if (SnsUploadUI.this.tQO instanceof a) {        Bundle bundle = new Bundle();        bundle.putInt("param_is_privated", privated);        bundle.putString("param_description", SnsUploadUI.this.desc);        bundle.putStringArrayList("param_with_list", new ArrayList(SnsUploadUI.this.uij.getAtList()));        bundle.putInt("param_paste_len", pasterLen);        try {            bundle.putByteArray("param_localtion", SnsUploadUI.this.uik.getLocation().toByteArray());        } catch (IOException e2) {            ab.printErrStackTrace("MicroMsg.SnsUploadUI", e2, "parse location error", new Object[0]);        }        bundle.putBoolean("param_is_black_group", SnsUploadUI.this.tQS);        bundle.putStringArrayList("param_group_user", SnsUploadUI.this.tQR);        bundle.putInt("param_contact_tag_count", SnsUploadUI.this.tOk);        bundle.putInt("param_temp_user_count", SnsUploadUI.this.tOl);        pInt.value = ((a) SnsUploadUI.this.tQO).getContentType();        z unused4 = SnsUploadUI.this.tQO;    } else {        SnsUploadUI.this.tQO.a(privated, syncFlag2, SnsUploadUI.this.tKm.getTwitterAccessToken(), SnsUploadUI.this.desc, SnsUploadUI.this.uij.getAtList(), SnsUploadUI.this.uik.getLocation(), (LinkedList) null, pasterLen, SnsUploadUI.this.tQS, SnsUploadUI.this.tQR, pInt, SnsUploadUI.this.tOj, SnsUploadUI.this.tOk, SnsUploadUI.this.tOl);    }}
           

這就是a方法調用的地方,根據這段代碼和編寫hook a方法的代碼來推出它的參數。hook代碼如下:

var ae = Java.use('com.tencent.mm.plugin.sns.ui.ae');var ae_a = ae.a.overload("int","int","org.b.d.i","java.lang.String","java.util.List","com.tencent.mm.protocal.protobuf.bdi","java.util.LinkedList","int","boolean","java.util.List","com.tencent.mm.pointers.PInt","java.lang.String","int","int");ae_a.implementation = function(isPrivate,syncFlag2,twitterAccessToken,desc,atList,location,list1,pasterLen,bool1,list2,pint1,str1,num1,num2){    var ret = ae_a.call(this,isPrivate,syncFlag2,twitterAccessToken,desc,atList,location,list1,pasterLen,bool1,list2,pint1,str1,num1,num2);    console.log("************Basic Info************");    console.log("isPrivate = " + isPrivate);    console.log("syncFlag2 = " + syncFlag2);    console.log("twitterAccessToken = " + twitterAccessToken);    console.log("desc = " + "'" + desc + "'");    if(atList.size()>0){        for(var i=0;i            console.log("atList[" + i + "] = " + atList.get(0));        }    }    if(location != null){        if(location.yRD.value != null){            console.log("location.yRD = " + location.yRD.value);        }        if(location.yRE.value != null){            console.log("location.yRE = " + location.yRE.value);        }    }    console.log("list1 = " + list1);    console.log("pasterLen = " + pasterLen);    console.log("bool1 = " + bool1);    if(list2 != null){        console.log("list2 = " + list2.size());    }    else{        console.log("list2 = " + list2);    }    console.log("pint1 = " + pint1.value.value);    console.log("str1 = " + str1);    console.log("num1 = " + num1);    console.log("num1 = " + num1);    return ret;}//ae.a
           

hook成功後,發一條純文字的朋友圈動态,列印出:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  是以,可得:

- privated(int):動态是否私密:0公開,1私密- desc(String):朋友圈的文本- AtList(List):艾特人的wxid- Location(com.tencent.mm.protocal.protobuf.bdi):定位資訊
           

好多參數我們不知道是什麼,不過問題不大,那些我們需要的參數已經能搞懂了。懂得a方法的參數,那能否嘗試直接調用它? 先來看一下com.tencent.mm.plugin.sns.ui.ae類的構造函數能否調用:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  構造函數有Activity類型的參數,Activity類型的參數是很難構造的,是以放棄構造com.tencent.mm.plugin.sns.ui.ae類來調用a方法。那我們直接去看a方法,看能不能找到有用的東西。 由于是文字動态,是以我們着重關注傳入的文本,即com.tencent.mm.plugin.sns.ui.SnsUploadUI類的desc成員,在a方法中它是第4個參數:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  可見this.desc在a方法中它是str,而且在a方法中,str隻有一處引用:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  str傳給了ayVar.adk()方法,找一下ayVar來自哪裡,它在a方法裡初始化,而且初始化方式很簡單:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  隻傳入一個數字就能初始化,我們初始化ay類的時候不用深究這個數字是什麼,和它傳入一樣的值即可。 在a方法的尾部,還看到一個引人注目的commit方法:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  猜測這就是釋出朋友圈的方法,寫個簡單的frida腳本來驗證一下:

if(Java.available){    Java.perform(function(){        var ay_class = Java.use("com.tencent.mm.plugin.sns.model.ay");        var desc = "To be, or not to be, that is a question.";        var ayInstance = ay_class.$new(2);        ayInstance.adk(desc);        ayInstance.commit();    });}
           

文字動态的内容是:To be, or not to be, that is a question. 果不其然,腳本運作後,文字動态發表成功了。  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  經過對com.tencent.mm.plugin.sns.ui.ae的a方法的分析,我們可以知道,a方法主要傳入一些發朋友圈動态所需要的通用的資料,比如動态是否私密,動态的文字描述,艾特的人,定位資訊等,這些資訊在其他類型的朋友圈動态中也會用得到。我們還知道發文字動态隻需要文字描述就能發表成功。 0x3 帶圖檔的朋友圈動态分析 帶圖檔的的動态和文字動态差不多,隻是多加了圖檔的參數,我們在分析此類動态時多關注圖檔在哪傳入即可。 帶圖檔的動态對應的類是com.tencent.mm.plugin.sns.ui.ai,有了上面的經驗,我們直接去看它的a方法(ai類有許多a方法,注意這裡說的a方法參數和com.tencent.mm.plugin.sns.ui.z接口裡的a方法參數一緻)。 在a方法的開頭,看到利用疊代器去周遊一個清單,周遊過程中組裝com.tencent.mm.plugin.sns.data.j類的資料,然後把j類放傳入連結表linkedList2中:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  在組裝資料的時候,我們看到j類構造時傳入一個字元串和數字,而這個字元串對應j類的path字段,這可能就是圖檔的路徑:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  那麼我們猜測j類就是存儲朋友圈的動态圖檔資訊的類,上面提到j類被放傳入連結表linkedList2中,那麼來看linkedList2被哪裡引用了  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  看到醒目的字元串:commit pic size,這應該是日志要列印的字元串,現在基本上可以确定j類就是存儲要發表的圖檔的資訊的類了,那麼linkedList2就是存儲所有将要發表的圖檔資訊,繼續往下尋找linkedList2還被哪裡引用了  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  可以看到linkedList2傳入兩個地方,一處傳入a方法:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  另一處傳入com.tencent.mm.plugin.sns.ui.ai$a類構造函數:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  linkedList2傳入a類後,又指派給成員變量tPF,這個tPF成員變量隻在a類的dU方法中被引用。  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  而dU方法在哪裡調用呢? 在a類的父類:com.tencent.mm.plugin.sns.model.h中,我們看到dU方法在u方法被調用:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

  而u方法在ai類的a方法中調用(可以回看前面的圖)。 分析到這,上面的linkedList2傳出去之後都終有所屬了,即最終都傳入了com.tencent.mm.plugin.sns.model.ay類ey方法。 知道圖檔往哪傳了,就寫段frida代碼調用試試吧。

if(Java.available){    Java.perform(function(){        var ay_class = Java.use("com.tencent.mm.plugin.sns.model.ay");        var j_class = Java.use("com.tencent.mm.plugin.sns.data.j")        var desc = "To be, or not to be, that is a question.";        var likedList_class = Java.use("java.util.LinkedList");        var linkedListInstance = likedList_class.$new();        var ayInstance = ay_class.$new(1);        var jInstance1 = j_class.$new("/storage/emulated/0/test1.jpg",2);        var jInstance2 = j_class.$new("/storage/emulated/0/test2.jpg",2);        var jInstance3 = j_class.$new("/storage/emulated/0/test3.jpg",2);        linkedListInstance.add(jInstance1);        linkedListInstance.add(jInstance2);        linkedListInstance.add(jInstance3);        ayInstance.ey(linkedListInstance);        ayInstance.adk(desc);        ayInstance.commit();    });}
           

上面的代碼在發送文本動态代碼的基礎上初始化三個j類,分别傳入三個本地圖檔路徑,再将三個類執行個體添加到連結清單,再将連結清單傳入ay類的ey方法,最後調用ay類的commit方法将動态發送出去,代碼運作,發現帶圖檔的朋友圈動态發表成功:  

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态
android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

- End -

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

看雪ID:落葉似秋

https://bbs.pediy.com/user-815293.htm 

*本文由看雪論壇 落葉似秋 原創,轉載請注明來自看雪社群。

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

推薦文章++++

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

* 為了了解反彙編引擎而寫的X86/X64反彙編引擎

* 捆綁包驅動鎖首病毒分析

* **遊戲逆向分析筆記

* 對寶馬車載apps協定的逆向分析研究

* x86_64架構下的函數調用及棧幀原理

好書推薦

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

﹀ ﹀ ﹀

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态

公衆号ID:ikanxue 官方微網誌:看雪安全 商務合作:[email protected]

android hook截取其他程式的按鈕事件_Android微信逆向實作發朋友圈動态