fart源码分析

其实在寒冰大佬的介绍种已经很详细了,但是还是要自己过一下才行
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的调用链就是大概是这样的。这是我的见解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345