摘要:前提這篇文章我主要會講我所掌握逆向的一些小技巧,及如何一步步的爬取到微信朋友圈的數據的過程。關于微信逆向的工作,可能很多小伙伴呢都干過這事,最讓人頭疼的就是如何快速定位一個點。
前提這篇文章我主要會講我所掌握逆向的一些小技巧,及如何一步步的爬取到微信朋友圈的數據的過程。關于微信逆向的工作,可能很多小伙伴呢都干過這事,最讓人頭疼的就是如何快速定位一個Hook點。還有就是如何理清被混淆之后的代碼。這兩個點弄清楚,可能Hook你所要的東西,那就是很輕松了。我這里只是針對Android的微信7.0.3版本最新版本7.0.4
生活在這樣的大數據年代,當然數據是最重要的,如果作為一個商戶,你有N多個客戶,那如何對這些商戶進行分類,其次就是做到精準營銷,然后那就是Money了。 那位對于朋友圈數據的分析會起到非常重要的作用。給每個商戶打上一個Tag。我這只是一個簡單的舉例,不妨礙大家閱讀下面的文章,那么廢話不多說了。開始編碼
如果你掌握了以下內容,你將可以獲取好友的朋友圈圖片,視頻,評論,點贊及你之前所看到的內容,即使他已經關閉朋友圈,只要你看過,那就是存在數據庫中,我們就可以拿得到
準備工作一臺root的手機
一個微信Apk
Xpose 工具
Jadx
加載微信源碼以上除了手機root外,其他的工具我都附上了鏈接,大家直接去下載即可。對于如何root手機,作為一個Android 開發者,自行Google吧 推薦通過TWRP+SuperSu 的方式,很多機型都可以燒成功。網上很多案例。xpose 的使用這里我就不過多的贅述了,一些Api的調用而已。Jadx這個工具呢必不可少,當然你也可以使用其他的反編譯工具(dex2jar,jeb,jd-gui....)用著順手就行,我這里使用的是Jadx,啊真的很好用。
以下的操作會讓你加快Hook點定位
用Jadx 打開下載好的微信Apk,編譯成功應該是如下頁面
把編譯好的項目另存為Gradle項目
用AS 打開剛剛另存的Gradle項目,AS 可以幫們建立快速索引,方便我們調試找Hook點。可能還有更好的操作,有同學知道的可以留言區交流以下
以上操作完成之后,準備工作已經大部分都完成了。
朋友圈數據庫這里我就簡單介紹一下,網上也有這方面的資料,SnsMicroMsg.db這個就是保存我們朋友圈數據的數據庫,SnsInfo這個表是具體內容,我們可以在/data/data/com.tencent.mm/MicroMsg/md5({mm+uin})賬號目錄下找到這個文件,而且也沒有對數據庫加密,可一直直接Navicat打開的,但是微信聊天記錄數據庫就是加密的,需要破解密碼才能打開,密碼一直沒變過還是通過{IMEI+UIN}md5即可,回到主題,但是雖然你可以拿到朋友圈數據庫,但是你發現并沒有什么卵用,里面的數據全部加密了,完全摸不著頭腦。這里大家可以看看這個數據庫文件。除了能看到userName 和createTime 有點用,其他的字段真是看不到頭緒
我們接下來注意觀察第二張圖片,content,attrBuf,這二個字段里面全是BLOB格式的,可以發現,小老弟把數據藏起來了啊。那難道我們就沒有辦法了嗎!當然還是可以拿的到的,那就是通過微信源碼查看了,我們接下來的工作就是,看微信是如何拿到這個BLOB,如何進行解析的。我們也通過他的方式進行解析不就得了嗎。接下來進入主題。
Hook朋友圈Activity我這里Hook點是個人朋友圈入口,通過點擊通訊錄,進入個人的朋友圈頁面,然后找到這個Activity是哪一個Activity,然后查看這個頁面的源碼。目前到現在為止,還沒有用到Xpose 這個工具,那么我們如何知道這個Activity是哪個呢,其實Android系統給我們提供了一個dumpsys的工具,根據包名可以獲取到當前是哪個Activity,我們只需要adb shell 登進去
點擊進入某個人的這個頁面,然后調用一下代碼就可以獲取當前是哪個activity了
dumpsys activity |grep -i com.tencent.mm
打開微信個人朋友圈頁面執行上述代碼之后即可獲取當前頁面,這時候可以在terminal里面看到如下
com.tencent.mm/.plugin.sns.ui.SnsUserUI
到此為止,我們已經知道了個人朋友圈頁面在哪里了。接下來我們去剛剛AS打開的微信項目里面找到這個SnsUserUI Activity了,打開頁面可以發現很多代碼已經混淆的很難讀懂了,什么a.a.a,b.c.da這樣的包名已經方法名。但是我們這時候不要慌張。接下來打開手機,我們可以通過微信這個頁面的UI看到,這是一個list,那肯定不是listview就是recyclerview了。這是后打開AS找到SnsUserUI這個類,然后倒開他的structure目錄
可以發現里面有個方法initView 是不是很熟悉,我們經常寫代碼也會這樣寫,那么我們這就點進去看一下, 這里就不粘貼很多代碼。有興趣可以自行反編譯看看
public final void initView() {
this.qXD = (RelativeLayout) findViewById(f.sns_user_year_tip_layout);
this.qXE = (TextView) findViewById(f.sns_user_year_tip);
this.qXD.post(new Runnable() {
public final void run() {
LayoutParams layoutParams = new LayoutParams(-1, -2);
layoutParams.topMargin = x.aj(SnsUserUI.this) + SnsUserUI.this.getResources().getDimensionPixelSize(i.d.ActionBarHeight);
SnsUserUI.this.qXD.setLayoutParams(layoutParams);
}
});
this.qXz = new as(this, new a() {
public final void fc(int i, int i2) {
super.fc(i, i2);
}
}, this.hMy, new as.c() {
});
this.qXA.naj.setAdapter(this.qXz);
this.qXA.naj.setOnItemClickListener(new OnItemClickListener() {
public final void onItemClick(AdapterView<");
打開觀察之后,發現這里面有個setAdapter的地方,對的!肯定數據就在這個Adapter里面,因為我們平時寫list 渲染的時候數據都是通過adapter 渲染到list 上的,我們繼續點進去看看這個adapter 是如何寫的。
朋友圈Adapter深入我們通過上述的方法已經找到了Adapter了,我們點擊(this.qXz)這個字段發現他的類名是as,點擊進去發現,果然沒錯。
public final class as extends BaseAdapter {
private Activity coM;
boolean cog = false;
private String country;
List list = new ArrayList();
String lnO = "";
private String ngt = "";
Map qBc = new HashMap();
Map qBd = new HashMap();
int qBe = 0;
int qBf = 0;
String qHI = "";
private bd qKW = null;
private az qQo;
Map qQp = new HashMap();
private f qQq;
boolean qQr = false;
at qQs;
private c qQt;
int qQu = BaseClientBuilder.API_PRIORITY_OTHER;
int qQv = 0;
private long qQw = 0;
private long qQx = 0;
int qQy = 0;
protected OnClickListener qQz = new OnClickListener() {
public final void onClick(View view) {
if (view.getTag() instanceof TimeLineObject) {
TimeLineObject timeLineObject = (TimeLineObject) view.getTag();
if (as.Yp(timeLineObject.Id)) {
h.ptS.X(10231, "1");
com.tencent.mm.av.a.agc();
} else {
h.ptS.X(10090, "1,0");
if (!(com.tencent.mm.q.a.bN(as.this.coM) || com.tencent.mm.q.a.bL(as.this.coM))) {
com.tencent.mm.av.e a = g.a(af.getAccPath(), timeLineObject, 8);
a.fuR = as.this.userName;
com.tencent.mm.av.a.b(a);
}
}
as.this.notifyDataSetChanged();
}
........省略很多代碼........
那我們接下來怎么找呢,Adapter已經拿到了,難道還是一點點翻嗎?當然不是了,想想我們日常寫listview的Adapter時候,是不是在getView的時候進行給view賦值的操作,這時候微信也一樣,我們同樣打開as這個類的structure目錄
從圖中可以發a這個方法嫌疑很大,里面有各種view
private void a(int i, QFadeImageView qFadeImageView, TextView textView, TextView textView2, TextView textView3, TextView textView4, int i2, d dVar, int i3) {
n nVar = (n) getItem(i);
TimeLineObject cmi = nVar.cmi();
bys q = aj.q(nVar);
Object obj = null;
if (q != null && (((q.vUS & 2) == 2 && q.wnx != null) || ((q.vUS & 4) == 4 && q.vTG != null))) {
obj = 1;
}
if (!(!this.cog || q == null || obj == null || this.userName == null || !this.userName.equals(nVar.field_userName))) {
textView3.setBackgroundResource(com.tencent.mm.plugin.sns.i.e.personactivity_sharephoto_icon);
textView3.setVisibility(0);
}
........省略很多代碼........
我們點進去可以看到,果然如同我們所想一樣,第一行就暴露了, n nVar = (n) getItem(i); 可以發現這個n類就是每個item的數據源,而且往下看一行,這個類名也很有嫌疑TimeLineObject,而且是通過n 調用cmi方法返回的,那會不會是數據庫里面的那個BLOB字段呢。容我們點進去看看
Map qAb = new ConcurrentHashMap();
........省略很多代碼........
public final TimeLineObject cmi() {
if (this.field_content == null) {
return e.ahM();
}
TimeLineObject timeLineObject;
if (this.qzT == null) {
this.qzT = g.u(this.field_content) + g.u(this.field_attrBuf);
}
if (qAb.containsKey(this.qzT)) {
timeLineObject = (TimeLineObject) qAb.get(this.qzT);
if (timeLineObject != null) {
return timeLineObject;
}
}
try {
timeLineObject = (TimeLineObject) new TimeLineObject().parseFrom(this.field_content);
qAb.put(this.qzT, timeLineObject);
return timeLineObject;
} catch (Exception e) {
ab.e("MicroMsg.SnsInfo", "error get snsinfo timeline!");
return e.ahM();
}
}
這時候發現了不可思議的東西 field_content 和 field_attrBuf 這兩個字段,這個不就是數據庫里面定義的content和attrbuf字段嗎。原來是通過這樣的方式轉化的。繼續往下看,可以發現如果 " qAb.get(this.qzT) " 拿不到TimeLineObject的話,就會自己new 一個然后存儲到這個Map集合中,做緩存作用,那我們是不是可以通過這種方式呢拿到這個TimeLineObject,當然是可以的。
解碼朋友圈BLOB字段通過上面我們已經知道了如何解析朋友圈數據庫content 字段了,通過TimeLineObject的parseFrom方法進行轉化。但是當我們點到TimeLineObject這個類里面你會發現,臥槽,怎么這樣。
public class TimeLineObject extends a { public String Id; public int dhE; public int eRm; public String hPC; public String jfn; public int ozl; public String qEy; public String qXr; public av qiN; public csw qiP; public String uzJ; public int vTa; public int wsA; public String wsB; public ccr wsC; public cqv wsD; public int wsE; public String wsu; public axc wsv; public du wsw; public ta wsx; public String wsy; public int wsz;
可以發現里面嵌套了很多的類。這是我時候我們怎么做呢。我也沒有很好的辦法,然后就通過一個類一個類的點開,然后打印它里面是String字段,這里應該來個表情捂臉,有好方法的同學們可以留言區交流,但是當我打印到了wsu這個字段的時候發現,可以拿得到我們發朋友圈時的標題了。瞬間感覺到了輕松。但是這只是一個很小的進步,我們要拿的可是朋友圈圖片視頻評論點贊所有內容啊。
獲取朋友圈信息源具體內容那么如何獲取到圖片視頻等信息呢。那么我們還是要回到adapter 這個類里面。在他的a方法中有個構造參數QFadeImageView看到ImageView 感覺離獲取圖片地址不遠了。那么我們繼續觀察這個方法。
private void a(int i, QFadeImageView qFadeImageView, TextView textView, TextView textView2, TextView textView3, TextView textView4, int i2, d dVar, int i3) {
n nVar = (n) getItem(i);
TimeLineObject cmi = nVar.cmi();
bys q = aj.q(nVar);
Object obj = null;
if (q != null && (((q.vUS & 2) == 2 && q.wnx != null) || ((q.vUS & 4) == 4 && q.vTG != null))) {
obj = 1;
}
........省略很多代碼........
if (cmi.wsx.vqt == 1) {
qFadeImageView.setVisibility(0);
af.cjr().a(cmi.wsx.vqu, (View) qFadeImageView, this.coM.hashCode(), com.tencent.mm.plugin.sns.model.g.a.IMG_SCENE_SNSSUSER, azVar);
} else if (cmi.wsx.vqt == 2) {
textView4.setText(bp.bb(cmi.wsx.Desc, ""));
textView4.setVisibility(0);
} else if (cmi.wsx.vqt == 21) {
nVar.cmA();
boolean z = true;
if (this.cog) {
z = true;
} else if (m.a(nVar, q)) {
z = false;
}
qFadeImageView.setVisibility(0);
af.cjr().a(cmi.wsx.vqu, (View) qFadeImageView, this.coM.hashCode(), com.tencent.mm.plugin.sns.model.g.a.IMG_SCENE_SNSSUSER, azVar, z);
}
........省略很多代碼........
從上面的代碼中我們可以看得到,微信這邊調用了這個方法把 af.cjr().a(xxxxx)把Imageview 傳入了進入,點擊查看了其他的參數只有cmi.wsx.vqu這個參數有用。可以發現他調用了TimeLineObject里面ta這個類里面的集合vqu字段。
public final class ta extends a { public String Desc; public String Title; public String Url; public int vqt; public LinkedListvqu = new LinkedList(); public int vqv; public String vqw; public ayd vqx;
先不管調用了imageview 這個方法干了什么,我們先把這個集合里面的東西給打印出來。
azc.toString{id:13055580620160049319 Desc:黃昏 Title: Url:http://mmsns.qpic.cn/mmsns/kEgG3ynkRxponsiaCyzhl9Gniaz7GdWLujZdSMV4kmlME6fia07m577MQ3OU9kMKibjAIiaMc7MWHKF0/0 ckO:null qDg: vSY:http://mmsns.qpic.cn/mmsns/kEgG3ynkRxponsiaCyzhl9Gniaz7GdWLujZdSMV4kmlME6fia07m577MQ3OU9kMKibjAIiaMc7MWHKF0/150 vTc: vTf: vTh: vTi: vTj: vTm:f814a6351db8cd3ef118e14e6ff70b80 vTn:WSEN6qDsKwV8A02w3onOGQYfxnkibdqSOkmHhZGNB4DFJ9qdBeATTF8UiaDA1go3GLryav2ukPJK06SOFjchiaqJA vTp:14604729124651202068 vTq:WSEN6qDsKwV8A02w3onOGQYfxnkibdqSOkmHhZGNB4DFJ9qdBeATTF8UiaDA1go3GLwHGCkbxWxDWW5dsMhLsBUg vTs:14604729124651202068 vTt:}
不出我們所料,里面果然有我們需要的東西,而且把Desc,url都給了我們,通過我的測試發現圖片的url并不能打開。但是視頻鏈接的url是可以打開的。是不是很happy。這時候我們已經拿到了視頻源和分享的鏈接源,但是如何拿到圖片源呢。那我們繼續查看源碼,發現點擊剛剛給上述調用了這個集合的方法里面并沒有什么東西,都是一些賦值的操作。沒有獲取具體圖片源信息的位置。
這時候我是這樣操作的,因為這個獲取到的Url雖然我們解析不了,但是我們可以點擊查看Url的引用,看哪里有著對這個Url字段的引用,微信內部是如何解析的 當你點擊開的時候會發現并沒有什么用處,見下圖
引用的地方太多了,根本不知道如何下手。這時候又會一頭霧水不知道如何進行下一步。那么我們只能回到起點,再次尋找下一個hook點。
進入朋友圈詳情二次Hook既然上一個頁面我們拿不到圖片的數據,那我們就深入進入朋友圈具體內容頁面,然后進行二次Hook,獲取到朋友圈具體圖片內容。我們還是以同樣的方法進行,hook,打開此頁面然后通過 dumpsys 獲取當前的activity,進入這個activity查看源碼下手找到圖片的具體位置。
通過代碼查看到這是一個com.tencent.mm.plugin.sns.ui.SnsGalleryUI,顧名思義,朋友圈相冊頁面。那么我們點進去看看具體寫了些什么東西
public class SnsGalleryUI extends SnsBaseGalleryUI implements a {
private int qKn = 0;
private String userName = "";
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
getWindow().addFlags(128);
wS(this.mController.xyi.getResources().getColor(c.dark_actionbar_color));
LV(this.mController.xyi.getResources().getColor(c.dark_actionbar_color));
initView();
}
........省略很多代碼........
并沒有什么東西可看的。沒有具體的內容,我們進入父類SnsBaseGalleryUI看看做了些什么進入看了一下,發現方法體也沒有什么東西,但是一個字段引起了我的注意
public abstract class SnsBaseGalleryUI extends MMActivity implements a {
private boolean jop = true;
private LinearLayout qKf;
r qKg;
private LinearLayout qKh;
s qKi;
private boolean qKj = true;
private TextView qKk = null;
protected SnsInfoFlip qKl;
protected Button qKm;
........省略很多代碼........
我們從上面的字段可以猜到哪一個是引起了我的注意,很明顯那就是SnsInfoFlip這個類。繼續點擊去看看里面寫了些什么內容。據我猜測這應該也是個adapter,viewpager的adapter,通過structure目錄可以發現里面有個getView的方法,但是反編譯并沒有完全把smail 內容轉換成java 類型,看著比較費勁,那我們繼續往下尋找,這時候你會有驚喜
可以看到這個函數里面參數是azc記憶力好的小伙伴應該還記得,前面所說的,azc這個類里面包含著url,這時候找到了用的地方了。把代碼貼上大家可以看一下
private void a(azc azc, int i, String str) {
String str2;
long j = 0;
if (this.qNm != null && (this.qNm instanceof MMGestureGallery)) {
float f;
float f2;
float f3;
if (azc.vTb != null) {
f = azc.vTb.vTP;
f2 = azc.vTb.vTO;
} else {
f = 0.0f;
f2 = 0.0f;
}
if (f <= 0.0f || r5 <= 0.0f) {
if (azc.Id.startsWith("Locall_path")) {
str2 = an.fQ(af.getAccSnsPath(), azc.Id) + com.tencent.mm.plugin.sns.data.i.m(azc);
} else {
str2 = an.fQ(af.getAccSnsPath(), azc.Id) + com.tencent.mm.plugin.sns.data.i.d(azc);
}
Options akG = com.tencent.mm.sdk.platformtools.d.akG(str2);
........省略很多代碼........
上面的字段local_path,博主感覺找到了那個點,開始hook這個method an.fQ(af.getAccSnsPath(), azc.Id) + com.tencent.mm.plugin.sns.data.i.m(azc);,surprise這個方法返回的值就是朋友圈圖片的本地路徑。打印下來是這樣的路徑
/storage/emulated/0/tencent/MicroMsg/cd1c67e86728a83c6079e2b48ia/sns/1/4/snst_13069507190820253764
//這個路徑通過adb pull 出來,就是一張圖片
總結
通過以上的分析,我們已經定位到了哪些類和哪些方法可以幫助我們獲取到朋友圈的數據。
接下來我們要做的事爬取朋友圈數據,這里兩種方式爬取數據
xpose
這種方式是最簡單粗暴的,hook微信的內部方法
首先通過sqlite拿到SnsMicroMsg.db 然后query SnsInfo這個表里面的數據content 通過XposedHelpers 實例化TimeLineObject 調用TimeLineObject的parseFrom方法(可獲取朋友圈標題) 然后獲取TimeLineObject里面的ta類,獲取到集合azc(視頻鏈接可直接通過這個類獲取) 然后循環這個集合,通過SnsInfoFlip里面的a方法獲取到thumb的地址。
classloader
這種方式呢,也是可行的,只需要手機root即可不需要xpose框架
首先把當前微信目錄下的SnsMicroMsg這個數據庫導出到Sdcard中 然后一樣通過sqlite打開數據庫,query SnsInfo table 接著DexClassLoader把微信apkloader起來,同時把我們上訴介紹到的類都給load進去。 然后通過反射的方式執行第一種xpose 方式也行。
通過以上兩種方式皆可完成爬朋友圈數據功能,下面拋個爬取數據成功的頁面。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7306.html
摘要:時間永遠都過得那么快,一晃從年注冊,到現在已經過去了年那些被我藏在收藏夾吃灰的文章,已經太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設置私密了,不收拾不好看呀。 ...
摘要:吃飽喝足,兩個人扶著腰走在路上炫腹還是女盆友的提醒說,你不是會小程序嗎,能不能寫一個點贊的小程序來用。哎還真是,我自己擼一個也是闊以的,說不定還能給其他人用。比較適合想要練手小程序和的童鞋全部的代碼還請移步我的歡迎和。 showImg(https://segmentfault.com/img/remote/1460000015245489?w=530&h=153); 發生背景: ???...
閱讀 940·2021-09-27 13:36
閱讀 888·2021-09-08 09:35
閱讀 1064·2021-08-12 13:25
閱讀 1437·2019-08-29 16:52
閱讀 2907·2019-08-29 15:12
閱讀 2726·2019-08-29 14:17
閱讀 2606·2019-08-26 13:57
閱讀 1012·2019-08-26 13:51