最近在学习心心念的 Fart,研究了几天也翻阅了许多文章,但还是发现在有许多的问题不易理解(大佬们的笔记总是惜墨如金),最后决定把每一行代码的分析记录下来,便于日后温习,我始终坚信好记性不如烂笔头,分析再明白的的东西,时间久了也难免生疏,并且本篇笔记分析的 Fart 的源码也会放一份在最后,方便大家下载对比阅读本篇笔记,最后感谢 Fart 的作者 hanbingle 将这么优秀的框架开源供大家学习。
Fart 的作者封装了一些工具类方法,想要彻底理解 Fart 的源码,对这些工具类方法的分析就是基础,而这些工具类方法的分析其实只是是对 Java 反射机制的一个温习,和 Fart 主体的流程关系并不大,所以本篇笔记将分为工具类分析和主体流程分析两部分。
为了方便理解这个函数,这里写了个 demo
为了方便理解这个函数,这里写了个 demo
找到 fart 执行的起始点
本来是想做个流程图的,但我梳理了一下通篇笔记的流程,感觉 Fart 的流程还是清晰明了并不复杂,跟寻我的分析思路,基本上是一条主线,没什么分支,所以这里就偷个懒了。
说一下本篇笔记的意义,对于笔者来讲,当然是温故而知新,可以为师矣,对于小白来讲,可以温习反射机制,熟悉 Fart 的机制,并且还可以移植魔改 Fart,还记得我在笔记中多次提到了高版本的问题,至于大佬嘛……大佬们当然是可以点赞、收藏并投币啦!!!
FART_aosp8.0.7z
public
static
Field getClassField(ClassLoader classloader, String class_name, String filedName) {
try
{
Class obj_class = classloader.loadClass(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
return
field;
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
null
;
}
public
static
Field getClassField(ClassLoader classloader, String class_name, String filedName) {
try
{
Class obj_class = classloader.loadClass(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
return
field;
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
null
;
}
public
static
Object getClassFieldObject(
ClassLoader classloader, String class_name, Object obj, String filedName) {
try
{
Class obj_class = classloader.loadClass(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
Object result =
null
;
result = field.get(obj);
return
result;
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
return
null
;
}
public
static
Object getClassFieldObject(
ClassLoader classloader, String class_name, Object obj, String filedName) {
try
{
Class obj_class = classloader.loadClass(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
Object result =
null
;
result = field.get(obj);
return
result;
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
return
null
;
}
package
com.example.demo3;
public
class
Person {
private
int
age =
22
;
public
int
get_age()
{
return
age *
2
;
}
}
public
class
MainActivity
extends
AppCompatActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person =
new
Person();
try
{
ClassLoader classLoader = getClassLoader();
Class classObj = classLoader.loadClass(
"com.example.demo3.Person"
);
Field field = classObj.getDeclaredField(
"age"
);
field.setAccessible(
true
);
Object myage = field.get(person);
Log.d(
"lxz"
, String.valueOf(myage));
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
package
com.example.demo3;
public
class
Person {
private
int
age =
22
;
public
int
get_age()
{
return
age *
2
;
}
}
public
class
MainActivity
extends
AppCompatActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person =
new
Person();
try
{
ClassLoader classLoader = getClassLoader();
Class classObj = classLoader.loadClass(
"com.example.demo3.Person"
);
Field field = classObj.getDeclaredField(
"age"
);
field.setAccessible(
true
);
Object myage = field.get(person);
Log.d(
"lxz"
, String.valueOf(myage));
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
public
static
Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules) {
try
{
Class obj_class = Class.forName(class_name);
Method method = obj_class.getMethod(method_name, pareTyple);
return
method.invoke(
null
, pareVaules);
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(NoSuchMethodException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
null
;
}
public
static
Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules) {
try
{
Class obj_class = Class.forName(class_name);
Method method = obj_class.getMethod(method_name, pareTyple);
return
method.invoke(
null
, pareVaules);
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(NoSuchMethodException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
null
;
}
public
class
Person {
private
static
int
age =
22
;
public
static
int
get_age(
int
a ,
int
b)
{
return
age + a + b;
}
}
public
class
MainActivity
extends
AppCompatActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person =
new
Person();
try
{
Class obj_class = Class.forName(
"com.example.demo3.Person"
);
Method method = obj_class.getMethod(
"get_age"
,
new
Class[]{
int
.
class
,
int
.
class
});
Object myage = method.invoke(
null
,
new
Object[]{
1
,
2
});
Log.d(
"lxz"
,
"myage is "
+ myage.toString());
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
}
public
class
Person {
private
static
int
age =
22
;
public
static
int
get_age(
int
a ,
int
b)
{
return
age + a + b;
}
}
public
class
MainActivity
extends
AppCompatActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person =
new
Person();
try
{
Class obj_class = Class.forName(
"com.example.demo3.Person"
);
Method method = obj_class.getMethod(
"get_age"
,
new
Class[]{
int
.
class
,
int
.
class
});
Object myage = method.invoke(
null
,
new
Object[]{
1
,
2
});
Log.d(
"lxz"
,
"myage is "
+ myage.toString());
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
}
public
static
Object getFieldOjbect(String class_name, Object obj, String filedName) {
try
{
Class obj_class = Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
return
field.get(obj);
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(NullPointerException e) {
e.printStackTrace();
}
return
null
;
}
public
static
Object getFieldOjbect(String class_name, Object obj, String filedName) {
try
{
Class obj_class = Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(
true
);
return
field.get(obj);
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(NoSuchFieldException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(NullPointerException e) {
e.printStackTrace();
}
return
null
;
}
public
static
ClassLoader getClassloader() {
ClassLoader resultClassloader =
null
;
Object currentActivityThread = invokeStaticMethod(
"android.app.ActivityThread"
,
"currentActivityThread"
,
new
Class[]{},
new
Object[]{});
Object mBoundApplication = getFieldOjbect(
"android.app.ActivityThread"
, currentActivityThread,
"mBoundApplication"
);
Application mInitialApplication = (Application) getFieldOjbect(
"android.app.ActivityThread"
,
currentActivityThread,
"mInitialApplication"
);
Object loadedApkInfo = getFieldOjbect(
"android.app.ActivityThread$AppBindData"
,
mBoundApplication,
"info"
);
Application mApplication = (Application) getFieldOjbect(
"android.app.LoadedApk"
, loadedApkInfo,
"mApplication"
);
resultClassloader = mApplication.getClassLoader();
return
resultClassloader;
}
public
static
ClassLoader getClassloader() {
ClassLoader resultClassloader =
null
;
Object currentActivityThread = invokeStaticMethod(
"android.app.ActivityThread"
,
"currentActivityThread"
,
new
Class[]{},
new
Object[]{});
Object mBoundApplication = getFieldOjbect(
"android.app.ActivityThread"
, currentActivityThread,
"mBoundApplication"
);
Application mInitialApplication = (Application) getFieldOjbect(
"android.app.ActivityThread"
,
currentActivityThread,
"mInitialApplication"
);
Object loadedApkInfo = getFieldOjbect(
"android.app.ActivityThread$AppBindData"
,
mBoundApplication,
"info"
);
Application mApplication = (Application) getFieldOjbect(
"android.app.LoadedApk"
, loadedApkInfo,
"mApplication"
);
resultClassloader = mApplication.getClassLoader();
return
resultClassloader;
}
}
catch
(Exception e) {
if
(!mInstrumentation.onException(activity, e)) {
throw
new
RuntimeException(
"Unable to start activity "
+ component
+
": "
+ e.toString(), e);
}
}
fartthread();
return
activity;
}
}
catch
(Exception e) {
if
(!mInstrumentation.onException(activity, e)) {
throw
new
RuntimeException(
"Unable to start activity "
+ component
+
": "
+ e.toString(), e);
}
}
fartthread();
return
activity;
}
public
static
void
fartthread() {
new
Thread(
new
Runnable() {
@Override
public
void
run() {
try
{
Log.e(
"ActivityThread"
,
"start sleep......"
);
Thread.sleep(
1
*
60
*
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
Log.e(
"ActivityThread"
,
"sleep over and start fart"
);
fart();
Log.e(
"ActivityThread"
,
"fart run over"
);
}
}).start();
}
public
static
void
fartthread() {
new
Thread(
new
Runnable() {
@Override
public
void
run() {
try
{
Log.e(
"ActivityThread"
,
"start sleep......"
);
Thread.sleep(
1
*
60
*
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
Log.e(
"ActivityThread"
,
"sleep over and start fart"
);
fart();
Log.e(
"ActivityThread"
,
"fart run over"
);
}
}).start();
}
public
static
void
fart() {
ClassLoader appClassloader = getClassloader();
ClassLoader tmpClassloader=appClassloader;
ClassLoader parentClassloader=appClassloader.getParent();
if
(appClassloader.toString().indexOf(
"java.lang.BootClassLoader"
)==-
1
)
{
fartwithClassloader(appClassloader);
}
while
(parentClassloader!=
null
){
if
(parentClassloader.toString().indexOf(
"java.lang.BootClassLoader"
)==-
1
)
{
fartwithClassloader(parentClassloader);
}
tmpClassloader=parentClassloader;
parentClassloader=parentClassloader.getParent();
}
}
public
static
void
fart() {
ClassLoader appClassloader = getClassloader();
ClassLoader tmpClassloader=appClassloader;
ClassLoader parentClassloader=appClassloader.getParent();
if
(appClassloader.toString().indexOf(
"java.lang.BootClassLoader"
)==-
1
)
{
fartwithClassloader(appClassloader);
}
while
(parentClassloader!=
null
){
if
(parentClassloader.toString().indexOf(
"java.lang.BootClassLoader"
)==-
1
)
{
fartwithClassloader(parentClassloader);
}
tmpClassloader=parentClassloader;
parentClassloader=parentClassloader.getParent();
}
}
public
static
void
fartwithClassloader(ClassLoader appClassloader) {
List<Object> dexFilesArray =
new
ArrayList<Object>();
Field pathList_Field = (Field) getClassField(appClassloader,
"dalvik.system.BaseDexClassLoader"
,
"pathList"
);
Object pathList_object = getFieldOjbect(
"dalvik.system.BaseDexClassLoader"
, appClassloader,
"pathList"
);
Object[] ElementsArray = (Object[]) getFieldOjbect(
"dalvik.system.DexPathList"
, pathList_object,
"dexElements"
);
Field dexFile_fileField =
null
;
try
{
dexFile_fileField = (Field) getClassField(appClassloader,
"dalvik.system.DexPathList$Element"
,
"dexFile"
);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
Class DexFileClazz =
null
;
try
{
DexFileClazz = appClassloader.loadClass(
"dalvik.system.DexFile"
);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
Method getClassNameList_method =
null
;
Method defineClass_method =
null
;
Method dumpDexFile_method =
null
;
Method dumpMethodCode_method =
null
;
for
(Method field : DexFileClazz.getDeclaredMethods()) {
if
(field.getName().equals(
"getClassNameList"
)) {
getClassNameList_method = field;
getClassNameList_method.setAccessible(
true
);
}
if
(field.getName().equals(
"defineClassNative"
)) {
defineClass_method = field;
defineClass_method.setAccessible(
true
);
}
if
(field.getName().equals(
"dumpDexFile"
)) {
dumpDexFile_method = field;
dumpDexFile_method.setAccessible(
true
);
}
if
(field.getName().equals(
"dumpMethodCode"
)) {
dumpMethodCode_method = field;
dumpMethodCode_method.setAccessible(
true
);
}
}
Field mCookiefield = getClassField(appClassloader,
"dalvik.system.DexFile"
,
"mCookie"
);
Log.v(
"ActivityThread->methods"
,
"dalvik.system.DexPathList.ElementsArray.length:"
+ ElementsArray.length);
for
(
int
j =
0
; j < ElementsArray.length; j++) {
Object element = ElementsArray[j];
Object dexfile =
null
;
try
{
dexfile = (Object) dexFile_fileField.get(element);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
if
(dexfile ==
null
) {
Log.e(
"ActivityThread"
,
"dexfile is null"
);
continue
;
}
if
(dexfile !=
null
) {
dexFilesArray.add(dexfile);
Object mcookie = getClassFieldObject(appClassloader,
"dalvik.system.DexFile"
, dexfile,
"mCookie"
);
if
(mcookie ==
null
) {
Object mInternalCookie = getClassFieldObject(appClassloader,
"dalvik.system.DexFile"
, dexfile,
"mInternalCookie"
);
if
(mInternalCookie!=
null
)
{
mcookie=mInternalCookie;
}
else
{
Log.v(
"ActivityThread->err"
,
"get mInternalCookie is null"
);
continue
;
}
}
String[] classnames =
null
;
try
{
classnames = (String[]) getClassNameList_method.invoke(dexfile, mcookie);
}
catch
(Exception e) {
e.printStackTrace();
continue
;
}
catch
(Error e) {
e.printStackTrace();
continue
;
}
if
(classnames !=
null
) {
for
(String eachclassname : classnames) {
loadClassAndInvoke(appClassloader, eachclassname, dumpMethodCode_method);
}
}
}
}
return
;
}
public
static
void
fartwithClassloader(ClassLoader appClassloader) {
List<Object> dexFilesArray =
new
ArrayList<Object>();
Field pathList_Field = (Field) getClassField(appClassloader,
"dalvik.system.BaseDexClassLoader"
,
"pathList"
);
Object pathList_object = getFieldOjbect(
"dalvik.system.BaseDexClassLoader"
, appClassloader,
"pathList"
);
Object[] ElementsArray = (Object[]) getFieldOjbect(
"dalvik.system.DexPathList"
, pathList_object,
"dexElements"
);
Field dexFile_fileField =
null
;
try
{
dexFile_fileField = (Field) getClassField(appClassloader,
"dalvik.system.DexPathList$Element"
,
"dexFile"
);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
Class DexFileClazz =
null
;
try
{
DexFileClazz = appClassloader.loadClass(
"dalvik.system.DexFile"
);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
Method getClassNameList_method =
null
;
Method defineClass_method =
null
;
Method dumpDexFile_method =
null
;
Method dumpMethodCode_method =
null
;
for
(Method field : DexFileClazz.getDeclaredMethods()) {
if
(field.getName().equals(
"getClassNameList"
)) {
getClassNameList_method = field;
getClassNameList_method.setAccessible(
true
);
}
if
(field.getName().equals(
"defineClassNative"
)) {
defineClass_method = field;
defineClass_method.setAccessible(
true
);
}
if
(field.getName().equals(
"dumpDexFile"
)) {
dumpDexFile_method = field;
dumpDexFile_method.setAccessible(
true
);
}
if
(field.getName().equals(
"dumpMethodCode"
)) {
dumpMethodCode_method = field;
dumpMethodCode_method.setAccessible(
true
);
}
}
Field mCookiefield = getClassField(appClassloader,
"dalvik.system.DexFile"
,
"mCookie"
);
Log.v(
"ActivityThread->methods"
,
"dalvik.system.DexPathList.ElementsArray.length:"
+ ElementsArray.length);
for
(
int
j =
0
; j < ElementsArray.length; j++) {
Object element = ElementsArray[j];
Object dexfile =
null
;
try
{
dexfile = (Object) dexFile_fileField.get(element);
}
catch
(Exception e) {
e.printStackTrace();
}
catch
(Error e) {
e.printStackTrace();
}
if
(dexfile ==
null
) {
Log.e(
"ActivityThread"
,
"dexfile is null"
);
continue
;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-9-24 12:32
被简单的简单编辑
,原因: 文末的链接挂了,补一下