美文网首页
fart源码分析

fart源码分析

作者: 超威蓝猫l | 来源:发表于2020-07-13 18:39 被阅读0次

    其实在寒冰大佬的介绍种已经很详细了,但是还是要自己过一下才行
    https://bbs.pediy.com/thread-252630.htm

    ActivityThread

    首先对比一下这里,看一下做了什么操作

    • handleBindApplication


      image.png

      绑定application的时候打了一句log

    • performLaunchActivity
      启动activity的时候打了log,并且开线程开始dump


      image.png
      image.png
    public static void fartthread() {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        Log.e("ActivityThread", "start sleep,wait for fartthread start......");
                        Thread.sleep(1 * 60 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("ActivityThread", "sleep over and start fartthread");
                    fart();
                    Log.e("ActivityThread", "fart run over");
    
                }
            }).start();
        }
    

    睡60s,然后调用fart方法

    public static void fart() {
            ClassLoader appClassloader = getClassloader();
            List<Object> dexFilesArray = new ArrayList<Object>();
            Field pathList_Field = (Field) getClassField(appClassloader, "dalvik.system.BaseDexClassLoader", "pathList");
            Object pathList_object = getFieldOjbect("dalvik.system.BaseDexClassLoader", appClassloader, "pathList");
            Object[] ElementsArray = (Object[]) getFieldOjbect("dalvik.system.DexPathList", pathList_object, "dexElements");
            Field dexFile_fileField = null;
            try {
                dexFile_fileField = (Field) getClassField(appClassloader, "dalvik.system.DexPathList$Element", "dexFile");
            } catch (Exception e) {
                e.printStackTrace();
            }
            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);
                }
            }
            Field mCookiefield = getClassField(appClassloader, "dalvik.system.DexFile", "mCookie");
            for (int j = 0; j < ElementsArray.length; j++) {
                Object element = ElementsArray[j];
                Object dexfile = null;
                try {
                    dexfile = (Object) dexFile_fileField.get(element);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (dexfile == null) {
                    continue;
                }
                if (dexfile != null) {
                    dexFilesArray.add(dexfile);
                    Object mcookie = getClassFieldObject(appClassloader, "dalvik.system.DexFile", dexfile, "mCookie");
                    if (mcookie == 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);
                        }
                    }
    
                }
            }
            return;
        }
    

    这一串代码主要是拿到DexFile的Class,拿到所有dex的引用,循环遍历出className,然后传给loadClassAndInvoke方法。

    public static void loadClassAndInvoke(ClassLoader appClassloader, String eachclassname, Method dumpMethodCode_method) {
            Log.i("ActivityThread", "go into loadClassAndInvoke->" + "classname:" + eachclassname);
            Class resultclass = null;
            try {
                resultclass = appClassloader.loadClass(eachclassname);
            } 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中自定义添加的方法

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

    ScopedObjectAccess的变体,它没有可运行的过渡。只能由“快速”使用
    JNI方法。


    image.png

    ScopedFastNativeObjectAccess 。从翻译来讲,这东西只能用于fastJni模式中,暂时忽略这个方法

    重点是下面的通过FromReflectedMethod方法拿到ArtMethod后,就调用myfartInvoke方法

    art_method.cc

        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");
        }
    

    其中Thread指针是空
    在Invoke中判断到是空即为自己构造的调用,


    image.png
    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;
            }
    
        }
    

    这里代码主要是根据artmethod的base和length 把dex dump出来

    然后再获取codeItem。

    这里codeitem分两种,带try和不带try,因此要区分不同,不然dump下来的指令可能不全

    image.png

    读取一个无符号的LEB128值,将给定的指针更新为point
    刚好超过读取值的末尾。这个功能可以容忍
    第五个编码字节中的非零高阶位。

    这个地方的codeitem_end不知道是计算个啥,虽然知道最后结果是计算出末尾的地址,然后减去开头就是长度,但是这个计算过程还是不是很了解

        uint8_t *codeitem_end(const uint8_t ** pData) {
            uint32_t num_of_list = DecodeUnsignedLeb128(pData);
            for (; num_of_list > 0; num_of_list--) {
                int32_t num_of_handlers =
                    DecodeSignedLeb128(pData);
                int num = num_of_handlers;
                if (num_of_handlers <= 0) {
                    num = -num_of_handlers;
                }
                for (; num > 0; num--) {
                    DecodeUnsignedLeb128(pData);
                    DecodeUnsignedLeb128(pData);
                }
                if (num_of_handlers <= 0) {
                    DecodeUnsignedLeb128(pData);
                }
            }
            return (uint8_t *) (*pData);
        }
    

    虽然不懂是怎么计算的,但是通过搜索可以发现
    dexhunter,fupk3中也用到了这段代码。总之就是计算出了codeitem的大小。
    这段中的128是编码方式
    https://blog.csdn.net/new_abc/article/details/36412081

    初次之外,还有一个方法是dumpDex的

        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;
            }
    
        }
    
    image.png
    image.png

    在解释模式下,如果判断到有初始化函数,也就是init,即构造函数。就dempdex。而在activityThread中,我们刚好就调用了构造函数的主动调用链

    整个fart的调用链就是大概是这样的。这是我的见解

    相关文章

      网友评论

          本文标题:fart源码分析

          本文链接:https://www.haomeiwen.com/subject/lqfzcktx.html