穿透骨頭撫摸妳 发表于 2019-7-29 12:37

[xposed]掌中英语_6.4.0 VIP破解By_kwaiching

0x0. 非會員只可以打開試聽章節







0x1. ApkKiller反編譯, 發現軟件經過360加固

       

0x2. 脫殼, 根據界面信息, 搜尋VIP

       

0x3. isVip 應該是關鍵

        isVipUser                // 是否為VIP用戶

        isVipVideo                // 是否為VIP視頻


0x4. 以下三個類有isVipUser 相關

com.zzenglish.client.vo.VoCollectionBean{

    public int getIsVipUser() {
      return this.isVipUser;
    }


    public int getIsvip() {
      return this.isvip;
    }

}


/* renamed from: com.zzenglish.client.vo.VoOpeningTalk */
public class VoOpeningTalk {
    private ArrayList<VoCollectionBean> datas;
    private int isVipUser;

    public int getIsVipUser() {
      return this.isVipUser;
    }

    public void setIsVipUser(int i) {
      this.isVipUser = i;
    }

    public ArrayList<VoCollectionBean> getDatas() {
      return this.datas;
    }

    public void setDatas(ArrayList<VoCollectionBean> arrayList) {
      this.datas = arrayList;
    }
}

com.zzenglish.client.vo.VoVideoPractise{

    public int getIsVipVideo() {
      return this.isVipVideo;
    }

    public int getIsVipUser() {
      return this.isVipUser;
    }


}

0x5. Xposed hook 相關函數

      val mVoCollectionBeanClass: Class<*>?
      val mVoOpeningTalkClass: Class<*>?
      val mVoVideoPractiseClass: Class<*>?
      val mDBZZUserClass: Class<*>?
      try {
            mVoCollectionBeanClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoCollectionBean")
            mVoOpeningTalkClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoOpeningTalk")
            mVoVideoPractiseClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoVideoPractise")
            mDBZZUserClass = dexClassLoader.loadClass("com.zzenglish.client.db.DBZZUser")
      } catch (e: Exception) {
            XposedBridge.log("錯誤$e")
            return
      }

      XposedHelpers.findAndHookMethod(mVoCollectionBeanClass, "getIsVipUser",object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = 1
            }
      })

      XposedHelpers.findAndHookMethod(mVoOpeningTalkClass, "getIsVipUser",object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = 1
            }
      })

      XposedHelpers.findAndHookMethod(mVoVideoPractiseClass, "getIsVipUser",object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = 1
            }
      })

發現沒什麼反應


0x6. 胡亂再觀察一波, 搜尋"未開通"

    public static String getOpenProgramText(int i) {
      return i == -1 ? "审核不通过" : i == 1 ? "已开通" : i == 2 ? "审核中" : "未开通";
    }


此類中有用戶信息:

Lcom/zzenglish/client/activity/EditUserActivity

    private void setUser() {
      this.vOUser2 = DBZZUser.getInstance().getUser(MDataBase.UUID);
      if (this.vOUser2 != null) {                // 用戶信息不為空
            if (StrUtil.isNotBlank(this.vOUser2.getMoblile())) {                // 手機綁定
                this.info_phone_text.setText(this.vOUser2.getMoblile());
            } else {
                this.info_phone_text.setText("请绑定");
            }
            initHead();
            TextView textView = (TextView) findViewById(C0422R.C0420id.info_vip_state_text);
            String str = "会员状态:";
            StringBuilder stringBuilder;
            if (DBZZUser.getInstance().isForeverMember(MDataBase.UUID)) {                // 1 永久會員
                stringBuilder = new StringBuilder();
                stringBuilder.append(str);
                stringBuilder.append("终身会员");
                str = stringBuilder.toString();
                ((ImageView) findViewById(C0422R.C0420id.info_head_img_vip)).setImageResource(C0422R.drawable.zz_personal_v);
                ((TextView) findViewById(C0422R.C0420id.info_vip_state_operator)).setText("续费");
            } else if (DBZZUser.getInstance().isMember(MDataBase.UUID)) {                // 會員
                try {
                  long memberEndTimeMillis = DBZZUser.getInstance().getMemberEndTimeMillis(MDataBase.UUID);
                  if (memberEndTimeMillis > 0) {
                        stringBuilder = new StringBuilder();
                        stringBuilder.append(str);
                        stringBuilder.append("<font color=\"#f58927\">");
                        stringBuilder.append(new SimpleDateFormat(DateUtil.DEFAULT_FORMAT).format(Long.valueOf(memberEndTimeMillis)));
                        stringBuilder.append("到期</font>");
                        str = stringBuilder.toString();
                  }
                } catch (Exception e) {
                  e.printStackTrace();
                }
                this.zz_personal_vip.setImageResource(C0422R.drawable.zz_personal_v);
                ((TextView) findViewById(C0422R.C0420id.info_vip_state_operator)).setText("续费");
            } else {
                stringBuilder = new StringBuilder();
                stringBuilder.append(str);
                stringBuilder.append("未开通");
                str = stringBuilder.toString();
                ((ImageView) findViewById(C0422R.C0420id.info_head_img_vip)).setImageResource(C0422R.drawable.zz_personal_v_off);
                ((TextView) findViewById(C0422R.C0420id.info_vip_state_operator)).setText("开通");
            }
            textView.setText(Html.fromHtml(str));
            ((TextView) findViewById(C0422R.C0420id.info_nickname_text)).setText(URLDecoder.decode(this.vOUser2.getNick()));
            this.birth = (TextView) findViewById(C0422R.C0420id.info_birthday_text);
            this.birth.setText(this.vOUser2.getBirth());
            String sex = this.vOUser2.getSex();
            this.sexbt = (TextView) findViewById(C0422R.C0420id.info_gender_text);
            if (StrUtil.equals(sex, "F")) {
                this.sexbt.setText("女");
            } else if (StrUtil.equals(sex, "M")) {
                this.sexbt.setText("男");
            } else {
                this.sexbt.setText("未知");
            }
      }
      this.student.setVisibility(SpSet.get().isTeacher() ? 8 : 0);                // 是否為殭屍
      ZZImageLoader.getInstance(this).displayImage(this.background_image, getIntent().getStringExtra(VStatic.BACKGROUND), (int) C0422R.drawable.person_default);
    }

0x7. 追蹤方法DBZZUser

com.zzenglish.client.db.DBZZUser{


    public boolean isForeverMember(int i) {
      boolean z = false;
      if (i <= 0) {
            return false;
      }
      long memberEndTimeMillis = getMemberEndTimeMillis(i);
      long memberDurationTimeMillis = getMemberDurationTimeMillis(i);
      boolean isLogin = isLogin(i);
      if (i > 0 && isLogin // 登陸
                                        && (DBMyGood.get().isPurchaseByPid(String.valueOf(i), MDataBase.GID_MEMBER)
                                        || DBMyGood.get().isPurchaseByPid(String.valueOf(i), MDataBase.GID_SETMEAL)
                                        || (memberDurationTimeMillis > 0 && 0 == memberEndTimeMillis))) {
            z = true;
      }
      return z;
    }

    public boolean isMember(int i) {
      boolean z = false;
      if (i <= 0) {
            return false;
      }
      if (isForeverMember(i) || isCommonMember(i)) {
            z = true;
      }
      return z;
    }


    public boolean isCommonMember(int i) {
      return i > 0 && isLogin(i) && checkMemberTimeMillis(i);
    }
       
    public boolean hasBuyHelper(int i) {
      boolean z = false;
      if (i <= 0) {
            return false;
      }
      if (isMember(i) || DBMyGood.get().isPurchaseByPid(String.valueOf(i), MDataBase.GID_HELPER)) {
            z = true;
      }
      return z;
    }


}

0x8. 永久會員isForeverMember調用DBMyGood

/* renamed from: com.zzenglish.client.db.DBMyGood */
public class DBMyGood {
       
    public boolean isPurchaseByPid(String str, String str2) {
      Exception e;
      Throwable th;
      boolean z = false;
      if (StrUtil.isBlank(str2)) {
            return false;
      }
      Cursor cursor = null;
      try {
            MySQLiteHelper instance = MySQLiteHelper.getInstance();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("select col_purchase from ");
            stringBuilder.append(TB_NAME);
            stringBuilder.append(" where ");
            stringBuilder.append(COL_UID);
            stringBuilder.append("=? and ");
            stringBuilder.append(COL_PID);
            stringBuilder.append("=?");
            Cursor select = instance.select(stringBuilder.toString(), new String[]{str, str2});
            if (select != null) {
                try {
                  if (select.moveToFirst()) {
                        StringBuilder stringBuilder2 = new StringBuilder();
                        stringBuilder2.append(str);
                        stringBuilder2.append(1);
                        stringBuilder2.append(str2);
                        z = Util.MD5(stringBuilder2.toString()).equals(select.getString(0));
                  }
                } catch (Exception e2) {
                  e = e2;
                  cursor = select;
                  try {
                        e.printStackTrace();
                        if (cursor != null) {
                        }
                        return z;
                  } catch (Throwable th2) {
                        th = th2;
                        if (cursor != null) {
                        }
                        throw th;
                  }
                } catch (Throwable th3) {
                  th = th3;
                  cursor = select;
                  if (cursor != null) {
                        try {
                            cursor.close();
                        } catch (Exception unused) {
                        }
                  }
                  throw th;
                }
            }
            if (select != null) {
                try {
                  select.close();
                } catch (Exception unused2) {
                }
            }
      } catch (Exception e3) {
            e = e3;
            e.printStackTrace();
            if (cursor != null) {
                cursor.close();
            }
            return z;
      }
      return z;
    }

}


0x9. Xposed HOOK

    val mVoCollectionBeanClass: Class<*>?
      val mVoOpeningTalkClass: Class<*>?
      val mVoVideoPractiseClass: Class<*>?
      val mDBZZUserClass: Class<*>?
      val mDBMyGoodClass: Class<*>?
      try {
            mVoCollectionBeanClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoCollectionBean")
            mVoOpeningTalkClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoOpeningTalk")
            mVoVideoPractiseClass = dexClassLoader.loadClass("com.zzenglish.client.vo.VoVideoPractise")
            mDBZZUserClass = dexClassLoader.loadClass("com.zzenglish.client.db.DBZZUser")
            mDBMyGoodClass = dexClassLoader.loadClass("com.zzenglish.client.db.DBMyGood")
      } catch (e: Exception) {
            XposedBridge.log("掌上英語錯誤$e")
            return
      }

      XposedHelpers.findAndHookMethod(mDBMyGoodClass, "isPurchaseByPid", String::class.java, String::class.java, object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = true
            }
      })

      // hasBuyHelper

      XposedHelpers.findAndHookMethod(mDBZZUserClass, "isForeverMember", Int::class.java, object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = true
            }
      })

      XposedHelpers.findAndHookMethod(mDBZZUserClass, "hasBuyHelper", Int::class.java, object : XC_MethodHook() {
            @Throws(Throwable::class)
            override fun afterHookedMethod(param: MethodHookParam) {
                param.result = true
            }
      })

0xA. 破解效果





0xB. 成品





毒瘤 发表于 2019-7-29 15:59

打开一看“360”字样特别显眼 我很明显不会脱360:'(weeqw,所以我还是先学习最新360,不过楼主这篇帖子非常不错,用XPOSED下刀,值得学习

棉花绒人 发表于 2019-7-29 13:12

这个压缩包解压后等到的文件名是
掌中英語_1.0_20190729.apk,暂时不打算安装到手机,先收藏了!感谢楼主分享!

SUwenjian1995 发表于 2019-7-29 12:57

+谢谢分享

idyllic639 发表于 2019-7-29 12:58

下载试试,感谢楼主分享

historyboy 发表于 2019-7-29 13:07

感谢楼主分享方法,学习了{:1_893:}

qazxcvbnm 发表于 2019-7-29 13:30

下载试试看。

soleilyang 发表于 2019-7-29 13:36

这个需要root在xposed里面安装才是和谐版吗

穿透骨頭撫摸妳 发表于 2019-7-29 13:38

soleilyang 发表于 2019-7-29 13:36
这个需要root在xposed里面安装才是和谐版吗

對, 安裝原版再安裝這個

jiang196771 发表于 2019-7-29 13:38

可以分享一下别的模块吗&#128541;&#128541;&#128541;

SunerC 发表于 2019-7-29 14:00

想要红橙黄绿青蓝紫那个模块:lol
页: [1] 2 3 4
查看完整版本: [xposed]掌中英语_6.4.0 VIP破解By_kwaiching