: :其他软件 2019-09-15 14:43:13
1、用android sdk里的 monitor 工具进行 dump view hierarchy 分析,发现朋友圈的列表用的是ListView,条目中广告标识的 layout 是 LinearLayout ,resource id 是 eb9
2、接着就是MT管理器发挥作用的时候了,对微信的安装包里的 resources.arsc 进行 id 过滤,找到 eb9 对应的16进制id 是 7F111B0F。
接下来用MT管理器的 Dex编辑器++ 选择所有的dex文件,搜索 7F111B0F 的16进制整数,结果里 7F111B0F 对应的名称都是 ad_info_ll。
于是再对 ad_info_ll 进行代码搜索,发现搜索结果多了一条,在 com.tencent.mm.plugin.sns.ui.bb 里被使用,打开该类并反编译成java代码
构造函数里对相关 view 进行查找,并且该类还有一个 setVisibility 方法,广告标识要显示,传入的就应该是View.VISIBLE,对应的 int 的值为 0public bb(View view) {
this.view = view;
ab.i("MicroMsg.TimeLineAdView", "adView init lan " + this.mRP);
this.qGB = (TextView) this.view.findViewById(f.ad_info_tv);
this.qGC = (TextView) this.view.findViewById(f.ad_link_tv);
this.qGD = this.view.findViewById(f.ad_info_tv_arrow);
this.qGE = this.view.findViewById(f.ad_lbs_icon_tv);
this.qGA = (LinearLayout) this.view.findViewById(f.ad_info_ll);
this.qGB.setText(" " + this.view.getResources().getString(j.sns_ad_tip) + " ");
}
上面的第一行代码对方法的最后一个参数进行了一系列调用,返回 com.tencent.mm.plugin.sns.ui.av 类的对象。av avVar = (av) auVar.cky().cim().get(Integer.valueOf(i));
...
...
if (avVar.qkM) {
baseViewHolder.pJU.setVisibility(8);
} else {
ab.i("MicroMsg.BaseTimeLineItem", "adatag " + avVar.qDU);
baseViewHolder.pJU.x(Long.valueOf(avVar.qDK), new com.tencent.mm.plugin.sns.data.b(baseViewHolder.pJU, baseViewHolder.position, avVar.qml, avVar.qDK, avVar.qDQ));
baseViewHolder.pJU.a(avVar.qDT, avVar.qDS);
baseViewHolder.pJU.setVisibility(0);
if (baseViewHolder.qzu != null && baseViewHolder.qzu.getVisibility() == 0) {
if (baseViewHolder.pJU.qGC.getVisibility() != 0) {
obj = 1;
} else {
obj = null;
}
if (obj != null) {
LayoutParams layoutParams = (LayoutParams) baseViewHolder.qzu.getLayoutParams();
layoutParams.setMargins(layoutParams.leftMargin, BackwardSupportUtil.b.b(this.mActivity, 0.0f), layoutParams.rightMargin, layoutParams.bottomMargin);
baseViewHolder.qzu.setLayoutParams(layoutParams);
}
}
}
而该变量又是在该类的构造方法进行赋值的,对构造方法进行hook,打印调用栈。发现在类 com.tencent.mm.plugin.sns.ui.a.a 的构造方法public final w cky() {
return this.qBs;
}
中进行实例化,传入的参数值是 this,即该类变量本身,找到代码 av avVar = (av) auVar.cky().cim().get(Integer.valueOf(i)) 中 cim 方法返回的是成员变量 qHUpublic a(MMActivity mMActivity, ListView listView, com.tencent.mm.plugin.sns.ui.d.b bVar, i iVar, String str, b bVar2) {
this.qHT = new au(mMActivity, listView, bVar, iVar, this);
this.qHT.qta = true;
if (bVar2 == null) {
bVar2 = new c();
}
this.qHU = bVar2;
this.qHU.a(mMActivity, this.qHT, str);
b bVar3 = this.qHU;
com.tencent.mm.vending.f.a.i("Vending.ForwardVending", "Vending.setRangeSize(%s)", new Object[]{Integer.valueOf(10)});
bVar3.a = 10;
this.qHU.addVendingDataChangedCallback(this.qHW);
}
该变量在上面的构造函数中被赋值,不是参数中类 com.tencent.mm.plugin.sns.ui.a.b.b 的实例 bVar2,就是实例化一个 com.tencent.mm.plugin.sns.ui.a.b.c。public final Vending cim() {
return this.qHU;
}
这下有点思路了,根据类 au 的成员变量 qkM 可以分析是否是广告,而 au 可从代码 av avVar = (av) auVar.cky().cim().get(Integer.valueOf(i)) 中获得,在调用链最后的 get 方法中保存广告条目,在 getView 中判断是否是广告,是广告就返回空视图,不是就继续调用 this.qHT.h(i, view),不就可以去除广告了吗。public final View getView(int i, View view, ViewGroup viewGroup) {
return this.qHT.h(i, view);
}
调用父类 com.tencent.mm.vending.base.Vending 的同名方法public final <T> T get(int i) {
if (this.c != 0) {
return super.get(Integer.valueOf(i));
}
a.e("Vending.ForwardVending", "mCount is 0, why call get()?", new Object[0]);
return null;
}
上面 get 方法调用一个参数的 a 方法,一个参数的 a 方法调用两个参数的 a 方法,最终调用需要子类实现的 resolveAsynchronous 方法。public <T> T get(_Index _Index) {
return a((Object) _Index);
}
private _Struct a(_Index _Index) {
Looper myLooper = Looper.myLooper();
if (myLooper != this.c && myLooper != this.d) {
throw new IllegalAccessError("Call from wrong looper");
} else if (this.g.get()) {
return null;
} else {
i lock = getLock(_Index);
if (invalidIndex(_Index)) {
return lock.b;
}
if (myLooper == this.c) {
return b(lock, _Index).b;
}
a(lock, (Object) _Index);
return lock.b;
}
}
private boolean a(i<_Struct, _Index> iVar, _Index _Index) {
synchronized (iVar.c) {
if (!iVar.f || iVar.d || iVar.e) {
this.q = true;
Object resolveAsynchronous = resolveAsynchronous(_Index);
this.q = false;
if (iVar.g) {
return false;
}
a((i) iVar, (Object) _Index, resolveAsynchronous);
return false;
}
return true;
}
}
protected abstract _Struct resolveAsynchronous(_Index _Index);
在子类 com.tencent.mm.plugin.sns.ui.a.b.a 中发现了 Cx 方法,protected Object resolveAsynchronous(Object obj) {
return Cx(((Integer) obj).intValue());
}
protected abstract _Struct Cx(int i);
该方法调用了同类的 Cw 方法,改方法的代码有点长,就不贴了,最终返回了我们需要的类 av 的实例public final Object Cx(int i) {
return Cw(i);
}
所有源代码已上传 https://github.com/ColoThor/WeChatSnsAdprivate Context context;
private final Set<Integer> adSet = new HashSet<>();
private void hookA(final XC_LoadPackage.LoadPackageParam lpParam) {
try {
String className = "com.tencent.mm.plugin.sns.ui.a.a";
final Class clazz = lpParam.classLoader.loadClass(className);
if (clazz != null) {
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
if (constructor.getParameterTypes().length == 6) {
XposedHelpers.findAndHookConstructor(clazz, constructor.getParameterTypes()[0],
constructor.getParameterTypes()[1], constructor.getParameterTypes()[2],
constructor.getParameterTypes()[3], constructor.getParameterTypes()[4],
constructor.getParameterTypes()[5], new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
context = (Context) param.args[0];
adSet.clear();
}
});
break;
}
}
XposedHelpers.findAndHookMethod(clazz, "getView", int.class, View.class, ViewGroup.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
if (adSet.contains(param.args[0])) {
return new View(context);
}
return XposedBridge.invokeOriginalMethod(param.method, param.thisObject,
param.args);
}
});
} else {
LogUtil.e(TAG, "class " + className + " not found");
}
className = "com.tencent.mm.plugin.sns.ui.a.a$1";
final Class dataChangeClazz = lpParam.classLoader.loadClass(className);
if (dataChangeClazz != null) {
XposedHelpers.findAndHookMethod(dataChangeClazz, "cll", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
adSet.clear();
}
});
} else {
LogUtil.e(TAG, "class " + className + " not found");
}
} catch (Throwable e) {
LogUtil.e(TAG, "hookA err:" + Log.getStackTraceString(e));
}
}
private void hookC(final XC_LoadPackage.LoadPackageParam lpParam) {
try {
final String className = "com.tencent.mm.plugin.sns.ui.a.b.a";
final String avClassName = "com.tencent.mm.plugin.sns.ui.av";
final Class clazz = lpParam.classLoader.loadClass(className);
final Class avClazz = lpParam.classLoader.loadClass(avClassName);
if (avClazz == null) {
LogUtil.e(TAG, "class " + className + " not found");
return;
}
final Field isAdField = XposedHelpers.findFieldIfExists(avClazz, "qkM");
if (clazz != null) {
XposedHelpers.findAndHookMethod(clazz, "Cw", int.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Object result = param.getResult();
if (result == null) {
return;
}
boolean isAd = (boolean) isAdField.get(result);
if (isAd) {
int position = (int) param.args[0];
adSet.add(position);
LogUtil.e(TAG, "position: " + position + " is ad");
}
}
});
} else {
LogUtil.e(TAG, "class " + className + " not found");
}
} catch (Throwable e) {
LogUtil.e(TAG, "hookA err:" + Log.getStackTraceString(e));
}
}
TAG: Xposed,微信,朋友圈,广告,教程
10-12游戏程序设计教程,从游戏引擎构建到实际应
10-12VS2010之MFC入门到精通教程
10-12Linux 开发教程
10-12Visual C++MFC入门教程
10-12单片机C语言编程教程
10-12点云库PCL学习教程
10-11QT从入门到精通教程
10-11数据结构教程(第五版)
10-11wps表格组合图表制作,图表制作教程
10-11wps抢先版自动生成目录的教程
10-11word制作书法字帖模板的详细教程
10-06Excel制作一张混合型图表图文教程
10-06PPT表格行高调整的教程
10-06excel打印时显示所有批注图文教程
10-04excel账本的详细制作图文教程
10-04word自定义水印并添加的教程
10-04wps放大文字详细图文教程