移植Youpk到Aosp10上

打印 上一主题 下一主题

主题 1047|帖子 1047|积分 3141

移植Youpk到Aosp10上

参考文章:
https://github.com/jitcor/Youpk8
https://github.com/Youlor/Youpk
https://bbs.kanxue.com/thread-271358-1.htm#msg_header_h3_0
https://www.jianshu.com/p/18ff0b8e0b01
Fart脱壳王课程
更新说明:

本文章会定时更新,有题目直接向我提问,我会补充到文章
同时我会在完善编译的全部流程(为小白铺路)
并完善移植原理的解读(为想要相识原理的大佬解答)
同时我会完善我如何从aosp8移植到10的过程,如何去探求变更api(授人以渔,大家学会可以自行把我现在的移植到aosp12
实时更新地址:
https://fortunate-decimal-730.notion.site/Youpk-Aosp10-ead82bb5990c4574a9fc0e5d899beaa1?pvs=4
媒介:

Youpk是一个很强盛的框架,他的模块化组织形式非常新奇,但是随着安卓体系的不停更新,移植难度也非常大,由于使用了大量的api,导致移植有肯定的难度,与fart相比,模块化的插桩更加优雅。
已经有大佬做了fart10的移植(见参考文章3),我这里就不和他重复了,来尝试下youpk的移植,并去除特征指纹
测试装备:pixel1
aosp移植版本:aosp10.0.0_r2(本来想移植fartext,失败了)
(aosp11与10的api相关类似,可以自己尝试)
移植前需要注意的

1.需要你有底子的aosp编译修改经验,简单修改能编译成功

  • 需要有趁手的ide修改经验 我使用的是android studio(调试java层) clion(调试native层)
  • 可能你移植全部完成以后,还是过不去企业壳,但是你能相识根本的art修改思想
  • 假如你预备好开始,检测你的内存和硬盘 内存保举大于32G(ide要占用10+10左右)
硬盘需要大于1TB 我是32G+2TB 编译体验非常差 有条件保举上4TB+64G
如何开启clion和android studio导入项目源码:
编译的时候要注意repo的python版本,最好大于3.7 假如在低版本ubuntu体系,需要自己编译python
repo fatal: error unknown url type: https
原因:python没有设置ssl
./configure --prefix=/usr/local/python3 --with-ssl
办理文档:
https://stackoverflow.com/questions/18317682/android-aosp-repo-init-fatal-error-unknown-url-type-https
办理:在编译的时候配置ssl
找到一个趁手的编译情况+IDE情况是成功编译的第一步调
开始初步移植 JAVA层

由于7.1→8.0→10.0有多版本跨度,我们选择youpk8的源码进行移植,来减少api差异
第一步,导入unpacker类到

这里我们可以第一步去除指纹,修改包名
我这里的包名是com.jiqiu

  1. package com.jiqiu;
  2. import android.app.ActivityThread;
  3. import android.os.Looper;
  4. import java.io.BufferedReader;
  5. import java.io.FileReader;
  6. import java.io.File;
  7. public class Unpacker {
  8. //    public static String UNPACK_CONFIG = "/data/local/tmp/unpacker.config";
  9.         //        去指纹位置2,修改配置名文件,不一定需要config尾缀
  10.     public static String UNPACK_CONFIG = "/data/local/tmp/gagaga";
  11.     public static int UNPACK_INTERVAL = 10 * 1000;
  12.     public static Thread unpackerThread = null;
  13.     public static boolean shouldUnpack() {
  14.         boolean should_unpack = false;
  15.         String processName = ActivityThread.currentProcessName();
  16.         BufferedReader br = null;
  17.         try {
  18.             br = new BufferedReader(new FileReader(UNPACK_CONFIG));
  19.             String line;
  20.             while ((line = br.readLine()) != null) {
  21.                 if (line.equals(processName)) {
  22.                     should_unpack = true;
  23.                     break;
  24.                 }
  25.             }
  26.             br.close();
  27.         }
  28.         catch (Exception ignored) {
  29.         }
  30.         return should_unpack;
  31.     }
  32.     public static void unpack() {
  33.         if (Unpacker.unpackerThread != null) {
  34.             return;
  35.         }
  36.         if (!shouldUnpack()) {
  37.             return;
  38.         }
  39.         //开启线程调用
  40.         Unpacker.unpackerThread = new Thread() {
  41.             @Override public void run() {
  42.                 while (true) {
  43.                     try {
  44.                         Thread.sleep(UNPACK_INTERVAL);
  45.                     }
  46.                     catch (InterruptedException e) {
  47.                         e.printStackTrace();
  48.                     }
  49.                     Unpacker.unpackNative();
  50.                 }
  51.             }
  52.         };
  53.         Unpacker.unpackerThread.start();
  54.     }
  55.     public static native void unpackNative();
  56. }
复制代码
类名也完全可以修改,在修改后要在

这个文件里加上自己的包名,否则编译不外
比如我打的包名是com.jiqiu
在里面就是

之后进入
core/java/android/app/ActivityThread.java
导入自己的包名

在app启动后,注入自己的脱壳线程

注意:假如你修改了类名,这里的导入和调用也需要修改
NATIVE层移植

想比于fart,youpk的主动调用部分在native层实现,java层仅仅是启动一个线程 启动native函数

第一步修改dexopt.cc 路径如上
  1. --- a/dex2oat/dex2oat.cc
  2. +++ b/dex2oat/dex2oat.cc
  3. @@ -1036,6 +1036,8 @@ class Dex2Oat final {
  4.          CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
  5.      key_value_store_->Put(OatHeader::kConcurrentCopying,
  6.                            kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
  7. +
  8. +
  9.      if (invocation_file_.get() != -1) {
  10.        std::ostringstream oss;
  11.        for (int i = 0; i < argc; ++i) {
  12. @@ -1089,7 +1091,23 @@ class Dex2Oat final {
  13.        *out = true;
  14.      }
  15.    }
  16. -
  17. +    //patch by Youlor
  18. +    //++++++++++++++++++++++++++++
  19. +    const char* UNPACK_CONFIG = "/data/local/tmp/gagaga";
  20. +    bool ShouldUnpack() {
  21. +        std::ifstream config(UNPACK_CONFIG);
  22. +        std::string line;
  23. +        if(config) {
  24. +            while (std::getline(config, line)) {
  25. +                std::string package_name = line.substr(0, line.find(':'));
  26. +                if (oat_location_.find(package_name) != std::string::npos) {
  27. +                    return true;
  28. +                }
  29. +            }
  30. +        }
  31. +        return false;
  32. +    }
  33. +    //++++++++++++++++++++++++++++
  34.    // Parse the arguments from the command line. In case of an unrecognized option or impossible
  35.    // values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
  36.    // returns, arguments have been successfully parsed.
  37. @@ -1240,7 +1258,14 @@ class Dex2Oat final {
  38.      ProcessOptions(parser_options.get());
  39.      // Insert some compiler things.
  40. +
  41.      InsertCompileOptions(argc, argv);
  42. +    //patch by Youlor
  43. +    //++++++++++++++++++++++++++++
  44. +      if (ShouldUnpack()) {
  45. +          compiler_options_->SetCompilerFilter(CompilerFilter::kVerify);
  46. +      }
  47. +  //++++++++++++++++++++++++++++
  48.    }
复制代码
注意,这里的config路径肯定要与java层设置的一致,见去指纹2
const char* UNPACK_CONFIG = “/data/local/tmp/gagaga”;
拷贝youpk项目到art/runtime目录下


而且修改Android.bp
添加目标编译文件

添加编译文件后我们就可以初步处理youpk的全部不兼容api,附件提供了修改前后的youpk文件夹,在数十次的编译中,已经修改成安卓体系最新支持api

在新版体系编译,这个宏定义视为不安全,直接使用math库的同名函数即可过编译,记得注释原来的

在新版体系中,dex相关库文件移动到了libdexfile文件夹下,我们只需改动libdexfile/Android.bp
导出其依靠的库,并按新版文件调用,即可办理依靠题目
  1.    // Check whether the oat output files are writable, and open them for later. Also open a swap
  2. diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
  3. index 30d1bcd..2ff2f10 100644
  4. --- a/libdexfile/Android.bp
  5. +++ b/libdexfile/Android.bp
  6. @@ -95,7 +95,7 @@ cc_defaults {
  7.          },
  8.      },
  9.      generated_sources: ["dexfile_operator_srcs"],
  10. -    export_include_dirs: ["."],
  11. +    export_include_dirs: [".","dex"],
  12. }
复制代码

globals位置发生改变
#include "base/globals.h”
mirror::Class*指针修改为ObjPtrmirror::Class
setstatus的状态码发生改变 mirror::Class::kStatusInitialized变为ClassStatus::kInitialized
删除size_t Unpacker::getCodeItemSize(ArtMethod* method)方法 新版有api可以直接实现


uint32_t code_item_size = method->GetDexFile()->GetCodeItemSize(*code_item);
可以直接获取到codeitem的size 省去了上面的函数


method->GetCodeItem()->insns_;
新版本的codeitem没有insns_属性,需要迭代器访问
见参考文章4
修改为 const uint16_t* const insns = CodeItemInstructionAccessor(*method->GetDexFile(),method->GetCodeItem()).Insns();即可编译通过
最后修改注册函数

REGISTER_NATIVE_METHODS(“com/jiqiu/Unpacker”);
包名和类名修改过的可以去修改下
art各生命周期函数插桩

artmethod.cc注册函数

  1. --- a/runtime/runtime.cc
  2. +++ b/runtime/runtime.cc
  3. @@ -15,7 +15,9 @@
  4.   */
  5. #include "runtime.h"
  6. -
  7. +//add
  8. +#include "unpacker/unpacker.h"
  9. +//addend
  10. // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
  11. #include <sys/mount.h>
  12. #ifdef __linux__
  13. @@ -1907,6 +1909,10 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
  14.    register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
  15.    register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(env);
  16.    register_sun_misc_Unsafe(env);
  17. +
  18. +  //add
  19. +  Unpacker::register_cn_youlor_Unpacker(env);
  20. +  //addend
  21. }
  22. std::ostream& operator<<(std::ostream& os, const DeoptimizationKind& kind) {
复制代码
修改art/runtime下的Android.bp 使其走向选择分支解释模式

  1. --- a/runtime/Android.bp
  2. +++ b/runtime/Android.bp
  3. @@ -350,6 +352,9 @@ libart_cc_defaults {
  4.                  // ART is allowed to link to libicuuc directly
  5.                  // since they are in the same module
  6.                  "-DANDROID_LINK_SHARED_ICU4C",
  7. +                 "-Wno-error",
  8. +                "-DART_USE_CXX_INTERPRETER=1",
  9.              ],
  10.          },
复制代码
class_linker.h增长友元函数,使其可以访问内部的dex缓存字段

  1. --- a/runtime/class_linker.h
  2. +++ b/runtime/class_linker.h
  3. @@ -1385,6 +1385,9 @@ class ClassLinker {
  4.    class FindVirtualMethodHolderVisitor;
  5.    friend class AppImageLoadingHelper;
  6. +  //add
  7. +  friend class Unpacker;
  8. +  //addend
  9.    friend class ImageDumper;  // for DexLock
  10.    friend struct linker::CompilationHelper;  // For Compile in ImageTest.
  11.    friend class linker::ImageWriter;  // for GetClassRoots
  12. diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
  13. index 36cfee4..b6e5ff6 100644
复制代码
修改artmethod 增长判断分支(这里和youpk原版移植方式一样)

  1. diff --git a/runtime/art_method.cc b/runtime/art_method.cc
  2. index 0890da8..2cd96d2 100644
  3. --- a/runtime/art_method.cc
  4. +++ b/runtime/art_method.cc
  5. @@ -50,7 +50,9 @@
  6. #include "runtime_callbacks.h"
  7. #include "scoped_thread_state_change-inl.h"
  8. #include "vdex_file.h"
  9. -
  10. +//add
  11. +#include "unpacker/unpacker.h"
  12. +//addend
  13. namespace art {
  14. using android::base::StringPrintf;
  15. @@ -322,13 +324,28 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*
  16.    // If the runtime is not yet started or it is required by the debugger, then perform the
  17.    // Invocation by the interpreter, explicitly forcing interpretation over JIT to prevent
  18.    // cycling around the various JIT/Interpreter methods that handle method invocation.
  19. -  if (UNLIKELY(!runtime->IsStarted() ||
  20. -               (self->IsForceInterpreter() && !IsNative() && !IsProxyMethod() && IsInvokable()) ||
  21. -               Dbg::IsForcedInterpreterNeededForCalling(self, this))) {
  22. +
  23. +//  if (UNLIKELY(!runtime->IsStarted() ||
  24. +//               (self->IsForceInterpreter() && !IsNative() && !IsProxyMethod() && IsInvokable()) ||
  25. +//               Dbg::IsForcedInterpreterNeededForCalling(self, this))) {
  26. +//add
  27. +    if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this)
  28. +                 || (Unpacker::isFakeInvoke(self, this) && !this->IsNative()))) {
  29. +
  30. +        //addend
  31.      if (IsStatic()) {
  32.        art::interpreter::EnterInterpreterFromInvoke(
  33.            self, this, nullptr, args, result, /*stay_in_interpreter=*/ true);
  34.      } else {
  35. +        //patch by Youlor
  36. +        //++++++++++++++++++++++++++++
  37. +        //如果是主动调用fake invoke并且是native方法则不执行
  38. +        if (Unpacker::isFakeInvoke(self, this) && this->IsNative()) {
  39. +            // Pop transition.
  40. +            self->PopManagedStackFragment(fragment);
  41. +            return;
  42. +        }
  43. +        //++++++++++++++++++++++++++++
  44.        mirror::Object* receiver =
  45.            reinterpret_cast<StackReference<mirror::Object>*>(&args[0])->AsMirrorPtr();
  46.        art::interpreter::EnterInterpreterFromInvoke(
  47. diff --git a/runtime/class_linker.h b/runtime/class_linker.h
复制代码
解释器分支移植

a/runtime/interpreter/interpreter_switch_impl-inl.h
注意这个不是在cpp中实现了,在interpreter_switch_impl-inl.h头中实现
而且youpk插桩的宏定义在aosp10中改为了函数判断,无法在函数中插桩
只需要在相应位置插桩即可办理(已给出patch)
  1. --- a/runtime/interpreter/interpreter_switch_impl-inl.h
  2. +++ b/runtime/interpreter/interpreter_switch_impl-inl.h
  3. @@ -18,7 +18,9 @@
  4. #define ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_INL_H_
  5. #include "interpreter_switch_impl.h"
  6. -
  7. +//add
  8. +#include "unpacker/unpacker.h"
  9. +//addend
  10. #include "base/enums.h"
  11. #include "base/globals.h"
  12. #include "base/memory_tool.h"
  13. @@ -225,6 +227,7 @@ class InstructionHandler {
  14.      if (!CheckForceReturn()) {
  15.        return false;
  16.      }
  17. +
  18.      if (UNLIKELY(instrumentation->HasDexPcListeners())) {
  19.        uint8_t opcode = inst->Opcode(inst_data);
  20.        bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT);
  21. @@ -243,6 +246,8 @@ class InstructionHandler {
  22.          return false;
  23.        }
  24.      }
  25. +
  26. +      //addend
  27.      return true;
  28.    }
  29. @@ -2643,12 +2648,25 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
  30.        << "Entered interpreter from invoke without retry instruction being handled!";
  31.    bool const interpret_one_instruction = ctx->interpret_one_instruction;
  32. +
  33. +  //add
  34. +  int inst_count=-1;
  35. +  //addend
  36.    while (true) {
  37.      dex_pc = inst->GetDexPc(insns);
  38.      shadow_frame.SetDexPC(dex_pc);
  39.      TraceExecution(shadow_frame, inst, dex_pc);
  40.      inst_data = inst->Fetch16(0);
  41.      {
  42. +    //add
  43. +    inst_count++;                                                                               \
  44. +    bool dumped = Unpacker::beforeInstructionExecute(self, shadow_frame.GetMethod(),            \
  45. +                                                     dex_pc, inst_count);                       \
  46. +
  47. +      if(dumped) {
  48. +          return;
  49. +      }
  50. +      //addend
  51.        bool exit_loop = false;
  52.        InstructionHandler<do_access_check, transaction_active> handler(
  53.            ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);
  54. @@ -2662,6 +2680,7 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
  55.          continue;
  56.        }
  57.      }
  58. +
  59.      switch (inst->Opcode(inst_data)) {
  60. #define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v)                                    \
  61.        case OPCODE: {                                                                              \
  62. @@ -2681,6 +2700,13 @@ DEX_INSTRUCTION_LIST(OPCODE_CASE)
  63.      if (UNLIKELY(interpret_one_instruction)) {
  64.        break;
  65.      }
  66. +      //patch by Youlor
  67. +      //++++++++++++++++++++++++++++
  68. +      bool dumped = Unpacker::afterInstructionExecute(self, shadow_frame.GetMethod(), dex_pc, inst_count);
  69. +      if (dumped) {
  70. +          return ;
  71. +      }
  72. +      //++++++++++++++++++++++++++++
  73.    }
  74.    // Record where we stopped.
  75.    shadow_frame.SetDexPC(inst->GetDexPc(insns));
  76. diff --git a/runtime/runtime.cc b/runtime/runtime.cc
  77. index 51a40e7..275324c 100644
复制代码
成品测试







关于Youpk与FART检测的思路

youpk独特检测思路


  • 由于作者指定了特定名称的包名,以是可以通过反射的方法来检测是否存在这个类,假如存在,则判断为脱壳机情况(在体系的类里面,可以使用hideapi来绕过反射限定)
https://github.com/LSPosed/AndroidHiddenApiBypass

  • 由于作者独特的包名过滤机制,需要配置特定名字的config在data/local/tmp下 其他app可扫描这个目录下的config,来判断脱壳情况
办理方法:
换一个注册的名字,可以选定一些厂商的特殊包名注册,如xiaomi meizu huawei等特殊包名
修改config名字或落地地方挑选app不可达路径(之后会会合讨论)
fart8独特检测思路

DexFIile静态注册了一个函数,作为与native桥接的函数,由于比较独特,直接可以通过反射调用,乃至可以做进一步下沉,在libart.so的导出表中发现这个函数
在ActivityThred中出现了许多工具类函数,可以反射调用检测,以及在art_method中有额外的导出函数,可以通过扫描libart.so的导出表来扫描订定名字
办理方法:

  • 学习youpk,将逻辑打包到一个包里,进行插桩复用
  • 进行api复用(fart10已经做到),将须要的逻辑实现在体系api里,通过参数判断是否返回
共同监测点

文件落地的检测,比如fart选择落地在sdcard下的文件夹,厂商可以选择对sdcard做扫描,来判断该机器是否是脱壳机,以及自己私有目录下非常文件的扫描(以及user落地后无法提取)

难点:
权限的申请(脱壳机一般都有)
用户隐私的保护 (厂商一般不管)
办理办法
修改体系selinux,注册全新的selinux标签,编写体系应用,进行文件的存储和获取(小肩膀沙盒定制思路)
注册体系服务,app通过调用,存储到/system 目录下
以上两种方法均可进行dex和config文件的落地
导出函数的名字,以及导出函数数量的api的检测

难点:
各大厂商对于rom均有定制,libart.so数量均不一致
无法太大的做到导出函数数量的特征(这个是真的致命打击)
办理办法:
建立机型库,对机型的各个文件进行模型建立,检测是否为非常libart,判断是否为非常机型
aosp的检测 以及机型的检测

脱壳机一般使用pixel以及nexus等机型做定制rom,可以针对这些机型做风控(误杀率高)
以是可以对aosp的定制进行检测,对aosp的指纹进行检测(现在大部分脱壳机过不去企业壳都折在了这里)
办理办法:
使用开源的lineageos 以及pixel experience体系进行定制,这些体系都已经去除了许多aosp的特征
(除非日后国内哪家厂商开源了他们的操纵体系)
使用支持这些rom的手机进行定制,这里我强烈保举一加手机,简直无敌 随便刷随便解锁 还能9008救砖(比某xel6代好太多了)
附件: 提供下载

unpacker修改前.zip
unpacker修改后.zip
移植后的unpcker模块
全部patch:
Untitled

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

傲渊山岳

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表