- 致编者:请牢记我们的域名wiki.mcbe-dev.net!
- 致编者:欢迎加入本Wiki的官方交流QQ群或Discord服务器!
- 基岩版1.19.31现已发布!(了解更多)
- Inner Core现已支持Xbox模组联机!(了解更多)
- 如果您是第一次来到本Wiki,欢迎注册一个账户
- 点击顶部的“编辑”或“编辑源代码”按钮即可编辑当前页面
- 请知悉:在不登录时也可以编辑和新建页面,但是您当前的IP地址会记录在编辑历史中
教程:利用Aide在Android设备上构建原生模组
From Minecraft基岩版开发Wiki
- Nmod是一种用来更改Minecraft的工具。具体实现是使用 Cydia Substrate 来Hook掉
libminecraftpe.so
里的函数。 - 本教程不讲解c++的语法,因此你需要一定的c++基础
- 本教程的目的是获取到编译完成的so文件,如何进行使用参见InnerCore原生模组教程
- so的文件位于
目录下./libs/armeabi-v7a
链接[edit]
- Aide高级版官网:https://www.aidepro.top/
- Aide高级版可用NDK:https://pan.baidu.com/s/1oHedYeVo93OV7p0INs2PKA?pwd=jktg
- Native模板链接:https://pan.baidu.com/s/1do_-0sBMl0B6cjT8J5iaCg?pwd=8733
- Disassembler链接:https://pan.baidu.com/s/1xHZqg2-ORP8Q5dfbY-tXMQ?pwd=2476
- 可咨询QQ群:795440679
- 本文作者QQ:2584300846
第一步:环境配置[edit]
- 确认你当前Android设备的版本
- 进入 Aide高级版 官网并下载
- 下载好后,进入app页面(这里以2.8.3版本为例)
- 点击右上角
- 点击更多
- 点击设置
- 点击构建&运行
- 点击管理Native代码支持包
- 如果你的 Android设备版本 < 13.0 请选择在线安装,如果 Android设备版本 >= 13.0 请指定NDK路径,手动安装
第二步:下载模板[edit]
- 如果你有能力自己通过内置模板进行改编,可跳过本步骤,否则请下载我给出的Native模板,此模板在原有的基础上进行了一定程度的改编,你可以以此为基础,创建你自己的原生模组
内容讲解[edit]
Native模板中的库[edit]
hook.h
//
// Created by zheka on 18/07/19.
//
#include <functional>
#ifndef HORIZON_HOOK_H
#define HORIZON_HOOK_H
typedef long long int64_t;
namespace SubstrateInterface {
/** change protection mode of given memory address and surrounding pages
* - address - address to change protection
* - offset - offset in pages before page, that contains address
* - size - size in pages
* - mode - protection mode
* returns 0 on success or error code
* */
int protect(void* address, int offset, int size, int mode);
/**
* technical hook function, use HookManager::addCallback
* - origin - function to hook address
* - hook - function to replace
* - result - pointer, to pass original hooked function
* returns true on success
*/
bool hook(void *origin, void *hook, void **result);
}
/**
* core namespace for callback creation
*/
namespace HookManager {
enum CallbackType {
// usual listener, does not create any special conditions
LISTENER = 0,
// called as target, will force all RETURN callbacks to be called after it
REPLACE = 4
};
enum CallbackTarget {
// called before target call and cannot be prevented
CALL = 0,
// called after "CALL" callbacks, can be prevented by ones before it
ACTION = 1,
// called just before target call if its not prevented, cannot be prevented by other such callback
TARGET = 2,
// called after target or replace callback is called to process return value and change it if required. RETURN | REPLACE combination is illegal
RETURN = 3
};
enum CallbackParams {
// should be passed, if callback returns result, otherwise engine will ignore it
RESULT = 16,
// should be passed, if callback requires controller, HookManager::CallbackController* will be passed as first parameter
CONTROLLER = 32
};
// priority for adding callbacks, greater priority = earlier call inside one CallbackTarget
enum CallbackPriority {
PRIORITY_MIN = -10,
PRIORITY_LESS = -5,
PRIORITY_DEFAULT = 0,
PRIORITY_GREATER = 5,
PRIORITY_MAX = 10
};
// not implemented for now
struct CallbackAddStatus {
};
/**
* used to access callback logic, result, status, ect.
* will be passed as first parameter, if CONTROLLER flag is given
*/
struct CallbackController {
// returns, if callback was prevented
bool isPrevented();
// returns, if callback was replaced
bool isReplaced();
// returns, if contains result from previous calls
bool hasResult();
// returns saved result
void* getResult();
// prevents callback, all future calls wont happen, this will force all RETURN callbacks to execute right after
void prevent();
// replaces callback, prevents target and TARGET callbacks from being called, equivalent to REPLACE flag effect
void replace();
// returns pointer to target function
void* getTarget();
// calls target with given params and casts result to R, usage: int result = controller->call<int>(1, "a");
template<typename R, typename... ARGS>
R call (ARGS... args) {
return ((R(*)(ARGS...)) getTarget())(args ...);
}
template<typename R, typename... ARGS>
R callAndReplace (ARGS... args) {
replace();
return ((R(*)(ARGS...)) getTarget())(args ...);
}
};
// technical struct
struct Hook {
public:
void* getCaller();
void* getTarget();
void* getAddress();
};
template<typename R, typename... ARGS>
class CallInterface;
/*
* represents interface to call specified function, its callback or target
* CallInterface is immune to future addCallback calls for its target method
*/
template<typename R, typename... ARGS>
class CallInterface<R(ARGS...)> {
public:
// calls target (original) function
R target(ARGS... args);
// calls function callback, or original function, if no callbacks exist
R hook(ARGS... args);
// equivalent to hook(), but as operator
R operator()(ARGS... args);
// creates same interface for other type
template<typename T>
CallInterface<T>* cast();
// returns target (original) function address
void* getTargetCaller();
// returns hook function address
void* getHookCaller();
void* getAddrHookCaller();
};
/*
* returns call interface for given function, call interface allows to call both callback and original function and immune to creating callbacks of this method
* usage:
* auto method = HookManager::getCallInterface<void*(int, int)>(...);
*/
template<typename T>
CallInterface<T>* getCallInterface(void* addr);
// -- function pointers --
/*
* adds func as callback for address with given params and priority
* - addr - address to add
* - func - function pointer, cast to void*
* - flags - all flags are described above, default combination is CALL | LISTENER
* - priority - higher priority - earlier call, default is DEFAULT_PRIORITY (0)
* */
CallbackAddStatus* addCallback(void* addr, void* func, int flags, int priority);
CallbackAddStatus* addCallback(void* addr, void* func, int flags);
CallbackAddStatus* addCallback(void* addr, void* func);
// -"- with usage of LAMBDA()
CallbackAddStatus* addCallback(void* addr, int64_t lambda, int flags, int priority);
CallbackAddStatus* addCallback(void* addr, int64_t lambda, int flags);
CallbackAddStatus* addCallback(void* addr, int64_t lambda);
}
#define LAMBDA(ARGS, CODE, VALUES, ...) ((int64_t) new std::function<void ARGS>([VALUES] ARGS CODE))
#endif //HORIZON_HOOK_H
该库提供了两种hook方法
- SubstrateInterface
- HookManager
- 如果你想写独立的原生模组,推荐使用SubstrateInterface,HookManager在多数情况下应用于与Java和JavaScript的交互中
- 值得一提的是,在比较新的版本中HookManager方法可能存在报错情况
mod.h
//
// Created by zheka on 18/07/15.
//
#include <jni.h>
#include <functional>
#include <vector>
#include <fstream>
#include <map>
#include "definitions.h"
#ifndef HORIZON_MOD_H
#define HORIZON_MOD_H
class Module;
/* represents mod library instance */
class ModLibrary {
public:
// library handle ptr
void* handle = nullptr;
// initialization result
int result = 0;
// returns list of all modules
std::vector<Module*> getModules();
};
/*
* Modules are main structural units of most mod libraries, that can receive and handle events, contain information etc.
* - Custom module class must extend Module class or some of its subclasses
* - Modules must be instantiated in library entry point (MAIN {...})
* - All initialization logic, including adding callbacks, must happen inside module's initialize() method
*
* Properties:
* - Modules can have name IDs and inherit other modules, this will be represented in UI to show hierarchy
* - Module name ID is used to search its description in manifest
*
* Tips:
* - Modules are used to divide all code into separate logic pieces
* - You should have global variables of module instances, created in MAIN {...}
* - All global variables should be put inside modules and accessed through its instances
* - All API events should be processed inside modules through addListener
* - Modules also used for profiling and crash handling
*/
class Module {
private:
ModLibrary* library = NULL;
Module* parent = NULL;
const char* id = NULL;
bool _isInitialized = false;
std::map<std::string, std::vector<std::function<void()>*>> listeners;
public:
// receives parent or name ID or both, root module must have name ID
Module(Module* parent, const char* id);
Module(const char* id);
Module(Module* parent);
// adds mod listener method, that will be called upon given event
template <typename T>
void addListener(std::string event, std::function<T> const& function);
// invokes all listeners for given event
template <typename... ARGS>
void onEvent(std::string event, ARGS... args);
// all initialization must happen here
virtual void initialize();
// separate method for java initialization
virtual void initializeJava(JNIEnv* env);
// returns parent module
Module* getParent();
// returns name ID
const char* getNameID();
// returns type, that used inside UI
virtual const char* getType();
// used to create separate mod log, used, if current profiled section belongs to this module
virtual std::ofstream* getLogStream();
bool isInitialized();
// returns mod library, this module belongs to
ModLibrary* getLibrary();
};
/*
* same class as module, but interpreted as mod inside UI, root module of your mod should implement this class
*/
class Mod : public Module {
public:
Mod(Module *parent, const char *id);
Mod(Module *parent);
Mod(const char *id);
};
namespace ModuleRegistry {
// returns all modules for given name ID
std::vector<Module*> getModulesById(std::string id);
// invokes event with given name and args for all modules, if filter function returned true
template <typename... ARGS>
void onFilteredEvent(std::string event, std::function<bool(Module*)> filter, ARGS... args);
// invokes event with given name and args for all modules
template <typename... ARGS>
void onEvent(std::string event, ARGS... args);
// invokes event with given name and args for all modules with given name ID
template <typename... ARGS>
void onTargetEvent(std::string module, std::string event, ARGS... args);
// invokes method for all modules
void onAction(std::function<void(Module*)> action);
};
namespace SignalHandler {
// initializes signal handles inside given process, this usually happens automatically
void initialize();
}
#define JNI_VERSION JNI_VERSION_1_4
/**
* describes library entry point
* - There must be only one entry point per library
* - In most cases used only for module instantiation
* - Inside its body there are variables ModLibrary* library - instance of this mod library and int* result - pointer to initialization result (0 is OK)
* MAIN {
*
* }
*/
#define NO_JNI_MAIN \
void __entry(ModLibrary* library, int* result); \
int __mod_main(ModLibrary* library) {\
int result = 0; \
SignalHandler::initialize();\
__entry(library, &result);\
return result;\
}\
void __entry(ModLibrary* library, int* result)
#define MAIN \
NO_JNI_MAIN
#endif //HORIZON_MOD_H
- 该库主要负责原生模组之间的隔离和原生模组的运行
- 本库已经过编者的修改,你可以重载JNI_OnLoad方法来获取jvm
- 一定要注意,每个原生模组的Module名称不可相同,否则会出现闪退情况
symbol.h
//
// Created by zheka on 18/07/19.
//
#ifndef HORIZON_SYMBOL_H
#define HORIZON_SYMBOL_H
#include "definitions.h"
/*
* represents dynamic library handle
* */
class DLHandle {
private:
const char* name = "<unknown>";
void* handle = nullptr;
// if dlsym failed, uses elf elf_dlsym instead
bool elf_support = true;
public:
void* symbol(const char* name);
};
/*
* interface to access dynamic libraries and symbols
* */
namespace DLHandleManager {
DLHandle* getHandle(const char* name);
/*
* initializes dynamic library handle, that can be further accessed by SYMBOL macros
* name - full library name
* key - name to access handle from SYMBOL, equals to name by default
* flags - flags, that are passed to dlopen, RTLD_LAZY by default
* support_elf, if dlsym fails, tries internal method, based on ELF format, true by default
* */
DLHandle* initializeHandle(const char* name, const char* key, int flags, bool support_elf);
DLHandle* initializeHandle(const char* name, int flags, bool support_elf);
DLHandle* initializeHandle(const char* name, const char* key, int flags);
DLHandle* initializeHandle(const char* name, int flags);
DLHandle* initializeHandle(const char* name, const char* key);
DLHandle* initializeHandle(const char* name);
// used in macros
void* _symbol(DLHandle* handle, const char* symbol);
void* _symbol(const char* dlname, const char* symbol);
}
// converts any type to (void*)
#define ADDRESS(X) ((void*) X)
// returns symbol address, if search failed, returns NULL and writes error to log
// HANDLE - DLHandle* or string, representing dynamic library to search ("mcpe" represents minecraft pe library)
// NAME - symbol name
#define SYMBOL(HANDLE, NAME) (DLHandleManager::_symbol(HANDLE, NAME))
// converts function pointer to (void*)
#define FUNCTION(X) ((void*) ((unsigned long long) &(X)))
#endif //HORIZON_SYMBOL_H
- 这个库是原生模组想要运行的关键库,你可以通过该库获取so文件的Handle,从而进行操作
其它Hook方法[edit]
dlfcn库[edit]
- 如果你熟悉dlfcn库的话,可以通过dlopen,dlsym函数之间的配合,通过函数名称实现Hook
- 如果你熟悉内存地址,也可以省略符号名,使用内存地址进行Hook(此方法编者未研究)
头文件[edit]
- 如果你接触过ModdedPE,并了解它的模组编写方式也可以使用头文件的方法进行Hook