Skip to content

Commit

Permalink
通过配置文件配置hook点
Browse files Browse the repository at this point in the history
  • Loading branch information
kooritea committed Oct 24, 2020
1 parent 566fa20 commit 3bc8332
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 69 deletions.
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,35 @@
使用xposed让被完全停止的应用响应fcm,让fcm送达率达到100%,不错过任何通知

- 允许fcm唤醒选中的应用来发送通知
- 修复在国内网络下出现重连服务出现负数问题(仅203615037有效)
- 固定心跳间隔为117s(仅203615037有效)
- 修复在国内网络下出现重连服务出现负数问题(可选)(需要查看自己手机上gms的版本编辑配置文件)
- 固定心跳间隔为117s(可选)(需要查看自己手机上gms的版本编辑配置文件)

## 注意
在国内版miui上,除了在本应用中勾选目标应用之外,还要给予目标应用自启动权限中的允许系统唤醒权限(eu版和国际版则不需要给自启动权限)

## todo
没想到版本更新后混淆名称会变,支持多版本的事等之后有空再说吧
## 由于gms更新较快,代码遭到混淆,hook点几乎每个版本都会变动,所以需要手动修改配置文件
- 1. 确保xposed模块已经运行,如果存在/data/data/com.google.andorid.gms/shared_prefs/fcmfix_config.xml则证明模块已经成功运行,这是配置文件,之后都是编辑这个文件的内容。
- 2. 下载MT管理器等可以进行反编译的工具
- 3. 对/data/app/com.google.android.gms-/base.apk进行反编译(在MT管理器对apk文件选择查看,点击classes.dex使用Dex编辑器++打开,全选 -> 确认)
- 4. 搜索 "Previous alarms will stay active" ,路径: / ,搜索类型: 代码,按道理应该只有一个搜索结果,将搜到类名(一般是4个字母)填入配置文件的timer_class项中
- 5. 回到MT管理器点击刚才搜索到的类,看文件最上面第九行左右开始属性声明,`.field private final d:Landroid/content/Intent;`将这个私有属性类型是Intent的属性`d(按自己实际情况填)`填入配置文件的timer_intent_property
- 6. 寻找一个没有返回值,只有一个长整形参数的public方法,一般是第90行左右的`.method public final a(J)V`,认准这个`final``(J)``V`找这个方法,把方法名`a`填入配置文件的timer_settimeout_method
- 7. 继续查看刚在找到的这个timer_settimeout_method,这个方法往下几行的`iget-wide v0, p0, L[xxxx];->[f]:J`,这个xxxx是最开始的类名,这个f就是我们要找的属性名,将这个找到的属性名`f`填入配置文件的timer_next_time_property
- 8. 修改完上面的配置项,现在配置文件大概是这样的
```xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<long name="heartbeatInterval" value="117000" />
<string name="timer_intent_property">d</string>
<string name="timer_next_time_property">f</string>
<boolean name="enable" value="false" />
<string name="timer_settimeout_method">a</string>
<string name="timer_class">aazg</string>
<string name="gms_version">20.39.15 (120400-335085812)</string>
<boolean name="isInit" value="true" />
</map>

```
- 9. 最后将配置文件的enable修改true,保存,重启手机

- 10. 一般来说gms更新改变的只有类名也就是timer_class
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.kooritea.fcmfix"
minSdkVersion 27
targetSdkVersion 29
versionCode 2
versionName "0.0.2"
versionCode 3
versionName "0.0.3"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".XposedProvider"
android:authorities="com.kooritea.fcmfix.provider"
android:enabled="true"
android:exported="true"
>
</provider>
</application>

</manifest>
30 changes: 7 additions & 23 deletions app/src/main/java/com/kooritea/fcmfix/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -26,14 +29,18 @@
import android.widget.ListView;
import android.widget.TextView;

import com.kooritea.fcmfix.util.ContentProviderHelper;

import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Observer;
import java.util.Set;

public class MainActivity extends AppCompatActivity {
Expand Down Expand Up @@ -161,39 +168,16 @@ public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
appListAdapter.notifyDataSetChanged();
}
});
this.initHeartbeatInterval();
}

private void initHeartbeatInterval(){
if(this.sharedPreferences.getString("heartbeatInterval","").equals("")){
this.sharedPreferencesEditor.putString("heartbeatInterval","117000");
this.sharedPreferencesEditor.commit();
this.setWorldReadable();
}
}

private void addAppInAllowList(String packageName){
this.allowList.add(packageName);
this.sharedPreferencesEditor.putStringSet("allowList",this.allowList);
this.sharedPreferencesEditor.commit();
this.setWorldReadable();
}
private void deleteAppInAllowList(String packageName){
this.allowList.remove(packageName);
this.sharedPreferencesEditor.putStringSet("allowList",this.allowList);
this.sharedPreferencesEditor.commit();
this.setWorldReadable();
}

private void setWorldReadable() {
File dataDir = new File(getApplicationInfo().dataDir);
File prefsDir = new File(dataDir, "shared_prefs");
File prefsFile = new File(prefsDir, "config.xml");
if (prefsFile.exists()) {
for (File file : new File[]{dataDir, prefsDir, prefsFile}) {
file.setReadable(true, false);
file.setExecutable(true, false);
}
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/kooritea/fcmfix/XposedMain.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package com.kooritea.fcmfix;

import android.app.AndroidAppHelper;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

import com.kooritea.fcmfix.xposed.BroadcastFix;
import com.kooritea.fcmfix.xposed.ReconnectManagerFix;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
Expand All @@ -17,6 +26,8 @@ public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageP
if(loadPackageParam.packageName.equals("com.google.android.gms")){
XposedBridge.log("[fcmfix] start hook com.google.android.gms");
new ReconnectManagerFix(loadPackageParam);

}

}
}
85 changes: 85 additions & 0 deletions app/src/main/java/com/kooritea/fcmfix/XposedProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.kooritea.fcmfix;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class XposedProvider extends ContentProvider {

private static UriMatcher uriMatcher;

static
{
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.kooritea.fcmfix.provider","config",0);
}

@Override
public boolean onCreate() {
return false;
}

@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri))
{
default:
break;
}
return null;
}


@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//这里填写查询逻辑
SharedPreferences sharedPreferences = getContext().getSharedPreferences("config", Context.MODE_PRIVATE);
String[] COLUMN_NAME = { "key", "value" };
MatrixCursor data = new MatrixCursor(COLUMN_NAME);
switch (selection){
case "heartbeatInterval":
data.addRow(new Object[]{"heartbeatInterval",sharedPreferences.getLong("heartbeatInterval",117000L)});
break;
case "allowList":
for(String item : sharedPreferences.getStringSet("allowList",new HashSet<String>())){
data.addRow(new Object[]{"allowList",item});
}
break;
}
return data;
}

@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
//这里填写插入逻辑
return null;
}

@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
//这里填写更新逻辑
return 0;
}

@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
//这里填写删除逻辑
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.kooritea.fcmfix.util;

import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class ContentProviderHelper {

public ContentResolver contentResolver;
private Uri uri;
private ArrayList<ContentObserver> contentObservers = new ArrayList<>();
public Boolean useDefaultValue = false;

public ContentProviderHelper(Context context, String uri){
contentResolver = context.getContentResolver();
this.uri = Uri.parse(uri);
}
public ContentProviderHelper(){
useDefaultValue = true;
}

public Long getLong(String selection,Long defaultValue){
if(useDefaultValue){
return defaultValue;
}
Cursor cursor = contentResolver.query(uri, null, selection, null,null);
if(cursor == null){
return defaultValue;
}
cursor.getCount();
while(cursor.moveToNext()) {
if(selection.equals(cursor.getString(cursor.getColumnIndex("key")))){
cursor.close();
return cursor.getLong(cursor.getColumnIndex("value"));
}
}
cursor.close();
return defaultValue;
}
public String getString(String selection,String defaultValue){
if(useDefaultValue){
return defaultValue;
}
Cursor cursor =contentResolver.query(uri, null, selection, null,null);
if(cursor == null){
return defaultValue;
}
cursor.getCount();
while(cursor.moveToNext()) {
if(selection.equals(cursor.getString(cursor.getColumnIndex("key")))){
cursor.close();
return cursor.getString(cursor.getColumnIndex("value"));
}
}
cursor.close();
return defaultValue;
}
public Set<String> getStringSet(String selection){
if(useDefaultValue){
return new HashSet<String>();
}
Cursor cursor = contentResolver.query(uri, null, selection, null,null);
if(cursor == null){
return new HashSet<String>();
}
cursor.getCount();
Set<String> result = new HashSet<String>();
while(cursor.moveToNext()) {
if(selection.equals(cursor.getString(cursor.getColumnIndex("key")))){
result.add(cursor.getString(cursor.getColumnIndex("value")));
}
}
cursor.close();
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.kooritea.fcmfix.xposed;

import android.app.AndroidAppHelper;
import android.content.ContextWrapper;
import android.content.Intent;

import com.kooritea.fcmfix.util.ContentProviderHelper;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
Expand All @@ -15,8 +18,12 @@

public class BroadcastFix extends XposedModule {

protected ContentProviderHelper contentProviderHelper;

public BroadcastFix(XC_LoadPackage.LoadPackageParam loadPackageParam) {
super(loadPackageParam);
contentProviderHelper = new ContentProviderHelper(AndroidAppHelper.currentApplication().getApplicationContext(),"content://com.kooritea.fcmfix.provider/config");
this.startHook();
}

protected void startHook(){
Expand Down Expand Up @@ -58,7 +65,7 @@ protected void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwa

private boolean targetIsAllow(String packageName){
if (!packageName.equals("com.tencent.mm")) {
Set<String> allowList = this.getXSharedPreferences().getStringSet("allowList", new HashSet<String>());
Set<String> allowList = this.contentProviderHelper.getStringSet("allowList");
for (String item : allowList) {
if (item.equals(packageName)) {
return true;
Expand Down
Loading

0 comments on commit 3bc8332

Please sign in to comment.