首页 > 编程语言 >android脱壳 细节源码解析

android脱壳 细节源码解析

时间:2024-11-16 17:57:41浏览次数:1  
标签:dex std 脱壳 源码 file android size method 加载

加固和脱壳

加固:在 Android 中,应用的代码是通过 DEX 文件存储的,DEX(Dalvik Executable)文件包含了用于在 Dalvik 或 ART 虚拟机中执行的字节码。这些 DEX 文件通常是应用的核心代码,包含了类、方法、字段等信息。

  • DEX 文件通常被压缩或加密,以防止直接提取和反编译。
  • 加壳通常是通过对 DEX 文件进行加密或混淆,或者将其存储在一个非标准的格式(比如自定义的容器)中来实现。

分类为:dex加固,整体加固(将整个dex进行加固,反编译得到是壳的代码)、抽取加固(将细节函数进行抽取,比如抽空或者是smail代码为nop)、VMP、dex2c等;so加固,对so结构进行处理、对so数据进行加密、自定义linker一般处理方式就是so dump,然后so修复

脱壳:将加固app运行过程中,解密后加载的dex文件保存下来 与加密算法差不多,一个加密的是字符/字节数据,一个加密的是文件(也是字节数据)逆向加密算法主要要的是过程,做算法还原,而脱壳要的是解密后的dex文件。

整体加固: 可分为落地加载、内存加载

本质上都是将app自身的dex整体加密,app运行过程中解密后加载
有些壳还会抹掉dex文件头、dex的文件大小filesize等
一般会有字符串加密、资源加密、反调试、签名验证

正常一个Dex文件的运行过程:

  1. 获取加密或混淆的 DEX 文件

如果应用的 DEX 文件经过加密或混淆,它们可能被存储在 APK 文件的某个特定位置,并且会在运行时被动态解密或解压缩。例如,开发者可以在加载 DEX 文件之前,通过一些方法(如使用 DexClassLoader 或自定义的解密机制)来解密它们。

  1. 使用 DexFile 加载 DEX 文件 (不一定是使用DexFile来加载,但是一定会加载DEX文件)

    在 ART 环境中,DexFile 是用来表示和加载 DEX 文件的类。它负责加载 DEX 文件并解析出其中的字节码。在没有加壳或没有加密的情况下,应用会直接从磁盘上加载并执行 DEX 文件。但在加壳情况下,必须首先解密或恢复 DEX 文件才能被 ART 或 Dalvik 解释执行。

    • DexFile 类通过 DexFileLoader 等类加载 DEX 文件。
    • 加载 DEX 文件时,通常会涉及到校验和验证步骤。
  2. 破解 DEX 加密或脱壳过程(对于加密或者加壳的程序总会到真正程序执行的位置)

    1. 动态加载: 在应用运行时,解密 DEX 文件的代码通常是通过动态加载机制(如 DexClassLoader)来执行的。这意味着在加载过程中,DEX 文件的内容可以被解密、反调试或修改。
    2. 绕过加壳: 如果加壳机制采用了简单的加密(例如 AES 加密),攻击者可以通过静态分析(例如通过调试或反编译应用)查找加密密钥或解密过程。绕过加壳过程的方式通常包括:
      • 破解加密密钥:如果密钥在代码中明文存储,通过反编译找到密钥。
      • 调试:通过调试工具捕获解密过程,或手动修改程序流来直接获取解密后的 DEX 文件。
      • Hook 技术:通过在运行时修改应用的行为,绕过加密或解密过程,直接提取 DEX 文件。

所以我们需要就是在完成加密之后把dex文件dump下来。通过 ART 提供的 DexFile 或其他加载机制,可以获取到解密后的 DEX 文件,最后我们需要做的就是通过 DexFile 对象获取 DEX 文件中的字节码,反编译 DEX 文件,得到原始的 Java 类、方法、字段等代码,修改字节码后,可以重新打包并绕过原先的加壳机制。从而完成脱壳

dex相关类InMemoryDexClassLoader源码分析:

这个类是最初处理dex文件数据的位置,其自身是去继承了父类的类对象BaseDexClassLoader

    public InMemoryDexClassLoader(@NonNull ByteBuffer @NonNull [] dexBuffers,
            @Nullable String librarySearchPath, @Nullable ClassLoader parent) {
        super(dexBuffers, librarySearchPath, parent);
    }

在BaseDexClassLoader中去实现了parent属性的赋值,以及initByteBufferDexPath初始化

    public BaseDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.sharedLibraryLoaders = null;
        this.sharedLibraryLoadersAfter = null;
        this.pathList = new DexPathList(this, librarySearchPath);
        this.pathList.initByteBufferDexPath(dexFiles);
        // Run background verification after having set 'pathList'.
        this.pathList.maybeRunBackgroundVerification(this);
    }

在这里开始去处理dex文件了——>new DexFile(dexFiles, definingContext, null_elements)

 void initByteBufferDexPath(ByteBuffer[] dexFiles) {
        final List<IOException> suppressedExceptions = new ArrayList<IOException>();
        try {
            Element[] null_elements = null;
            DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
            dexElements = new Element[] { new Element(dex) };
        } catch (IOException suppressed) {
            System.logE("Unable to load dex files", suppressed);
            suppressedExceptions.add(suppressed);
            dexElements = new Element[0];
        }

        if (suppressedExceptions.size() > 0) {
            dexElementsSuppressedExceptions = suppressedExceptions.toArray(
                    new IOException[suppressedExceptions.size()]);
        }
    }

mCookie

这里出现了我们的mCookie了,脱壳工具blackdex通过mCookie来脱壳 ,也就是说这里的mCookie可以被利用于脱壳的

    DexFile(ByteBuffer[] bufs, ClassLoader loader, DexPathList.Element[] elements)
            throws IOException {
        mCookie = openInMemoryDexFiles(bufs, loader, elements);
        mInternalCookie = mCookie;
        mFileName = null;
    }

继续往里走

这里其实不用太仔细看,因为我们需要的是mCookie,也就是这个函数的返回值的位置,所以可以进入openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements)可以看出来这是Native化的

    private static Object openInMemoryDexFiles(ByteBuffer[] bufs, ClassLoader loader,
            DexPathList.Element[] elements) throws IOException {
        // Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
        // the backing array (non-direct buffers only) and start/end positions
        // so that the native method does not have to call Java methods anymore.
        byte[][] arrays = new byte[bufs.length][];
        int[] starts = new int[bufs.length];
        int[] ends = new int[bufs.length];
        for (int i = 0; i < bufs.length; ++i) {
            arrays[i] = bufs[i].isDirect() ? null : bufs[i].array();
            //这里实现了 arrays之后要被转为jbyteArray  也就是说是另外一种形式的bufs
            starts[i] = bufs[i].position();
            ends[i] = bufs[i].limit();
        }
        return openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements);
    }

Native化的 DexFile_openInMemoryDexFilesNative

static jobject DexFile_openInMemoryDexFilesNative(JNIEnv* env,
                                                  jclass,
                                                  jobjectArray buffers,
                                                  jobjectArray arrays,
                                                  jintArray jstarts,
                                                  jintArray jends,
                                                  jobject class_loader,
                                                  jobjectArray dex_elements) {
  jsize buffers_length = env->GetArrayLength(buffers);
  CHECK_EQ(buffers_length, env->GetArrayLength(arrays));
  CHECK_EQ(buffers_length, env->GetArrayLength(jstarts));
  CHECK_EQ(buffers_length, env->GetArrayLength(jends));

  ScopedIntArrayAccessor starts(env, jstarts);
  ScopedIntArrayAccessor ends(env, jends);

  // Allocate memory for dex files and copy data from ByteBuffers.
  std::vector<MemMap> dex_mem_maps;
  dex_mem_maps.reserve(buffers_length);
  for (jsize i = 0; i < buffers_length; ++i) {
    jobject buffer = env->GetObjectArrayElement(buffers, i); //这是buffers转jobject的buffer了
    jbyteArray array = reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(arrays, i));
    jint start = starts.Get(i);
    jint end = ends.Get(i);

    MemMap dex_data = AllocateDexMemoryMap(env, start, end);//这里申请的空间就是最后dex存储的位置
    if (!dex_data.IsValid()) {
      DCHECK(Thread::Current()->IsExceptionPending());
      return nullptr;
    }

    if (array == nullptr) {
      // Direct ByteBuffer
      uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
      if (base_address == nullptr) {
        ScopedObjectAccess soa(env);
        ThrowWrappedIOException("dexFileBuffer not direct");
        return nullptr;
      }
      size_t length = static_cast<size_t>(end - start);
      memcpy(dex_data.Begin(), base_address + start, length);
    }         //之前说了array是bufs的jobject的形式,上面是按照buffer来实现获取各个dex文件的内容
      else {
      // ByteBuffer backed by a byte array
      jbyte* destination = reinterpret_cast<jbyte*>(dex_data.Begin());
      env->GetByteArrayRegion(array, start, end - start, destination);
    }//这里是按照array来实现获取各个dex文件的内容
--------------------------------------------------------------------------------------------------
    //最后push进dex_mem_maps对象
    dex_mem_maps.push_back(std::move(dex_data));
  }
    
  std::vector<std::string> error_msgs;
  const OatFile* oat_file = nullptr;
  std::vector<std::unique_ptr<const DexFile>> dex_files =
      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
                                                                  class_loader,
                                                                  dex_elements,
                                                                  /*out*/ &oat_file,
                                                                  /*out*/ &error_msgs);
  return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);

到这里其实也只是把每一个的dex文件的起始地址转为了jbyte*或者是memcpy(dex_data.Begin(), base_address + start, length);到了申请了对应的空间的起始地址,最后又到了下一个函数了

CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);

static jobject CreateCookieFromOatFileManagerResult(
    JNIEnv* env,
    std::vector<std::unique_ptr<const DexFile>>& dex_files,
    const OatFile* oat_file,
    const std::vector<std::string>& error_msgs) {
  ClassLinker* linker = Runtime::Current()->GetClassLinker();
  if (dex_files.empty()) {
    ScopedObjectAccess soa(env);
    CHECK(!error_msgs.empty());
    // The most important message is at the end. So set up nesting by going forward, which will
    // wrap the existing exception as a cause for the following one.
    auto it = error_msgs.begin();
    auto itEnd = error_msgs.end();
    for ( ; it != itEnd; ++it) {
      ThrowWrappedIOException("%s", it->c_str());
    }
    return nullptr;
  }
  jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
  if (array == nullptr) {
    ScopedObjectAccess soa(env);
    for (auto& dex_file : dex_files) {
      if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
        dex_file.release();  // NOLINT
      }
    }
  }
  return array;
}

跟踪dex_files往下走到最后一个函数了ConvertDexFilesToJavaArray(env, oat_file, dex_files);顾名思义,将dexfile文件转化为javaarray去了,这里是最后的函数了,因为这里的返回值是array了,也就是ConvertDexFilesToJavaArray的返回值了

static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
                                             const OatFile* oat_file,
                                             std::vector<std::unique_ptr<const DexFile>>& vec) {
  // Add one for the oat file.
  jlongArray long_array = env->NewLongArray(static_cast<jsize>(kDexFileIndexStart + vec.size()));
  if (env->ExceptionCheck() == JNI_TRUE) {
    return nullptr;
  }

  jboolean is_long_data_copied;
  jlong* long_data = env->GetLongArrayElements(long_array, &is_long_data_copied);
  if (env->ExceptionCheck() == JNI_TRUE) {
    return nullptr;
  }

  long_data[kOatFileIndex] = reinterpret_cast64<jlong>(oat_file);
  for (size_t i = 0; i < vec.size(); ++i) {
    long_data[kDexFileIndexStart + i] = reinterpret_cast64<jlong>(vec[i].get());
  }

  env->ReleaseLongArrayElements(long_array, long_data, 0);
  if (env->ExceptionCheck() == JNI_TRUE) {
    return nullptr;
  }

  // Now release all the unique_ptrs.
  for (auto& dex_file : vec) {
    dex_file.release();  // NOLINT
  }

  return long_array;
}

首先不看函数,为什么是返回的long_array 这样的长整型数组??我们为什么会利用mCookie来实现dex脱壳的突破点??想想就可以知道了,长整型用于存储地址。

  long_data[kOatFileIndex] = reinterpret_cast64<jlong>(oat_file);
  for (size_t i = 0; i < vec.size(); ++i) {
    long_data[kDexFileIndexStart + i] = reinterpret_cast64<jlong>(vec[i].get());
  }

我们之前获取的std::vector<std::unique_ptr>& vec 容器vector里面存储的是每个dex文件的起始地址,所以我们返回的长整型的数组其实是每一个dex文件的起始地址,同样还包含了size,这就是为什么mCookie为什么可以作为dex脱壳的脱壳点的原因了

openCommen和DexFile

[DexFile_openInMemoryDexFilesNative]的函数中,略过了

  std::vector<std::unique_ptr<const DexFile>> dex_files =
      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
                                                                  class_loader,
                                                                  dex_elements,
                                                                  /*out*/ &oat_file,
                                                                  /*out*/ &error_msgs);

这个函数的分析,可以看出来这里是将DexFile文件转为Oat的处理,从这里可以找到另外的脱壳点openCommen和DexFile

跟踪OpenDexFilesFromOat——>看到到dex_mem_maps作为参数进入了OpenDexFilesFromOat_Impl,跟进

std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
    std::vector<MemMap>&& dex_mem_maps,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {
  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenDexFilesFromOat_Impl(
      std::move(dex_mem_maps),
      class_loader,
      dex_elements,
      out_oat_file,
      error_msgs);

OpenDexFilesFromOat_Impl:这里删除了各种检测校验的代码

std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_Impl(
    std::vector<MemMap>&& dex_mem_maps,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {
  ScopedTrace trace(__FUNCTION__);
  std::string error_msg;
  DCHECK(error_msgs != nullptr);

  // Extract dex file headers from `dex_mem_maps`.
  const std::vector<const DexFile::Header*> dex_headers = GetDexFileHeaders(dex_mem_maps);

  // Determine dex/vdex locations and the combined location checksum.
  std::string dex_location;
  std::string vdex_path;
  // Load dex files. Skip structural dex file verification if vdex was found
  // and dex checksums matched.
  std::vector<std::unique_ptr<const DexFile>> dex_files;
  for (size_t i = 0; i < dex_mem_maps.size(); ++i) {
    static constexpr bool kVerifyChecksum = true;
    ArtDexFileLoader dex_file_loader(std::move(dex_mem_maps[i]),
                                     DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()));
    std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
        dex_headers[i]->checksum_,
        /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
        kVerifyChecksum,
        &error_msg));
    if (dex_file != nullptr) {
      dex::tracking::RegisterDexFile(dex_file.get());  // Register for tracking.
      dex_files.push_back(std::move(dex_file));
    } else {
      error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
    }
  }

可以看到最重要的代码位置了

    ArtDexFileLoader dex_file_loader(std::move(dex_mem_maps[i]),
                                     DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()));
    std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
        dex_headers[i]->checksum_,
        /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
        kVerifyChecksum,
        &error_msg));

这里加载了dex( Load dex files.)我们往dex_file_loader.Open的位置走(这里传入的是四个参数,但是对象类里面没有,是因为继承了父类DexFileLoader的Open())

  std::unique_ptr<const DexFile> Open(uint32_t location_checksum,
                                      const OatDexFile* oat_dex_file,
                                      bool verify,
                                      bool verify_checksum,
                                      std::string* error_msg) {
    std::unique_ptr<const DexFile> dex_file = OpenOne(
        /*header_offset=*/0, location_checksum, oat_dex_file, verify, verify_checksum, error_msg);
    // This API returns only singe DEX file, so check there is just single dex in the container.
    CHECK(dex_file == nullptr || dex_file->IsDexContainerLastEntry()) << location_;
    return dex_file;
  }

  std::unique_ptr<const DexFile> Open(uint32_t location_checksum,
                                      bool verify,
                                      bool verify_checksum,
                                      std::string* error_msg) {
    return Open(location_checksum,
                /*oat_dex_file=*/nullptr,
                verify,
                verify_checksum,
                error_msg);
  }

这里的四个参数的open返回了五个参数的open,到了上面那个open的函数体从而跟进OpenOne()

从这里出现了OpenCommon的一个脱壳点了

std::unique_ptr<const DexFile> DexFileLoader::OpenOne(size_t header_offset,
                                                      uint32_t location_checksum,
                                                      const OatDexFile* oat_dex_file,
                                                      bool verify,
                                                      bool verify_checksum,
                                                      std::string* error_msg) {
  DEXFILE_SCOPED_TRACE(std::string("Open dex file ") + location_);

  uint32_t magic;
  if (!InitAndReadMagic(header_offset, &magic, error_msg) || !MapRootContainer(error_msg)) {
    DCHECK(!error_msg->empty());
    return {};
  }
  DCHECK(root_container_ != nullptr);
  DCHECK_LE(header_offset, root_container_->Size());
  std::unique_ptr<const DexFile> dex_file = OpenCommon(root_container_,
                                                       root_container_->Begin() + header_offset,
                                                       root_container_->Size() - header_offset,
                                                       location_,
                                                       location_checksum,
                                                       oat_dex_file,
                                                       verify,
                                                       verify_checksum,
                                                       error_msg,
                                                       nullptr);
  return dex_file;
}

OpenCommon:

std::unique_ptr<DexFile> DexFileLoader::OpenCommon(std::shared_ptr<DexFileContainer> container,
                                                   const uint8_t* base,
                                                   size_t app_compat_size,
                                                   const std::string& location,
                                                   std::optional<uint32_t> location_checksum,
                                                   const OatDexFile* oat_dex_file,
                                                   bool verify,
                                                   bool verify_checksum,
                                                   std::string* error_msg,
                                                   DexFileLoaderErrorCode* error_code) {
  if (container == nullptr) {
    // We should never pass null here, but use reasonable default for app compat anyway.
    container = std::make_shared<MemoryDexFileContainer>(base, app_compat_size);
  }
  CHECK_GE(base, container->Begin());
  CHECK_LE(base, container->End());
  const size_t size = container->End() - base;
  if (error_code != nullptr) {
    *error_code = DexFileLoaderErrorCode::kDexFileError;
  }
  std::unique_ptr<DexFile> dex_file;
  auto header = reinterpret_cast<const DexFile::Header*>(base);
  if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
    uint32_t checksum = location_checksum.value_or(header->checksum_);
    dex_file.reset(new StandardDexFile(base, location, checksum, oat_dex_file, container));
  } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
    uint32_t checksum = location_checksum.value_or(header->checksum_);
    dex_file.reset(new CompactDexFile(base, location, checksum, oat_dex_file, container));

这里设置了dex_file(DexFile类)的reset方法,new了两个类

new StandardDexFile(base, location, checksum, oat_dex_file, container)
new CompactDexFile(base, location, checksum, oat_dex_file, container)

这个两个类都去实现了DexFile类对象的初始化

  StandardDexFile(const uint8_t* base,
                  const std::string& location,
                  uint32_t location_checksum,
                  const OatDexFile* oat_dex_file,
                  // Shared since several dex files may be stored in the same logical container.
                  std::shared_ptr<DexFileContainer> container)
      : DexFile(base,
                location,
                location_checksum,
                oat_dex_file,
                std::move(container),
                /*is_compact_dex*/ false) {}
 ------------------------------------------------------------------------------------
 CompactDexFile::CompactDexFile(const uint8_t* base,
                               const std::string& location,
                               uint32_t location_checksum,
                               const OatDexFile* oat_dex_file,
                               std::shared_ptr<DexFileContainer> container)
    : DexFile(base,
              location,
              location_checksum,
              oat_dex_file,
              std::move(container),
              /*is_compact_dex=*/true),
      debug_info_offsets_(DataBegin() + GetHeader().debug_info_offsets_pos_,
                          GetHeader().debug_info_base_,
                          GetHeader().debug_info_offsets_table_offset_) {}
 

这里就是DexFile的脱壳点了

DexFile::DexFile(const uint8_t* base,
                 const std::string& location,
                 uint32_t location_checksum,
                 const OatDexFile* oat_dex_file,
                 std::shared_ptr<DexFileContainer> container,
                 bool is_compact_dex)
    : begin_(base),
      data_(GetDataRange(base, container.get())),
      location_(location),
      location_checksum_(location_checksum),
      header_(reinterpret_cast<const Header*>(base)),
      string_ids_(GetSection<StringId>(&header_->string_ids_off_, container.get())),
      type_ids_(GetSection<TypeId>(&header_->type_ids_off_, container.get())),
      field_ids_(GetSection<FieldId>(&header_->field_ids_off_, container.get())),
      method_ids_(GetSection<MethodId>(&header_->method_ids_off_, container.get())),
      proto_ids_(GetSection<ProtoId>(&header_->proto_ids_off_, container.get())),
      class_defs_(GetSection<ClassDef>(&header_->class_defs_off_, container.get())),
      method_handles_(nullptr),
      num_method_handles_(0),
      call_site_ids_(nullptr),
      num_call_site_ids_(0),
      hiddenapi_class_data_(nullptr),
      oat_dex_file_(oat_dex_file),
      container_(std::move(container)),
      is_compact_dex_(is_compact_dex),
      hiddenapi_domain_(hiddenapi::Domain::kApplication) 

这里包含了dex文件的很多信息,全在这个DexFile文件中,这也就是为什么可以作为脱壳点的原因。

youpk的脱壳原理(借用DexCacheData)

youpk:通过ClassLinker的DexCacheData进一步得到DexFile

 ClassLinker* linker = Runtime::Current()->GetClassLinker();

linker在代码中一般由此获取得到

youpk脱壳源码中:

std::list<const DexFile*> Unpacker::getDexFiles() {
  std::list<const DexFile*> dex_files;
  Thread* const self = Thread::Current();
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  ReaderMutexLock mu(self, *class_linker->DexLock());
  const std::list<ClassLinker::DexCacheData>& dex_caches = class_linker->GetDexCachesData();
  for (auto it = dex_caches.begin(); it != dex_caches.end(); ++it) {
    ClassLinker::DexCacheData data = *it;
    const DexFile* dex_file = data.dex_file;
    const std::string& dex_location = dex_file->GetLocation();
    if (dex_location.rfind("/system/", 0) == 0) {
      continue;
    }
    dex_files.push_back(dex_file);
  }
  return dex_files;
}

实现了对于ClassLinker的获取,并且通过ClassLinker的GetDexCachesData()来获取 CachesData结构体

通过获取CachesData结构体的data.dex_file属性来直接获取DexFile类对象,进而实现脱壳

FART 8.0 脱壳原理——>整体加固脱壳(借用artmethod):

FART8.0的脱壳点存在于

static inline JValue Execute   //存在于  runtime下的interpreter.cc的中

这里存在了内联函数的问题:inline 这里细致的说一下

内联函数:内联函数是一种优化手段,旨在通过将函数调用替换为函数体来减少调用开销,提高程序运行速度。当函数被标记为 inline 时,编译器会尝试在调用该函数的位置插入函数体的代码,而不是进行通常的函数调用(跳转到函数地址执行)

具体来说就是希望系统用函数体来直接实现代码,而不是通过函数调用的形式。

之前遇到过一次讨论栈帧的时候,在函数调用过程中并没有去实现栈帧的开辟,而是直接利用了函数体来实现的,当时不知道这是内联

inline int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 10;
    int result = add(x, y); // 调用 add 函数
    return 0;
}

内联优化: ——————>

int main() {
    int x = 5, y = 10;
    int result = x + y; // 函数体直接替换到调用位置
    return 0;
}

具体操作 : 脱壳的实现就是获取dexfile ——> begin size 这里通过ShadowFrame——>artMethod——>dexfile

为什么会选取在Execute的位置作为脱壳点?

Method对象有invoke()方法是用于进行函数调用的
这里去了解一下函数调用invoke()方法的源代码,invoke()直接是native函数,按照类名和函数的结构搜索到了Method_invoke函数
——————————————————————————————————————————————————————————————————————
Method_invoke——>InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
——————————————————————————————————————————————————————————————————————
而在这里实现了java的Method转为ArtMethod方法
ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(javaMethod);
600  const bool accessible = executable->IsAccessible();
601  ArtMethod* m = executable->GetArtMethod();
——————————————————————————————————————————————————————————————————————
InvokeMethod——> InvokeWithArgArray(soa, m, &arg_array, &result, shorty);
——————————————————————————————————————————————————————————————————————
这里的函数很简单主要还是去调用了ArtMethod 对象的Invoke方法
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
——————————————————————————————————————————————————————————————————————
InvokeWithArgArray————> method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
——————————————————————————————————————————————————————————————————————
这里到达了Invoke方法
 if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) {
   if (IsStatic()) {
     art::interpreter::EnterInterpreterFromInvoke(
          self, this, nullptr, args, result, /*stay_in_interpreter*/ true);
    } 
这里去判断对应的程序是否是进入解释器模式,而在抽取加固的过程中是处于解释器模式的
——————————————————————————————————————————————————————————————————————
method->Invoke————> art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result, /*stay_in_interpreter*/ true)
——————————————————————————————————————————————————————————————————————
而在这里调用了Execute方法
EnterInterpreterFromInvoke————>Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter);
static inline JValue Execute(
    Thread* self,
    const DexFile::CodeItem* code_item,
    ShadowFrame& shadow_frame,
    JValue result_register,
    bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
		//shadow_frame.GetMethod() return ——> artMethod
	if(strstr(shadow_frame.GetMethod()->PrettyMethod().c_str(),"<clinit>"))
	{
		dumpdexfilebyExecute(shadow_frame.GetMethod());
	}

这里的shadow_frame.GetMethod()得到的是artMethod对象,可以直接去获取dexfile

dumpdexfilebyExecute:

extern "C" void dumpdexfilebyExecute(ArtMethod* artmethod)  REQUIRES_SHARED(Locks::mutator_lock_) {
			char *dexfilepath=(char*)malloc(sizeof(char)*1000);	
			if(dexfilepath==nullptr)
			{
				LOG(ERROR)<< "ArtMethod::dumpdexfilebyArtMethod,methodname:"<<artmethod->PrettyMethod().c_str()<<"malloc 1000 byte failed";
				return;
			}
			int result=0;
			int fcmdline =-1;
			char szCmdline[64]= {0};
			char szProcName[256] = {0};
			int procid = getpid();//pid
			sprintf(szCmdline,"/proc/%d/cmdline", procid);///proc/7480/cmdline    cmdline ——>com.chen_chen_chen.myapplication
			fcmdline = open(szCmdline, O_RDONLY,0644);
			if(fcmdline >0)
			{
				result=read(fcmdline, szProcName,256);  //szProcName = packageName
				if(result<0) 
				{
					LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open cmdline file error";
					}
				close(fcmdline);
				
			}
			
			if(szProcName[0])
			{
				
					  const DexFile* dex_file = artmethod->GetDexFile();
					  const uint8_t* begin_=dex_file->Begin();  // Start of data.
					  size_t size_=dex_file->Size();  // Length of data.
					  
					  memset(dexfilepath,0,1000);
					  int size_int_=(int)size_;
					  
					  memset(dexfilepath,0,1000);
					  sprintf(dexfilepath,"%s","/sdcard/fart");
					  mkdir(dexfilepath,0777);
							  
					  memset(dexfilepath,0,1000);///sdcard/fart
					  sprintf(dexfilepath,"/sdcard/fart/%s",szProcName);//    /sdcard/fart/com.chen_chen_chen.myapplication
					  mkdir(dexfilepath,0777);
							  	  
					  memset(dexfilepath,0,1000);
					  sprintf(dexfilepath,"/sdcard/fart/%s/%d_dexfile_execute.dex",szProcName,size_int_);//    /sdcard/fart/com.chen_chen_chen.myapplication/(dex size)_dexfile_execute.dex
					  int dexfilefp=open(dexfilepath,O_RDONLY,0666);
					  if(dexfilefp>0){
						  close(dexfilefp);
						  dexfilefp=0;
						  
						  }else{
									  int fp=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
									  if(fp>0)
									  {
										  result=write(fp,(void*)begin_,size_);
										  if(result<0)
										  {
											  LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open dexfilepath error";
											  }
										  fsync(fp); 
										  close(fp);  
										  memset(dexfilepath,0,1000);
										  sprintf(dexfilepath,"/sdcard/fart/%s/%d_classlist_execute.txt",szProcName,size_int_);//     /sdcard/fart/com.chen_chen_chen.myapplication/(dex size)_classlist_execute.txt
										  int classlistfile=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
											if(classlistfile>0)
											{
												for (size_t ii= 0; ii< dex_file->NumClassDefs(); ++ii) 
												{
													const DexFile::ClassDef& class_def = dex_file->GetClassDef(ii);
													const char* descriptor = dex_file->GetClassDescriptor(class_def);
													result=write(classlistfile,(void*)descriptor,strlen(descriptor));
													if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";								
														}
													const char* temp="\n";
													result=write(classlistfile,(void*)temp,1);
													if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";
														}
													}
												  fsync(classlistfile); 
												  close(classlistfile); 	
												}
										  }
									  }
			}
			if(dexfilepath!=nullptr)
			{
				free(dexfilepath);
				dexfilepath=nullptr;
			}		
}

FART 8.0 脱壳原理——>抽取加固脱壳:

抽取加固:
1. 运行方法后回填,运行完后不再抽取(这样的结果就是运行完成之后就不会被抽取了,我们直接把运行之后的程序dump下来就可以实现抽取加固的脱壳)————> 延时dump       
2. 运行方法后回填,运行完后又抽取(这样函数被调用之后,回填以后执行完成之后又被抽取加固,这样的结果dump就没有用了,只能去实现主动调用对应抽取的函数,在执行过程中去保存加固的代码)—————> FART、youpk主动调用
3. 抽取加固中函数的加密过程问题(通过在加固的函数中添加解密函数,实现对于原函数内存的加密,实现解密)————>
解决思路:被抽取函数肯定要解密运行,抓住这个时机,dump被抽取的数据

我们这里主要针对的是抽取加固过程中的主动调用的方法

首先是,我们要实现对于这些抽取加固的函数的主动调用需要的是得到这个函数地址

函数地址——>类——>dex文件——>ClassLoader

在函数的执行过程中,利用反射以及NDK开发中,我们可以知道函数的执行过程都是通过classload来加载dex,加载类,最后来加载method,来实现函数的执行的。

所以首先我们要得到classload来能得到method

ClassLoader

BootClassLoader:单例模式,用来加载系统类
(如,String,read,write.....系统函数都是通过BootClassLoader来实现加载的)

BaseDexClassLoader:是PathClassLoader、DexClassLoader、InMemoryDexClassLoader的父类,类加载的主要逻辑都是在BaseDexClassLoader完成的

PathClassLoader: 是默认使用的类加载器,用于加载app自身的dex(如加载一个自身的app,com.chen_chen_chen.appliciton也是通过pathClassLoader来实现的)

DexClassLoader: 用于实现插件化、热修复、dex加固等(这个主要用于开发者就是使用,可以实现parent的设置)本地加载dex

InMemoryDexClassLoader: 安卓8.0以后引入,用于内存加载dex

在未加固一个app的执行过程中和抽取加固过程中ClassLoader的关系

这里首先是mClassLoader的常识认知:PathClassLoader加载dex以后,会记录在LoadedApk的mClassLoader属性中,默认使用这个ClassLoader去寻找类。也就是说mClassLoader是指向的PathClassLoader(来加载app的dex的类加载器)

未加固的app:
BootClassLoader  加载系统类
pathClassLoader 加载app的dex    <——————  mClassLoader    未加固的app  PathClassLoader直接加载dex

抽取加固的app:

BootClassLoader  加载系统类
pathClassLoader  壳的dex(壳dex执行完成之后利用自定义的classload加载) <——————  mClassLoader 加固的app PathClassLoader直接加载壳的dex
DexClassLoader  app的dex  <————壳的dex执行完成之后会索引到这里来

这里插入双亲委派机制的内容:

双亲委派机制的工作原理
1.1 如果一个类加载器收到了类加载请求,会先把这个请求委托给父类的加载器去执行
1.2 如果父类加载器还存在其父类加载器,则进一步向上委托,依次类推,最终到达顶层的启动类加载器
1.3 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载

大概的意思是,类加载器加载过程中会先让最顶层的父类去加载,加载不了才往子类去加载。

从上面在未加固一个app的执行过程中和抽取加固过程中ClassLoader的关系中我们就可以发现问题

加固的修正

BootClassLoader  加载系统类
pathClassLoader  壳的dex(壳dex执行完成之后利用自定义的classload加载) <——————  mClassLoader 加固的app PathClassLoader直接加载壳的dex
DexClassLoader  app的dex  <————壳的dex执行完成之后会索引到这里来

通过加固的app,是不能通过mClassLoader来实现找到app自身dex的,最多只能找到Boot和Path的ClassLoader

所以会进行app的修正,通过替换或者是插入的形式进行修正

插入ClassLoader
	BootClassLoader
	DexClassLoader
	PathClassLoader  mClassLoader
替换ClassLoader
	BootClassLoader
	PathClassLoader
	DexClassLoader mClassLoader

FART 8.0 实现ClassLoader的获取

我们需要做的事情就是找到每一个ClassLoader,获取每一个ClassLoader加载的类,函数。

通过双亲委派机制可以知道,我们只需要知道一个ClassLoader就可以知道所有的ClassLoader

Fart函数代码:

    public static void fart() {
        ClassLoader appClassloader = getClassloader();//获取一个classloader
        ClassLoader parentClassloader=appClassloader.getParent();//获取parent
        if(appClassloader.toString().indexOf("java.lang.BootClassLoader")==-1)
		{
			fartwithClassloader(appClassloader);//只要不是BootClassLoader就去主要调用然后dump
		}
        while(parentClassloader!=null){
			if(parentClassloader.toString().indexOf("java.lang.BootClassLoader")==-1)
			{
				fartwithClassloader(parentClassloader);
			}
            parentClassloader=parentClassloader.getParent();
        }
    }

获取一个classloader

    public static ClassLoader getClassloader() {
        ClassLoader resultClassloader = null;
        Object currentActivityThread = invokeStaticMethod(
                "android.app.ActivityThread", "currentActivityThread",
                new Class[]{}, new Object[]{});//获取currentActivityThread类对象
        Object mBoundApplication = getFieldOjbect(
                "android.app.ActivityThread", currentActivityThread,
                "mBoundApplication");//获取mBoundApplication类对象
        Object loadedApkInfo = getFieldOjbect(
                "android.app.ActivityThread$AppBindData",
                mBoundApplication, "info");//通过mBoundApplication——>获取字段info
        Application mApplication = (Application) getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplication");//获取mApplication类对象
        resultClassloader = mApplication.getClassLoader();
        return resultClassloader;
    }//最终获取一个classloader

fartwithClassloader函数实现 加载classloader

    public static void fartwithClassloader(ClassLoader appClassloader) {
        List<Object> dexFilesArray = new ArrayList<Object>();
        Object pathList_object = getFieldOjbect("dalvik.system.BaseDexClassLoader", appClassloader, "pathList");
        //通过当前的classloader,去获取父类BaseDexClassLoader的pathList 
        Object[] ElementsArray = (Object[]) getFieldOjbect("dalvik.system.DexPathList", pathList_object, "dexElements");//通过获取pathList类来得到的Elements[]  这里面每一个元素都是dexfile
        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");
            //加载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;
//获取每一个dexfile中对应的属性、函数的字段位置,
        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");
        
        -----------------------------实现对应的dexfile的获取--------------------------------------------------------
        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);//通过dexFile_fileField字段来获取element中的dexFile_fileField的值,其实也就是dexfile的值
            } 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);
                        //这里实现加载class和调用了
                    }
                }

            }
        }
        return;
    }
   

floadClassAndInvoke函数实现 真正的classload加载类和调用函数

    public static void loadClassAndInvoke(ClassLoader appClassloader, String eachclassname, Method dumpMethodCode_method) {
        Class resultclass = null;
        Log.i("ActivityThread", "go into loadClassAndInvoke->" + "classname:" + eachclassname);
        try {
            resultclass = appClassloader.loadClass(eachclassname);//加载 class.loadClass(classname)加载类
        } catch (Exception e) {
            e.printStackTrace();
            return;
        } catch (Error e) {
            e.printStackTrace();
            return;
        }
        if (resultclass != null) {
            try {
                Constructor<?> cons[] = resultclass.getDeclaredConstructors();//得到类中的构造函数
                for (Constructor<?> constructor : cons) {
                    if (dumpMethodCode_method != null) {
                        try {
                            dumpMethodCode_method.invoke(null, constructor);//这里去实现构造器调用
                        } catch (Exception e) {
                            e.printStackTrace();
                            continue;
                        } catch (Error e) {
                            e.printStackTrace();
                            continue;
                        }
                    } else {
                        Log.e("ActivityThread", "dumpMethodCode_method is null ");
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            } catch (Error e) {
                e.printStackTrace();
            }
            try {
                Method[] methods = resultclass.getDeclaredMethods();//所有函数的调用
                if (methods != null) {
                    for (Method m : methods) {
                        if (dumpMethodCode_method != null) {
                            try {
                               dumpMethodCode_method.invoke(null, m);//实现加载
                             } catch (Exception e) {
                                e.printStackTrace();
                                continue;
                            } catch (Error e) {
                                e.printStackTrace();
                                continue;
                            }
                        } else {
                            Log.e("ActivityThread", "dumpMethodCode_method is null ");
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } catch (Error e) {
                e.printStackTrace();
            }
        }
    }

而dumpMethodCode是定义在了DexFile.java的位置的native函数

    private static native void dumpMethodCode(Object m);

dumpMethodCode在dalvik_system_DexFile.cc中的DexFile_dumpMethodCode()函数

static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
  if(method!=nullptr)
  {
		  ArtMethod* proxy_method = jobject2ArtMethod(env, method);
		  myfartInvoke(proxy_method);
	  }	 
  return;
}//这里把java method 转为了ArtMethod 然后进入了myfartInvoke

在art_method.cc中实现了myfartInvoke()

extern "C" void myfartInvoke(ArtMethod* artmethod)  REQUIRES_SHARED(Locks::mutator_lock_) {
	JValue *result=nullptr;
	Thread *self=nullptr;
	uint32_t temp=6;
	uint32_t* args=&temp;
	uint32_t args_size=6;
	artmethod->Invoke(self, args, args_size, result, "fart");
}//这里实现了调用Invoke()方法的参数初始化,不过肯定不会是正确的参数
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
                       const char* shorty) {
						   
		if (self== nullptr) {
		dumpArtMethod(this);
		return;
		}
    .........................
    //这里是去修改了一下ArtMethod::Invoke的源码的,加入了这一句代码,也就是当Thread传入null的时候才会去走到dumpArtMethod函数去,实现我们对应函数的主动调用

dumpArtMethod:代码实现

extern "C" void dumpArtMethod(ArtMethod* artmethod)  REQUIRES_SHARED(Locks::mutator_lock_) {
			char *dexfilepath=(char*)malloc(sizeof(char)*1000);	
			if(dexfilepath==nullptr)
			{
				LOG(ERROR) << "ArtMethod::dumpArtMethodinvoked,methodname:"<<artmethod->PrettyMethod().c_str()<<"malloc 1000 byte failed";
				return;
			}
			int result=0;
			int fcmdline =-1;
			char szCmdline[64]= {0};
			char szProcName[256] = {0};
			int procid = getpid();
			sprintf(szCmdline,"/proc/%d/cmdline", procid);//读取对应app包名 ————>com.chen_chen_chen.appliction
			fcmdline = open(szCmdline, O_RDONLY,0644);//写入
			if(fcmdline >0)
			{
				result=read(fcmdline, szProcName,256);
				if(result<0)
				{
					LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open cmdline file file error";									
				}
				close(fcmdline);
			}
			
			if(szProcName[0])
			{
					  const DexFile* dex_file = artmethod->GetDexFile();
					  const uint8_t* begin_=dex_file->Begin();  // Start of data.
					  size_t size_=dex_file->Size();  // Length of data.
					  
					  memset(dexfilepath,0,1000);
					  int size_int_=(int)size_;
					  
					  memset(dexfilepath,0,1000);
					  sprintf(dexfilepath,"%s","/sdcard/fart");
					  mkdir(dexfilepath,0777);
							  
					  memset(dexfilepath,0,1000);
					  sprintf(dexfilepath,"/sdcard/fart/%s",szProcName);//     /sdcard/fart/com.chen_chen_chen.appliction
					  mkdir(dexfilepath,0777);
							  	  
					  memset(dexfilepath,0,1000);
					  sprintf(dexfilepath,"/sdcard/fart/%s/%d_dexfile.dex",szProcName,size_int_);//    /sdcard/fart/com.chen_chen_chen.appliction/(dex_size)_dexfile.dex
					  int dexfilefp=open(dexfilepath,O_RDONLY,0666);
					  if(dexfilefp>0){
						  close(dexfilefp);
						  dexfilefp=0;
						  
						  }else{
									  int fp=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
									  if(fp>0)
									  {
										  result=write(fp,(void*)begin_,size_);//写入dex   begin——>begin+size_
										  if(result<0)
											{
												LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open dexfilepath file error";
														
											}
										  fsync(fp); 
										  close(fp);  
										  memset(dexfilepath,0,1000);
										  sprintf(dexfilepath,"/sdcard/fart/%s/%d_classlist.txt",szProcName,size_int_);
										  int classlistfile=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
											if(classlistfile>0)
											{
												for (size_t ii= 0; ii< dex_file->NumClassDefs(); ++ii) 
												{
													const DexFile::ClassDef& class_def = dex_file->GetClassDef(ii);
													const char* descriptor = dex_file->GetClassDescriptor(class_def);
													result=write(classlistfile,(void*)descriptor,strlen(descriptor));//这里写入对了类的函数
													if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";
														
														}
													const char* temp="\n";
													result=write(classlistfile,(void*)temp,1);
													if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";
														
														}
													}
												  fsync(classlistfile); 
												  close(classlistfile); 
												
												}
										  }
                      }

//--------------------------------------------------------------------------这里以上是整体加固的脱壳
//---------------------------------------------------------------------------以下是抽取加固的主动调用的脱壳                
									  
						  const DexFile::CodeItem* code_item = artmethod->GetCodeItem();
						  if (LIKELY(code_item != nullptr)) 
						  {
								  int code_item_len = 0;
								  uint8_t *item=(uint8_t *) code_item;
								  if (code_item->tries_size_>0) {
									  const uint8_t *handler_data = (const uint8_t *)(DexFile::GetTryItems(*code_item, code_item->tries_size_));
									  uint8_t * tail = codeitem_end(&handler_data);
									  code_item_len = (int)(tail - item);
								  }else{
									  code_item_len = 16+code_item->insns_size_in_code_units_*2;
								  }  //获取CodeItem的字节大小
									  memset(dexfilepath,0,1000);
									  int size_int=(int)dex_file->Size();  
									  uint32_t method_idx=artmethod->GetDexMethodIndexUnchecked();
									  sprintf(dexfilepath,"/sdcard/fart/%s/%d_ins_%d.bin",szProcName,size_int,(int)gettidv1());//生成对应的文件
								      int fp2=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
									  if(fp2>0){
										  lseek(fp2,0,SEEK_END);
										  memset(dexfilepath,0,1000);
										  int offset=(int)(item - begin_);
										  sprintf(dexfilepath,"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",artmethod->PrettyMethod().c_str(),method_idx,offset,code_item_len);//写入对应的函数CodeItem的信息
										  int contentlength=0;
										  while(dexfilepath[contentlength]!=0) contentlength++;
										  result=write(fp2,(void*)dexfilepath,contentlength);
										  if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
														
														}
										  long outlen=0;
										  char* base64result=base64_encode((char*)item,(long)code_item_len,&outlen);
										  result=write(fp2,base64result,outlen); //base64编码写入 CodeItem的内容
										  if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
														
														}
										  result=write(fp2,"};",2);
										  if(result<0)
													{
														LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
														
														}
										  fsync(fp2); 
										  close(fp2);
										  if(base64result!=nullptr){
											  free(base64result);
											  base64result=nullptr;
											  }
										   }
					
							}

					
			}
			
			if(dexfilepath!=nullptr)
			{
				free(dexfilepath);
				dexfilepath=nullptr;
			}
		
}

标签:dex,std,脱壳,源码,file,android,size,method,加载
From: https://www.cnblogs.com/ovo-fisherman/p/18549617

相关文章

  • 基于SpringBoot+Vue实现剧本杀服务平台【源码+LW+PPT+部署】
     作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企......
  • SpringBoot云南省大学生支教平台017q3程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景云南省部分偏远地区教育资源匮乏,师资力量薄弱,制约了当地教育事业的发展。随着大学生社会责任感的增强,越来越多的青年选择投身支教事业......
  • SpringBoot游泳馆会员管理系统q26c5(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着健康意识的提升,游泳馆作为重要的健身场所,其会员管理效率直接影响到顾客体验和经营效益。传统的会员管理方式存在信息记录不......
  • springboot毕设高校双创管理系统pc端 源码+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着高校创新创业教育的不断发展,培养具有创新精神和创业能力的高素质人才成为高校的重要任务之一。传统的管理模式难以满足双创活动日益增长的需......
  • ssm125四六级报名与成绩查询系统+jsp(论文+源码)_kaic
     毕业设计(论文)题目:四六级报名与成绩查询系统的设计与实现      摘 要互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对四六级报名信息管理混......
  • springboot毕设饭搭子外卖平台源码+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着现代社会生活节奏的加快,人们的生活方式发生了巨大的改变。一方面,许多人由于工作忙碌、生活压力大等原因,没有足够的时间和精力去准备食物,外卖......
  • springboot004基于springboot004网页时装购物系统(源码+包运行+LW+技术指导)
    项目描述临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问题,今天给在家介绍一篇基于springboot004网页时装购物系统设计与实......
  • 计算机毕业设计—15565 nodejs 蒲公英旅游系统(源码免费领)
    摘 要随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。蒲公英旅游系统设计,主要的模块包括查看后台首页、轮播图(轮播图管理)、公告管理(公告)、资源管理(旅游资讯、资讯分类)、交流管理(旅游攻略、攻略分类)、系统用户......
  • 计算机毕业设计—12795 Ssm网上考试系统(源码免费领)
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用SSM框架开发基......
  • 计算机毕业设计—11578 学生信息管理系统的设计与实现(源码免费领)
    摘要从20年代开始,计算机疯狂的出现在人们的生活以及工作当中,成为人们生活、工作的好帮手,计算机深入到每家每户当中,网络办公,网络教学更是替换了传统手工记录管理的方式,使用计算机办公可以不必局限于固定的时间和固定的地点,通过计算机系统可以轻松实现跨区域的交流。随着高......