Fart脱壳-源码分析

发布时间 2023-05-05 14:58:43作者: 梦过无声

源码分析

首先看java代码,在APP启动时候反射了DexFile.java中三个方法,分别是

  1. getClassNameList

  2. defineClassNative

  3. dumpMethodCode

其中前面两个方法是android自带的,dumpMethodCode方法是fart自己添加的

// frameworks/base/core/java/android/app/ActivityThread.java
public static void fart() {
        ...
        Class DexFileClazz = null;
        try {
            DexFileClazz = appClassloader.loadClass("dalvik.system.DexFile");
        } catch (Exception e) {
            e.printStackTrace();
        }
        Method getClassNameList_method = null;
        Method defineClass_method = null;
        Method dumpDexFile_method = null;
        Method dumpMethodCode_method = null;

        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("dumpMethodCode")) {
                dumpMethodCode_method = field;
                dumpMethodCode_method.setAccessible(true);
            }
        }
}

重点: ArtMethod::FromReflectedMethod 获取 ArtMethod 指针

static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
ScopedFastNativeObjectAccess soa(env);
  if(method!=nullptr)
  {
      ArtMethod* artmethod = ArtMethod::FromReflectedMethod(soa, method);
      myfartInvoke(artmethod);
  }

  return;
}



    extern "C" void myfartInvoke(ArtMethod * artmethod)
     SHARED_LOCKS_REQUIRED(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");
    }

注意第一个参数self,这里为null,会进入到dumpArtMethod中,也是fart添加的

    void ArtMethod::Invoke(Thread * self, uint32_t * args,
                   uint32_t args_size, JValue * result,
                   const char *shorty) {
 

        if (self == nullptr) {
            dumpArtMethod(this);
            return;
        }
        ....
     }

重点
artmethod->GetDexFile() 获取 DexFile
dex_file->Begin() 对应dex在内存中的起点
dex_file->size() 对应dex文件大小

DexFile::CodeItem 是什么目前还不清楚

    extern "C" void dumpArtMethod(ArtMethod * artmethod)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
        char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
        if (dexfilepath == nullptr) {
            LOG(INFO) <<
                "ArtMethod::dumpArtMethodinvoked,methodname:"
                << PrettyMethod(artmethod).
                c_str() << "malloc 2000 byte failed";
            return;
        }
        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]) {

            const DexFile *dex_file = artmethod->GetDexFile();
            const char *methodname = PrettyMethod(artmethod).c_str();
            const uint8_t *begin_ = dex_file->Begin();
            size_t size_ = dex_file->Size();

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

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

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

            memset(dexfilepath, 0, 2000);
            sprintf(dexfilepath, "/sdcard/fart/%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);
                }
            }
            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;
                }
                memset(dexfilepath, 0, 2000);
                int size_int = (int) dex_file->Size();  // Length of data
                uint32_t method_idx = artmethod->get_method_idx();
                sprintf(dexfilepath, "/sdcard/fart/%s/%d_%ld.bin", szProcName, size_int, gettidv1());
                int fp2 = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);
                if (fp2 > 0) {
                    lseek(fp2, 0, SEEK_END);
                    memset(dexfilepath, 0, 2000);
                    int offset = (int) (item - begin_);
                    sprintf(dexfilepath, "{name:%s, method_idx:%d, offset:%d, code_item_len:%d, ins:",
                        methodname, method_idx, offset, code_item_len);
                    int contentlength = 0;
                    while (dexfilepath[contentlength] != 0)
                        contentlength++;
                    write(fp2, (void *) dexfilepath, contentlength);
                    long outlen = 0;
                    char *base64result = base64_encode((char *) item, (long)code_item_len, &outlen);
                    write(fp2, base64result, outlen);
                    write(fp2, "};", 2);
                    fsync(fp2);
                    close(fp2);
                    if (base64result != nullptr) {
                        free(base64result);
                        base64result = nullptr;
                    }
                }
            }
        }

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

    }

dumpDexFileByExecute 是在Execute中被调用, 也就是是执行指令的时候

// art/runtime/interpreter/interpreter.cc
static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item,
                             ShadowFrame& shadow_frame, JValue result_register) {

  if(strstr(PrettyMethod(shadow_frame.GetMethod()).c_str(),"<clinit>") != nullptr)
  {
    dumpDexFileByExecute(shadow_frame.GetMethod());
  }
  ...

    extern "C" void dumpDexFileByExecute(ArtMethod * artmethod)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
        char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
        if (dexfilepath == nullptr) {
            LOG(INFO) <<
                "ArtMethod::dumpDexFileByExecute,methodname:"
                << PrettyMethod(artmethod).
                c_str() << "malloc 2000 byte failed";
            return;
        }
        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]) {

            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, 2000);
            int size_int_ = (int) size_;

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

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

            memset(dexfilepath, 0, 2000);
            sprintf(dexfilepath,
                "/sdcard/fart/%s/%d_dexfile_execute.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 (dexfilepath != nullptr) {
            free(dexfilepath);
            dexfilepath = nullptr;
        }
    }

测试实际效果

某平台加固后


fart之后

总结

Hook点有2个:

  1. 是app启动时候,在ActivityThread中利用反射,拿到ArtMethod,再通过ArtMethod->GetDexFile()拿到DexFile
  2. 在安卓虚拟机执行指令时候Execute中进行dump出DexFile,相较于早期的DexFile::OpenCommon(const uint8_t* base, size_t size, ...)中进行脱壳更加底层