文章总结: 本文详细介绍了通过修改AOSP8源码实现so注入并集成SandHook框架的技术方案。作者在ActivityThread.java的handleBindApplication处注入自定义so,配合修改System.java的loadLibrary方法实现对目标so中函数的hook。文章包含完整的代码实现步骤,演示了hooklibc的open函数和应用程序so中密钥获取函数的具体方法,并提供了实际运行效果验证。 综合评分: 85 文章分类: 移动安全,逆向分析,安全开发,漏洞分析,红队
AOSP源码定制-so注入并集成hook框架
哆啦安全
2024年6月27日 08:55 四川
在小说阅读器读本章
去阅读
以下文章来源于gakki的童养夫 ,作者新垣结衣唯一丈夫
gakki的童养夫 .
emmmmm gakki是我的
AOSP源码定制-so注入并集成hook框架
介绍
最近研究so的hook相关,看到了一些文章,尝试配合修改系统源码进行so注入hook,配合sandhook框架对测试app进行hook。 下面还是用AOSP8来演示。
简单测试
这里我通过修改源码去注入so,so注入的时机我开始的选择是越早越好。 这里选在handleBindApplication处,创建ContextImpl对象时进行一系列的复制注入操作。 我们流程选择先将需要注入的so放到sd卡目录下,然后判断app为非系统app时进行复制到app目录,注入app等一系列操作。我们找到源码,目录AOSP/frameworks/base/core/java/android/app/ActivityThread.java,找到handleBindApplication,定位到”final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);”这一行。
开始加入我们自己的代码:
1. final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
3. //add
4. String soPath = "/sdcard/f0.so";
5. File sofile = new File(soPath);
6. if (sofile.exists()){
7. Log.e("fukuyama", "satrt inject init copy");
8. ContextImpl context = appContext;
9. ActivityManager mAm = (ActivityManager)context.getSystemService("activity");
10. String activity_packageName = mAm.getRunningTasks(1).get(0).topActivity.getPackageName();
11. if(activity_packageName.indexOf("com.android")<0){
12. String targetPath = "/data/data/" + activity_packageName + "/f0.so";
13. String targetPath2 = "/data/data/" + activity_packageName + "/f032.so";
14. File file1 = new File(targetPath);
15. File file2 = new File(targetPath2);
16. Persist.mycopy("/sdcard/f0.so", targetPath);
17. Persist.mycopy("/sdcard/f032.so", targetPath2);
18. // Log.e("fukuyama", activity_packageName);
19. Log.e("fukuyama", "copy successful");
21. int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
22. FileUtils.setPermissions(targetPath, perm, -1, -1);
23. FileUtils.setPermissions(targetPath2, perm, -1, -1);
24. FileUtils.setPermissions(settingpath, perm, -1, -1);
26. if(file1.exists()){
27. Log.e("fukuyama", System.getProperty("os.arch"));
28. if(System.getProperty("os.arch").indexOf("64")>=0){
29. System.load(targetPath);
30. Log.e("fukuyama", "successful64");
31. file1.delete();
32. }else{
33. System.load(targetPath2);
34. Log.e("fukuyama", "successful32");
35. file2.delete();
36. }
37. }
38. }
39. }else {
40. Log.e("fukuyama", "not found so!");
41. }
43. //add
mcpoy的代码如下,封装进自己注册的工具类:
1. public static void mycopy(String srcFileName, String destFileName){
2. InputStream in = null;
3. OutputStream out = null;
4. try{
5. in = new FileInputStream(srcFileName);
6. out = new FileOutputStream(destFileName);
7. byte[] bytes = new byte[1024];
9. int i = 0;
10. while((i = in.read(bytes))!=-1){
11. out.write(bytes,0,i);
12. }
13. } catch (Exception e) {
14. e.printStackTrace();
15. }finally {
16. try{
17. if(in != null){
18. in.close();
19. }
20. if(out != null){
21. out.flush();
22. out.close();
23. }
24. }catch (Exception e) {
25. e.printStackTrace();
26. }
27. }
28. }
编译系统并刷机。
编译so
另一边我们开始编译要注入的so,这里我选择用sandhook框架hook libc中的open函数,并打印path。 导入sandhook框架(地址:https://github.com/asLody/SandHook ): 将项目下SandHook/nativehook/src/main/cpp/中的文件全部导入
在CMakeList文件中加上我们自己的c文件。
开始编写hook代码,这里选择执行时机在JNI_Onload中:
1. #include <jni.h>
2. #include <string>
3. #include <android/log.h>
4. #include <unistd.h>
5. #include "sandhook_native.h"
7. int (*old_open)(const char *, int, ...) = nullptr;
8. int new_open(const char *arg0, int arg1, ...) {
9. va_list args;
10. va_start(args, arg1);
11. int mode = va_arg(args, int);
12. va_end(args);
13. __android_log_print(4, "fukuyama", "open path -> %s", arg0);
14. return old_open(arg0, arg1, mode);
15. }
18. void startHookJni(){
19. char* target_lib = "libc.so";
20. int size = 1000;
21. char* lib_path = static_cast<char *>(malloc(size));
22. getSoPath(lib_path, target_lib, size);
23. __android_log_print(4, "fukuyama", "target so path:%s", lib_path);
24. if(!lib_path){
25. return;
26. }
27. old_open = reinterpret_cast<int (*)(const char *, int, ...)>(SandInlineHookSym(lib_path,
28. "open",
29. reinterpret_cast<void *>(new_open)));
30. __android_log_print(4, "fukuyama", "hook startHookJni end!");
31. }
34. extern "C" void _init(void){
35. __android_log_print(4, "fukuyama", "called init");
36. }
38. extern "C" int JNICALL JNI_OnLoad(JavaVM* vm, void *resuerved){
39. __android_log_print(4, "fukuyama", "called JNI_OnLoad");
40. startHookJni();
41. return JNI_VERSION_1_6;
42. }
编译后,解压将lib中的两个so推到手机sd卡目录,命名为上面源码中的f0.s0,f032.so。 记得开启测试app的读写sd卡权限。
查看hook结果
运行app,查看logcat输出日志,首先是会有提示复制成功,架构类型等字样。
进入hook后,打印出open的参数。
hook app载入的so
前面完成了对libc的hook,这里开始对app中载入的so中的函数进行hook。 因为我这里选择的载入的so时机比较早,实际中,无法hook到app后载入的so,这里我一开始选择增加一个配置文件,文件内容包含要hook的目标so,手动在handleBindApplication中读写配置文件并载入目标so,结果发现对so载入流程理解太浅,这里so会载入两次,导致我手动载入的so是没有被调用的,自然也就无法hook。 后面采用本方法,我修改/libcore/ojluni/src/main/java/java/lang/System.java中的loadLibrary方法,增加一个判断,判断libname是否为目标so的名称,在app本身完成so载入后,我再载入自己的so。 这里要修改前面的源码,将文件删除以及载入so的代码去除,在System.java中去载入自己的so。
1. if(file1.exists()){
2. Log.e("fukuyama", System.getProperty("os.arch"));
3. if(System.getProperty("os.arch").indexOf("64")>=0){
4. // System.load(targetPath);
5. Log.e("fukuyama", "successful64");
6. // file1.delete();
7. }else{
8. // System.load(targetPath2);
9. Log.e("fukuyama", "successful32");
10. // file2.delete();
11. }
12. }
修改System.java:
1. @CallerSensitive
2. public static void loadLibrary(String libname) {
3. Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
4. //add
5. if(libname == "soname"){ //要hook的so名称
6. if(System.getProperty("os.arch").indexOf("64")>=0){
7. System.load("/data/data/app_packagename/f0.so"); //对应app目录下的自己的so文件
8. }else{
9. System.load("/data/data/app_packagename/f032.so");
10. }
11. }
12. //add
13. }
再修改下注入的so的代码,hook一个获取密钥的函数,让他在logcat中吐出来:
1. #include <jni.h>
2. #include <string>
3. #include <android/log.h>
4. #include <string>
5. #include <unistd.h>
6. #include <pthread.h>
7. #include <thread>
8. #include "sandhook_native.h"
11. jstring (*old_func)(JNIEnv* env, jobject instance);
13. void getSoPath(char *result, char* target_lib, int size){
14. FILE* f = fopen("/proc/self/maps", "r");
15. char so_path[size];
16. if (f){
17. while(EOF != fscanf(f, "%s", so_path)){
18. if(strstr(so_path, target_lib)){
19. strncpy(result, so_path, size);
20. break;
21. }
22. }
23. }
24. }
26. jstring new_func(JNIEnv* env, jobject instance){
27. __android_log_print(4, "fukuyama", "hook new_func success");
28. jstring res = old_func(env, instance);
29. const char *nativeString = env->GetStringUTFChars(res, 0);
30. __android_log_print(4, "fukuyama", "-----output key-----");
31. __android_log_print(4, "fukuyama", "%s", nativeString);
32. return res;
33. }
36. void startHookJni(){
37. char* target_lib = "libxxxxxxxx.so";
38. int size = 1000;
39. char* lib_path = static_cast<char *>(malloc(size));
40. getSoPath(lib_path, target_lib, size);
41. __android_log_print(4, "fukuyama", "target so path:%s", lib_path);
42. if(!lib_path){
43. return;
44. }
45. old_func = reinterpret_cast<jstring (*)(JNIEnv *, jobject)>(reinterpret_cast<jstring *(*)(
46. JNIEnv *, jobject)>(SandInlineHookSym(lib_path,
47. "Java_xxxxxxxxxxxxx_getSecretKey_stringFromJNI",
48. reinterpret_cast<void *>(new_func))));
49. __android_log_print(4, "fukuyama", "hook startHookJni end!");
50. }
53. extern "C" void _init(void){
54. __android_log_print(4, "fukuyama", "called init");
55. }
57. extern "C" int JNICALL JNI_OnLoad(JavaVM* vm, void *resuerved){
58. __android_log_print(4, "fukuyama", "called JNI_OnLoad");
59. startHookJni();
60. return JNI_VERSION_1_6;
61. }
重复上面的操作,编译刷机,推送so再运行app。
可以发现已经成功hook并吐出了我们需要的key。
优化
到这里基本实现了hook相关的操作。 但这里还不完善,hook一个so就要修改源码,还是要封装一个函数,在System.loadLibrary进行按照配置文件进行注入操作。 这里向我注册的一个白名单类中添加一个方法,用于注入so,在system.java中存在无法导入某些类,这里用反射解决。 注册类中相关方法:
1. public static String getPackageName() {
2. String PackageName = null;
3. try {
4. final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Persist.class.getClassLoader())
5. .getDeclaredMethod("currentPackageName", (Class<?>[]) new Class[0]);
6. declaredMethod.setAccessible(true);
7. final Object invoke = declaredMethod.invoke(null, new Object[0]);
8. if (invoke instanceof String) {
9. PackageName = (String) invoke;
10. }
11. } catch (Throwable e) {
12. }
13. return PackageName;
14. }
17. public static void injectso(String libname){
18. String activity_packageName = getPackageName();
19. String targetPath = "/data/data/" + activity_packageName + "/f0.so";
20. String targetPath2 = "/data/data/" + activity_packageName + "/f032.so";
21. String settingpath = "/data/data/" + activity_packageName + "/config.txt";
22. File file1 = new File(targetPath);
23. File file2 = new File(targetPath2);
24. File settingfile = new File(settingpath);
25. if(file1.exists()){
26. Log.e("fukuyama", System.getProperty("os.arch"));
27. if(System.getProperty("os.arch").indexOf("64")>=0){
28. if(settingfile.exists()){
29. Log.e("fukuyama", settingpath);
30. try {
31. FileReader fileReader = new FileReader(settingpath);
32. BufferedReader bufferedReader = new BufferedReader(fileReader);
33. String line;
34. while ((line = bufferedReader.readLine()) != null) {
35. if(libname.equals(line)){
36. Log.e("fukuyama", "successful64");
37. System.load(targetPath);
38. file1.delete();
39. }else{
40. Log.e("fukuyama", "not equal");
41. }
42. }
43. bufferedReader.close();
44. } catch (IOException e) {
45. e.printStackTrace();
46. }
47. }else{
48. Log.e("fukuyama", "not found "+settingpath);
49. }
50. }else{
51. if(settingfile.exists()){
52. Log.e("fukuyama", settingpath);
53. try {
54. FileReader fileReader = new FileReader(settingpath);
55. BufferedReader bufferedReader = new BufferedReader(fileReader);
56. String line;
57. while ((line = bufferedReader.readLine()) != null) {
58. if(libname.equals(line)){
59. Log.e("fukuyama", "successful32");
60. System.load(targetPath2);
61. file2.delete();
62. }else{
63. Log.e("fukuyama", "not equal");
64. }
65. }
66. bufferedReader.close();
67. } catch (IOException e) {
68. e.printStackTrace();
69. }
70. }else{
71. Log.e("fukuyama", "not found "+settingpath);
72. }
73. }
74. }
76. }
在System.java中反射调用,记得导入reflect相关的库。
1. @CallerSensitive
2. public static void loadLibrary(String libname) {
3. Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
5. try {
6. final Method declaredMethod = Class.forName("com.fukuyama.Persist")
7. .getDeclaredMethod("injectso",String.class);
8. declaredMethod.setAccessible(true);
9. final Object invoke = declaredMethod.invoke(null, libname);
10. } catch (Throwable e) {
11. }
13. }
再编译刷机测试:
推入so到sd卡目录,这里增加了一个配置文件,内容就是需要hook的so名称,比如hook libnative-lib.so,就在sd卡目录下的config.txt中写入native-lib。
总结
从注入so的出发,了解了sandhook框架的使用,学习参考了很多大佬的文章,收益匪浅。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:哆啦安全 《AOSP源码定制-so注入并集成hook框架》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论