1.在数据库WRReader中的,userInfo表中
image.png
2.下面这个函数去设置时间
image.png
3.界面上显示“可兑XX书币“
XX的值来自于下面com.tencent.weread.home.fragment.PersonalController类的函数。可以看到参数userInfo的getCanExchange函数就是用来获取可以兑换多少书币的。这个UserInfo第一次初始化肯定是访问服务器取得的。单单修改这里肯定不可行,会被服务器的值覆盖掉的。因为Hook点不再这里,需要Hook住计算时间的算法。
private void renderUserInfo(UserInfo userInfo) {
this.mPersonalView.renderSignature(userInfo.getSignature());
this.mPersonalView.renderBookInfo(userInfo.getBuyCount());
this.mPersonalView.renderMyReviewAccessory(userInfo.getNoteBookCount(), userInfo.getReviewLikedCount(), userInfo.getReviewCommentedCount());
this.mPersonalView.renderFollowAccessory(userInfo.getFollowerCount(), userInfo.getFollowingCount(), userInfo.getWechatFriendCount());
this.mPersonalView.renderBookInventoryAccessory(userInfo.getBooklistCount() + userInfo.getBooklistCollectCount(), userInfo.getBooklistCollectCount());
this.mPersonalView.renderArticleAccessory(!ai.isNullOrEmpty(userInfo.getArticleBookId()), userInfo.getArticleCount(), userInfo.getArticleReadCount(), userInfo.getArticleWordCount());
this.mCanExchange = userInfo.getCanExchange();
if (this.mFriendRank != null) {
this.mPersonalView.renderRankAndExchange(this.mFriendRank, this.mCanExchange);
}
}
我们可以hook住这个函数,强制执行setCanExchange,可以更改界面的显示。但这个只是能更改显示,并不是修改最终值。
Class<?> userInfoClass = XposedHelpers.findClass("com.tencent.weread.model.domain.UserInfo", classLoader);
if (userInfoClass != null) {
XposedBridge.log("wenfengtou load class " + userInfoClass.toString());
Log.i(TAG,"wenfengtou load class " + userInfoClass.toString());
}
XposedHelpers.findAndHookMethod("com.tencent.weread.home.fragment.PersonalController", lpparam.classLoader, "renderUserInfo", userInfoClass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Object userInfoObj = param.args[0];
Log.i(TAG, "before renderUserInfo");
XposedHelpers.callMethod(userInfoObj, "setCanExchange", 400);
Log.i(TAG, "userInfoObj = " + userInfoObj);
}
});
我们要继续追溯这个UserInfo是怎么来的。可以看到是通过一个Cache的clone方法来的。
public void convertFrom(Cursor cursor) {
String[] columnNames = cursor.getColumnNames();
(!Cache.from(abstractCursor.getDatabase()).getCache(UserInfo.class).clone(abstractCursor, this, null)) {
}
查看UserInfo函数,发现是通过convertFrom函数来的。所以这些原始的数据
image.png
但是发现根本没有调用到这个函数,天!
只能通过trace看看UserInfo哪里有调用了,囧
image.png
看上去是通过Rxjava这里返回了结果
image.png
所以要看下UserInfo数据表是怎么建立起来的?
UserInfo表是数据库WRReader中。
通过跟踪Trace,
UserInfo.createTable
WRReader.onCreate
WRBookSQLiteHelper.onCreate
WRBookSQLiteHelper.onCreate
com.tencent.moai.database.sqlite.SQLiteOpenHelper.getDatabaseLocked
com.tencent.moai.database.sqlite.SQLiteOpenHelper.getReadableDatabase
找到Rxjava调用onNext的入口
image.png
今日实验步骤:
确定数据库的时间什么时候写入的?
image.png
听音频这个时间是不会变的
看书的话会变
image.png
通过实验发现,是在开始阅读的时候写入的数据库。所以应该是都会从服务器获取数据。所以hook的点应该要在上传数据的时候或者时间计算的算法。
image.png image.png image.png image.png那么我们是不是可以修改这个加速的值来实现加速呢?答案是肯定的!!
ProgressReportStrategy.java
int acceleration = ((int) (this.readingTime / 1000)) * ((ReadingTimeAcceleration) Features.of(ReadingTimeAcceleration.class)).acceleration();
他是通过feature来控制的。
package moai.feature;
import com.tencent.weread.book.feature.Accelerate1;
import com.tencent.weread.book.feature.Accelerate10;
import com.tencent.weread.book.feature.Accelerate100;
import com.tencent.weread.book.feature.ReadingTimeAcceleration;
import moai.feature.wrapper.IntFeatureWrapper;
public final class ReadingTimeAccelerationWrapper extends IntFeatureWrapper<ReadingTimeAcceleration> {
public ReadingTimeAccelerationWrapper(FeatureStorage featureStorage, Class cls) {
super(featureStorage, "reading_time_acceleration", Integer.valueOf(1), cls, 3, "阅读时间加速", Groups.FEATURE);
}
/* Access modifiers changed, original: protected|final */
public final void initializeIndex() {
mapIndex(Integer.valueOf(10), 0, Accelerate10.class);
mapIndex(Integer.valueOf(1), 1, Accelerate1.class);
mapIndex(Integer.valueOf(100), 2, Accelerate100.class);
}
}
直接修改发现有报错
05-01 07:49:09.341 2769 2769 I HookMain: after calculateReadingTime10870685
05-01 07:49:09.341 2769 2769 I HookMain: before WRLog logProgressReportStrategy saveLastReadAndReport page:421 mode:NORMAL
05-01 07:49:09.345 2769 2769 I HookMain: before WRLog logProgressReportStrategy ProgressReport pageNumber[421], totalPage[427]
05-01 07:49:09.345 2769 2769 I HookMain: after acceleration
05-01 07:49:09.365 2769 3045 I HookMain: before WRLog logReadingProgressReporter report called. bookId:840704,chapterUid:52,reviewId:,htmlPos:384,progress:99,deltaTime:11470,source:0,enableProgress:false,summary:9
05-01 07:49:09.366 2769 3045 I HookMain: before WRLog logReadingProgressReporter report called cancel, because deltaTime too large.
上传取消了。
联网传送的话,可能服务器有验证。因此尝试断网的控制
可以看到有下面的log。
05-01 13:25:11.699 15559 15745 I HookMain: before WRLog logReportService resendOfflineReadingInfos success
05-01 13:25:11.699 15559 15745 I HookMain: before WRLog logReportService resendOfflineReadingInfos success
网友评论