工具:JEB ,apkiller,AS,smaliIDE(动态调试)
BuildProp Enhancer 是一个Xposed软件,可以让apk在不开启debugable的时候进行动态调试
在你无从下手的时候我使用DDMS
中的traceView
工具来帮助我们减少工作量。
打开微信界面
打开DDMS
,在divices
对应的启动应用列表中选择腾讯微信对应包名(com.tencent.mm
)
如果列表太多,可以使用adb shell dumpsys activity top
查看当前android手机最前面的界面的pid和一些信息
然后在回到ddms顺着pid查找即可
如果在这里看不到应用,是因为app
没有在清单文件打开debugable=true
,在这里你可以选择以下两种方式。
1 使用apktools反编译 修改清单文件 在装回去(微信没有加壳,就算加了估计大家也可以在网上找到对应退壳教程)
略
2 使用 BuildProp Enhancer
,装载xposed开启后,在其启动界面勾选和配置如下
记得重启手机
3 刷机修改android
系统源码中的 ro.debugable=1
和ro.secure=1
(不推荐)
正式开始使用traceView
工具(我发现很多人居然不知道这个工具的名字= =但却使用过。)
鼠标点击Start Method Profiling(开始方法性能分析,对应上图红色箭头按钮)。
然后弹出一个界面,这个界面就是要问你要不要设置采样率(每隔多少毫秒进行一次方法堆栈记录(就是记录方法的使用过程时间信息)统计)
我们这里点击了一个塞子对吧?那必然触发android点击事件回调。关于事件分发机制这里不做过多讲解 ,我们在界面最下方的filter
输入click
然后发现其Children
有两个分别 是self
和********performItemClick(Landroid/view/View;IJ)Z**
根据分析只能是第二个选项然后点击********performItemClick(Landroid/view/View;IJ)Z**
我们发现有两个函数 占用cpu时间,所以我们需要跳去两个函数的父函数进行静态分析。
这里我们用jeb2.2进行反编译成java源文件导入Android Studio( 后续下文简称AS)
如果提示 JAVA heap 之类的错误 修改jeb目录下 bin/jeb.ini
jeb3.0+请修改批处理文件xxx.bat
接下来再回过头来看看我们要跳到父函数()
可不看部分:方法前面有个synthetic注释 。内部类私有属性或者方法被外部类访问时会成成synthetic类或者synthetic方法。比如你会看access$001等。
如何证明? 反射机制就有这个相关api
Method method;
method.isSynthetic()
为什么圈出两个函数?因为我们前面tracView不是说显示两个函数占用Cpu时间之和为100%嘛(肯定有喷子说不是还有其他函数调用吗为什么没有显示占用CPU?答不在采样频率窗口期)。
又因为后一个函数要使用前面一个的返回值,所以很用可能前面才是关键。
所以我们这里跳转到实现c函数处,在AS双击c函数只会跳转到接口或者抽象类
((c) g.n(c.class)).getProvider().c(emojiInfo);
实现类可以在tracview看到了
同理有三个子函数分别占用cpu时间比,那么在AS跳转到父函数 ****emoji.e.g.c*****
先分析第一个方法返回Cursor
对象,这个对象大家在做撸码DAO层的时候应该不陌生把?很明显跟这个没关系,第二个方法同理Du.getCount()
,那么我们最后肯定落在第三个方法上
第一个方法:
Cursor Du = i.aEA().igx.Du(bi.getInt(emojiInfo.getContent(), 0));
第二个方法
Du.getCount()
第三个方法
int eF = bi.eF(Du.getCount() - 1, 0);
分析到这里发现一个断言和一个用于生成随机数的函数。我们假设是不是用这里随机函数生成塞子点数呢?我们继续返回****emoji.e.g.c*****
这个函数回头看看
如何证明我们说的呢?
我们在eF函数加入两个函数,一个log,和一个打印方法堆栈函数
public static int eF(int i, int i2) {
Thread.dumpStack();
Assert.assertTrue(i > i2);
int rel = new Random(System.currentTimeMillis()).nextInt((i - i2) + 1) + i2;
Log.d("FMY", "eF() called with: i = [" + i + "], i2 = [" + i2 + "] ,rel =["+rel+"]" );
return rel;
}
然后反编译回去,在安装到手机点击塞子查看日志(log)
当然如果能直接操作java代码在回编译改多好,上面对应smali代码(使用java2smali)
.method public static eF(II)I
.registers 6
.param p0, "i" # I
.param p1, "i2" # I
.prologue
.line 17
invoke-static {}, Ljava/lang/Thread;->dumpStack()V
.line 19
if-le p0, p1, :cond_4f
const/4 v1, 0x1
:goto_6
invoke-static {v1}, Ljunit/framework/Assert;->assertTrue(Z)V
.line 20
new-instance v1, Ljava/util/Random;
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v2
invoke-direct {v1, v2, v3}, Ljava/util/Random;->(J)V
sub-int v2, p0, p1
add-int/lit8 v2, v2, 0x1
invoke-virtual {v1, v2}, Ljava/util/Random;->nextInt(I)I
move-result v1
add-int v0, v1, p1
.line 22
.local v0, "rel":I
const-string v1, "FMY"
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;->()V
const-string v3, "eF() called with: i = ["
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2, p0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v2
const-string v3, "], i2 = ["
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v2
const-string v3, "] ,rel =["
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v2
const-string v3, "]"
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v2
invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 23#删除 其他line同理 全部删除
return v0
.line 19#删除 其他line同理 全部删除
.end local v0 # "rel":I
:cond_4f
const/4 v1, 0x0
goto :goto_6
.end method
删除所有java2smali工具生成的行号 就的.line xxx
这里我用apkkiller修改反编译回去即可。
在这里打log是为了大家后面方便改塞子和猜拳代码的观察和修改
最后大家生活愉快。
另一种思路:全局搜索 Ramdom 关键字然后逐个分析,但是我不推荐,前提是你知道他们开发者使用了这个函数用来生成塞子关键代码