一步步带你移植 FART 到 Android 10,实现自动化脱壳

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

FART 源码

FART 是一种基于主动调用的自动化脱壳方案,能够在 ART 虚拟机的执行流程中精准捕获并还原被抽空的函数,实现对被加固的 dex 文件整体 dump。

FART 相关链接:

目前 FART 是基于 Android 6.0 实现,源码文件结构如下:

word/media/image1.png
这里以把 FART 源码移植到 LineageOS 17.1(Android 10)为例。

LineageOS 源码的下载、编译、签名参考:

移植 FART 到 Android 10

对比 android 6.0 中的源码和 FART 的源码找到修改的地方并移植到 Android 10 源码中

word/media/image2.png

interpreter.cc

路径:art/runtime/interpreter/interpreter.cc

Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/interpreter/interpreter.cc

namespace art 中增加 dumpDexFileByExecute 函数声明

//add
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod);

在 Execute 函数头部增加 dumpDexFileByExecute 调用

static inline JValue Execute(
    Thread* self,
    const CodeItemDataAccessor& accessor,
    ShadowFrame& shadow_frame,
    JValue result_register,
    bool stay_in_interpreter = false,
    bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {

    //add
    LOG(INFO) << "[Execute]" << shadow_frame.GetMethod()->PrettyMethod();          
    if(strstr(PrettyMethod(shadow_frame.GetMethod()).c_str(),"<clinit>")!=nullptr) {
      dumpDexFileByExecute(shadow_frame.GetMethod());
    }        
    
    ...                                                           
}        

dalvik_system_DexFile.cc

路径:art/runtime/native/dalvik_system_DexFile.cc

Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/native/dalvik_system_DexFile.cc

导入头文件:

//add
#include "scoped_fast_native_object_access-inl.h"

namespace art 中增加 myfartInvoke 函数声明

//add
extern "C" void myfartInvoke(ArtMethod* artmethod);
//add
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod);

添加 DexFile_dumpMethodCode 函数实现(注意:要在 gMethods 之前)

//add
static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
    if(method!=nullptr) {
        ArtMethod* proxy_method = jobject2ArtMethod(env, method);
        myfartInvoke(proxy_method);
    }
    return;
}

在 gMethods 中增加 dumpMethodCode 的函数签名

static JNINativeMethod gMethods[] = {
  NATIVE_METHOD(DexFile, dumpMethodCode, "(Ljava/lang/Object;)V"),
};

java_lang_reflect_Method.cc

在 namespace art 中增加 jobject2ArtMethod 方法实现

//add
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod) {
    ScopedFastNativeObjectAccess soa(env);
    ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
    return method;
}

art_method.cc

路径:art/runtime/art_method.cc

Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/art_method.cc

添加头文件

//add
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "runtime.h"
#include <android/log.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>

增加宏定义

//add
#define gettidv1() syscall(__NR_gettid)
#define LOG_TAG "ActivityThread"
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

namespace art 中增加以下函数

//add
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);
}

//add
extern "C" char *base64_encode(char *str,long str_len,long* outlen){
    long len;
    char *res;
    int i,j;
    const char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    if(str_len % 3 == 0)
        len=str_len/3*4;
    else
        len=(str_len/3+1)*4;

    res=(char*)malloc(sizeof(char)*(len+1));
    res[len]='\0';
    *outlen=len;

    for(i=0,j=0;i<len-2;j+=3,i+=4){
        res[i]=base64_table[str[j]>>2];
        res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)];
        res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)];
        res[i+3]=base64_table[str[j+2]&0x3f];
    }

    switch(str_len % 3){
        case 1:
            res[i-2]='=';
            res[i-1]='=';
            break;
        case 2:
            res[i-1]='=';
            break;
    }
    return res;
}

//add
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
        char szCmdline[64] = {0};
        char szProcName[256] = {0};
        int procid = getpid();
        snprintf(szCmdline, sizeof(szCmdline), "/proc/%d/cmdline", procid);

        int fcmdline = open(szCmdline, O_RDONLY);
        if (fcmdline > 0) {
            ssize_t result = read(fcmdline, szProcName, sizeof(szProcName) - 1);
            if (result < 0) {
                LOG(ERROR) << "dumpDexFileByExecute: Failed to read cmdline";
            }
            close(fcmdline);
        }

        if (szProcName[0] == '\0') return;

        const DexFile* dex_file = artmethod->GetDexFile();
        const uint8_t* begin_ = dex_file->Begin();
        size_t size_ = dex_file->Size();
        int size_int = static_cast<int>(size_);

        std::string base_dir = "/sdcard/fart/";
        std::string app_dir = base_dir + szProcName;
        std::string dex_path = app_dir + "/" + std::to_string(size_int) + "_dexfile_execute.dex";
        std::string classlist_path = app_dir + "/" + std::to_string(size_int) + "_classlist_execute.txt";

        mkdir(base_dir.c_str(), 0777);
        mkdir(app_dir.c_str(), 0777);

        int dexfilefp = open(dex_path.c_str(), O_RDONLY);
        if (dexfilefp > 0) {
            close(dexfilefp);
        } else {
            int fp = open(dex_path.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
            if (fp > 0) {
                ssize_t w1 = write(fp, begin_, size_);
                if (w1 < 0) {
                    LOG(ERROR) << "dumpDexFileByExecute: Failed to write dex file";
                }
                fsync(fp);
                close(fp);

                int classlistfile = open(classlist_path.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
                if (classlistfile > 0) {
                    for (size_t ii = 0; ii < dex_file->NumClassDefs(); ++ii) {
                        const dex::ClassDef& class_def = dex_file->GetClassDef(ii);
                        const char* descriptor = dex_file->GetClassDescriptor(class_def);

                        ssize_t w2 = write(classlistfile, descriptor, strlen(descriptor));
                        if (w2 < 0) {
                            LOG(ERROR) << "dumpDexFileByExecute: Failed to write class descriptor";
                        }

                        ssize_t w3 = write(classlistfile, "\n", 1);
                        if (w3 < 0) {
                            LOG(ERROR) << "dumpDexFileByExecute: Failed to write newline";
                        }
                    }
                    fsync(classlistfile);
                    close(classlistfile);
                }
            }
        }
}

//add
extern "C" void dumpArtMethod(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
        char szProcName[256] = {0};
        int procid = getpid();

        // 获取进程名
        char szCmdline[64] = {0};
        snprintf(szCmdline, sizeof(szCmdline), "/proc/%d/cmdline", procid);
        int fcmdline = open(szCmdline, O_RDONLY);
        if (fcmdline > 0) {
            ssize_t result = read(fcmdline, szProcName, sizeof(szProcName) - 1);
            if (result < 0) {
                LOG(ERROR) << "ArtMethod::dumpArtMethod: read cmdline failed.";
            }
            close(fcmdline);
        }

        if (szProcName[0] == '\0') return;

        const DexFile* dex_file = artmethod->GetDexFile();
        const uint8_t* begin_ = dex_file->Begin();
        size_t size_ = dex_file->Size();
        int size_int = static_cast<int>(size_);

        // 路径拼接
        std::string baseDir = "/sdcard/fart/";
        std::string processDir = baseDir + szProcName;
        mkdir(baseDir.c_str(), 0777);
        mkdir(processDir.c_str(), 0777);

        // 保存 dex 文件
        std::string dexPath = processDir + "/" + std::to_string(size_int) + "_dexfile.dex";
        int dexfilefp = open(dexPath.c_str(), O_RDONLY);
        if (dexfilefp > 0) {
            close(dexfilefp);
        } else {
            int fp = open(dexPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
            if (fp > 0) {
                ssize_t w = write(fp, begin_, size_);
                if (w < 0) {
                    LOG(ERROR) << "ArtMethod::dumpArtMethod: write dexfile failed -> " << dexPath;
                }
                fsync(fp);
                close(fp);

                // 保存 class 列表
                std::string classListPath = processDir + "/" + std::to_string(size_int) + "_classlist.txt";
                int classlistfile = open(classListPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
                if (classlistfile > 0) {
                    for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
                        const dex::ClassDef& class_def = dex_file->GetClassDef(i);
                        const char* descriptor = dex_file->GetClassDescriptor(class_def);

                        ssize_t w1 = write(classlistfile, descriptor, strlen(descriptor));
                        if (w1 < 0) {
                            LOG(ERROR) << "ArtMethod::dumpArtMethod: write class descriptor failed";
                        }

                        ssize_t w2 = write(classlistfile, "\n", 1);
                        if (w2 < 0) {
                            LOG(ERROR) << "ArtMethod::dumpArtMethod: write newline failed";
                        }
                    }
                    fsync(classlistfile);
                    close(classlistfile);
                }
            }
        }

        // 保存指令码
        const dex::CodeItem* code_item = artmethod->GetCodeItem();
        if (LIKELY(code_item != nullptr)) {
            uint8_t* item = (uint8_t*)code_item;
            int code_item_len = 0;
            CodeItemDataAccessor accessor(*dex_file, code_item);
            if (accessor.TriesSize() > 0) {
                const uint8_t* handler_data = accessor.GetCatchHandlerData();
                uint8_t* tail = codeitem_end(&handler_data);
                code_item_len = static_cast<int>(tail - item);
            } else {
                code_item_len = 16 + accessor.InsnsSizeInCodeUnits() * 2;
            }

            uint32_t method_idx = artmethod->GetDexMethodIndex();
            int offset = static_cast<int>(item - begin_);
            pid_t tid = gettidv1();
            std::string insPath = processDir + "/" + std::to_string(size_int) + "_ins_" + std::to_string(tid) + ".bin";

            int fp2 = open(insPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
            if (fp2 > 0) {
                lseek(fp2, 0, SEEK_END);
                std::string header = "{name:" + artmethod->PrettyMethod() +
                                     ",method_idx:" + std::to_string(method_idx) +
                                     ",offset:" + std::to_string(offset) +
                                     ",code_item_len:" + std::to_string(code_item_len) +
                                     ",ins:";

                ssize_t w3 = write(fp2, header.c_str(), header.length());
                if (w3 < 0) {
                    LOG(ERROR) << "ArtMethod::dumpArtMethod: write header failed";
                }

                long outlen = 0;
                char* base64result = base64_encode((char*)item, (long)code_item_len, &outlen);
                if (base64result != nullptr) {
                    ssize_t w4 = write(fp2, base64result, outlen);
                    if (w4 < 0) {
                        LOG(ERROR) << "ArtMethod::dumpArtMethod: write base64 ins failed";
                    }
                    free(base64result);
                }

                ssize_t w5 = write(fp2, "};", 2);
                if (w5 < 0) {
                    LOG(ERROR) << "ArtMethod::dumpArtMethod: write tail failed";
                }

                fsync(fp2);
                close(fp2);
            }
        }
}

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

ArtMethod::Invoke 中添加判断如果是 fart 的主动调用就 dump

void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
                       const char* shorty) {
    //add
    if (self == nullptr) {
        dumpArtMethod(this);
        return;
    }
  
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值