0x0. 非會員只可以打開試聽章節
0x1. ApkKiller反編譯, 發現軟件經過360加固
0x2. 脫殼, 根據界面信息, 搜尋VIP
0x3. isVip 應該是關鍵
[Asm] 纯文本查看 复制代码 isVipUser // 是否為VIP用戶
isVipVideo // 是否為VIP視頻
0x4. 以下三個類有isVipUser 相關
[Java] 纯文本查看 复制代码 com.zzenglish.client.vo.VoCollectionBean{
public int getIsVipUser() {
return this.isVipUser;
}
public int getIsvip() {
return this.isvip;
}
}
[Java] 纯文本查看 复制代码 /* 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;
}
}
[Java] 纯文本查看 复制代码 com.zzenglish.client.vo.VoVideoPractise{
public int getIsVipVideo() {
return this.isVipVideo;
}
public int getIsVipUser() {
return this.isVipUser;
}
}
0x5. Xposed hook 相關函數
[Python] 纯文本查看 复制代码 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. 胡亂再觀察一波, 搜尋"未開通"
[Java] 纯文本查看 复制代码 public static String getOpenProgramText(int i) {
return i == -1 ? "审核不通过" : i == 1 ? "已开通" : i == 2 ? "审核中" : "未开通";
}
此類中有用戶信息:
Lcom/zzenglish/client/activity/EditUserActivity
[Java] 纯文本查看 复制代码 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
[Java] 纯文本查看 复制代码 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
[Java] 纯文本查看 复制代码 /* 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
[Python] 纯文本查看 复制代码 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. 成品
掌中英語_1.0_20190729.7z
(788.63 KB, 下载次数: 255)
|