Xposed

Xposed(LSposed)

Android启动流程详解

[原创]Lsposed 技术原理探讨 && 基本安装使用-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com

LSposed安装

《安卓逆向这档事》一、模拟器环境搭建 - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 

原理:

控制zygote进程:通过替换 /system/bin/app_process 程序控制zygote进程 使得它在系统启动的过程中会加载 xposed framework 的一个jar文件(XposedBridge.jar) 从而完成对zygote进程及其创建的 Dalvik/art 虚拟机的劫持

 

流程:

先执行Xposed中内容

后执行被hook内容

hook构造函数

1.调用 XposedHelpers.findAndHookConstructor

2.实现 beforeHookedMethod 和 afterHookedMethod

3.findAndHookConstructor 中写入重载类

hook普通函数/内部类函数/匿名函数

1.调用 XposedHelpers.findAndHookMethod(区别在于 className 参数)

2.param.setResult() 获得结果

修改对象属性

1.通过反射

其中构造函数 在beforeHookedMethod时 各函数值为系统赋值 而非人为赋值内容

所以需把内容写入afterHookedMethod中

1
2
3
4
5
6
7
8
Class myclass = loadPackageParam.classLoader.loadClass("com.example.hooktest.tohook");

//反射写法
Field age = myclass.getDeclaredField("age");
//private属性 加入 .setAccessible(true)
age.setAccessible(true);
//.set中两个参数 (实例, 修改值)
age.set(param.thisObject, 888);

 

2.通过Xposed的api

1
2
3
4
5
6
7
Class myclass = loadPackageParam.classLoader.loadClass("com.example.hooktest.tohook");

//api写法
//三个参数 (实例, 参数名, 修改值)
XposedHelpers.setIntField(param.thisObject, "age", 777);
XposedHelpers.setObjectField(param.thisObject, "name", "hookedname");
XposedHelpers.setBooleanField(param.thisObject, "hair", false);

xposed主动调用

1.通过反射完成

2.使用xposed api完成

获取实例方法

1.xposed api

2.hook构造函数

3.hook参数

4.反射

 

通过使用反射或者xposed api获得实例并完成主动调用

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
Class myclass = loadPackageParam.classLoader.loadClass("com.example.hooktest.tohook");

//static函数声明↓
//public static String publicstatic() {return "i'm from public static";}
//无需实例
//反射
Method publicstatic = myclass.getDeclaredMethod("publicstatic");
String result = (String) publicstatic.invoke(null);
Log.e("result==>", result);

//api
String result = (String) XposedHelpers.callStaticMethod(myclass, "publicstatic");
Log.e("result==>", result);



//public函数声明↓
//public String publicmethod() {return "i'm from public method";}

Method publicmethod = myclass.getDeclaredMethod("publicmethod");

//or 使用 .newInstance() 获取实例 (只支持空参)
>-: Object publicobject = myclass.newInstance();
//or 使用 .getConstructor()
>-: Constructor publiccons = myclass.getConstructor(int.class);
>-: Object publicobject = publiccons.newInstance();
//or 使用 xposed api
>-: Object publicobject = XposedHelpers.newInstance(myclass, 1);

String result = (String) publicmethod.invoke(publicobject);
Log.e("result==>", result);



//private函数声明↓
//private String privatemethod() {return "i'm from private method";}

Method privatemethod = myclass.getDeclaredMethod("privatemethod");

//or 使用 .newInstance() 获取实例 (只支持空参)
>-: Object privateobject = myclass.newInstance();
//or 使用 xposed api
>-: Object privateobject = XposedHelpers.newInstance(myclass, 1);

//private属性 加入 .setAccessible(true)
privatemethod.setAccessible(true);
String result = (String) privatemethod.invoke(privateobject);
Log.e("result==>", result);

 

通过hook获得实例并完成主动调用(函数声明同上)

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
//hook构造函数 其实例为 .thisObject
XposedHelpers.findAndHookConstructor(myclass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

Method publicmethod = myclass.getDeclaredMethod("publicmethod");
String publicstr = (String) publicmethod.invoke(param.thisObject);

Method privatemethod = myclass.getDeclaredMethod("privatemethod");
privatemethod.setAccessible(true);
String privatestr = (String) privatemethod.invoke(param.thisObject);

Log.e("publicstr==>", publicstr);
Log.e("privatestr==>", privatestr);
}
});



//hook参数
//public static String hookstr(tohook a) {return "\nage:" + String.valueOf(a.age) + "\nname:" + a.name + "\nhair:" + a.hair;}
XposedHelpers.findAndHookMethod(myclass,"hookstr", myclass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);

//or 使用反射
>-: Method publicmethod = myclass.getDeclaredMethod("publicmethod");
>-: String publicstr = (String) publicmethod.invoke(param.args[0]);
//or 使用api
>-: String publicstr = (String) XposedHelpers.callMethod(param.args[0], "publicmethod");

Method privatemethod = myclass.getDeclaredMethod("privatemethod");
privatemethod.setAccessible(true);
String privatestr = (String) privatemethod.invoke(param.args[0]);

Log.e("publicstr==>", publicstr);
Log.e("privatestr==>", privatestr);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});

动态加载dex

将dexdemo写入

build为apk文件后解压

将class.dex push至/sdcard目录中 并赋予权限

1
2
3
public class todex {
public static void dexdemo() {Log.e("dexhookdemo==>", "from dexdemo.dex");}
}

 

hookapp中写入动态加载 .dex (反射)文件内容

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
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

DexClassLoader dexloader = new DexClassLoader("/sdcard/dexdemo.dex", this.getCacheDir().getAbsolutePath(), null, this.getClassLoader());

try {
Class myclass = dexloader.loadClass("com.example.dexhook.todex");
Method dexdemomethod = myclass.getDeclaredMethod("dexdemo");
dexdemomethod.invoke(null);


} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

 

在 AndroidManifest.xml 中加入 sdcard 读写权限

并打开 hookapp 的权限

1
2
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

 

hook dex

通过 classLoader 获取 class

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
//通过 classLoader 找到 class
//Android源码中 BaseDexClassLoader.java 中存在 private属性的 DexPathList
//DexPathList.java 中存在 private属性的 Element[] dexElements 内部类
//内部类 Element[] 中包含 DexFile属性
//DexFile.java 中存在 entries()函数 其返回值为 String 即类列表
//一层一层取到实例

//反射写法
public void classbyclassloader(ClassLoader loader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class basedexclass = loader.loadClass("dalvik.system.BaseDexClassLoader");
Field dexpathlist = basedexclass.getDeclaredField("pathList");
dexpathlist.setAccessible(true);
Object dexpathlistobj = dexpathlist.get(loader);


Class dexpathlistclass = loader.loadClass("dalvik.system.DexPathList");
Field dexelements = dexpathlistclass.getDeclaredField("dexElements");
dexelements.setAccessible(true);
Object[] dexelementsobj = (Object[]) dexelements.get(dexpathlistobj);


for(Object a:dexelementsobj){

Class dexelementsclass = loader.loadClass("dalvik.system.DexPathList$Element");
Field dexfile = dexelementsclass.getDeclaredField("dexFile");
dexfile.setAccessible(true);
DexFile dexfileobj = (DexFile) dexfile.get(a);


Enumeration<String> b = dexfileobj.entries();

while (b.hasMoreElements()) {
String hookclass = b.nextElement();
Log.e("hookclass", hookclass);
}
}
}


//xposed api 写法
public void classbyclassloader(ClassLoader loader) {
Object dexpathlistobj = XposedHelpers.getObjectField(loader, "pathList");
Object[] dexelementsobj = (Object[]) XposedHelpers.getObjectField(dexpathlistobj, "dexElements");

for(Object a:dexelementsobj){

DexFile dexfileobj = (DexFile) XposedHelpers.getObjectField(a, "dexFile");

Enumeration<String> b = dexfileobj.entries();

while (b.hasMoreElements()) {
String hookclass = b.nextElement();
Log.e("hookclass", hookclass);
}
}
}

hook dex替换的壳函数

一代壳获得classloader

[原创]FART:ART环境下基于主动调用的自动化脱壳方案-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com

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
//

//反射写法
public ClassLoader get_classloader(ClassLoader loader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class Act_Thr_class = loader.loadClass("android.app.ActivityThread");
Method cur_Act_Thr_met = Act_Thr_class.getDeclaredMethod("currentActivityThread");
cur_Act_Thr_met.setAccessible(true);
Object Act_Thr_obj = cur_Act_Thr_met.invoke(null);

Field application = Act_Thr_class.getDeclaredField("mInitialApplication");
application.setAccessible(true);
Object appli_obj = application.get(Act_Thr_obj);

Class App_class = loader.loadClass("android.app.Application");
Field mloadapk = App_class.getDeclaredField("mLoadApk");
mloadapk.setAccessible(true);
Object mloadapk_obj = mloadapk.get(appli_obj);

Class mCl_Loa_class = loader.loadClass("android.app.LoadedApk");
Field mclassloader = mCl_Loa_class.getDeclaredField("mClassLoader");
mclassloader.setAccessible(true);
ClassLoader mcla_loa_obj = (ClassLoader) mclassloader.get(mloadapk_obj);

return mcla_loa_obj;
}


//xposed api 写法
public ClassLoader get_classloader(ClassLoader loader) throws ClassNotFoundException {
Class Act_Thr_class = loader.loadClass("android.app.ActivityThread");
Object Act_Thr_obj = XposedHelpers.callStaticMethod(Act_Thr_class, "currentActivityThread");

Object appli_obj = XposedHelpers.getObjectField(Act_Thr_obj, "mInitialApplication");

Object mloadapk_obj = XposedHelpers.getObjectField(appli_obj, "mLoadApk");

ClassLoader mcla_loa_obj = (ClassLoader) XposedHelpers.getObjectField(mloadapk_obj, "mClassLoader");

return mcla_loa_obj;
}

native层处理

inlinehook(arm32)

需要在 build.gradle 中 android{ defaultConfig{ } } 里添加

1
ndk {abiFilters 'armeabi-v7a'}

 

先将 inlinehook 解压包放置 main->cpp 中

之后在 main->cpp->native-lib.cpp 中写入

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
#include <jni.h>
#include <string>
#include <stdlib.h>
#include <dlfcn.h>
#include "android/log.h"

extern "C" {
#include "include/inlineHook.h"
}


int (*old_strstr)(const char *, char *) = nullptr;

int new_strstr(const char* string1, char* string2) {

if (strcmp(string2, "yunyun") == 0) {
return 1;
}
else {
return old_strstr(string1, string2);
}
}

int hook() {

void *libc_addr = dlopen("libc.so", RTLD_NOW);
void *strstr_addr = dlsym(libc_addr, "strstr");
__android_log_print(4, "cppyunaddr", "hook is ok==> %p", strstr_addr);

if (registerInlineHook((uint32_t) strstr_addr, (uint32_t) new_strstr, (uint32_t **) & old_strstr) != ELE7EN_OK) {
return -1;
}
if (inlineHook((uint32_t) strstr_addr) != ELE7EN_OK) {
return -1;
}
return 0;
}


extern "C" JNIEXPORT jstring JNICALL
Java_com_example_lsposedso_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}


//or
extern "C" void _init() {
hook();
}

//or
extern "C" jint JNICALL JNI_OnLoad(JavaVM *vm, void *res) {
__android_log_print(4, "cppyun", "from JNI_Onload");
hook();
return JNI_VERSION_1_6;
}

//or
extern "C"
JNIEXPORT void JNICALL
Java_com_example_lsposedso_sohook_strstrhook(JNIEnv *env, jobject thiz) {
hook();
}

 

打包成apk文件后解压将 lib.so 文件提取

push 至 Android 公有目录

使用 System.load() 写入绝对路径后进行加载

(系统不会自己加载个人编写的 so 文件 所以需要手动实现)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class sohook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

Class myclass = loadPackageParam.classLoader.loadClass("java.lang.Runtime");
XposedBridge.hookAllMethods(myclass, "loadLibrary0", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
System.load("/data/local/tmp/liblsposedso.so");
}
});
}
}

sandhook(arm64)

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
#include <jni.h>
#include <string>
#include "sandhook_native.h"


void * (*old_strstr)(char*, char*) = nullptr;
void * new_strstr(char* a, char* b) {
if(strcmp(b, "yunyun") == 0) {
int a = 1;
return &a;

} else {
return old_strstr(a,b);
}
}


extern "C" JNICALL jint JNI_OnLoad(JavaVM *vm, void* res) {
old_strstr = reinterpret_cast<void *(*)(char *, char *)> (SandInlineHookSym("/system/lib64/libc.so", "strstr", reinterpret_cast<void *>(new_strstr)));
return JNI_VERSION_1_6;
}


extern "C" JNIEXPORT jstring JNICALL
Java_com_example_lsposedso_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}