|
|
本帖最后由 Sunika 于 2024-9-29 09:37 编辑 工具:MT管理器
演示软件:格间日记 1.22.5
网页版详细教程需回复,全文pdf版本2个币(链接在文章末尾),与网页版无区别
因为有人反映看不懂,而且图也出问题了(昨天晚上太赶了),所以我对文章内容优化了一下,之前买过pdf的可以重新下载,如果还有其它问题请在评论区留言
速成版(不要混淆对抗后使用速成版):
进入类io.git.zjoker.gj_diary.pay.bean.Vip,查找type的get调用,全部改为常数1;再查找beginTime的get调用,全部改为常数-1
进入类f32,找到方法Illllllllllllllllll(),把方法内代码改为
- new-instance v0, Lio/git/zjoker/gj_diary/pay/bean/Vip;
- invoke-direct {v0}, Lio/git/zjoker/gj_diary/pay/bean/Vip;-><init>()V
- return-object v0
复制代码
以下为详细教程,请回复食用
首先先了解软件的会员情况
我这里用临时邮箱登录,一般能用邮箱注册的软件我都会用邮箱登录而不是破解免登录,一是省去免登录步骤,二是减少其他问题带来的麻烦
看到有一些关于会员状态的内容
再随便尝试一个会员功能,我这里以主题皮肤为例(事实上,软件全部功能中一部分是判断type解锁的,另一部分是判断begintime解锁的,主题皮肤属于后者,这里了解即可)
通过购买PRO权益中看到,软件是有永久会员的,那我们就直接在Arsc编辑器中搜索“永久”二字
“已获得永久会员,无需再购买订阅”:如果你是永久会员,这条消息才会显示;否则不会显示。那么我们可以通过这个字符串定位到判断是否是永久会员的地方(其它地方也可以)
到dex编辑器搜索一下字符串id
只有一个调用,进去看看
发现有混淆
不管混淆能不能继续?能。但是分析起来会非常不方便。恰好我之前还没用过MT的混淆对抗,所以可以尝试用一下(如果这个功能要会员,那没会员的朋友可以跳过这部分)
进入DEX混淆对抗后打开高级选项
第一次使用这个功能的朋友应该只有前面三行内容
这里涉及正则表达式的知识,就是匹配符合自定义规则的方法名和类名
第一行:匹配任何包含非字母、非数字、非下划线、非美元符号、非斜杠的字符串
第二行:匹配任何包含至少3个连续的0/o/O的字符串
第三行:与第二行类似
那我们刚刚看到被混淆的方法名有W和w组成的一串,我们就可以添加匹配规则[Ww]{6,}来匹配任何包含至少6个连续的W/w组成的方法名
其他需要混淆对抗的方法名可以重新回到dex编辑器看看。我这里一共就5行。添加完后点确定等待混淆对抗完成
进入混淆对抗完的包中,回到字符串定位的方法来(我这里混淆对抗后是by()),转为java代码
发现这里有最内层判断当i为1时使用了字符串“已获得永久会员,无需再购买订阅”,而当i2=1的分支使用的7f100273对应字符串是“请先取消现有订阅”
- private void by() {
- VipType vipType;
- int i;
- int i2;
- if (x31.c()) {
- Vip ax = f32.a().ax();
- if (ax != null && (vipType = this.au) != null && (i = ax.type) != (i2 = vipType.id)) {
- if (i == 1) {
- tv1.a(this, App.g(0x7f10026e, new Object[0]));
- return;
- } else if (i2 == 1) {
- new GJDialog(this).r(App.g(0x7f100273, new Object[0])).o(App.g(0x7f1000bb, new Object[0]), new l71(this)).i(App.g(0x7f10019a, new Object[0]), new k71(this)).Wwww();
- return;
- }
- }
- cb();
- return;
- }
- cb();
- }
复制代码 说明中间层判断中的i(即ax.type)就应该为1,表示为永久会员,ax实例(即ax方法返回值)应不为空,表示是会员。那么vipType(即this.au)和i2(即vipType.id)的值我们怎么办呢?其实我们不用管它,因为vipType表示的是你购买的会员的id,而你购买会员时au也不为空了。更外层的if的判断条件——x31.c方法恒为1,可自行查看
根据上面代码分析,我们要把Vip类的type属性改为1,方法f32.a().ax()返回不为空。先跳转到Vip类中
因为里面的属性都是和vip内容相关,所以我们可以直接让f32.a().ax()方法返回Vip类的新实例
对于其它地方使用Vip类的属性,就像type一样直接赋常量即可
具体操作如下
在Vip类中查找type的get调用,使用正则替换一键赋值常数1。推荐使用MT插件Smali正则转换(使用方法为进入一个查询结果,选中要转换的句子,选择翻译功能,点击上面一条横线把插件切换到Smali正则转换,点击左下角复制结果即可)
跳转到ax方法,发现调用的是bn
再查找bn的get调用,发现只有ax一个
那我们就直接给ax返回一个Vip实例
再初始化一下即可(可以进Vip类导航长按<init>方法复制invoke代码)
- .method public ax()Lio/git/zjoker/gj_diary/pay/bean/Vip;
- .registers 2
-
- new-instance v0, Lio/git/zjoker/gj_diary/pay/bean/Vip;
- invoke-direct {v0}, Lio/git/zjoker/gj_diary/pay/bean/Vip;-><init>()V
- return-object v0
-
- .end method
复制代码
转java没报错,而且逻辑没错,说明代码应该没问题
安装测试发现显示永久会员,但主题皮肤还是不能正常使用。经测试发现,相比一开始功能从“已完成”变成了“已拥有”的可以使用;而像主题皮肤仍是“已完成”,还不能用。实际上这里就是我一开始就提到的软件全部功能解锁要分为两部分,现在假装我们不知道
dex搜索“已拥有”对应的id试试
点进第二个再转java,下面是关键代码部分
- if (aVar.i()) {
- g = App.g(0x7f10025b, new Object[0]);
- textView2.setTextColor(u32.Www(this, 0x7f04027a));
- textView2.getPaint().setFakeBoldText(true);
- } else {
- textView2.setTextColor(u32.Www(this, 0x7f0401d5));
- g = App.g(0x7f1000f3, new Object[0]);
- }
复制代码
再跳转i方法,转java
- public boolean i() {
- Vip ax = f32.a().ax();
- if (ax == null) {
- return false;
- }
- if (ax.beginTime == -1) {
- return true;
- }
- return h() && this.e <= ax.endTime;
- }
复制代码
跳转到beginTime,查找所有get调用,全部赋值为-1
再次安装测试发现全部已拥有,且功能也可以正常使用了
总结逆向流程:为了解软件会员情况->定位突破点->发现混淆,处理混淆->分析代码->修改type属性->修改ax方法返回值->发现部分功能不可用,再次定位->分析代码->修改beginTime属性->完成逆向
至此教程结束 https://sb-baidu-sb.lanzout.com/i5pGJ2b495mj
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
-
查看全部评分
总评分:金币 +1
|