//-------------------------华丽分割线---------------------------//
Response:
{
“code”: 11,
“message”: “activationCode length is illegal”
}
看下这个message 它说激活码太短,那么多长才算正常长度呢?后面我买了了个激活码,发现这个激活码长度为32个字符,正好是个md5的转成BigInteger的长度。这是后话。。。
这~~~这个授权的请求和和验证已经算是非常简单的了,请求和返回数据都是http协议,而且都是明文,而且请求体没有校验参数。这里猜测一下哈,客户端拿到这么个请求结果就能判断是不是已经激活。理论上来说激活成功和激活失败的返回的json格式是一致的,当然也不排除会有不同的情况存在。先不管,先按激活成功和失败的返回结构一样来猜测,那么一般我们用返回的code来判断。这个code一般为错误码,而且一般来说0,1,-1 是有很特殊含义的。这里我们尝试用0,1,-1 来替换上面的11 。(这里只是按照一种正向开发的方式推测)
那么怎么能让手机收到被修改后的json呢?我用的Charles的URL的映射功能 菜单栏Tools —>Map Local Settings
,然后添加一条映射规则,大概长这么个样子: 
那个ss.json 文件里面我们放如下字符串:
{
“code”: 0,
“message”: “activationCode length is illegal”
}
其实到这里呢,这个app就已经破解完了,理论上来说付费功能就可以使用了。能这么做主要是其一本地验证太过于简单,而且验证的地方太少就一次验证。
2、修改反编译后的Smali代码绕过授权验证
理论上来说所有的发起网络请求都点击激活按钮开始的,那么我们就从激活的这个按钮下手,看下激活按钮触发后都进行了什么操作,以及app是如何处理网络返回的数据的。
通过ADM 找到,这个激活按钮的 id,如下图所示:
然后去 public.xml 里面找到 name 为a1 并且 type 为id的值 然后在as 里面全局搜索下id 这个值,发现并不多,一共就是四个,如下图:

找一下规律,这个16进制的数全局一共能够搜索到四处,虽然这四个在四个不同的文件里面,但是他们对应的变量名都是一样的都是active,那么依然是全局搜索:active

然后我们就通过这个按钮id的引用锁定了这个按钮的点击事件,其实寻找点击事件的方式很多,针对从这个app的角度来说,如果你输入一个字符,会有一个toast,说“激活码长度不能小于9位”,然后可以从这个提示入手,依然能定位到 d.b.a.c.h.b.a 这个类。如果从寻找点击事件的角度来说的话,通过Xposed或者 通过Android Device Monitor 里面的 Method Profiling 方法都能获取到。
//.class public Ld/b/a/c/h/b/a; (d.b.a.c.h.b.a)类里面
public void onClick(View view) {
if (this.g.a()) {
int id = view.getId();
if (id == this.c.getId()) {
d.b.a.c.j.a.a(this.e, “pud”, true);
d.b.a.c.h.a.a.a(getActivity());
} else if (id == this.b.getId()) {
dismiss();
} else if (id == d.b.a.c.h.j.a.active) { //下面是核心代码
Object obj = this.a.getText().toString();
if (!TextUtils.isEmpty(obj)) {
String trim = obj.trim();
if (!TextUtils.isEmpty(trim) && trim.length() >= 10) { //这里会判断输入的字符串长度
i b = b(); //关键的就是他了
if (b == null) {
ad.a(this.e, c.payable_is_null, 1); //这应该是个log
CrashReport.postCatchedException(new TrineaUploadException(“payable is null in DonationVersionDialogFragment”, new NullPointerException()));
return;
}
b.d(trim); //调用 i 的 d方法也就是 c.b.c 类里面的d(String str)方法
dismiss();
}
}
ad.b(this.e, c.code_length_tip, Integer.valueOf(9)); //提示激活码不能小于9位
}
}
}
如果本地简单的验证成功会把字符串发往服务器,网络请求是这个类。看里面的网络成功的回调,这个回调匿名对象对应的Smali 文件为.class Ld/b/a/c/h/a$1;

找到 Smali对应的文件,先反转这个if(找到 Smali里面的代码 把if-eqz改成if-nez),我们试试能不能破解。测试发现反转了if就能破解了、然后就没有必要再分析其他代码的含义了。重新打包签名就可以使用了。
注意:apktool重打包会失败。解决方法看后面
处理到这一步,其实已经算是破解了收费功能,但是他里面还有个激活码长度限制,那么这里购买一个真正的激活码看看,发现他是32个字符。哈哈32个字符~恩 md5。那这里其实自己随机生成一个字符串,md5一下就可以,然后把我们的代码插入到 d.b.a.c.h.a(String,String,b)
方法开始的时候,修改第二个参数。就可以了。
然后我们破解这个10的限制,基本流程如下:
- 这个10 (Smali 里面搜索 0xa) 有两个地方验证到了、一个是:图形化界面 d.b.a.c.h.b.a 一个是网络请求类 d.b.a.c.h.a
- 我们把验证长度都改成1(也就是0x1),这样只要随便输入一个字符串就能验证成功了
- 但是好像这样有bug,也就是本地还有地方用到了这个激活码,并且对长度有限制
- 那也好说,我们发送的时候,把用户输入的激活码md5一下。
- 然后就可以啦
上面的难点是如何修改原来的代码,把拦截用户输入的激活码,修改后再发送给服务器。
我们在AndroidStudio中创建一个工具类,把包名改成上面和上面的类同一个包名
package d.b.a.c.h;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
- Author: liuqiang
- Date: 2018-11-03
- Time: 13:42
- Description:获取一个字符串的md5的字符串的表现形式类似于:
- 914674d1b303467d54c0673893a19ab3 个形式
*/
public class bbch {
public static String getHash(String s) {
try {
MessageDigest md5 = MessageDigest.getInstance(“MD5”);
md5.update(s.getBytes(“UTF-8”));
byte[] md5Array = md5.digest();
//byte[]通常我们会转化为十六进制的32位长度的字符串
return new BigInteger(1, md5Array).toString(16);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
return “1234567890”;
}
}
}
我们把这个类通过as编译成Smali文件,然后在d/b/a/c/h/a
类的a方法的开头处引用如下代码:
#d.b.a.c.h.a 类里面的方法
.method public a(Ljava/lang/String;Ljava/lang/String;Ld/b/a/c/b;)V
.locals 8
const/4 v0, 0x0
#在这里引用我们自定义的工具类
invoke-static {p2}, Ld/b/a/c/h/bbch;->getHash(Ljava/lang/String;)Ljava/lang/String;
#从而修改第二个参数的值
move-result-object p2
.line 57
#…省略其他代码
.end method
如果翻译成java代码如下图所示
然后再重新打包签名,运行app。选择 使用支付宝购买–>填写任意激活码—>点击激活按钮。就能使用啦~~~
3、 遇到的几个错误
重打包失败。这个我还真的不知道啥原因,不过猜测是因为这个app的资源混淆的问题。即使 使用apktool d -r xxx.apk
命令不反编译资源文件重打包运行之后也是出错,那么只好去用Smali/baksmali 去搞了。
具体步骤如下:
//前提条件 会 baksmali/smali 的基本操作
//用zip的方式解压apk
//把 classes.dex 单独 搞出来
//然后运行 baksmali d classes.dex -o out
//然后classes.dex 会被反编译成 Smali文件存放到 -o 指定的目录
//修改 out 中的对应文件
//然后把Smali编译成dex 运行: smali a out/ -o ./out/classes.dex
//然后把out 文件夹中的 classes.dex 覆盖上面解压的apk中的dex
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声
面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Android核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、热修复设计、插件化框架解读、组件化框架设计、图片加载框架、网络、设计模式、设计思想与代码质量优化、程序性能优化、开发效率优化、设计模式、负载均衡、算法、数据结构、高级UI晋升、Framework内核解析、Android组件内核等。
不仅有学习文档,视频+笔记提高学习效率,还能稳固你的知识,形成良好的系统的知识体系。这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《2017-2021字节跳动Android面试历年真题解析》
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
题汇总。)
[外链图片转存中…(img-rx19mREq-1712219595547)]
《2017-2021字节跳动Android面试历年真题解析》
[外链图片转存中…(img-bOyVwG5t-1712219595547)]