Rolly破解

[toc]

Rolly是我查看rss订阅的一款App,中间插着一个广告看着很烦,所以闲着没事用了一上午把本地专业版破解了一下,也就有机会水一篇博客了,(终于想起来更新博客了

Rolly介绍(搬运自酷安)

懒得整了,直接放链接吧,2333…:Rolly RSS Reader-实用RSS阅读器

破解分析

使用Rolly会发现他的专业版是用激活码激活,因此打开string.xml,他这个有国际化,找到zh就可以,搜索激活码,一点点分析。这里从“请输入激活码”开始。

public final void onClick(DialogInterface dialogInterface, int i) {
            EditText editText = this.d;
            h.a((Object) editText, "etCode");
            Editable text = editText.getText();
            String obj = text != null ? text.toString() : null;
            if (obj == null || m.b(obj)) {
                Button button = this.c.d;
                if (button != null) {
                    Snackbar.make(button, (int) R.string.please_input_code, 0).setAction(17039370, new a(this)).show();
                } else {
                    h.b("btnActiveCode");
                    throw null;
                }
            } else {
                this.c.a((ProFragment) obj);
            }
        }

Snackbar.make(button, (int) R.string.please_input_code, 0).setAction(17039370, new a(this)).show() 一路点到关键点:

public final void b(String str) {
        if (getContext() != null) {
            if (!g.b.d()) {
                a.a.a.f.e.e.b(getContext());
                return;
            }
            View inflate = getLayoutInflater().inflate(R.layout.panel_input_activation_code, (ViewGroup) null, false);
            EditText editText = (EditText) inflate.findViewById(R.id.etCode);
            TextView textView = (TextView) inflate.findViewById(R.id.txtClear);
            TextView textView2 = (TextView) inflate.findViewById(R.id.txtPaste);
            if (str != null) {
                editText.setText(str);
            }
            textView.setOnClickListener(new c(editText));
            textView2.setOnClickListener(new d(this, editText));
            Context context = getContext();
            if (context != null) {
                new AlertDialog.Builder(context).setTitle(R.string.activation_code).setView(inflate).setPositiveButton(R.string.next, new e(this, editText)).setNegativeButton(17039360, (DialogInterface.OnClickListener) null).show();
            } else {
                h.b();
                throw null;
            }
        }
    }

可以看出来这个对应了输入激活码的弹框

继续点下去:

 public k invoke(CodeResult codeResult) {
            CodeResult codeResult2 = codeResult;
            if (codeResult2 != null) {
                AlertDialog alertDialog = this.d;
                if (alertDialog != null) {
                    alertDialog.cancel();
                }
                if (codeResult2.isSuccess()) {
                    a.a.a.e.a.c.d();
                    a.a.a.f.e.e.a(ProFragment.a(this.c), R.string.done);
                } else {
                    Context context = this.c.getContext();
                    if (context != null) {
                        new AlertDialog.Builder(context).setTitle(17039380).setMessage(a.a.a.f.e.e.a(codeResult2.getCode())).setNegativeButton(17039360, (DialogInterface.OnClickListener) null).setPositiveButton(R.string.retry, new a.a.a.a.b.d.a(this)).show();
                    } else {
                        h.b();
                        throw null;
                    }
                }
                return k.f671a;
            }
            h.a("it");
            throw null;
        }

这是代码无效的提示框,可以在这个地方重试(可以安装正版看一看,方便理解)

这里直接把if (codeResult2.isSuccess()) 改为if (!codeResult2.isSuccess()) ,这里直接把smali的841行的代码改为if-eqz v1, :cond_26

改完这里就可以实现直接跳转到完成。这里我也抓包看过,应该就是返回的json中code的值为1或者0,比较直白。

然后点进去success之后的方法,细心分析能发现进入了一个多线程:

public static final class e implements Runnable {
        public static final e c = new e();

        public final void run() {
            a.a.a.f.e eVar = a.a.a.f.e.e;
            a.a.a.b.a a2 = a.c.a();
            SharedPreferences sharedPreferences = g.f140a;
            if (sharedPreferences != null) {
                String string = sharedPreferences.getString("token", null);
                if (string != null) {
                    DataResult a3 = eVar.m0a((q.b) a2.a(string));
                    if (a3.isSuccess()) {
                        a.c.a((String) a3.getData());
                        return;
                    }
                    return;
                }
                n.q.c.h.b();
                throw null;
            }
            n.q.c.h.b("preferences");
            throw null;
        }
    }

这里抓包code的返回值就是1,点到a3.isSuccess()中的a.c.a((String) a3.getData());

 public final void a(String str) {
        if (str == null) {
            SharedPreferences sharedPreferences = g.f140a;
            if (sharedPreferences != null) {
                sharedPreferences.edit().putLong("expirationTime", -1).apply();
            } else {
                n.q.c.h.b("preferences");
                throw null;
            }
        } else {
            Date parse = a.a.a.f.e.e.c().parse(str);
            Date date = new Date();
            n.q.c.h.a((Object) parse, "date");
            long time = (parse.getTime() - date.getTime()) / ((long) 86400000);
            if (time < ((long) -1)) {
                a.a.a.f.e.e.a(Event.ProExpired, Long.valueOf(Math.abs(time)));
            }
            a.a.a.f.e.e.a(Event.ProInfoChange, (Object) null);
            long time2 = parse.getTime();
            SharedPreferences sharedPreferences2 = g.f140a;
            if (sharedPreferences2 != null) {
                sharedPreferences2.edit().putLong("expirationTime", time2).apply();
            } else {
                n.q.c.h.b("preferences");
                throw null;
            }
        }
    }

看到parse方法str解析成"yyyy-MM-dd HH:mm:ss"的格式。这里上Frida,直接把str改成”2099-12-12 02:30:22″,看一哈效果。

Frida代码:

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

jscode = """
Java.perform(function () {
  // Function to hook is defined here
  var MainActivity = Java.use('a.a.a.e.a');
  var functionA = MainActivity.a;
  // Whenever button is clicked
  MainActivity.a.overload("java.lang.String").implementation = function (v) {
    v = "2099-12-12 02:30:22"
    console.log('Done:' + v);
    // Call the original onClick handler
    functionA.call(this, v);

  };
});
"""

process = frida.get_usb_device().attach('com.blend.rolly')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()

结果灰常nice:

所以直接修改smali,把str赋值就ojbk了。

P.S. smali代码写错导致卡第一屏,导致我以为有签名校验,其实没有,有被自己感(xiao)动(si)。

附件

这里提供下成品,100天后过期。链接:Rolly成品

评论

  1. Adnice
    3 年前
    2021-9-11 16:43:19

    兄弟,逛油猴看到你的插件
    因一些不可说的原因来逛逛你的主站
    没有想到你主站做得这么大(虽然有几个板块没有完善)

    以上吐槽,以下问题

    你搭个这玩意拿什么支撑?
    还是只是爱好而已?
    不可能一直赔钱搭着就为了爱好吧兄弟?
    询问完毕哈哈哈!

    期待回复

    • 博主
      Adnice
      3 年前
      2021-9-18 17:38:00

      当作进步的踏板和分享的平台~

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇