0x00 前言
最近老是下雨,突发奇想:想和喜欢的人去拍照,感受下雨天的浪漫
然后就找到这个App:ProCCD复古CCD相机胶片滤镜
第一次尝试着去逆向Pro会员,一波三折,还好赶在周末之前完成了
0x01 准备工作
1、下载最新版本的ProCCD复古CCD相机胶片滤镜
应用宝App介绍主页:
https://sj.qq.com/appdetail/com.cerdillac.proccd.cn
应用宝直链下载:
https://8ffcfc786802a12cddaea5ca998645ad.dlied1.cdntips.net/dd.myapp.com/sjy.00004/16891/apk/428A0EF4CDB2A17595F0F2AA085BF1B6.apk?mkey=63bf5b17db80b624&f=0000&fsname=com.cerdillac.proccd.cn_2.4.6_64.apk&cip=219.128.144.209&proto=https

2、用到的工具
去签:MT管理器、NP管理器
逆向:jadx、Android Killer
0x02 静态分析
“常用”的 isVIP
注意:软件有签名校验,自行使用 MT 或 NP ,去签之后再进行分析
1、先把 apk 拖进 jadx 中进行一下静态分析,然后搜索 isVIP
关键字,关注到有个 VipState

2、跟进 VipState
,看到这里大概就是这连绵阴雨天气中的第一缕阳光

3、返回 Android Killer 修改代码、保存、编译、安装
以下为原始代码,未作修改,然后继续往下看
.class public Lcom/lightcone/wxpay/billing/bean/VipState;
.super Ljava/lang/Object;
.source "VipState.java"
# instance fields
.field public expiredTime:J
.field public isVip:Z
# direct methods
.method public constructor <init>()V
.locals 2
.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
const-wide/16 v0, -0x1
.line 2
iput-wide v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->expiredTime:J
return-void
.end method
# virtual methods
.method public formatVipExpiresTime()Ljava/lang/String;
.locals 5
.annotation runtime Lcom/fasterxml/jackson/annotation/JsonIgnore;
.end annotation
.line 1
iget-boolean v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->isVip:Z
if-eqz v0, :cond_1
iget-wide v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->expiredTime:J
const-wide/16 v2, 0x0
cmp-long v4, v0, v2
if-gtz v4, :cond_0
goto :goto_0
.line 2
:cond_0
new-instance v0, Ljava/text/SimpleDateFormat;
const-string v1, "yyyy/MM/dd"
invoke-direct {v0, v1}, Ljava/text/SimpleDateFormat;-><init>(Ljava/lang/String;)V
.line 3
new-instance v1, Ljava/util/Date;
iget-wide v2, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->expiredTime:J
invoke-direct {v1, v2, v3}, Ljava/util/Date;-><init>(J)V
invoke-virtual {v0, v1}, Ljava/text/SimpleDateFormat;->format(Ljava/util/Date;)Ljava/lang/String;
move-result-object v0
return-object v0
:cond_1
:goto_0
const-string v0, ""
return-object v0
.end method
.method public isPermanentVip()Z
.locals 5
.annotation runtime Lcom/fasterxml/jackson/annotation/JsonIgnore;
.end annotation
.line 1
iget-wide v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->expiredTime:J
const-wide/16 v2, 0x0
cmp-long v4, v0, v2
if-nez v4, :cond_0
iget-boolean v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->isVip:Z
if-eqz v0, :cond_0
const/4 v0, 0x1
goto :goto_0
:cond_0
const/4 v0, 0x0
:goto_0
return v0
.end method
.method public isVipEffective()Z
.locals 1
.annotation runtime Lcom/fasterxml/jackson/annotation/JsonIgnore;
.end annotation
.line 1
iget-boolean v0, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->isVip:Z
if-eqz v0, :cond_0
invoke-virtual {p0}, Lcom/lightcone/wxpay/billing/bean/VipState;->isVipExpires()Z
move-result v0
if-nez v0, :cond_0
const/4 v0, 0x1
goto :goto_0
:cond_0
const/4 v0, 0x0
:goto_0
return v0
.end method
.method public isVipExpires()Z
.locals 5
.annotation runtime Lcom/fasterxml/jackson/annotation/JsonIgnore;
.end annotation
.line 1
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v0
iget-wide v2, p0, Lcom/lightcone/wxpay/billing/bean/VipState;->expiredTime:J
cmp-long v4, v0, v2
if-lez v4, :cond_0
const-wide/16 v0, 0x0
cmp-long v4, v2, v0
if-eqz v4, :cond_0
const/4 v0, 0x1
goto :goto_0
:cond_0
const/4 v0, 0x0
:goto_0
return v0
.end method
在这个代码中不管改什么、怎么改、始终都是无法成功逆向Pro会员 ,此刻觉得自己真的还是好菜
原因分析
1、回头仔细把每个函数都仔细观察了一遍,发现了疑点
isVipEffective() 这个方法是检测 VIP 的有效性,但是检测的逻辑和判断似乎不在这

2、那么知道了 isVipEffective() 是判断 VIP 的有效性了,那么就得去寻找哪个判断逻辑调用了这个方法
jadx 直接查找用例

逆向 Pro 会员
1、通过观察查找用例的代码,发现方法 k 似乎有点特殊

2、跟进 k ,就可以看到判断的逻辑了
先是判断 VipState 不为 null , 接着调用 isVipEffective()

3、复制 jadx 中的文件名到 Android Killer 查找对应的 smail 文件
文件路径为:e.e.r.g.d

4、先来看下 smail代码
第二种快速定位的方法:可以直接搜索关键词 VipState

找到对应的 k 方法

5、我觉得 smail 代码好像和 jadx 编译的有点出入,然后打开了 JD-GUI 看了一下

然后复制了一下 JD-GUI 的 smail 文件路径,对比一下
*\ProjectSrc\smali\!\e\e\r\g\d.class
修改后,会员滤镜已经可以使用了

总结
每个逆向工具编译的代码可能不一样,要学会使用多工具对比法
期待拍照那天的到来