在将 dex 文件编译为 oat 文件的过程中 , 只要出现了 DexFile 对象 , 就可以将该对象对应的 dex 文件导出

已知的脱壳点

/art/runtime/dex_file.cc#OpenMemory
OpenMemory算是常见的脱壳点,在# frida-unpack中也是使用此脱壳点来导出dex对象。

std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base, 
size_t size,  
const std::string& location,   
uint32_t location_checksum,  
MemMap* mem_map, 
const OatDexFile* oat_dex_file, 
std::string* error_msg) { 
CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned  
std::unique_ptr<DexFile> dex_file(
     new DexFile(base, size, location, location_checksum, mem_map, oat_dex_file)); 
if (!dex_file->Init(error_msg)) {
    dex_file.reset();
  }
  return std::unique_ptr<const DexFile>(dex_file.release());
}

添加导出代码

#include <sys/types.h>  //添加额外的库
#include <sys/stat.h>
#include <fcntl.h>

int dexCount = 0;  //注意位置

  char output[100]={0};
  int pid = getpid();
  sprintf(output, "/sdcard/%d_%d_output.dex", pid, dexCount);
  dexCount++;
  int fd = open(output,O_CREAT|O_RDWR,666);
  if (fd > 0)
  {
  	write(fd, base, size);
  	close(fd);
  }

DexFile::DexFile()

在17年的DEF CON 25 黑客大会中,Avi Bashan 和 SlavaMakkaveev 提出的通过修改DexFile的构造函数DexFile::DexFile(),以及OpenAndReadMagic()函数来实现对加壳应用的内存中的dex的dump来脱壳技术

DexFile::DexFile(const uint8_t* base, size_t size,
                 const std::string& location,
                 uint32_t location_checksum,
                 MemMap* mem_map,
                 const OatDexFile* oat_dex_file)
    : begin_(base),
      size_(size),
      location_(location),
      location_checksum_(location_checksum),
      mem_map_(mem_map),
      header_(reinterpret_cast<const Header*>(base)),
      string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
      type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
      field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
      method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
      proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
      find_class_def_misses_(0),
      class_def_index_(nullptr),
      oat_dex_file_(oat_dex_file) {
  CHECK(begin_ != nullptr) << GetLocation();
  CHECK_GT(size_, 0U) << GetLocation();
}

添加代码

+   //------------------------------------------------------------------
+   // DEX file unpacking
+   //------------------------------------------------------------------
+
+   // let's limit processing file list
+
    
LOG(WARNING) << "Dex File: Filename: "<< location;
if (location.find("/data/data/") != std::string::npos) {
    LOG(WARNING) << "Dex File: OAT file unpacking launched";
    std::ofstream dst(location + "__unpacked_oat", std::ios::binary);
    dst.write(reinterpret_cast<const char*>(base), size);
    dst.close();
} else {
    LOG(WARNING) << "Dex File: OAT file unpacking not launched";
}
          
+   //------------------------------------------------------------------

OpenFile
这个函数跟OpenMemory类似,同样是调用了OpenMemory的返回,也可以在这里直接导出dexfile.

std::unique_ptr<const DexFile> dex_file(OpenMemory(location, dex_header->checksum_, map.release(), error_msg));
  if (dex_file.get() == nullptr) {
    *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location, error_msg->c_str());
    return nullptr;
  }

Execute
这个函数是寒冰大佬公布的,dex2oat对类的初始化函数并没有进行编译,进入到interpreter.cc文件中的Execute函数,进而进入ART下的解释器解释执行。

#include <fcntl.h>
 static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item,
                            ShadowFrame& shadow_frame, JValue result_register) {
 char *dexfilepath=(char*)malloc(sizeof(char)*1000);   
    if(dexfilepath!=nullptr)
    {
    ArtMethod* artmethod=shadow_frame.GetMethod();
    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.
    int size_int_=(int)size_;
    int fcmdline =-1;
    char szCmdline[64]= {0};
    char szProcName[256] = {0};
    int procid = getpid();
    sprintf(szCmdline,"/proc/%d/cmdline", procid);
    fcmdline = open(szCmdline, O_RDONLY,0644);
    if(fcmdline >0)
    {
        read(fcmdline, szProcName,256);
        close(fcmdline);
    }
             
    if(szProcName[0])
    {
            memset(dexfilepath,0,1000);               
            sprintf(dexfilepath,"/sdcard/%s_%d_dexfile.dex",szProcName,size_int_);     
            int dexfilefp=open(dexfilepath,O_RDONLY,0666);
            if(dexfilefp>0){
                                close(dexfilefp);
                                dexfilefp=0;
                                       
                            }else{
                                        int fp=open(dexfilepath,O_CREAT|O_RDWR,666);
                                        if(fp>0)
                                        {
                                            write(fp,(void*)begin_,size_);
                                            fsync(fp); 
                                            close(fp);  
                                            }  
                             
                                }
    }
 
    if(dexfilepath!=nullptr)
    {
        free(dexfilepath);
        dexfilepath=nullptr;
    }                        
   }
//=======================

其他脱壳点

看了寒冰大佬的文章,按照寻找脱壳点的办法找到几个新的脱壳点,这里也来记录一下,利用的是dexcache来到出dexfile。曾经有过类似的调用,也有不少利用dexcache来查找和导出的办法,比如在Java层hook函数getDex。

DexCache_getDexNative

namespace art {
static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
  ScopedFastNativeObjectAccess soa(env);
  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
  // Should only be called while holding the lock on the dex cache.
  DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId());
  const DexFile* dex_file = dex_cache->GetDexFile();
  // =======================新增
int fcmdline = -1;
char szCmdline[64] = { 0 };
char szProcName[256] = { 0 };
int procid = getpid();
sprintf(szCmdline, "/proc/%d/cmdline", procid);
fcmdline = open(szCmdline, O_RDONLY, 0644);
if (fcmdline > 0) {
    read(fcmdline, szProcName, 256);
    close(fcmdline);
}
char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
const uint8_t *begin_ = dex_file->Begin();   //dex的起始和大小
size_t size_ = dex_file->Size();

memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;

memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "%s", "/sdcard/fiart");
mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "/sdcard/fiart/%s",szProcName);  //创建保存的文件
mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);
sprintf(dexfilepath,"/sdcard/fiart/%s/%d_dexfile.dex",szProcName, size_int_);
int dexfilefp = open(dexfilepath, O_RDONLY, 0666);
if (dexfilefp > 0) {
    close(dexfilefp);
    dexfilefp = 0;
} else {
    dexfilefp = open(dexfilepath, O_CREAT | O_RDWR,0666);
    if (dexfilefp > 0) {
        write(dexfilefp, (void *) begin_,size_);
        fsync(dexfilefp);
        close(dexfilefp);
	}
}
// ================================
  if (dex_file == nullptr) {
    return nullptr;
  }

GetNameAsString
按照如下新增代码,需要新增库。

mirror::String* ArtMethod::GetNameAsString(Thread* self) {
  CHECK(!IsProxyMethod());
  StackHandleScope<1> hs(self);
  Handle<mirror::DexCache> dex_cache(hs.NewHandle(GetDexCache()));
  auto* dex_file = dex_cache->GetDexFile();
  // ================================
    char *dexfilepath=(char*)malloc(sizeof(char)*1000);   
    const uint8_t* begin_=dex_file->Begin();  // Start of data.
    size_t size_=dex_file->Size();  // Length of data.
    int size_int_=(int)size_;
    int fcmdline =-1;
    char szCmdline[64]= {0};
    char szProcName[256] = {0};
    int procid = getpid();
    sprintf(szCmdline,"/proc/%d/cmdline", procid);
    fcmdline = open(szCmdline, O_RDONLY,0644);
    if(fcmdline >0)
    {
        read(fcmdline, szProcName,256);
        close(fcmdline);
    }
             
    if(szProcName[0])
    {
            memset(dexfilepath,0,1000);               
            sprintf(dexfilepath,"/sdcard/%s_%d_dexfile.dex",szProcName,size_int_);     
            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)
                      {
                       write(fp,(void*)begin_,size_);
                       fsync(fp); 
                       close(fp);  
                       }  
             }
    }
 
    if(dexfilepath!=nullptr)
    {
        free(dexfilepath);
        dexfilepath=nullptr;
    } 
  //==================================
  uint32_t dex_method_idx = GetDexMethodIndex();
  const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
  return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_,dex_cache);
}

EqualParameters

也是类似如上,利用dex缓存来导出的dexfile。

bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) {
  auto* dex_cache = GetDexCache();
  auto* dex_file = dex_cache->GetDexFile();
  //============
  char *dexfilepath=(char*)malloc(sizeof(char)*1000);   
    const uint8_t* begin_=dex_file->Begin();  // Start of data.
    size_t size_=dex_file->Size();  // Length of data.
    int size_int_=(int)size_;
    int fcmdline =-1;
    char szCmdline[64]= {0};
    char szProcName[256] = {0};
    int procid = getpid();
    sprintf(szCmdline,"/proc/%d/cmdline", procid);
    fcmdline = open(szCmdline, O_RDONLY,0644);
    if(fcmdline >0)
    {
        read(fcmdline, szProcName,256);
        close(fcmdline);
    }
    if(szProcName[0])
    {
        memset(dexfilepath,0,1000);               
        sprintf(dexfilepath,"/sdcard/%s_%d_dexfile.dex",szProcName,size_int_);     
        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){
                  write(fp,(void*)begin_,size_);
                  fsync(fp); 
                  close(fp);  
              }  
          }
    }
 	if(dexfilepath!=nullptr)
    {
        free(dexfilepath);
        dexfilepath=nullptr;
    }                        
    //======================
  const auto& method_id = dex_file->GetMethodId(GetDexMethodIndex());
  const auto& proto_id = dex_file->GetMethodPrototype(method_id);
  const DexFile::TypeList* proto_params = dex_file->GetProtoParameters(proto_id);
  auto count = proto_params != nullptr ? proto_params->Size() : 0u;
  auto param_len = params.Get() != nullptr ? params->GetLength() : 0u;




# Android逆向  

tocToc: