从AndFix源码看Android热修复

2024-06-15 10:48
文章标签 android 源码 修复 andfix

本文主要是介绍从AndFix源码看Android热修复,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前几篇文章介绍过注入Dex实现热修复:http://blog.csdn.net/u011686167/article/details/78966936 。现在探讨阿里系的底层替换虚拟机的方法指针实现热修复。Android系统中存在两种虚拟机:dalvik和art。5.0版本以前是dalvik,而5.0版本后官方建议使用art。

dalvik虚拟机引用的头文件结构:

enum {ACC_PUBLIC = 0x00000001,       // class, field, method, icACC_PRIVATE = 0x00000002,       // field, method, icACC_PROTECTED = 0x00000004,       // field, method, icACC_STATIC = 0x00000008,       // field, method, icACC_FINAL = 0x00000010,       // class, field, method, icACC_SYNCHRONIZED = 0x00000020,       // method (only allowed on natives)ACC_SUPER = 0x00000020,       // class (not used in Dalvik)ACC_VOLATILE = 0x00000040,       // fieldACC_BRIDGE = 0x00000040,       // method (1.5)ACC_TRANSIENT = 0x00000080,       // fieldACC_VARARGS = 0x00000080,       // method (1.5)ACC_NATIVE = 0x00000100,       // methodACC_INTERFACE = 0x00000200,       // class, icACC_ABSTRACT = 0x00000400,       // class, method, icACC_STRICT = 0x00000800,       // methodACC_SYNTHETIC = 0x00001000,       // field, method, icACC_ANNOTATION = 0x00002000,       // class, ic (1.5)ACC_ENUM = 0x00004000,       // class, field, ic (1.5)ACC_CONSTRUCTOR = 0x00010000,       // method (Dalvik only)ACC_DECLARED_SYNCHRONIZED = 0x00020000,       // method (Dalvik only)ACC_CLASS_MASK = (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT| ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),ACC_INNER_CLASS_MASK = (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED| ACC_STATIC),ACC_FIELD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC| ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC| ACC_ENUM),ACC_METHOD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC| ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS| ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC| ACC_CONSTRUCTOR | ACC_DECLARED_SYNCHRONIZED),
};typedef struct DexProto {u4* dexFile; /* file the idx refers to */u4 protoIdx; /* index into proto_ids table of dexFile */
} DexProto;typedef void (*DalvikBridgeFunc)(const u4* args, void* pResult,const void* method, void* self);struct Field {void* clazz; /* class in which the field is declared */const char* name;const char* signature; /* e.g. "I", "[C", "Landroid/os/Debug;" */u4 accessFlags;
};struct Method;
struct ClassObject;typedef struct Object {/* ptr to class object */struct ClassObject* clazz;/** A word containing either a "thin" lock or a "fat" monitor.  See* the comments in Sync.c for a description of its layout.*/u4 lock;
} Object;struct InitiatingLoaderList {/* a list of initiating loader Objects; grown and initialized on demand */void** initiatingLoaders;/* count of loaders in the above list */int initiatingLoaderCount;
};enum PrimitiveType {PRIM_NOT = 0, /* value is a reference type, not a primitive type */PRIM_VOID = 1,PRIM_BOOLEAN = 2,PRIM_BYTE = 3,PRIM_SHORT = 4,PRIM_CHAR = 5,PRIM_INT = 6,PRIM_LONG = 7,PRIM_FLOAT = 8,PRIM_DOUBLE = 9,
}typedef PrimitiveType;enum ClassStatus {CLASS_ERROR = -1,CLASS_NOTREADY = 0, CLASS_IDX = 1, /* loaded, DEX idx in super or ifaces */CLASS_LOADED = 2, /* DEX idx values resolved */CLASS_RESOLVED = 3, /* part of linking */CLASS_VERIFYING = 4, /* in the process of being verified */CLASS_VERIFIED = 5, /* logically part of linking; done pre-init */CLASS_INITIALIZING = 6, /* class init in progress */CLASS_INITIALIZED = 7, /* ready to go */
}typedef ClassStatus;typedef struct ClassObject {struct Object o; // emulate C++ inheritance, Collin/* leave space for instance data; we could access fields directly if wefreeze the definition of java/lang/Class */u4 instanceData[4];/* UTF-8 descriptor for the class; from constant pool, or on heapif generated ("[C") */const char* descriptor;char* descriptorAlloc;/* access flags; low 16 bits are defined by VM spec */u4 accessFlags;/* VM-unique class serial number, nonzero, set very early */u4 serialNumber;/* DexFile from which we came; needed to resolve constant pool entries *//* (will be NULL for VM-generated, e.g. arrays and primitive classes) */void* pDvmDex;/* state of class initialization */ClassStatus status;/* if class verify fails, we must return same error on subsequent tries */struct ClassObject* verifyErrorClass;/* threadId, used to check for recursive <clinit> invocation */u4 initThreadId;/** Total object size; used when allocating storage on gc heap.  (For* interfaces and abstract classes this will be zero.)*/size_t objectSize;/* arrays only: class object for base element, for instanceof/checkcast(for String[][][], this will be String) */struct ClassObject* elementClass;/* arrays only: number of dimensions, e.g. int[][] is 2 */int arrayDim;PrimitiveType primitiveType;/* superclass, or NULL if this is java.lang.Object */struct ClassObject* super;/* defining class loader, or NULL for the "bootstrap" system loader */struct Object* classLoader;struct InitiatingLoaderList initiatingLoaderList;/* array of interfaces this class implements directly */int interfaceCount;struct ClassObject** interfaces;/* static, private, and <init> methods */int directMethodCount;struct Method* directMethods;/* virtual methods defined in this class; invoked through vtable */int virtualMethodCount;struct Method* virtualMethods;/** Virtual method table (vtable), for use by "invoke-virtual".*/int vtableCount;struct Method** vtable;} ClassObject;typedef struct Method {struct ClassObject *clazz;u4 accessFlags;u2 methodIndex;u2 registersSize; /* ins + locals */u2 outsSize;u2 insSize;/* method name, e.g. "<init>" or "eatLunch" */const char* name;/** Method prototype descriptor string (return and argument types).*/DexProto prototype;/* short-form method descriptor string */const char* shorty;/** The remaining items are not used for abstract or native methods.*//* the actual code */u2* insns;/* cached JNI argument and return-type hints */int jniArgInfo;/** Native method ptr; could be actual function or a JNI bridge.*/DalvikBridgeFunc nativeFunc;#ifdef WITH_PROFILERbool inProfile;
#endif
#ifdef WITH_DEBUGGERshort debugBreakpointCount;
#endifbool fastJni;/** JNI: true if this method has no reference arguments.*/bool noRef;} Method;

art虚拟机引用的头文件:

namespace art {
namespace mirror {
class Object {
public:// The number of vtable entries in java.lang.Object.
//	static constexpr size_t kVTableLength = 11;static uint32_t hash_code_seed;uint32_t klass_;uint32_t monitor_;
};class Class: public Object {
public:enum Status {kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead.kStatusError = -1,kStatusNotReady = 0,kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.kStatusLoaded = 2,  // DEX idx values resolved.kStatusResolving = 3,  // Just cloned from temporary class object.kStatusResolved = 4,  // Part of linking.kStatusVerifying = 5,  // In the process of being verified.kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime.kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.kStatusVerified = 8,  // Logically part of linking; done pre-init.kStatusInitializing = 9,  // Class init in progress.kStatusInitialized = 10,  // Ready to go.kStatusMax = 11,};uint32_t annotation_type_;// Defining class loader, or null for the "bootstrap" system loader.uint32_t class_loader_;// For array classes, the component class object for instanceof/checkcastuint32_t component_type_;// DexCache of resolved constant pool entries (will be null for classes generated by the// runtime such as arrays and primitive classes).uint32_t dex_cache_;// For every interface a concrete class implements, we create an array of the concrete vtable_// methods for the methods in the interface.uint32_t iftable_;// Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeNameuint32_t name_;// The superclass, or null if this is java.lang.Object or a primitive type.uint32_t super_class_;// If class verify fails, we must return same error on subsequent tries.uint32_t verify_error_;// Virtual method table (vtable), for use by "invoke-virtual".uint32_t vtable_;// Access flags; low 16 bits are defined by VM spec.uint32_t access_flags_;// Short cuts to dex_cache_ member for fast compiled code access.uint64_t dex_cache_strings_;// ArtFields are allocated as a length prefixed ArtField array, and not an array of pointers to// ArtFields.uint64_t ifields_;uint64_t methods_;// Static fields length-prefixed array.uint64_t sfields_;// Class flags to help speed up visiting object references.uint32_t class_flags_;// Total size of the Class instance; used when allocating storage on gc heap.uint32_t class_size_;// Tid used to check for recursive <clinit> invocation.pid_t clinit_thread_id_;// ClassDef index in dex file, -1 if no class definition such as an array.int32_t dex_class_def_idx_;// Type index in dex file.int32_t dex_type_idx_;// Number of instance fields that are object refs.uint32_t num_reference_instance_fields_;// Number of static fields that are object refs,uint32_t num_reference_static_fields_;// Total object size; used when allocating storage on gc heap.uint32_t object_size_;// The lower 16 bits contains a Primitive::Type value. The upper 16// bits contains the size shift of the primitive type.uint32_t primitive_type_;// Bitmap of offsets of ifields.uint32_t reference_instance_offsets_;// State of class initialization.Status status_;// The offset of the first virtual method that is copied from an interface.uint16_t copied_methods_offset_;// The offset of the first declared virtual methods in the methods_ array.uint16_t virtual_methods_offset_;static uint32_t java_lang_Class_;
};class ArtField {
public:uint32_t declaring_class_;uint32_t access_flags_;uint32_t field_dex_idx_;uint32_t offset_;
};class ArtMethod {
public:// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".uint32_t declaring_class_;// Access flags; low 16 bits are defined by spec.uint32_t access_flags_;/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */uint32_t dex_code_item_offset_;// Index into method_ids of the dex file associated with this method.uint32_t dex_method_index_;/* End of dex file fields. */// Entry within a dispatch table for this method. For static/direct methods the index is into// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the// ifTable.uint16_t method_index_;// The hotness we measure for this method. Incremented by the interpreter.uint16_t hotness_count_;// Fake padding field gets inserted here.struct PtrSizedFields {// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.ArtMethod** dex_cache_resolved_methods_;// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.void* dex_cache_resolved_types_;// Pointer to JNI function registered to this method, or a function to resolve the JNI function,// or the profiling data for non-native methods, or an ImtConflictTable.void* entry_point_from_jni_;// Method dispatch from quick compiled code invokes this pointer which may cause bridging into// the interpreter.void* entry_point_from_quick_compiled_code_;} ptr_sized_fields_;};}}

dalvik初始化,加载dvm动态库:

JNIEXPORT jboolean JNICALL
Java_com_frank_fix_MainActivity_dalvikSetup(JNIEnv* env, int apilevel) {//加载dvm动态库void* dvm_hand = dlopen("libdvm.so", RTLD_NOW);if (dvm_hand) {dvmDecodeIndirectRef_fnPtr = (dvmDecodeIndirectRef_func) dvm_dlsym(dvm_hand,apilevel > 10 ?"_Z20dvmDecodeIndirectRefP6ThreadP8_jobject" :"dvmDecodeIndirectRef");if (!dvmDecodeIndirectRef_fnPtr) {return JNI_FALSE;}dvmThreadSelf_fnPtr = (dvmThreadSelf_func) dvm_dlsym(dvm_hand,apilevel > 10 ? "_Z13dvmThreadSelfv" : "dvmThreadSelf");if (!dvmThreadSelf_fnPtr) {return JNI_FALSE;}jclass clazz = env->FindClass("java/lang/reflect/Method");jClassMethod = env->GetMethodID(clazz, "getDeclaringClass","()Ljava/lang/Class;");return JNI_TRUE;} else {return JNI_FALSE;}
}

dalvik的方法替换,实现热修复:

JNIEXPORT void JNICALL
Java_com_frank_fix_MainActivity_dalvikReplaceMethod(JNIEnv* env, jobject src, jobject dest) {//调用替换后的methodjobject clazz = env->CallObjectMethod(dest, jClassMethod);ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(dvmThreadSelf_fnPtr(), clazz);//class的状态设置为已经初始化clz->status = CLASS_INITIALIZED;//分别得到原method和待替换的methodMethod* meth = (Method*) env->FromReflectedMethod(src);Method* target = (Method*) env->FromReflectedMethod(dest);//替换access flagmeth->accessFlags |= ACC_PUBLIC;meth->methodIndex = target->methodIndex;meth->jniArgInfo = target->jniArgInfo;meth->registersSize = target->registersSize;meth->outsSize = target->outsSize;meth->insSize = target->insSize;//替换方法的prototypemeth->prototype = target->prototype;meth->insns = target->insns;meth->nativeFunc = target->nativeFunc;
}

dalvik的field flag替换:

JNIEXPORT void JNICALL
Java_com_frank_fix_MainActivity_dalvikSetFieldFlag(JNIEnv* env, jobject field) {Field* dalvikField = (Field*) env->FromReflectedField(field);dalvikField->accessFlags = dalvikField->accessFlags & (~ACC_PRIVATE)| ACC_PUBLIC;
}

art的方法替换,实现热修复:

JNIEXPORT void JNICALL
Java_com_frank_fix_MainActivity_artFix(JNIEnv *env, jobject jclazz, jobject src, jobject dest) {//分别得到原method和待替换的methodart::mirror::ArtMethod* smeth = (art::mirror::ArtMethod*) env->FromReflectedMethod(src);art::mirror::ArtMethod* dmeth = (art::mirror::ArtMethod*) env->FromReflectedMethod(dest);reinterpret_cast<art::mirror::Class*>(dmeth->declaring_class_)->clinit_thread_id_ =reinterpret_cast<art::mirror::Class*>(smeth->declaring_class_)->clinit_thread_id_;reinterpret_cast<art::mirror::Class*>(dmeth->declaring_class_)->super_class_ = 0;//替换classsmeth->declaring_class_ = dmeth->declaring_class_;//替换access flagsmeth->access_flags_ = dmeth->access_flags_  | 0x0001;smeth->dex_code_item_offset_ = dmeth->dex_code_item_offset_;smeth->dex_method_index_ = dmeth->dex_method_index_;//替换methodsmeth->method_index_ = dmeth->method_index_;smeth->hotness_count_ = dmeth->hotness_count_;smeth->ptr_sized_fields_.dex_cache_resolved_methods_ =dmeth->ptr_sized_fields_.dex_cache_resolved_methods_;smeth->ptr_sized_fields_.dex_cache_resolved_types_ =dmeth->ptr_sized_fields_.dex_cache_resolved_types_;smeth->ptr_sized_fields_.entry_point_from_jni_ =dmeth->ptr_sized_fields_.entry_point_from_jni_;smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ =dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
}

art替换field flag:

JNIEXPORT void JNICALL
Java_com_frank_fix_MainActivity_setFieldFlag(JNIEnv* env, jobject field) {art::mirror::ArtField* artField =(art::mirror::ArtField*) env->FromReflectedField(field);artField->access_flags_ = artField->access_flags_ & (~0x0002) | 0x0001;
}

提供一个测试类,一个是存在bug的方法,一个是修复后的方法:

    /*** 模拟有bug的方法* @return "hello"*/public String sayHello(){Log.i("Test", "hello");return "hello";}/*** 修复bug后的方法* @return "goodbye"*/public String sayGoodbye(){Log.i("Test", "goodbye");return "goodbye";}

以art虚拟机热修复为例:

    private void doFix(){try {Class<?> clazz = Class.forName("com.frank.fix.Test");Object instance = clazz.newInstance();Method helloMethod = clazz.getDeclaredMethod("sayHello");helloMethod.setAccessible(true);//调用存在bug的方法helloMethod.invoke(instance);Method goodbyeMethod = clazz.getDeclaredMethod("sayGoodbye");goodbyeMethod.setAccessible(true);goodbyeMethod.invoke(instance);//调用art虚拟机替换方法,实现热修复artFix(helloMethod, goodbyeMethod);//调用修复后的方法,查看结果helloMethod.invoke(instance);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}}public native void artFix(Method src, Method method);
好了,通过虚拟机底层实现热修复过程分析完毕。如果大家有问题或建议,欢迎交流。

这篇关于从AndFix源码看Android热修复的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1063243

相关文章

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja

Nginx屏蔽服务器名称与版本信息方式(源码级修改)

《Nginx屏蔽服务器名称与版本信息方式(源码级修改)》本文详解如何通过源码修改Nginx1.25.4,移除Server响应头中的服务类型和版本信息,以增强安全性,需重新配置、编译、安装,升级时需重复... 目录一、背景与目的二、适用版本三、操作步骤修改源码文件四、后续操作提示五、注意事项六、总结一、背景与

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

在Android中使用WebView在线查看PDF文件的方法示例

《在Android中使用WebView在线查看PDF文件的方法示例》在Android应用开发中,有时我们需要在客户端展示PDF文件,以便用户可以阅读或交互,:本文主要介绍在Android中使用We... 目录简介:1. WebView组件介绍2. 在androidManifest.XML中添加Interne

修复已被利用的高危漏洞! macOS Sequoia 15.6.1发布

《修复已被利用的高危漏洞!macOSSequoia15.6.1发布》苹果公司于今日发布了macOSSequoia15.6.1更新,这是去年9月推出的macOSSequoia操作... MACOS Sequoia 15.6.1 正式发布!此次更新修复了一个已被黑客利用的严重安全漏洞,并解决了部分中文用户反馈的

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

电脑提示d3dx11_43.dll缺失怎么办? DLL文件丢失的多种修复教程

《电脑提示d3dx11_43.dll缺失怎么办?DLL文件丢失的多种修复教程》在使用电脑玩游戏或运行某些图形处理软件时,有时会遇到系统提示“d3dx11_43.dll缺失”的错误,下面我们就来分享超... 在计算机使用过程中,我们可能会遇到一些错误提示,其中之一就是缺失某个dll文件。其中,d3dx11_4

游戏闪退弹窗提示找不到storm.dll文件怎么办? Stormdll文件损坏修复技巧

《游戏闪退弹窗提示找不到storm.dll文件怎么办?Stormdll文件损坏修复技巧》DLL文件丢失或损坏会导致软件无法正常运行,例如我们在电脑上运行软件或游戏时会得到以下提示:storm.dll... 很多玩家在打开游戏时,突然弹出“找不到storm.dll文件”的提示框,随后游戏直接闪退,这通常是由于