安卓系統啟動
什麼zygote?
init是核心啟動的第一個使用者級程式,zygote是由init程式通過解析init.zygote.rc檔案而建立的,zygote所對應的具體可執行程式是app_process,所對應的原始檔是App_main.cpp,程式名稱為zygote。
init.zygote.rc:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
安卓應用執行?
在ART模式下,zygote被init程式建立出來,用來孵化和啟動其他App。zygote程式具有App所需要的所有核心庫。
新的App程式在生成後,就會載入本App的程式程式碼(apk中的dex檔案)
Xposed介紹
Xposed是安卓系統上能夠修改系統或三方應用資訊的框架。
Xposed構成
名稱 | 介紹 |
---|---|
Xposed | Xposed框架Native部分 |
XposedBridge | Xposed向開發者提供的API與相應工具類庫 |
XposedInstaller | Xposed框架Android端本地管理,環境框架,以及第三方module資源下載的工具 |
Xposed初始化大體工作流程
(1)xposed的主要介面在XposedBrigde.jar中,核心功能在替換的虛擬機器中實現。
(2)app_process是Android App的啟動程式(具體形式是zygote fork() 呼叫app_process作為Android app的載體)。
原始碼分析
初始化
app_process有兩個對應原始檔,Android.mk會在編譯時根據sdk版本選擇對應原始檔作為入口(app_main.cpp或app_main2.cpp)
...
ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))
LOCAL_SRC_FILES := app_main2.cpp
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32_xposed
LOCAL_MODULE_STEM_64 := app_process64_xposed
else
LOCAL_SRC_FILES := app_main.cpp
LOCAL_MODULE_STEM := app_process_xposed
endif
...
ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))
include frameworks/base/cmds/xposed/ART.mk
else
include frameworks/base/cmds/xposed/Dalvik.mk
endif
app_main#main
在系統開機時,會通過app_process去建立zygote虛擬機器,就會呼叫到app_main2.cpp中的main函式。
main函式中主要做兩件事:(1)初始化xposed;(2)建立虛擬機器
int main(int argc, char* const argv[])
{
if (xposed::handleOptions(argc, argv))
return 0;
//程式碼省略...
runtime.mParentDir = parentDir;
// 初始化xposed,主要是將jar包新增至Classpath中
isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);
if (zygote) {
// 如果xposed初始化成功,將zygoteInit 替換為 de.robv.android.xposed.XposedBridge,然後建立虛擬機器
runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
}
...
}
app_main#initialize
初始化xposed
(1)初始化xposed內相關變數
(2)呼叫addJarToClasspath將XposedBridge.jar新增至系統目錄。
bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {
...
// 初始化xposed的相關變數
xposed->zygote = zygote;
xposed->startSystemServer = startSystemServer;
xposed->startClassName = className;
xposed->xposedVersionInt = xposedVersionInt;
...
// 列印 release、sdk、manufacturer、model、rom、fingerprint、platform相關資料
printRomInfo();
// 主要在於將jar包加入Classpath
return addJarToClasspath();
}
frameworks.base.core.jni.AndroidRuntime#start
建立對應虛擬機器
start做了4件事:
(1)建立虛擬機器
(2)初始化虛擬機器
(3)傳入呼叫類de.robv.android.xposed.XposedBridge
(4)初始化XposedBridge
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
//建立虛擬機器
if (startVm(&mJavaVM, &env) != 0) {
return;
}
// 初始化虛擬機器,xposed對虛擬機器進行修改
onVmCreated(env);
// 虛擬機器初始化完成後,會呼叫傳入的de.robv.android.xposed.XposedBridge類,初始化java層XposedBridge.jar
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
...
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
...
}
}
Xposed.cpp#onVmCreated
xposed重寫了onVmCreated。
onVmCreated做了什麼:
1、xposedInitLib->onVmCreatedCommon->initXposedBridge,初始化XposedBridge
(1)將register_natives_XposedBridge中的函式註冊為Native方法
2、xposedInitLib->onVmCreatedCommon->onVmCreated,為xposed_callback_class與xposed_callback_method賦值;
(1)xposed_callback_class和xposed_callback_method變數賦值
3、 de.robv.android.xposed.XposedBridge#main,初始化java層XposedBridge.jar
(1)hook 住系統資源相關的方法;
(2)hook 住zygote 的相關方法;
(3)載入系統中已經安裝的xposed 模組。
void onVmCreated(JNIEnv* env) {
// Determine the currently active runtime
...
// Load the suitable libxposed_*.so for it 通過dlopen載入libxposed_art.so
void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW);
...
// Initialize the library 初始化xposed相關庫
bool (*xposedInitLib)(XposedShared* shared) = NULL;
// 根據動態連結庫操作控制程式碼與符號,返回符號對應的地址
*(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib");
if (!xposedInitLib) {
ALOGE("Could not find function xposedInitLib");
return;
}
...
// xposedInitLib -> onVmCreatedCommon -> initXposedBridge -> 註冊Xposed相關Native方法
if (xposedInitLib(xposed)) {
xposed->onVmCreated(env);
}
}
libxposed_art.cpp#xposedInitLib
/** Called by Xposed's app_process replacement. */
bool xposedInitLib(XposedShared* shared) {
xposed = shared;
xposed->onVmCreated = &onVmCreatedCommon;
return true;
}
libxposed_common.cpp#onVmCreatedCommon
void onVmCreatedCommon(JNIEnv* env) {
if (!initXposedBridge(env) || !initZygoteService(env)) {
return;
}
if (!onVmCreated(env)) {
return;
}
xposedLoadedSuccessfully = true;
return;
}
libxposed_common.cpp#initXposedBridge
bool initXposedBridge(JNIEnv* env) {
classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
...
classXposedBridge = reinterpret_cast<jclass>(env->NewGlobalRef(classXposedBridge));
ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);
// 將register_natives_XposedBridge中的函式註冊為Native方法
if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {
ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
// 獲取XposedBridge.jar中的handleHookedMethod方法,並將該方法賦值給methodXposedBridgeHandleHookedMethod,後續會賦值至全域性變數中
methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
"(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
...
return true;
}
libxposed_art.cpp#onVmCreated
/** Called very early during VM startup. */
bool onVmCreated(JNIEnv*) {
// TODO: Handle CLASS_MIUI_RESOURCES?
ArtMethod::xposed_callback_class = classXposedBridge;
ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod;
return true;
}
de.robv.android.xposed.XposedBridge#main
虛擬機器初始化完成後,會呼叫傳入的de.robv.android.xposed.XposedBridge類,初始化java層XposedBridge.jar,呼叫main函式
(1)hook 系統資源相關的方法;
(2)hook zygote 的相關方法;
(3)載入系統中已經安裝的xposed 模組。
protected static void main(String[] args) {
// Initialize the Xposed framework and modules
try {
if (!hadInitErrors()) {
initXResources();
SELinuxHelper.initOnce();
SELinuxHelper.initForProcess(null);
runtime = getRuntime();
XPOSED_BRIDGE_VERSION = getXposedVersion();
if (isZygote) {
XposedInit.hookResources();
XposedInit.initForZygote();
}
XposedInit.loadModules();
} else {
Log.e(TAG, "Not initializing Xposed because of previous errors");
}
}
// Call the original startup code
if (isZygote) {
ZygoteInit.main(args);
} else {
RuntimeInit.main(args);
}
}
初始化結束。
例子
static final String TAG = "XposedTest001";
//final XC_MethodReplacement replacementTrue = XC_MethodReplacement.returnConstant(true);
public CheckSNHook(ClassLoader cl) {
super();
XposedBridge.log("hooking checkSN.");
try {
Class clz = (Class<?>) XposedHelpers.findClass("com.droider.crackme0201.MainActivity", cl);
//XposedBridge.hookAllMethods(clz, "checkSN", replacementTrue);
Log.d(TAG, "hooking clz");
XposedHelpers.findAndHookMethod(clz,
"checkSN",
String.class, String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
XposedBridge.log("1CheckSN afterHookedMethod called.");
String s1 = (String) param.args[0];
String s2 = (String) param.args[1];
Log.d(TAG, "s1:" + s1);
Log.d(TAG, "s2:" + s2);
param.setResult(true);
super.afterHookedMethod(param);
}
});
} catch (Exception e) {
e.printStackTrace();
}
XposedBridge.log("1hook checkSN done.");
}
Hook原理分析
XposedBridge#findAndHookMethod
1、根據函式名獲取對應Method物件
2、呼叫XposedBridge.hookMethod函式
public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_MethodHook))
throw new IllegalArgumentException("no callback defined");
// 封裝回撥函式
XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];
// 主要函式Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));
// 核心函式
return XposedBridge.hookMethod(m, callback);
}
XposedBridge#hookMethod
1、將回撥函式、引數型別、返回型別記錄到AdditionalHookInfo中
2、攔截指定函式呼叫,並使用其他函式替代(native函式)
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
...
// 將回撥函式、引數型別、返回型別記錄到AdditionalHookInfo中
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
// 攔截指定函式呼叫,並使用其他函式替代
hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
}
return callback.new Unhook(hookMethod);
}
private native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo);
libxposed.cpp#hookMethodNative
1、查詢我們需要hook的java Method對應的ArtMethod (每一個java層函式在ART下都有一個對應的ArtMethod)
void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
jobject, jint, jobject javaAdditionalInfo) {
...
// 獲取Java層Method對應native層的ArtMethod指標,將java函式描述為ArtMethod,查詢我們需要hook的java Method對應的ArtMethod
ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);
// Hook the method
artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}
EnableXposedHook
1、建立原函式備份
2、建立 XposedHookInfo 儲存原函式、before函式、after函式
3、設定機器指令入口地址,此時跳入到GetQuickProxyInvokeHandler()地址
void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) {
...
// 建立原函式備份
auto* cl = Runtime::Current()->GetClassLinker();
auto* linear_alloc = cl->GetAllocatorForClassLoader(GetClassLoader());
ArtMethod* backup_method = cl->CreateRuntimeMethod(linear_alloc);
backup_method->CopyFrom(this, cl->GetImagePointerSize());
// 設定識別符號kAccXposedOriginalMethod
backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod);
// Create a Method/Constructor object for the backup ArtMethod object
mirror::AbstractMethod* reflected_method;
if (IsConstructor()) {
reflected_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method);
} else {
reflected_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method);
}
reflected_method->SetAccessible<false>(true);
// 建立 XposedHookInfo 儲存原函式、before函式、after函式(reflected_method:被hook的函式,XposedHookInfo包含回撥函式)
XposedHookInfo* hook_info = reinterpret_cast<XposedHookInfo*>(linear_alloc->Alloc(soa.Self(), sizeof(XposedHookInfo)));
hook_info->reflected_method = soa.Vm()->AddGlobalRef(soa.Self(), reflected_method);
hook_info->additional_info = soa.Env()->NewGlobalRef(additional_info);
hook_info->original_method = backup_method;
...
//將entry_point_from_jni_指標指向hook資訊(目的是儲存),hook資訊包括原函式、before函式、after函式
SetEntryPointFromJniPtrSize(reinterpret_cast<uint8_t*>(hook_info), sizeof(void*));
// 設定機器指令入口地址,此時跳入到GetQuickProxyInvokeHandler()地址
SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
SetCodeItemOffset(0);
// Adjust access flags.
// 進行標誌位清除,此時這個ArtMethod物件對應是Hook後的方法,這個方法的實現不是native的
const uint32_t kRemoveFlags = kAccNative | kAccSynchronized | kAccAbstract | kAccDefault | kAccDefaultConflict;
SetAccessFlags((GetAccessFlags() & ~kRemoveFlags) | kAccXposedHookedMethod);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Runtime::Current()->GetThreadList()->ForEach(StackReplaceMethodAndInstallInstrumentation, this);
}
artQuickProxyInvokeHandler
extern "C" uint64_t artQuickProxyInvokeHandler(
ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
const bool is_xposed = proxy_method->IsXposedHookedMethod();//判斷 GetAccessFlags() 的kAccXposedHookedMethod 欄位
......
if (is_xposed) {
jmethodID proxy_methodid = soa.EncodeMethod(proxy_method);
self->EndAssertNoThreadSuspension(old_cause);
JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args);
local_ref_visitor.FixupReferences();
return result.GetJ();
}
......
}
InvokeXposedHandleHookedMethod
JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, jobject rcvr_jobj, jmethodID method, std::vector<jvalue>& args) {
//獲取ArtMethod 的 hookinfo 資訊,該資訊是EntryPointFromJniPtrSize所指向的資訊
const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo();
//將hookinfo 轉為一個陣列,以便和java 層進行通訊呼叫
jvalue invocation_args[5];
invocation_args[0].l = hookInfo->reflectedMethod;
invocation_args[1].i = 1;
invocation_args[2].l = hookInfo->additionalInfo;
invocation_args[3].l = rcvr_jobj;
invocation_args[4].l = args_jobj;
//通過CallStaticObjectMethodA 呼叫 xposed_callback_class 類裡面 xposed_callback_method 的方法
//xposed_callback_class: XposedBridge.java
//xposed_callback_method: handleHookedMethod 方法
//ArtMethod 的這兩個值,在系統開機時 在 onVmCreated 進行賦值的
jobject result = soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class,
ArtMethod::xposed_callback_method,
invocation_args);
}
InvokeXposedHandleHookedMethod
(1)獲取ArtMethod 的 hookinfo 資訊,該資訊是EntryPointFromJniPtrSize所指向的資訊
(2)通過CallStaticObjectMethodA 呼叫 xposed_callback_class 類裡面 xposed_callback_method 的方法
(3)此處xposed_callback_class,xposed_callback_method 是libxposed_art****.cpp#onVmCreated重寫時做的事
const XposedHookInfo* GetXposedHookInfo() {
DCHECK(IsXposedHookedMethod());
// 前面儲存EntryPointFromJniPtrSize指向的資訊
return reinterpret_cast<const XposedHookInfo*>(GetEntryPointFromJniPtrSize(sizeof(void*)));
}
GetXposedHookInfo:獲取EntryPointFromJniPtrSize儲存的資訊
Xposed.java#handleHookedMethod
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
Object thisObject, Object[] args) throws Throwable {
AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
...
// call "before method" callbacks
int beforeIdx = 0;
do {
try {
((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(t);
// reset result (ignoring what the unexpectedly exiting callback did)
param.setResult(null);
param.returnEarly = false;
continue;
}
if (param.returnEarly) {
// skip remaining "before" callbacks and corresponding "after" callbacks
beforeIdx++;
break;
}
} while (++beforeIdx < callbacksLength);
// call original method if not requested otherwise
if (!param.returnEarly) {
try {
param.setResult(invokeOriginalMethodNative(method, originalMethodId,
additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
} catch (InvocationTargetException e) {
param.setThrowable(e.getCause());
}
}
// call "after method" callbacks
int afterIdx = beforeIdx - 1;
do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
try {
((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(t);
// reset to last result (ignoring what the unexpectedly exiting callback did)
if (lastThrowable == null)
param.setResult(lastResult);
else
param.setThrowable(lastThrowable);
}
} while (--afterIdx >= 0);
// return
if (param.hasThrowable())
throw param.getThrowable();
else
return param.getResult();
}
XposedBridge.java 類的handleHookedMethod 方法,真正去處理 before、Original、after 這三個方法的呼叫關係。
ART函式呼叫原理
每一個Java函式在ART(虛擬機器)內部都由一個ArtMethod物件表示,ArtMethod物件中包含了函式名、引數型別、方法體程式碼入口地址等。
class ArtMethod {
...
protect:
HeapReference<Class> declaring_class_;
HeapReference<ObjectArray<ArtMethod>> dex_cache_resolved_methods_;
HeapReference<ObjectArray<Class>> dex_cache_resolved_types_;
uint32_t access_flags_;
uint32_t dex_code_item_offset_;
uint32_t dex_method_index_;
uint32_t method_index_;
struct PACKED(4) PtrSizedFields {
void* entry_point_from_interpreter_;
// 用於儲存jni函式資訊,非jni函式的無用,所以經常被hook框架將原方法儲存在entry_point_from_jni_
void* entry_point_from_jni_;
// ART HOOK常見的方法是替換入口點,執行hook的函式。(此處指向的是彙編程式碼,執行的是已經預處理過的機器碼)
void* entry_point_from_quick_compiled_code_;
#if defined(ART_USE_PORTABLE_COMPILER)
void* entry_point_from_portable_compiled_code_;
#endif
} ptr_sized_fields_;
static GcRoot<Class> java_lang_reflect_ArtMethod_;
}
替換entrypoint。將原函式對應的ArtMethod物件中entrypoint指向的機器碼替換為目標函式的機器碼,即可達到hook的目的。
總結
(1)準備包名、函式、引數型別、回撥函式呼叫Hook介面
(2)Xposed在找到art虛擬機器中找到方法對應的ArtMethod物件
(3)對ArtMethod物件進行備份
(4)修改備份物件的機器指令入口
(5)回撥handleHookedMethod函式
參考
Xposed 原始碼剖析1(初始話相關):https://blog.csdn.net/xiaolli/article/details/107506138
Xposed 原始碼剖析2:https://blog.csdn.net/a314131070/article/details/81092526
Xposed 原始碼剖析3:https://blog.csdn.net/a314131070/article/details/81092548
Xposed 原始碼剖析4:https://blog.csdn.net/xiaolli/article/details/107517039
Xposed 原始碼剖析5:https://egguncle.github.io/2018/02/04/xposed-art-hook-%E6%B5%85%E6%9E%90/
Xposed dalvik 原始碼剖析6:https://bbs.pediy.com/thread-247030.htm
ART入口點替換分析:https://www.jianshu.com/p/820eceabf219
ArtMethod結構:https://zhuanlan.zhihu.com/p/92267192
ArtMethod結構:https://bbs.pediy.com/thread-248898.htm
Dalvik與ART:https://www.jianshu.com/p/59d98244fb52
定製xposed:https://blog.csdn.net/qq_35834055/article/details/103256122