好友
阅读权限40
听众
最后登录1970-1-1
|
这次要破解的软件叫Stellio Music Player,是一款手机音乐播放器。
它的破解点是激活码。
用到的工具:
用Androidkiller反编译和修改代码,由于激活码是网络验证,所以用Charles抓包分析。
一、激活码破解篇
本篇的目的:你输入任意激活码都能激活成功。
1.去除自校验
这个软件有签名校验,先去除。
方法是常用的方法了,详见:BT种子搜索1.5.7签名校验破解过程简介
贴一下修改后的代码:
[Java] 纯文本查看 复制代码
invoke-virtual {p0}, Landroid/content/Context;->getPackageManager()Landroid/content/pm/PackageManager;
move-result-object v0
const/16 v3, 0x40
const-string p1, "/sdcard/123.apk"
//自定义验证包的路径
invoke-virtual {v0, p1, v3}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
//把getPackageInfo改为getPackageArchiveInfo
move-result-object v3
.line 128
sget-boolean v0, Lru/stellio/player/Utils/m;->a:Z
if-nez v0, :cond_3
iget-object v0, v3, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature;
//读取签名
//省略
2.破解激活码
自校验去掉之后,我们进去看看,发现软件只有10天试用期。
买买买!点击购买,会弹出一个弹框:
如图,它有2种解锁完整版的方式:
a.去购买解锁包解锁
b.用激活码激活解锁
这里选择激活码。
输入任意激活码,会有错误提示:
由于我们要求输入「任意」激活码都能激活成功,所以先去掉格式的限制。
、
按图索骥,最后在smali里删掉一个跳转即可,比较简单就不贴代码了。
去掉激活码格式的限制后,继续往下走。
由于是联网激活,所以抓包看一下吧。
我输入“呵呵”,然后点击激活。
一方面app上会提示激活码无效,另一方面Charles上显示以下数据:
也就是说,输入的key如果是无效的,发送到服务器上,会验证失败,然后返回error。
先把链接复制下来,搜索看一下。
搜索http://stellio.ru/api/license.php
[Java] 纯文本查看 复制代码
.method public static a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
.locals 6
.prologue
const/4 v5, 0x0
.line 47
new-instance v0, Ljava/util/ArrayList;
invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V
.line 49
new-instance v1, Lru/stellio/player/Helpers/i;
const-string v2, "key"
invoke-direct {v1, v2, p0}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 50
new-instance v1, Lru/stellio/player/Helpers/i;
const-string v2, "android_id"
invoke-direct {v1, v2, p1}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 51
new-instance v1, Lru/stellio/player/Helpers/i;
const-string v2, "imei"
invoke-static {}, Lru/stellio/player/App;->a()Lru/stellio/player/App;
move-result-object v3
invoke-static {v3}, Lru/stellio/player/Utils/k;->i(Landroid/content/Context;)Ljava/lang/String;
move-result-object v3
invoke-direct {v1, v2, v3}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 52
new-instance v1, Lru/stellio/player/Helpers/i;
const-string v2, "lock"
invoke-static {}, Lru/stellio/player/MainActivity;->g3()Ljava/lang/String;
move-result-object v3
invoke-direct {v1, v2, v3}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 54
invoke-static {}, Lru/stellio/player/Fragments/SettingsFragment;->d()Landroid/content/SharedPreferences;
move-result-object v1
.line 55
new-instance v2, Lru/stellio/player/Helpers/i;
const-string v3, "utm_source"
const-string v4, "utm_source"
invoke-interface {v1, v4, v5}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
invoke-direct {v2, v3, v4}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 56
new-instance v2, Lru/stellio/player/Helpers/i;
const-string v3, "utm_medium"
const-string v4, "utm_medium"
invoke-interface {v1, v4, v5}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-direct {v2, v3, v1}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 58
if-eqz p2, :cond_0
.line 59
new-instance v1, Lru/stellio/player/Helpers/i;
const-string v2, "bind"
invoke-direct {v1, v2, p2}, Lru/stellio/player/Helpers/i;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 61
:cond_0
const-string v1, "http://stellio.ru/api/license.php"
invoke-static {v1, v0}, Lru/stellio/player/Apis/c;->a(Ljava/lang/String;Ljava/util/List;)Ljava/lang/String;
move-result-object v0
.line 62
invoke-static {v0}, Lru/stellio/player/Apis/c;->b(Ljava/lang/String;)Z
move-result v0
return v0
.end method
结合刚才抓包的图,看来找到关键的地方了。
由于这个方法是布尔值类型的,我尝试在最后强制令其返回true,果然激活成功了。
不过为了搞清楚来龙去脉,可以再往上一层走。
因它最后调用了b(Ljava/lang/String;)Z,所以去b(Ljava/lang/String;)Z看一下。
[Java] 纯文本查看 复制代码
.method private static b(Ljava/lang/String;)Z
.locals 3
.prologue
.line 66
const-string v0, "ok"
invoke-virtual {v0, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
.line 67
const/4 v0, 0x1
.line 69
:goto_0
return v0
.line 68
:cond_0
const-string v0, "error"
invoke-virtual {v0, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_1
.line 69
const/4 v0, 0x0
goto :goto_0
.line 71
:cond_1
new-instance v0, Ljava/io/IOException;
new-instance v1, Ljava/lang/StringBuilder;
invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
const-string v2, "Unknown server response "
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v1
invoke-direct {v0, v1}, Ljava/io/IOException;-><init>(Ljava/lang/String;)V
throw v0
.end method
这时就很清楚了:只有当返回 "ok"时,才激活成功。刚才返回"error"自然是激活失败的。
改法:删掉第一个跳转,这样永远返回真,于是无论是"ok"还是"error"都能激活成功了。
激活前后对比:
至此激活码破解篇结束。
二、激活码改造篇
本篇的目的:你只能使用我指定的激活码才能激活成功。
本篇的想法来源于最近很火的口令红包:
你要拿我的红包,必须输入我指定的口令。
同理,我们DIY一个「口令」激活码吧!
首先分析本软件的激活流程,以找到适合的切入点。
由于激活码的验证部分已经破解了,所以可考虑在激活码的格式上做文章。
1.隐藏的彩蛋?
按图索骥,找到激活码的格式代码:
[Java] 纯文本查看 复制代码
.method public static a(Ljava/lang/String;)Z
.locals 3
.prologue
const/4 v1, 0x1
const/4 v0, 0x0
.line 48
invoke-static {p0}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z
move-result v2
if-eqz v2, :cond_1
//验证输入是否为空,非空跳到激活码的格式验证,否则返回false
.line 58
:cond_0
:goto_0
return v0
.line 51
:cond_1
const-string v2, "[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}"
//激活码的正确格式,和错误提示是一样。
invoke-static {v2}, Ljava/util/regex/Pattern;->compile(Ljava/lang/String;)Ljava/util/regex/Pattern;
move-result-object v2
.line 53
invoke-virtual {v2, p0}, Ljava/util/regex/Pattern;->matcher(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
move-result-object v2
invoke-virtual {v2}, Ljava/util/regex/Matcher;->matches()Z
move-result v2
if-eqz v2, :cond_2
//验证格式是否相符,相符则返回true
move v0, v1
.line 54
goto :goto_0
.line 55
:cond_2
const-string v2, "appoftheday"
invoke-virtual {v2, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v2
if-eqz v2, :cond_0
//虽然格式不相符,但只要输入的是appoftheday,一样返回true!
move v0, v1
.line 56
goto :goto_0
.end method
分析见注解,它既用了matches(相符),又用了equals(相等)。
如果格式相符,或者输入为"appoftheday",就通过激活码格式的验证了。
"appoftheday"作为隐藏要素,它是一枚特殊的激活码,经测试,也是无效的激活码。{:1_903:}
2.不一样的激活码:「口令」激活码
为了达到「口令」的效果,我们去掉matches代码,仅保留equals即可。
比如:
[Java] 纯文本查看 复制代码
.method public static a(Ljava/lang/String;)Z
.locals 3
const-string v0, "吾爱破解"
invoke-virtual {v0, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
//只能输入"吾爱破解",否则返回false
const/4 v0, 0x1
:goto_0
return v0
:cond_0
const/4 v0, 0x0
goto :goto_0
.end method
当然还要把提示改了,否则人家都不知道要输入什么了。
还有上面把激活码的格式限制去掉了,所以也要改回来的。
改造后的激活码如下图。
3.不一样的激活码:「口令」升级版
假如你不想这个「口令」千遍一律的话,也可以再改。
举个例子:别人必须输入手机的imei才能激活成功。
为了省事,首先看有没有哪个方法是描述读取手机imei的,然后我们改为调用该方法即可。
搜索getDeviceId。
[Java] 纯文本查看 复制代码
.method public static i(Landroid/content/Context;)Ljava/lang/String;
.locals 1
.prologue
.line 409
const-string v0, "android.permission.READ_PHONE_STATE"
invoke-static {p0, v0}, Landroid/support/v4/content/a;->a(Landroid/content/Context;Ljava/lang/String;)I
move-result v0
if-eqz v0, :cond_0
.line 410
const-string v0, ""
.line 413
:goto_0
return-object v0
.line 412
:cond_0
const-string v0, "phone"
invoke-virtual {p0, v0}, Landroid/content/Context;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
move-result-object v0
check-cast v0, Landroid/telephony/TelephonyManager;
.line 413
invoke-virtual {v0}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;
move-result-object v0
goto :goto_0
.end method
原来Lru/stellio/player/Utils/k;下的i(Landroid/content/Context;)Ljava/lang/String;就是读取imei的方法。
再搜索Lru/stellio/player/Utils/k;->i(Landroid/content/Context;)Ljava/lang/String; 时发现它已经被调用过了。
[Java] 纯文本查看 复制代码
const-string v2, "imei"
invoke-static {}, Lru/stellio/player/App;->a()Lru/stellio/player/App;
move-result-object v3
invoke-static {v3}, Lru/stellio/player/Utils/k;->i(Landroid/content/Context;)Ljava/lang/String;
move-result-object v3
简直天助我也,复制过去改一下就行了:
[Java] 纯文本查看 复制代码
.method public static a(Ljava/lang/String;)Z
.locals 3
invoke-static {}, Lru/stellio/player/App;->a()Lru/stellio/player/App;
move-result-object v0
invoke-static {v0}, Lru/stellio/player/Utils/k;->i(Landroid/content/Context;)Ljava/lang/String;
move-result-object v0
//调用了读取imei的方法
invoke-virtual {v0, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
//只能输入"手机imei",否则返回false
const/4 v0, 0x1
:goto_0
return v0
:cond_0
const/4 v0, 0x0
goto :goto_0
.end method
同样还要把提示改了,否则人家都不知道要输入什么了。
如你所见,这种「口令」激活码并非真正意义上的激活码,不过效果已经达到了。
至此激活码改造篇结束。
全文结束。代码贴多了,好像太长了。
附本文用到的app:
链接: http://pan.baidu.com/s/1dEnjHq5 密码: p7jh
有兴趣也可以研究一下,说不定有更多更有趣的发现哦!
|
免费评分
-
查看全部评分
|