init

vvb2060 2022-05-07 23:04:22 +08:00
commit 83e5721b88
No known key found for this signature in database
GPG Key ID: 59B2BF15A79E26FA
7 changed files with 401 additions and 0 deletions

25
Home.md Normal file

@ -0,0 +1,25 @@
# LSPosed official wiki
基于 Riru/Zygisk 的 ART hook 框架,提供与原版 Xposed 相同的 API, 使用 [LSPlant](https://github.com/LSPosed/LSPlant) 进行 hook。
> Xposed 框架是一套开放源代码的框架服务可以在不修改APK文件的情况下修改目标应用的运行基于它可以制作功能强大的模块且在功能不冲突的情况下同时运作。
LSPosed 框架和原版 Xposed 框架的不同之处
1. LSPosed 支持 Android 8.1 以后的版本
2. LSPosed 只有注入被勾选的应用,其他应用运行在干净的环境
3. 对于不需要注入系统服务的模块,重启目标应用即可激活
4. LSPosed 极难被检测,文件系统没有可疑痕迹,不需要安装独立的管理器应用
## Chat group / 用户讨论群组
- Telegram channel: [@LSPosed](https://t.me/LSPosed)
## Catalog / 目录
### For User / 用户
- [How to use it](https://github.com/LSPosed/LSPosed/wiki/How-to-use-it) / [如何使用](https://github.com/LSPosed/LSPosed/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8)
### For Developer / 开发者
- [New XSharedPreferences](https://github.com/LSPosed/LSPosed/wiki/New-XSharedPreferences)
- [Module Scope](https://github.com/LSPosed/LSPosed/wiki/Module-Scope)
- [Native Hook](https://github.com/LSPosed/LSPosed/wiki/Native-Hook)

19
How-to-use-it.md Normal file

@ -0,0 +1,19 @@
## Installation
1. Make sure you have [Magisk](https://topjohnwu.github.io/Magisk/install.html) 23.0+ and [Riru](https://github.com/RikkaApps/Riru#install) 25.0+ installed on your device.
1. Go to Magisk's `Modules` tab, search and install LSPosed.
1. Reboot your device and wait for a while, you will see the LSPosed icon.
1. Launch LSPosed and check the activation status.
### Install & Activate Modules
1. LSPosed is a framework and it cannot work without modules. You can browser from the `Repository` page from the app or the [web](http://modules.lsposed.org/).
1. Download and install modules as normal apps.
1. After installation, go to LSPosed's `Modules` page and click the module you installed, tick the enable switch.
1. Select apps to which the modules should apply. Most of the modules now support displaying recommended scope, which LSPosed will select automatically.
1. Some obsolete modules need to inject into each app. It's so dangerous that LSPosed doesn't support `Select All` and requires the user to tick each app one by one manually (the same policy as Magisk Hide).
### Dual App / Multiuser Support
1. Before that, you should notice that many obsolete modules were born in the era when multiuser was not popular and thus they may not support multiuser well. There's nothing LSPosed could do to make them work on multiuser mode.
1. Moreover, the LSPosed Manager will only work on the primary user, and it can manage modules and apps from all users. You DON'T have to install it on the other users.
1. As long as your device has multiple users, LSPosed shows a tab for each user on the `Modules` page. Switch between tabs to manager modules from different users.
1. You should install modules to the user on which you want it to work. You can do it manually (recommended) by switching to that user and install the modules. Or you can do it via LSPosed Manager (not recommended as long as you can do it manually).

28
Module-Scope.md Normal file

@ -0,0 +1,28 @@
### Background
Module scopes are required in LSPosed to activate a module. Of course, user can select their own scope for a specific module, but it's too advance for a user to determine the real module scope. So in LSPosed, a new meta-data is introduced for module developers to declare its scope.
### Example usage
If a module wants to hook package `com.example.a` and `com.example.b`, in `AndroidManifest.xml`, a meta-data named `xposedscope` should be defined as:
```
<meta-data
android:name="xposedscope"
android:resource="@array/example_scope" />
```
And in `array.xml`, a string array named `example_scope` (this name could be arbitrarily changed) should be defined as:
```
<string-array name="example_scope" >
<item>com.example.a</item>
<item>com.example.b</item>
</string-array>
```
If no [alternative resources](https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources), can hardcode it:
```
<meta-data
android:name="xposedscope"
android:value="com.example.a;com.example.b" />
```
### Advantage
By defining such a meta-data, all apps within this array will be selected by default when the module is enabled in the manager.

141
Native-Hook.md Normal file

@ -0,0 +1,141 @@
To use the native hook, you have to know how to write an NDK project first.
### How Native Hook Works
Native Hook can help you hook native functions of an app. Whenever a new native library loaded, LSPosed will invoke a callback of the module and the module can then perform hooks on this native library.
### Header File
To use Native hook, you have to first create a header file with the following content:
```c++
typedef int (*HookFunType)(void *func, void *replace, void **backup);
typedef int (*UnhookFunType)(void *func);
typedef void (*NativeOnModuleLoaded)(const char *name, void *handle);
typedef struct {
uint32_t version;
HookFunType hook_func;
UnhookFunType unhook_func;
} NativeAPIEntries;
typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries);
```
### Entries
After creating the header file, you can then create a source file with the entry function:
```c++
extern "C" [[gnu::visibility("default")]] [[gnu::used]]
NativeOnModuleLoaded native_init(const NativeAPIEntries *entries);
```
Notice the name `native_init` has to be kept and exported, so we have to use the attribute `[[gnu::visibility("default")]] [[gnu::used]]` for this purpose. `extern "C"` is to make the name `native_init` to be in c style.
The `NativeAPIEntries` object contains necessary util functions for you to hook (Do not try to modify its content. It will crash.). Currently, they are `hook_fun` and `unhook_func`, which can be used to hook and unhook functions respectively. The return value of the function is the callback function which will be called once a library is loaded by `dlopen`. We will talk about this callback function later.
You can perform hooks on system libraries in `native_init`.
### Callback
The callback function should be in type `NativeOnModuleLoaded`. It will be called by LSPosed each time a library is loaded. It receives two parameters: `name` is the name of the loaded library (e.g. `/xxx/libtarget.so`); `handle` is the handle of the library which you can use to `dlsym`. You can perform `dlsym` of this library to get the pointer of the target function can call the `hook_func`.
### JNIEnv Hooks
You may try to hook some JNIEnv functions like `GetMethodId`. Since the function is in `libart.so` which you cannot easily `dlopen`, you can try to create `JNI_OnLoad` to let Android System give you a `JavaVm` and then get a `JNIEnv` once your library is loaded. Then you can get the JNIEnv functions by the `JNIEnv` object.
### Tell Entries to Framework
Naturally, like Java hook, you have to tell which libraries of your module contain `native_init`. You can do this by placing the name of your libraries to the `assets/native_init` file of your apk. Then you have to manually `System.loadLibrary` your module once your module is loaded.
### Simple Example
Here is a simple example which use native hook:
assets/native_init
```
libexample.so
```
assets/xposed_init
```
org.lsposed.example.MainHook
```
AndroidManifest.xml
```xml
<application ...
android:multiArch="true"
android:extractNativeLibs="false"
>
```
MainHook.kt
```kotlin
package org.lsposed.example
class MainHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == "org.lsposed.target") {
try {
System.loadLibrary("example")
} catch (e: Throwable) {
LogUtil.e(e)
}
}
}
}
```
example.cc
```c++
static HookFunType hook_func = nullptr;
int (*backup)();
int fake() {
return backup() + 1;
}
FILE *(*backup_fopen)(const char *filename, const char *mode);
FILE *fake_fopen(const char *filename, const char *mode) {
if (strstr(filename, "banned")) return nullptr;
return backup_fopen(filename, mode);
}
jclass (*backup_FindClass)(JNIEnv *env, const char *name);
jclass fake_FindClass(JNIEnv *env, const char *name)
{
if(!strcmp(name, "dalvik/system/BaseDexClassLoader"))
return nullptr;
return backup_FindClass(env, name);
}
void on_library_loaded(const char *name, void *handle) {
// hooks on `libtarget.so`
if (std::string(name).ends_with("libtarget.so")) {
void *target = dlsym(handle, "target_fun");
hook_func(target, (void *) fake, (void **) &backup);
}
}
extern "C" [[gnu::visibility("default")]] [[gnu::used]]
jint JNI_OnLoad(JavaVM *jvm, void*) {
JNIEnv *env = nullptr;
jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
hook_func((void *)env->functions->FindClass, (void *)fake_FindClass, (void **)&backup_FindClass);
return JNI_VERSION_1_6;
}
extern "C" [[gnu::visibility("default")]] [[gnu::used]]
NativeOnModuleLoaded native_init(const NativeAPIEntries *entries) {
hook_func = entries->hook_func;
// system hooks
hook_func((void*) fopen, (void*) fake_fopen, (void**) &backup_fopen);
return on_library_loaded;
}
```

169
New-XSharedPreferences.md Normal file

@ -0,0 +1,169 @@
## New XSharedPreferences
Since LSPosed API 93, a new `XSharedPreferences` is provided targeted for modules with sdk > 27. Modules developers can enable this new `XSharedPreferences` support by modifying the meta-data of your modules to set `xposedminversion` to 93 or above, or add `xposedsharedprefs` meta-data.
### For the module
When enabled, LSPosed will automatically hook `ContextImpl.getPreferencesDir()` so that your `Context.getSharedPreferences` and `PreferenceFragment` could work without further modification. Also, LSPosed hooks `ContextImpl.checkMode(int)` so that you can easily get a world readable shared preference by passing `Context.MODE_WORLD_READABLE` to `Context.getSharedPreferences`.
If you are using `xposedsharedprefs` meta-data to indicate your module will use this new feature, you probably want to ensure this feature is working properly. The following code snippet will do the magic:
<details>
<summary>Java</summary>
```java
SharedPreferences pref;
try {
pref = context.getSharedPreferences(MY_PREF_NAME, Context.MODE_WORLD_READABLE);
} catch (SecurityException ignored) {
// The new XSharedPreferences is not enabled or module's not loading
pref = null; // other fallback, if any
}
```
</details>
<details>
<summary>Kotlin</summary>
```kotlin
val pref = try {
context.getSharedPreferences(MY_PREF_NAME, Context.MODE_WORLD_READABLE)
} catch (e: SecurityException) {
// The new XSharedPreferences is not enabled or module's not loading
null // other fallback, if any
}
```
</details>
Notice that LSPosed will help you to make the preference world readable if and only if you are getting preference in mode `Context.MODE_WORLD_READABLE`.
### For the hooked app
To read the preference from the hooked app, you can simply use `XSharedPreferences(String packageName)` or `XSharedPreferences(String packageName, String prefFileName)` to retrieve the preference. Notice that you cannot use the `XSharedPreferences(File prefFile)` because the preference file is stored in a random directory.
You should check the permission before you read anything from the module. You should also load the preference if you need it. Try to split the preference file to multiple files for different purposes. Here's an example:
<details>
<summary>Java</summary>
```java
public class XposedInit implements IXposedHookLoadPackage, IXposedHookZygoteInit, IXposedHookInitPackageResources {
private static SharedPreferences getPref(String path) {
SharedPreferences pref = new XSharedPreferences(BuildConfig.APPLICATION_ID, path);
return pref.getFile().canRead() ? pref : null;
}
@Override
public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) {
SharedPreferences pref = getPref("zygote_conf");
if (pref != null) {
// do things with it
} else {
Log.e(TAG, "Cannot load pref for zygote properly");
}
}
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
SharedPreferences pref;
switch (lpparam.packageName) {
case PACKAGE_NAME_A:
pref = getPref("a_conf");
if (pref != null) {
// do things with it for A
} else {
Log.e(TAG, "Cannot load pref for A properly");
}
break;
case PACKAGE_NAME_B:
pref = getPref("b_conf");
if (pref != null) {
// do things with it for B
} else {
Log.e(TAG, "Cannot load pref for B properly");
}
break;
case BuildConfig.APPLICATION_ID:
// hook myself
break;
default:
// skip
}
}
@Override
public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) {
// similar to handleLoadPackage
}
}
```
</details>
<details>
<summary>Kotlin</summary>
```kotlin
class XposedInit : IXposedHookLoadPackage, IXposedHookZygoteInit, IXposedHookInitPackageResources {
companion object {
fun getPref(path: String) : SharedPreferences? {
val pref = XSharedPreferences(BuildConfig.APPLICATION_ID, path)
return if(pref.file.canRead()) pref else null
}
// lazy loads when needed
val prefForZygote by lazy { getPref("zygote_conf") }
// lazy loads when needed
val prefForA by lazy { getPref("a_conf") }
// lazy loads when needed
val prefForB by lazy { getPref("b_conf") }
}
override fun initZygote(startupParam: StartupParam) {
prefForZygote?.let {
// do things with it
} ?: Log.e(TAG, "Cannot load pref for zygote properly")
}
override fun handleLoadPackage(lpparam: LoadPackageParam) {
when(lpparam.packageName) {
PACKAGE_NAME_A -> {
prefForA?.let {
// do things with it for A
} ?: Log.e(TAG, "Cannot load pref for A properly")
}
PACKAGE_NAME_B -> {
prefForB?.let {
// do things with it for B
} ?: Log.e(TAG, "Cannot load pref for B properly")
}
BuildConfig.APPLICATION_ID -> {
// hook myself
}
default -> {
// skip
}
}
}
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
// similar to handleLoadPackage
}
}
```
</details>
### Preference change listener in a hooked process
It is possible to register preference change listener within a hooked process to listen for changes of the preferences of a module.
Call `registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener)` on a XSharedPreferences instance passing
reference to your listener. XSharedPreferences will start watching for changes on the physical file that belongs to given XSharedPreferences instance and notify specified listener every time change on the preference file happens; allowing module running within a hooked process to react on the changes of the module preferences.
**Note that by design it is not possible to determine which particular preference changed and thus preference key in listener's callback invocation will always be null.**
Typical scenario would thus be to call `reload()` on the XSharedPrefereces instance to get a fresh set of the preferences after the change happened.
To stop watching for preference changes call `unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener)` passing a reference to the previously registered listener.

1
_Footer.md Normal file

@ -0,0 +1 @@
LSPosed Official Wiki

18
如何使用.md Normal file

@ -0,0 +1,18 @@
## 安装
1. 确保你的设备上已安装 [Magisk](https://topjohnwu.github.io/Magisk/install.html) 和 [Riru](https://github.com/RikkaApps/Riru#install)。
1. 进入Magisk的 "模块 "标签,搜索并安装 LSPosed。
1. 重新启动你的设备,等待一段时间,你会看到 LSPosed 图标。
1. 启动 LSPosed检查激活状态。
### 安装和激活模块
1. LSPosed 是一个框架,没有模块它就不能工作。你可以在应用内或[网页](http://modules.lsposed.org/)上浏览模块。
1. 下载并安装模块,就像普通的应用程序一样。
1. 安装后进入LSPosed的`模块`页面,点击你安装的模块,勾选启用开关。
1. 选择模块应该适用的应用程序。大多数模块现在支持显示推荐范围LSPosed会自动选择。
1. 一些过时的模块需要注入到每个应用程序中。这很危险LSPosed不支持 "全选"需要用户手动逐个勾选每个应用与Magisk Hide的策略相同
### 双开/多用户支持
1. 在这之前你应该注意到许多过时的模块诞生于多用户不流行的年代因此它们可能不能很好地支持多用户。LSPosed没有办法使它们在多用户模式下工作。
1. 此外LSPosed管理器将只在主用户上工作它可以管理所有用户的模块和应用程序。你不需要在其他用户上安装它。
1. 只要你的设备有多个用户LSPosed就会在 "模块 "页面为每个用户显示一个标签。在不同的标签之间切换,可以管理不同用户的模块。
1. 你应该把模块安装到你希望它工作的用户上。你可以通过切换到该用户并安装模块来手动完成推荐。或者你可以通过LSPosed管理器来做不推荐只要你能手动完成