Trouble Blog


  • Home

  • Tags

  • Archives

ClassLoader

Posted on 2025-08-03

APK加壳与脱壳学习一

  1. 类ClassLoader讲解

    1
    2
    3
    4
    5
    6
    JVM的类加载器包括三种
    1) BootStrap ClassLoader (引导类加载器) C/C++代码实现的加载器,加载指定的JDK的核心类库,比如java.lang、java.uti.。JAVA虚拟机的启动都是通过Bootstrap,该ClassLoader在java里无法获取,负责加载/lib下的类
    2)Extensions ClassLoader(拓展类加载器) JAVA实现类为ExtClassLoader,提供除了系统类之外的额外功能,可以在java里获取,负责加载/lib/ext下的类
    3)Application ClassLoader
    JAVA中实现类为AppClassLoader,开发人员开发的代码就是由它来加载,ClassLoader.getSystemClassLoader返回的就是这个类加载器。

  2. 自定义类加载器,通过继承java.lang.ClassLoader类的方式来实现自己的类加载器

  3. 双亲委派

    Bootstrap ClassLoader

    ​ |

    Extension ClassLoader
    

    ​ |

     Application ClassLoader
    

    ​ |

    Custom ClassLoader 自定义

    先Bootstrap ClassLoader加载核心类库,从上向下的顺序。

    1. 双亲委派模式(了解)

      什么事情先给祖先去完成,如果祖先完不成,再交给儿子完成,

      为什么要有双亲委派:

      A. 避免重复加载,如果已经加载过一次class, 可以直接读取已经加载的Class

      B. 更加安全,无法自定义类来替代系统的类,可以防止核心APi库备随意更改

    2. 类加载的时机:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      隐式加载:

      ​```
      创建类的实例
      访问类的静态变量
      调用类的静态方法
      使用发射方式来强制创建某个类或接口的java.lang.Class对象
      初始化某个类的子类

      ​```
      显示加载:

      LoadClass()

      forName()
      1. 类加载步骤:

        1
        2
        3
        4
        5
        6
        7
        装载 查找和导入Class文件
        链接
        a 检查 class文件数据的正确性
        b 准备 给类的静态变量分配存储空间
        c 解析 将符号引用转成直接引用
        初始化 调用<clinit>函数,对静态变量,静态代码块执行初始化工作

      2. Android ClassLoader的继承关系

        http://liuwangshu.cn/application/classloader/2-android-classloader.html

        ClassLoader

        BootClassLoader BaseDexClassLoader

        ​ PathClassLoader DexClassLoader InMemoryClassLoader

      四大组件是由PathClassLoader来加载的

      四大组件:Activity Service Content provider Broadcast

      例子:

      插件开发,通过ClassLoader来加载dex文件,DexClassLoader来加载放在sdcard目录下的dex

    3. https://luyanan.com/news/info/20370901287043.html

linker学习(一)

Posted on 2025-08-03

System.loadLibrary解析

System.loadLibrary和load的区别

1
2
3
4
5
6
7
8
loadLibrary只需要传入native-lib
load需要传入so的绝对路径

load比loadLibrary少了查找so路径的过程
load只支持应用本地存储路径/data/data/package-name,或者是系统lib目录 /system/lib /vendor/lib
load不能加载sdcard中的so路径
loadLibrary加载的是一开始就打包apk或系统的so文件,load可以加载任意一个时刻的so文件
最终都是调用java.lang.Runtime中的nativeLoad

首先从java层 java/lang/System.java

1
System.loadLibrary("libnative-lib.so");
1
2
3
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

/libcore/ojluni/src/main/java/java/lang/Runtime.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

998 synchronized void loadLibrary0(ClassLoader loader, String libname) {
//传入的so文件名,如果是路径,路径会抛出异常
999 if (libname.indexOf((int)File.separatorChar) != -1) {
1000 throw new UnsatisfiedLinkError(
1001 "Directory separator should not appear in library name: " + libname);
1002 }
1003 String libraryName = libname;
// 如果classLoader不为空,从classloader中获取so文件路径,首先调用BaseClassLoader的findLibray
// BaseClassLoader的findLibrary调用DexPathList的findLibrary,会加上lib前缀和so后缀
// 在BaseClassLoader的构造函数的时候DexPathList的构造函数调用,DexPathList的nativeLibraryDirectories字段保存apk中的so路径和系统 // so存放路径,应用的lib目录会放在第一个位置,so文件会先从应用本身的目录开始查找 不存在的话才会从系统lib路径(/system/lib、/vendor/lib等)
1004 if (loader != null) {
1005 String filename = loader.findLibrary(libraryName);
1006 if (filename == null) {
1007 // It's not necessarily true that the ClassLoader used
1008 // System.mapLibraryName, but the default setup does, and it's
1009 // misleading to say we didn't find "libMyLibrary.so" when we
1010 // actually searched for "liblibMyLibrary.so.so".
1011 throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
1012 System.mapLibraryName(libraryName) + "\"");
1013 }
1014 String error = doLoad(filename, loader); // 主要通过这个来寻找
1015 if (error != null) {
1016 throw new UnsatisfiedLinkError(error);
1017 }
1018 return;
1019 }
1020 // mapLibraryName 就是把 so加上lib + "libraryName" + ".so"
1021 String filename = System.mapLibraryName(libraryName);
1022 List<String> candidates = new ArrayList<String>();
1023 String lastError = null;
// getLibPaths 当ClassLoader为空的时候,获取system指定的vm library Path列表,少了app的lib路径
1024 for (String directory : getLibPaths()) {
1025 String candidate = directory + filename;
1026 candidates.add(candidate);
1027
1028 if (IoUtils.canOpenReadOnly(candidate)) {
1029 String error = doLoad(candidate, loader);
1030 if (error == null) {
1031 return; // We successfully loaded the library. Job done.
1032 }
1033 lastError = error;
1034 }
1035 }
1036
1037 if (lastError != null) {
1038 throw new UnsatisfiedLinkError(lastError);
1039 }
1040 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
1041 }
doLoad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1070    private String doLoad(String name, ClassLoader loader) {
1071 // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
1072 // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
1073
1074 // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
1075 // libraries with no dependencies just fine, but an app that has multiple libraries that
1076 // depend on each other needed to load them in most-dependent-first order.
1077
1078 // We added API to Android's dynamic linker so we can update the library path used for
1079 // the currently-running process. We pull the desired path out of the ClassLoader here
1080 // and pass it to nativeLoad so that it can call the private dynamic linker API.
1081
1082 // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
1083 // beginning because multiple apks can run in the same process and third party code can
1084 // use its own BaseDexClassLoader.
1085
1086 // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
1087 // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
1088
1089 // So, find out what the native library search path is for the ClassLoader in question...
1090 String librarySearchPath = null;
1091 if (loader != null && loader instanceof BaseDexClassLoader) {
1092 BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
1093 librarySearchPath = dexClassLoader.getLdLibraryPath();
1094 }
1095 // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
1096 // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
1097 // internal natives.
1098 synchronized (this) {
1099 return nativeLoad(name, loader, librarySearchPath);
1100 }
1101 }
1102
1103 // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
1104 private static native String nativeLoad(String filename, ClassLoader loader,
1105 String librarySearchPath);
doLoad 对应的native方法 JVM_NativeLoad

/art/runtime/openjdkjvm/OpenjdkJvm.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
323 jstring javaFilename,
324 jobject javaLoader,
325 jstring javaLibrarySearchPath) {
// 将jstring的javaFilename 转换为c++的 string filename
326 ScopedUtfChars filename(env, javaFilename);
327 if (filename.c_str() == NULL) {
328 return NULL;
329 }
330
331 std::string error_msg;
332 {
333 art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
//这边是实际执行so加载的操作调用
334 bool success = vm->LoadNativeLibrary(env,
335 filename.c_str(),
336 javaLoader,
337 javaLibrarySearchPath,
338 &error_msg);
339 if (success) {
340 return nullptr;
341 }
342 }
343
344 // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
345 env->ExceptionClear();
346 return env->NewStringUTF(error_msg.c_str());
347}
/art/runtime/java_vm_ext.cc
LoadNativeLibrary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
797 const std::string& path,
798 jobject class_loader,
799 jstring library_path,
800 std::string* error_msg) {
801 error_msg->clear();
802
803 // See if we've already loaded this library. If we have, and the class loader
804 // matches, return successfully without doing anything.
805 // TODO: for better results we should canonicalize the pathname (or even compare
806 // inodes). This implementation is fine if everybody is using System.loadLibrary.
807 SharedLibrary* library;
808 Thread* self = Thread::Current();
809 {
810 // TODO: move the locking (and more of this logic) into Libraries.
811 MutexLock mu(self, *Locks::jni_libraries_lock_);
812 library = libraries_->Get(path);
813 }
814 void* class_loader_allocator = nullptr;
815 {
816 ScopedObjectAccess soa(env);
817 // As the incoming class loader is reachable/alive during the call of this function,
818 // it's okay to decode it without worrying about unexpectedly marking it alive.
819 ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
820
821 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
822 if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
823 loader = nullptr;
824 class_loader = nullptr;
825 }
826
827 class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
828 CHECK(class_loader_allocator != nullptr);
829 }
830 if (library != nullptr) {
831 // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
832 if (library->GetClassLoaderAllocator() != class_loader_allocator) {
833 // The library will be associated with class_loader. The JNI
834 // spec says we can't load the same library into more than one
835 // class loader.
836 StringAppendF(error_msg, "Shared library \"%s\" already opened by "
837 "ClassLoader %p; can't open in ClassLoader %p",
838 path.c_str(), library->GetClassLoader(), class_loader);
839 LOG(WARNING) << error_msg;
840 return false;
841 }
842 VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
843 << " ClassLoader " << class_loader << "]";
844 if (!library->CheckOnLoadResult()) {
845 StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
846 "to load \"%s\"", path.c_str());
847 return false;
848 }
849 return true;
850 }
851
852 // Open the shared library. Because we're using a full path, the system
853 // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
854 // resolve this library's dependencies though.)
855
856 // Failures here are expected when java.library.path has several entries
857 // and we have to hunt for the lib.
858
859 // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
860 // class unloading. Libraries will only be unloaded when the reference count (incremented by
861 // dlopen) becomes zero from dlclose.
862
863 Locks::mutator_lock_->AssertNotHeld(self);
864 const char* path_str = path.empty() ? nullptr : path.c_str();
865 bool needs_native_bridge = false;
866 void* handle = android::OpenNativeLibrary(env,
867 runtime_->GetTargetSdkVersion(),
868 path_str,
869 class_loader,
870 library_path,
871 &needs_native_bridge,
872 error_msg);
873
874 VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
875
876 if (handle == nullptr) {
877 VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
878 return false;
879 }
880
881 if (env->ExceptionCheck() == JNI_TRUE) {
882 LOG(ERROR) << "Unexpected exception:";
883 env->ExceptionDescribe();
884 env->ExceptionClear();
885 }
886 // Create a new entry.
887 // TODO: move the locking (and more of this logic) into Libraries.
888 bool created_library = false;
889 {
890 // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
891 std::unique_ptr<SharedLibrary> new_library(
892 new SharedLibrary(env,
893 self,
894 path,
895 handle,
896 needs_native_bridge,
897 class_loader,
898 class_loader_allocator));
899
900 MutexLock mu(self, *Locks::jni_libraries_lock_);
901 library = libraries_->Get(path);
902 if (library == nullptr) { // We won race to get libraries_lock.
903 library = new_library.release();
904 libraries_->Put(path, library);
905 created_library = true;
906 }
907 }
908 if (!created_library) {
909 LOG(INFO) << "WOW: we lost a race to add shared library: "
910 << "\"" << path << "\" ClassLoader=" << class_loader;
911 return library->CheckOnLoadResult();
912 }
913 VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
914
915 bool was_successful = false;
916 void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
917 if (sym == nullptr) {
918 VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
919 was_successful = true;
920 } else {
921 // Call JNI_OnLoad. We have to override the current class
922 // loader, which will always be "null" since the stuff at the
923 // top of the stack is around Runtime.loadLibrary(). (See
924 // the comments in the JNI FindClass function.)
925 ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
926 self->SetClassLoaderOverride(class_loader);
927
928 VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
929 typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
930 JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
931 int version = (*jni_on_load)(this, nullptr);
932
933 if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
934 // Make sure that sigchain owns SIGSEGV.
935 EnsureFrontOfChain(SIGSEGV);
936 }
937
938 self->SetClassLoaderOverride(old_class_loader.get());
939
940 if (version == JNI_ERR) {
941 StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
942 } else if (JavaVMExt::IsBadJniVersion(version)) {
943 StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
944 path.c_str(), version);
945 // It's unwise to call dlclose() here, but we can mark it
946 // as bad and ensure that future load attempts will fail.
947 // We don't know how far JNI_OnLoad got, so there could
948 // be some partially-initialized stuff accessible through
949 // newly-registered native method calls. We could try to
950 // unregister them, but that doesn't seem worthwhile.
951 } else {
952 was_successful = true;
953 }
954 VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
955 << " from JNI_OnLoad in \"" << path << "\"]";
956 }
957
958 library->SetResult(was_successful);
959 return was_successful;
960}
1
2
3
4
5
LoadNativeLibrary主要任务是
1. 先判断是否加载过so,并且判断classLoader要匹配,同一个so不能被不同的classLoader加载
2.没加载过so,调用OpenNativeLibrary来加载,参数path.c_str()传递的是动态库的全路径,之所以还用提供搜索路径,因为包含依赖库
3.加载成功后,创建new SharedLibrary,然后查看这个so有没有JNI_OnLoad方法,有的话就调用
4.查看调用JNI_OnLoad的结果

C++概念学习(一)

Posted on 2025-08-03

左值和右值

左值: 拥有身份的对象,有持久的内存地址,可以在多行代码中持续访问,可以取地址
右值: 表达式是临时结果,核心特点是可被移动,不可以取地址

常见的右值类型

  1. 字面量: 42, true, ‘c’, “hello”
  2. 函数返回的非引用临时值
1
2
3
4
5
std::string get_string(){
return "hello world"; // 返回一个临时对象,是右值
}

std::string str = get_string(); // get_string()的返回值是右值
  1. 表达式的临时对象
1
2
int a=1, b=2;
int c = a + b; // a+b的结果是临时整数,是右值

拷贝构造和赋值操作符

为什么需要

当你的类直接管理资源时,类内部直接管理着手动释放的资源

五法则 c++11及以后

  1. 当你需要显式声明其中一个,就需要将五个都声明
1
2
3
4
5
1. 析构函数
2. 拷贝构造函数
3. 拷贝赋值操作符
4. 移动构造函数
5. 移动赋值操作符

最佳实践 零法则

优先使用标准库的资源管理类来包装资源,尽量用标准库的容器和智能指针来管理资源,而不是用裸指针

pycharm中lldb脚本环境配置

Posted on 2025-08-01

获取lldb所在目录

打开teminal

1
lldb -P

lldb

新建pycharm python项目

拷贝lldb文件夹到新建的项目里,设置为source root

测试

4 posts
3 tags
© 2025 trouble
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4