用到的工具
修改前截图
修改前.jpg (145.09 KB, 下载次数: 0)
下载附件
修改前
2022-7-10 14:08 上传
21.jpg (83.13 KB, 下载次数: 0)
下载附件
21
2022-7-10 14:08 上传
修改后截图
18.jpg (128.72 KB, 下载次数: 0)
下载附件
18
2022-7-10 14:08 上传
19.jpg (118.62 KB, 下载次数: 0)
下载附件
19
2022-7-10 14:08 上传
20.jpg (159.05 KB, 下载次数: 0)
下载附件
20
2022-7-10 14:19 上传
自动签到
1.jpg (208.88 KB, 下载次数: 0)
下载附件
1
2022-7-10 14:07 上传
2.jpg (265.72 KB, 下载次数: 0)
下载附件
2
2022-7-10 14:07 上传
3.jpg (164.18 KB, 下载次数: 0)
下载附件
3
2022-7-10 14:07 上传
4.jpg (218.18 KB, 下载次数: 0)
下载附件
4
2022-7-10 14:07 上传
setText 、 setOnClickListener 方法(新旧布局相同思路)
5.jpg (331.19 KB, 下载次数: 0)
下载附件
5
2022-7-10 14:07 上传
6.jpg (253.17 KB, 下载次数: 0)
下载附件
6
2022-7-10 14:08 上传
7.jpg (309.37 KB, 下载次数: 0)
下载附件
7
2022-7-10 14:08 上传
5.如果是使用 Xposed Hook的差不多到这就行了 反射获取这个控件调用 performClick() 即可(甚至可以反射获取这个控件的 getText 方法 来判断是不是 签到 这两个字)
5.如果是反编译则添加如下代码
// p1 为 控件 注意跟在 setOnClickListener 后 参数为啥就设定啥
// 至于为何是 调用 LinearLayout 类的 performClick()
// 因为它这个 QDUIButton 继承的是 LinearLayout
// 示例 invoke-virtual {p1, v2}, Landroid/widget/LinearLayout;->setOnClickListener(Landroid/view/View$OnClickListener;)V
invoke-virtual {p1}, Landroid/widget/LinearLayout;->performClick()Z
ps:注意 如果控件没有定义 setOnClickListener 调用 performClick() 会返回 flase 即无效
ps:查找ID只演示一次 后续都是通用方法
新旧版布局
8.jpg (343.62 KB, 下载次数: 0)
下载附件
8
2022-7-10 14:08 上传
本地至尊卡
无任何作用,仅仅是为了好看?
1.字符常量池搜索一下 已开通 直接有结果,就不去整那些花里胡哨的了
9.jpg (218.35 KB, 下载次数: 0)
下载附件
9
2022-7-10 14:08 上传
10.jpg (289.29 KB, 下载次数: 0)
下载附件
10
2022-7-10 14:08 上传
11.jpg (259.7 KB, 下载次数: 0)
下载附件
11
2022-7-10 14:08 上传
12.jpg (226.84 KB, 下载次数: 0)
下载附件
12
2022-7-10 14:08 上传
*5.直接有和用户相关的类 打开发现果然有 2个疑似的方法 修改getMemberType() 返回值为 2 getIsMember() 为 1 后成功点亮本地至尊卡
13.jpg (200.34 KB, 下载次数: 0)
下载附件
13
2022-7-10 14:08 上传
14.jpg (228.79 KB, 下载次数: 0)
下载附件
14
2022-7-10 14:08 上传
去除书架右下角浮窗
public void refresh(int arg9) {
// 忽略其他代码
// 这里为重写的方法 notifyDataSetChanged
this.notifyDataSetChanged();
}
public void notifyDataSetChanged() {
// 忽略其他代码
this.loadBookShelfAd();
}
15.jpg (382.32 KB, 下载次数: 0)
下载附件
15
2022-7-10 14:08 上传
去除底部导航栏中心广告
16.jpg (303.96 KB, 下载次数: 0)
下载附件
16
2022-7-10 14:08 上传
17.jpg (328.77 KB, 下载次数: 0)
下载附件
17
2022-7-10 14:08 上传
去除TX系广告
// 忽略部分代码
public class CustomPkgConstants {
public static Class getADActivityClass() throws Exception {
return CustomPkgConstants.a("jebdyn.dexdec.irsb.Activity_eb35bb3");
}
public static String getADActivityName() {
String v0 = GlobalSetting.getCustomADActivityClassName();
return StringUtil.isEmpty(v0) ? CustomPkgConstants.b : v0;
}
public static String getAssetPluginDir() {
return "gdt_plugin";
}
public static String getAssetPluginName() {
return "gdtadv2.jar";
}
}
package com.qq.e.comm;
// 此为检查各种权限的类 最上方
public final class b {
public static boolean a(Context arg4) {
try {
//这为最终调用方法 返回false 即可判断sdk无法使用
}
catch(Throwable v4) {
GDTLogger.e("Exception While check SDK Env", v4);
return 0;
}
return 0;
}
private static boolean a(Context arg6, Class[] arg7) {
int v1;
for(v1 = 0; v1
23.png (20.48 KB, 下载次数: 0)
下载附件
23
2022-7-10 14:08 上传
package com.qidian.QDReader.start;
public final class AsyncMainGDTTask extends QDDefaultSyncTask {
@Override // com.qidian.QDReader.start.QDDefaultSyncTask
public Object create(Context arg2) {
AppMethodBeat.i(0x33C4);
String v2 = this.create(arg2);
AppMethodBeat.o(0x33C4);
return v2;
}
@Nullable
public String create(@NotNull Context arg4) {
AppMethodBeat.i(0x33BF);
p.e(arg4, "context");
Trace.beginSection("AsyncMainGDTTask----");
// key值删了
// 或许有人问了 为什么不Hook initWith 因为此方法初始化失败会卡住无法进入
GDTADManager.getInstance().initWith(arg4, "key");
Trace.endSection();
AppMethodBeat.o(0x33BF);
return "GDTADManager";
}
}
package com.qidian.QDReader.start;
public final class AsyncMainGameADSDKTask extends QDDefaultAsyncMainTask {
@Override // com.qidian.QDReader.start.QDDefaultAsyncMainTask
public Object create(Context arg2) {
AppMethodBeat.i(12970);
String v2 = this.create(arg2);
AppMethodBeat.o(12970);
return v2;
}
@Nullable
public String create(@NotNull Context arg3) {
AppMethodBeat.i(12950);
p.e(arg3, "context");
this.initGameAdSDK(arg3);
AppMethodBeat.o(12950);
return "GameADSDK";
}
private final void initGameAdSDK(Context arg4) {
AppMethodBeat.i(0x32A0);
try {
KlevinManager.init(arg4, new Builder().appId("30030").debugMode(false).directDownloadNetworkType(16).customController(new AsyncMainGameADSDKTask.b()).build(), new AsyncMainGameADSDKTask.a());
}
catch(Exception v4) {
Logger.e(v4.getMessage());
}
AppMethodBeat.o(0x32A0);
}
}
22.png (342.23 KB, 下载次数: 0)
下载附件
22
2022-7-10 14:08 上传
去除底部小红点
最终定位到 PagerSlidingTabStrip.s()方法, 修改 v5.h() 返回值为 1 即可
package com.qidian.QDReader.ui.widget.maintab;
public class PagerSlidingTabStrip extends HorizontalScrollView {
// 忽略部分代码
public void s() {
//layout:view_tab_title_text
v6 = LayoutInflater.from(this.getContext()).inflate(0x7F0C08A4, null);
// id:tagView
QDUITagView v10 = (QDUITagView)v6.findViewById(0x7F09147E);
// id:dotImg
SmallDotsView v9_1 = (SmallDotsView)v6.findViewById(0x7F09057B);
v9_1.setBorderWidth(n.a(1f));
int v11 = v5.i();
// 让其返回 1
int v13 = v5.h();
// 判断 int类型 v13 如果等于 0 则 显示 1则隐藏
if(v13 == 0) {
// 判断 v11 不等于 1 或者 v10 是显示状态 则隐藏 反之显示
v9_1.setVisibility(v11 != 1 || v10.getVisibility() == 0 ? 8 : 0);
} else if(v13 == 1) {
// 等同于View.GONE
v9_1.setVisibility(8);
}
}
}
闪屏页相关
禁用闪屏页
不过如果本地已经有缓存了可以清空一下App存储空间,通过分析也能发现这个保存的数据存在应用私有目录下 /databases/QDConfig 数据库内,光删除这个数据库也行。
@Override // com.qidian.QDReader.ui.activity.BaseActivity
protected void onCreate(Bundle arg7) {
//忽略其他代码
//
SplashManager.f().k(this.getApplicationContext());
}
public void k(Context arg6) {
// 忽略部分代码
try {
// 使用 okhttp 网络请求
QDHttpClient v1_1 = new com.qidian.QDReader.framework.network.qd.QDHttpClient.b().c(false).e(false).b();
StringBuilder v2 = new StringBuilder();
// 拼接参数
v2.append(Urls.q6());
v2.append("?");
String v3 = this.g();
// 拼接参数
if(!t0.k(v3)) {
v2.append("localLabels=");
v2.append(v3);
}
// 进行请求
v1_1.l(this.toString(), v2.toString(), new SplashManager.a(this, arg6));
} catch(Exception v6) {
}
}
自定义闪屏页
@Override // com.qidian.QDReader.ui.activity.BaseActivity
protected void onCreate(Bundle arg8) {
AppMethodBeat.i(0x7190);
super.onCreate(arg8);
// 设置不可侧滑返回
this.setSwipeBackEnable(false);
// 接收 intent 传的参数
this.actionUrl = this.getIntent().getStringExtra("actionUrl");
String v1 = this.getIntent().getStringExtra("imageFilePath");
this.splashId = this.getIntent().getLongExtra("splashId", 0L);
this.style = this.getIntent().getIntExtra("splashStyle", 0);
// 设置布局
this.setContentView(0x7F0C077E); // layout:splash_image_layout
// 初始化控件
ImageView v8 = (ImageView)this.findViewById(0x7F090854); // id:imgSplashScreen
this.imgSplash = v8;
v8.setVisibility(8);
Button v8_1 = (Button)this.findViewById(0x7F0913CC); // id:splash_skip_button
this.btnSkip = v8_1;
v8_1.setVisibility(8);
this.mGotoActivity = (QDUIRoundRelativeLayout)this.findViewById(0x7F0906F7); // id:gotoActivity
this.mGotoActivityShimmer = (ShimmerFrameLayout)this.findViewById(0x7F0906F8); // id:gotoActivity_shimmer
this.pagClick = (PAGWrapperView)this.findViewById(0x7F0910A6); // id:pagClick
this.ivTop = (ImageView)this.findViewById(0x7F090AA8); // id:ivTop
this.layoutSwipeUp = (ConstraintLayout)this.findViewById(0x7F090CF7); // id:layoutSwipeUp
this.layoutShadow = (RelativeLayout)this.findViewById(0x7F090CE1); // id:layoutShadow
// 状态栏沉浸
if(n0.t()) {
ViewCompat.setOnApplyWindowInsetsListener(this.getWindow().getDecorView(), (View arg1, WindowInsetsCompat arg2) -> this.lambda$onCreate$0(arg1, arg2));
}
else {
this.fitWindowInsets();
}
this.mSplashHelper = new c(this, this.btnSkip);
// 展示闪屏图片
this.showSplashImage(v1);
HashMap v8_2 = new HashMap();
v8_2.put("actionUrl", String.valueOf(this.actionUrl));
v8_2.put("splashId", String.valueOf(this.splashId));
this.configActivityData(this, v8_2);
AppMethodBeat.o(0x7190);
}
public static void start(SplashActivity arg3, String arg4, String arg5, long arg6, int arg8) {
AppMethodBeat.i(29048);
Intent v1 = new Intent();
v1.setClass(arg3, SplashImageActivity.class);
// 传入本地图片地址
v1.putExtra("imageFilePath", arg4);
// 动作Url
v1.putExtra("actionUrl", arg5);
// 闪屏ID
v1.putExtra("splashId", arg6);
// 闪屏类型
v1.putExtra("splashStyle", arg8);
arg3.startActivity(v1);
AppMethodBeat.o(29048);
}
private void showSplashImage(String arg10) {
int v3;
int v1_1;
AppMethodBeat.i(0x71BA);
// 判断传入的本地图片路径是否为空
if(t0.k(arg10)) {
this.layoutSwipeUp.setVisibility(8);
this.mGotoActivityShimmer.setVisibility(8);
this.pagClick.setVisibility(8);
this.imgSplash.setVisibility(8);
this.btnSkip.setVisibility(8);
this.mSplashHelper.e();
}
else {
// 如果创建文件路径失败则调用 mSplashHelper的 e() 方法 也就是直接结束此页面到主页
if(!com.qidian.QDReader.audiobook.utils.c.h(arg10)) {
this.mSplashHelper.e();
AppMethodBeat.o(0x71BA);
return;
}
// 不用管这些方法
Point v1 = c1.e(this);
if(v1 == null) {
v1_1 = e.E().k();
v3 = e.E().j();
}
else {
int v3_1 = v1.x;
v3 = v1.y;
v1_1 = v3_1;
}
try {
this.mBitmap = n6.a.g(arg10, v1_1, v3, false);
}
catch(OutOfMemoryError v10) {
Logger.exception(v10);
}
c1.f(this.imgSplash, new BitmapDrawable(this.getResources(), this.mBitmap), v1_1, v3);
this.mGotoActivity.setOnClickListener((View arg1) -> this.lambda$showSplashImage$1(arg1));
this.imgSplash.setVisibility(0);
this.btnSkip.setVisibility(0);
this.mSplashHelper.l();
// 下面是之前传入的一些参数,动作Url、闪屏类型
if(TextUtils.isEmpty(this.actionUrl)) {
this.layoutSwipeUp.setVisibility(8);
this.mGotoActivityShimmer.setVisibility(8);
this.pagClick.setVisibility(8);
}
else if(this.style == 1) {
this.layoutShadow.setVisibility(0);
this.layoutSwipeUp.setVisibility(0);
this.mGotoActivityShimmer.setVisibility(8);
this.pagClick.setVisibility(8);
this.mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
this.layoutSwipeUp.setOnTouchListener((View arg1, MotionEvent arg2) -> this.lambda$showSplashImage$2(arg1, arg2));
}
else {
this.layoutShadow.setVisibility(8);
this.layoutSwipeUp.setVisibility(8);
this.mGotoActivityShimmer.setVisibility(0);
this.pagClick.setVisibility(0);
}
}
AppMethodBeat.o(0x71BA);
}
24.jpg (418.7 KB, 下载次数: 0)
下载附件
24
2022-7-10 14:08 上传
25.jpg (368.29 KB, 下载次数: 0)
下载附件
25
2022-7-10 14:08 上传
0.gif (1.32 MB, 下载次数: 0)
下载附件
0.gif
2022-7-10 14:07 上传
1.gif (1.4 MB, 下载次数: 0)
下载附件
1.gif
2022-7-10 14:07 上传
反射方法
/**
* 通过反射获取控件
* @param param 参数 为 Class 实例
* @param name 字段名
*/
@Throws(NoSuchFieldException::class, IllegalAccessException::class)
inline fun getView(param: Any, name: String): T? {
return getParam(param, name)
}
/**
* 反射获取任何类型
*/
@Throws(NoSuchFieldException::class, IllegalAccessException::class)
inline fun getParam(param: Any, name: String): T? {
val clazz: Class = param.javaClass
val field = clazz.getDeclaredField(name)
field.isAccessible = true
return field[param] as? T
}