AOSP源码定制-so注入并集成hook框架

admin 2023年12月29日03:11:01评论41 views字数 8657阅读28分51秒阅读模式

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);"这一行。

AOSP源码定制-so注入并集成hook框架

开始加入我们自己的代码:

  1. final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);

  2. //add

  3. String soPath = "/sdcard/f0.so";

  4. File sofile = new File(soPath);

  5. if (sofile.exists()){

  6. Log.e("fukuyama", "satrt inject init copy");

  7. ContextImpl context = appContext;

  8. ActivityManager mAm = (ActivityManager)context.getSystemService("activity");

  9. String activity_packageName = mAm.getRunningTasks(1).get(0).topActivity.getPackageName();

  10. if(activity_packageName.indexOf("com.android")<0){

  11. String targetPath = "/data/data/" + activity_packageName + "/f0.so";

  12. String targetPath2 = "/data/data/" + activity_packageName + "/f032.so";

  13. File file1 = new File(targetPath);

  14. File file2 = new File(targetPath2);

  15. Persist.mycopy("/sdcard/f0.so", targetPath);

  16. Persist.mycopy("/sdcard/f032.so", targetPath2);

  17. // Log.e("fukuyama", activity_packageName);

  18. Log.e("fukuyama", "copy successful");

  19. int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;

  20. FileUtils.setPermissions(targetPath, perm, -1, -1);

  21. FileUtils.setPermissions(targetPath2, perm, -1, -1);

  22. FileUtils.setPermissions(settingpath, perm, -1, -1);

  23. if(file1.exists()){

  24. Log.e("fukuyama", System.getProperty("os.arch"));

  25. if(System.getProperty("os.arch").indexOf("64")>=0){

  26. System.load(targetPath);

  27. Log.e("fukuyama", "successful64");

  28. file1.delete();

  29. }else{

  30. System.load(targetPath2);

  31. Log.e("fukuyama", "successful32");

  32. file2.delete();

  33. }

  34. }

  35. }

  36. }else {

  37. Log.e("fukuyama", "not found so!");

  38. }

  39. //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];

  8. int i = 0;

  9. while((i = in.read(bytes))!=-1){

  10. out.write(bytes,0,i);

  11. }

  12. } catch (Exception e) {

  13. e.printStackTrace();

  14. }finally {

  15. try{

  16. if(in != null){

  17. in.close();

  18. }

  19. if(out != null){

  20. out.flush();

  21. out.close();

  22. }

  23. }catch (Exception e) {

  24. e.printStackTrace();

  25. }

  26. }

  27. }

编译系统并刷机。

编译so

另一边我们开始编译要注入的so,这里我选择用sandhook框架hook libc中的open函数,并打印path。
导入sandhook框架(地址:https://github.com/asLody/SandHook ):
将项目下SandHook/nativehook/src/main/cpp/中的文件全部导入 

AOSP源码定制-so注入并集成hook框架在CMakeList文件中加上我们自己的c文件。
AOSP源码定制-so注入并集成hook框架开始编写hook代码,这里选择执行时机在JNI_Onload中:

  1. #include <jni.h>

  2. #include <string>

  3. #include <android/log.h>

  4. #include <unistd.h>

  5. #include "sandhook_native.h"

  6. int (*old_open)(const char *, int, ...) = nullptr;

  7. int new_open(const char *arg0, int arg1, ...) {

  8. va_list args;

  9. va_start(args, arg1);

  10. int mode = va_arg(args, int);

  11. va_end(args);

  12. __android_log_print(4, "fukuyama", "open path -> %s", arg0);

  13. return old_open(arg0, arg1, mode);

  14. }

  15. void startHookJni(){

  16. char* target_lib = "libc.so";

  17. int size = 1000;

  18. char* lib_path = static_cast<char *>(malloc(size));

  19. getSoPath(lib_path, target_lib, size);

  20. __android_log_print(4, "fukuyama", "target so path:%s", lib_path);

  21. if(!lib_path){

  22. return;

  23. }

  24. old_open = reinterpret_cast<int (*)(const char *, int, ...)>(SandInlineHookSym(lib_path,

  25. "open",

  26. reinterpret_cast<void *>(new_open)));

  27. __android_log_print(4, "fukuyama", "hook startHookJni end!");

  28. }

  29. extern "C" void _init(void){

  30. __android_log_print(4, "fukuyama", "called init");

  31. }

  32. extern "C" int JNICALL JNI_OnLoad(JavaVM* vm, void *resuerved){

  33. __android_log_print(4, "fukuyama", "called JNI_OnLoad");

  34. startHookJni();

  35. return JNI_VERSION_1_6;

  36. }

编译后,解压将lib中的两个so推到手机sd卡目录,命名为上面源码中的f0.s0,f032.so。
记得开启测试app的读写sd卡权限。

查看hook结果

运行app,查看logcat输出日志,首先是会有提示复制成功,架构类型等字样。
AOSP源码定制-so注入并集成hook框架进入hook后,打印出open的参数。
AOSP源码定制-so注入并集成hook框架

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"

  9. jstring (*old_func)(JNIEnv* env, jobject instance);

  10. void getSoPath(char *result, char* target_lib, int size){

  11. FILE* f = fopen("/proc/self/maps", "r");

  12. char so_path[size];

  13. if (f){

  14. while(EOF != fscanf(f, "%s", so_path)){

  15. if(strstr(so_path, target_lib)){

  16. strncpy(result, so_path, size);

  17. break;

  18. }

  19. }

  20. }

  21. }

  22. jstring new_func(JNIEnv* env, jobject instance){

  23. __android_log_print(4, "fukuyama", "hook new_func success");

  24. jstring res = old_func(env, instance);

  25. const char *nativeString = env->GetStringUTFChars(res, 0);

  26. __android_log_print(4, "fukuyama", "-----output key-----");

  27. __android_log_print(4, "fukuyama", "%s", nativeString);

  28. return res;

  29. }

  30. void startHookJni(){

  31. char* target_lib = "libxxxxxxxx.so";

  32. int size = 1000;

  33. char* lib_path = static_cast<char *>(malloc(size));

  34. getSoPath(lib_path, target_lib, size);

  35. __android_log_print(4, "fukuyama", "target so path:%s", lib_path);

  36. if(!lib_path){

  37. return;

  38. }

  39. old_func = reinterpret_cast<jstring (*)(JNIEnv *, jobject)>(reinterpret_cast<jstring *(*)(

  40. JNIEnv *, jobject)>(SandInlineHookSym(lib_path,

  41. "Java_xxxxxxxxxxxxx_getSecretKey_stringFromJNI",

  42. reinterpret_cast<void *>(new_func))));

  43. __android_log_print(4, "fukuyama", "hook startHookJni end!");

  44. }

  45. extern "C" void _init(void){

  46. __android_log_print(4, "fukuyama", "called init");

  47. }

  48. extern "C" int JNICALL JNI_OnLoad(JavaVM* vm, void *resuerved){

  49. __android_log_print(4, "fukuyama", "called JNI_OnLoad");

  50. startHookJni();

  51. return JNI_VERSION_1_6;

  52. }

重复上面的操作,编译刷机,推送so再运行app。
可以发现已经成功hook并吐出了我们需要的key。
AOSP源码定制-so注入并集成hook框架

优化

到这里基本实现了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. }

  15. public static void injectso(String libname){

  16. String activity_packageName = getPackageName();

  17. String targetPath = "/data/data/" + activity_packageName + "/f0.so";

  18. String targetPath2 = "/data/data/" + activity_packageName + "/f032.so";

  19. String settingpath = "/data/data/" + activity_packageName + "/config.txt";

  20. File file1 = new File(targetPath);

  21. File file2 = new File(targetPath2);

  22. File settingfile = new File(settingpath);

  23. if(file1.exists()){

  24. Log.e("fukuyama", System.getProperty("os.arch"));

  25. if(System.getProperty("os.arch").indexOf("64")>=0){

  26. if(settingfile.exists()){

  27. Log.e("fukuyama", settingpath);

  28. try {

  29. FileReader fileReader = new FileReader(settingpath);

  30. BufferedReader bufferedReader = new BufferedReader(fileReader);

  31. String line;

  32. while ((line = bufferedReader.readLine()) != null) {

  33. if(libname.equals(line)){

  34. Log.e("fukuyama", "successful64");

  35. System.load(targetPath);

  36. file1.delete();

  37. }else{

  38. Log.e("fukuyama", "not equal");

  39. }

  40. }

  41. bufferedReader.close();

  42. } catch (IOException e) {

  43. e.printStackTrace();

  44. }

  45. }else{

  46. Log.e("fukuyama", "not found "+settingpath);

  47. }

  48. }else{

  49. if(settingfile.exists()){

  50. Log.e("fukuyama", settingpath);

  51. try {

  52. FileReader fileReader = new FileReader(settingpath);

  53. BufferedReader bufferedReader = new BufferedReader(fileReader);

  54. String line;

  55. while ((line = bufferedReader.readLine()) != null) {

  56. if(libname.equals(line)){

  57. Log.e("fukuyama", "successful32");

  58. System.load(targetPath2);

  59. file2.delete();

  60. }else{

  61. Log.e("fukuyama", "not equal");

  62. }

  63. }

  64. bufferedReader.close();

  65. } catch (IOException e) {

  66. e.printStackTrace();

  67. }

  68. }else{

  69. Log.e("fukuyama", "not found "+settingpath);

  70. }

  71. }

  72. }

  73. }

在System.java中反射调用,记得导入reflect相关的库。

  1. @CallerSensitive

  2. public static void loadLibrary(String libname) {

  3. Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);

  4. try {

  5. final Method declaredMethod = Class.forName("com.fukuyama.Persist")

  6. .getDeclaredMethod("injectso",String.class);

  7. declaredMethod.setAccessible(true);

  8. final Object invoke = declaredMethod.invoke(null, libname);

  9. } catch (Throwable e) {

  10. }

  11. }

再编译刷机测试:
推入so到sd卡目录,这里增加了一个配置文件,内容就是需要hook的so名称,比如hook libnative-lib.so,就在sd卡目录下的config.txt中写入native-lib。AOSP源码定制-so注入并集成hook框架

总结

从注入so的出发,了解了sandhook框架的使用,学习参考了很多大佬的文章,收益匪浅。

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月29日03:11:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   AOSP源码定制-so注入并集成hook框架http://cn-sec.com/archives/2343841.html

发表评论

匿名网友 填写信息