<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    FartExt超進化之奇奇怪怪的新ROM工具MikRom

    VSole2022-03-08 17:00:40

    1

    前言

    目前這個工具不怎么完善,能湊合使用,剩下的部分使用中再慢慢完善。其中部分代碼我會開源,其實感覺實現的核心并不怎么復雜,算是一個萌新學習定制ROM過程的一個作品。而且還有個調試超級慢的BUG,如果有大佬知道是啥原因,還請指導一下。

    感謝:看雪高研班課程、FART脫殼王課程、珍惜大佬的android進階課程

    2

    FartExt

    詳細的介紹請看:FartExt之優化更深主動調用的FART10(https://bbs.pediy.com/thread-268760.htm)

    FartExt是我之前學習脫殼實踐時做的一個自動脫殼機,是基于FART的主動調用思想實現對特定的抽取殼進行優化處理的工具。由于原本的FART沒有配置相關的,所以我增加了配置對指定app脫殼。

    大致就是對FART的簡單優化。由于感覺當時做的功能并不怎么完善,所以只是短暫的放了下載地址,就刪掉了。不知道有沒有人實際使用。使用的效果到底咋樣。現在放出開源代碼。

    github:FartExt(https://github.com/dqzg12300/FartExt)

    3

    MikRom

    首先回顧一下當時FartExt文章最后的思考部分:

    整個流程梳理完成后,我們可以由此來借鑒來思考延伸一下。

    比如,包裝一些屬于自己的系統層api調用。便于我們使用xposed或者是frida來調用一些功能。

    再比如,加載應用時,讀取配置文件作為開關,我們來對網絡流量進行攔截寫入保存,或者對所有的jni函數調用,或者是java函數調用進行trace。這種就屬于是rom級別的打樁。

    再比如,可以做一個應用來讀寫作為開關的配置文件,而rom讀取配置文件后,對一些流程進行調整。例如控制FART是否使用更深調用。控制是否開啟rom級別的打樁。

    以上純屬個人瞎想。剛剛入門,想的有點多,以后了解更深了,我再看看如何定制一個專屬的rom逆向集合。

     

    MikRom就是當時個人瞎想的成果,做一個Rom層面的逆向工具,為我們提供比較常用的插樁、注入、脫殼等一系列功能。下面列上目前MikRom中包含的功能

    內核修改過反調試 
    開啟硬件斷點 
    USB調試默認連接 
    脫殼(黑名單、白名單過濾、更深的主動調用鏈) 
    ROM打樁(ArtMethod調用、RegisterNative調用、JNI函數調用) 
    frida持久化(支持listen,wait,script三種模式) 
    支持自行切換frida-gadget版本 
    反調試(通過sleep目標函數,再附加進程來過掉起始的反調試) 
    trace java函數(smali指令的trace) 
    內置dobby注入 
    注入so 
    注入dex(實現對應的接口觸發調用。目前還未測試)

    github:MikRom(https://github.com/dqzg12300/MikRom)

    4

    MikManager

    由于功能做的更加復雜了,我們自行編輯配置文件不再那么方便了,所以我特地做了一個界面化的工具來操作。最后將我們的設置保存到文件,然后MikRom會在打開app時讀取文件,解析后做對應的操作。MikManager就是用來管理這個配置的。

    界面較為簡陋,如果對MikRom感興趣的,但是感覺我的界面太丑,也可以自己編寫一個界面管理工具。我正向開發比較渣,所以代碼較為粗糙。不過目前使用沒啥問題。

    github:MikManager(https://github.com/dqzg12300/MikManager)

    5

    開發的始末

    雖然功能不是很多,但是做這個工具卻折騰了我很長的時間,這里我將記錄下這樣一個簡陋的Rom工具開發的歷程,由于并不是很清楚其他大佬是如何處理的,所以有些功能都是我不斷的試錯中找到的方案。在試錯的過程中,走了不少彎路,希望能幫到一些小伙伴。

    如果我采用的方案犯了什么比較低級的錯誤,還請大佬能指點一下。另外有大佬說,做越簡單化的工具,越危險。如果有什么和諧的風險或者是法律的問題,還請聯系我進行修改。

    6

    編譯版本

    早先FartExt我采用的是aosp10r2的源碼進行修改編譯。后來有同學覺得界面太簡陋了,看起來就像山寨系統。確實如此,然后我就參考了hanbingle老師在Fart脫殼王使用的rom,選擇了PixelExperience來進行修改。編譯的marlin版本,測試手機是pixel XL。

    參考文章:https://blog.csdn.net/weixin_42443075/article/details/118084535

    上面是我當時編譯參考的文章。另外在編譯PixelExperience時編譯碰到錯誤,需要修改build/blueprint/Blueprints。

    bootstrap_go_package {    name: "blueprint-pathtools",    pkgPath: "github.com/google/blueprint/pathtools",    deps: [        "blueprint-deptools",    ],    srcs: [        "pathtools/lists.go",        "pathtools/fs.go",        "pathtools/glob.go",    ],    testSrcs: [        //修改處,這里的內容刪掉    ],}
    

    另外一個問題就是這個rom居然沒法在mac進行編譯,最后沒辦法只能用ubuntu來開發了。

    7

    配置管理優化

    最早先在FartExt中的配置是我們自己在/data/local/tmp/中寫入一個fext.config的文件,然后在應用啟動過程中的handleBindApplication調用時,解析這個配置文件,來決定是否要脫殼。

    而現在配置更加復雜了,我們需要使用一個app來對配置進行管理生成,而app沒有對/data/local/tmp寫入文件的權限。所以我們這里就有了一個簡單的需求,要將配置文件落地到一個所有應用都有權限訪問的地方。

     

    有人就要說了,我們可以落地到sdcard,是的,早期我就是這么干的。配置文件落地到sdcard后,所有的app要使用功能,就必須先打開sdcard,并且,由于我使用的rom版本是安卓10的,而安卓10中,你想要直接訪問sdcard的任意目錄,是需要設置requestLegacyExternalStorage="true"。

    所以這就導致了,即使我們不嫌麻煩,每個想處理的app都打開sdcard,也會出現有些app無法訪問到配置文件。

    在這里我使用的方案是,創建一個系統服務,這個系統服務提供一個讀和寫的函數,然后通過調用系統服務將配置文件落地到/data/system/目錄中,然后每個應用打開時再通過這個系統服務來讀取配置。

    8

    添加系統服務

    這個系統服務目前主要就是為配置管理提供讀寫權限。可能這樣干會有些漏洞的問題,不過我這個rom本身就是逆向使用的工具,而面向正常用戶,所以就暫時不考慮漏洞問題了。

    參考文章:Android AOSP 添加系統服務【aidl接口服務】Java層(https://bbs.pediy.com/thread-260472.htm)

    參考文章:android 10 添加系統服務步驟(https://blog.csdn.net/a546036242/article/details/118221349)

    下面貼上我定義的系統服務

    interface IMikRom{    //讀取文件    String readFile(String path);    //寫入文件    void writeFile(String path,String data);    //執行shell命令    String shellExec(String cmd);}
    

    至于詳細的讀寫方法我就不貼了,就是和android正常的讀寫文件處理一樣。我就說一下在這里我碰到的坑,我按照參考文章一樣的方式做完之后,發現無法找到服務,但是service list|grep mikrom是可以匹配到我自己定義的服務的。

    最后經過排查日志發現selinux提示缺少了find權限,于是我修改文件system/sepolicy/public/untrusted_app.te,內容如下:

    type untrusted_app, domain;type untrusted_app_27, domain;type untrusted_app_25, domain;allow untrusted_app mikrom_service:service_manager find;allow untrusted_app_27 mikrom_service:service_manager find;allow untrusted_app_25 mikrom_service:service_manager find;
    

    最后成功找到服務,將配置文件寫入到了/data/system/目錄中。

    這里我只簡單的貼一下相關。

    MikManager的寫入的相關代碼:

    //獲取服務public class ServiceUtils {    private static IMikRom iMikRom = null;    public static IMikRom getiMikRom() {        if (iMikRom == null) {            try {                Class localClass = Class.forName("android.os.ServiceManager");                Method getService = localClass.getMethod("getService", new Class[] {String.class});                if(getService != null) {                    Object objResult = getService.invoke(localClass, new Object[]{"mikrom"});                    if (objResult != null) {                        IBinder binder = (IBinder) objResult;                        iMikRom = IMikRom.Stub.asInterface(binder);                    }                }            } catch (Exception e) {                Log.d("MikManager",e.getMessage());                e.printStackTrace();            }        }        return iMikRom;    }}//將json數據保存到指定路徑public static void SaveMikromConfig(List packageList){    Log.e(ConfigUtil.TAG,"SaveMikromConfig");    Gson gson = new Gson();    String savejson=gson.toJson(packageList);    try {        ServiceUtils.getiMikRom().writeFile(ConfigUtil.configPath,savejson);    } catch (RemoteException e) {        Log.e(ConfigUtil.TAG,"writeConfig err:"+e.getMessage());    }}
    

    MikRom中讀取數據:

    private static IMikRom iMikRom=null;public static IMikRom getiMikRom() {    if (iMikRom == null) {        try {            Class localClass = Class.forName("android.os.ServiceManager");            Method getService = localClass.getMethod("getService", new Class[] {String.class});            if(getService != null) {                Object objResult = getService.invoke(localClass, new Object[]{"mikrom"});                if (objResult != null) {                    IBinder binder = (IBinder) objResult;                    iMikRom = IMikRom.Stub.asInterface(binder);                }            }        } catch (Exception e) {            Log.d("MikManager",e.getMessage());            e.printStackTrace();        }    }    return iMikRom;} public static String getMikConfig(){    try {        IMikRom mikrom=getiMikRom();        if(mikrom==null){            return "";        }        return mikrom.readFile("/data/system/mik.conf");    } catch (RemoteException e) {        e.printStackTrace();    }    return "";}
    

    測試的配置文件內容如下:

    [{"appName":"crackme","breakClass":"","dexClassName":"","dexPath":"","enabled":true,"fridaJsPath":"","gadgetArm64Path":"","gadgetPath":"","isDeep":false,"isDobby":false,"isInvokePrint":false,"isJNIMethodPrint":true,"isRegisterNativePrint":false,"isTuoke":false,"packageName":"com.kanxue.crackme","port":0,"sleepNativeMethod":"","soPath":"","traceMethod":"","whiteClass":"","whitePath":""}]
    

    到這里第一步的優化就完成了,讀寫配置成功脫離了對sdcard的權限依賴,以及能全局應用都可以訪問到。具體的詳細相關代碼可參考我放出的部分源碼。

    9

    脫殼相關優化

    由于我走向了另外一條偏向更易于使用的優化方向,所以關于脫殼的關鍵部分我并沒有做什么優化,所以如果脫殼需求較大的朋友可以考慮看看hanbingle大佬的脫殼王是否能滿足需求。

    我對脫殼的優化主要分為兩點,由于FartExt當時的脫殼結果保存是在sdcard中,所以對于權限有一定依賴,所以我優化掉了這塊的依賴,讓我們不用再手動開啟sdcard權限,也能保存下脫殼結果。

    另外一點就是當時如果是抽取殼的情況,我們需要拿出兩個文件來手動修復一下。我這里也優化了一下,會直接將函數執行完的dex重新再保存一份,這樣就無需我們再手動修復了。當然同時也保留了原本的做法,仍然保存大佬說的幾個重要元素。

    優化1方案:解決sdcard權限問題

    我嘗試了很多種辦法來避免我們手動開啟sdcard權限的情況下保存脫殼結果文件。最后測試成功了兩種辦法。這兩種辦法我都簡單說下。

    第一種辦法,我們直接將數據可以寫入應用本身的內部空間中。也就是/data/data/中。但是這樣有權限寫入,但是沒有root的話就無法訪問到保存的結果了,當然沒有root的情況也是能訪問到應用內部數據的。我們可以通過命令run-as 來直接進入應用數據內部。但是也有人會問,如果對方應用沒有開啟debuggable,不就沒辦法使用run-as了嗎?

    這就是改Rom的優勢所在了。我們可以直接修改PackageParser中的函數parseBaseApplication。在里面為我們默認打開debuggable即可。這樣即使對方沒有設置,在加載xml的時候,也會打開這個功能。

    第二種辦法,既然第一個辦法最后可以直接改xml為我們默認打開debuggable,那么我們解決sdcard權限,也可以修改PackageParser中的函數parseBaseApplication來直接打開sdcard權限。

    不過由于安卓10的特殊性,即使打開了sdcard權限,也只能在sdcard中自己的目錄寫入數據,所以使用這個辦法時,脫殼結果應保存在/sdcard/Android/data//目錄中。

    下面貼上相關的代碼,其中包含了兩種解決方案:

    //這里我判斷如果非系統應用才增加這些權限if((ai.flags&ApplicationInfo.FLAG_SYSTEM)!=1){        //試錯中發現,開啟這些權限的時候,有個google和message相關的進程會崩潰,所以過濾一下        if(!ai.packageName.contains("google") && !ai.packageName.contains("message")){            //下面是sdcard權限開啟,以及debuggable權限開啟。如果擔心檢測,可以關掉debuggable的默認開啟            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;             ai.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;             ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;            // Debuggable implies profileable            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;        }    }
    

    優化2方案:解決抽取殼在脫殼完成后還需要手動修復

    在優化這個問題前,首先要意識到為什么會需要手動修復,當我們理解了大佬的處理之后,就發現hanbingle大佬為了避免每個函數主動調用都將dex給保存,所以只有文件不存在的時候才保存。也就意味著我們保存的dex是第一個主動調用執行時的dex。

    如果這個抽取殼是必須函數執行后才會恢復的,那么后面的函數在這個保存dex中都依然是被抽取的。FART的做法是將codeitem保存出來后,然后再修復。所以我將這里優化了一下。

    知道問題所在后,優化的思路就清晰了,我采用了比較簡單的一種優化方式,就是每個dex文件保存時,將這個dex的地址以及長度給保存下來。最后在所有主動調用完成時,重新將所有dex文件再保存一次。下面看看優化后的相關代碼:

    //存放dex的指針和長度static std::map<void*,size_t> dex_map; //主動調用函數的dump處理extern "C" void dumpArtMethod(ArtMethod* artmethod)  REQUIRES_SHARED(Locks::mutator_lock_) {                ...                int dexfilefp=open(dexfilepath,O_RDONLY,0666);                ///dex文件存在則不處理,避免主動調用每次都要重新保存dex                if(dexfilefp>0){                    close(dexfilefp);                    dexfilefp=0;                }else{                    LOG(ERROR) << "mikrom ArtMethod::dumpdexfilebyArtMethod save dex_map";                    //將這個地址給保存下來                    dex_map.insert(std::pair<void*,size_t>((void*)begin_,size_));                    int fp=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);                    ...                }}//主動調用完成時,重新保存到文件名_dexfile_repair.dex中extern "C" void dumpDexOver()  REQUIRES_SHARED(Locks::mutator_lock_) {    if(dex_map.size()<=0){            LOG(ERROR) << "mikrom dumpDexOver dex_map.size()<=0";        return;    }    char *dexfilepath=(char*)malloc(sizeof(char)*1000);    LOG(ERROR) << "mikrom ArtMethod::dumpDexOver";    int result=0;    char* packageName=ArtMethod::GetPackageName();    std::map<void*, size_t>::iterator iter;    for(iter = dex_map.begin(); iter != dex_map.end(); iter++) {        void* begin_=iter->first;        size_t size_=iter->second;        int size_int_=(int)size_;        memset(dexfilepath,0,1000);        sprintf(dexfilepath,"/sdcard/Android/data/%s/files/dump",packageName);        mkdir(dexfilepath,0777);        memset(dexfilepath,0,1000);        sprintf(dexfilepath,"/sdcard/Android/data/%s/files/dump/%d_dexfile_repair.dex",packageName,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)          {              result=write(fp,(void*)begin_,size_);              if(result<0)              {                  LOG(ERROR) << "mikrom ArtMethod::dumpDexOver,open dexfilepath error";              }              fsync(fp);              close(fp);              memset(dexfilepath,0,1000);          }        }    }    if(dexfilepath!=nullptr)    {        free(dexfilepath);        dexfilepath=nullptr;    }}
    

    最后生成的_repair.dex文件就是無需再修復的了,如果想要手動修復按照原來的方式也可以。當然,如果主動調用未能成功跑完,這個repair文件也是不會生成的。

    10

    新增功能

    1、修改內核反調試

    關于修改內核反調試已經有相當多的文章講解了,我這里也是和他們用的同一個方案,基本都是根據正向檢測調試的方式,來進行反調試,當然如果有人用其他方式來判斷是否被調試,這個反調試就無效了。詳細的過反調試原理可以看參考文章。

    參考文章:修改內核源碼繞過反調試檢測(Android10)(https://blog.csdn.net/u011426115/article/details/113144592)

    雖然我改了。但是沒測試效果咋樣。修改的代碼如下,kernel/google/marlin/fs/proc/array.c

    static const char * const task_state_array[] = {    "R (running)",        /*   0 */    "S (sleeping)",        /*   1 */    "D (disk sleep)",    /*   2 */    "S (sleeping)",        /*   4 */         //這里之前是 "T (stopped)",    "S (sleeping)",        /*   8 */         //這里之前是 "t (tracing stop)"    "X (dead)",        /*  16 */    "Z (zombie)",        /*  32 */}; static inline void task_state(struct seq_file *m, struct pid_namespace *ns,                struct pid *pid, struct task_struct *p){    struct user_namespace *user_ns = seq_user_ns(m);    struct group_info *group_info;    int g;    struct fdtable *fdt = NULL;    const struct cred *cred;    pid_t ppid = 0, tpid = 0;    struct task_struct *leader = NULL;     rcu_read_lock();    if (pid_alive(p)) {        struct task_struct *tracer = ptrace_parent(p);        if (tracer)            tpid = task_pid_nr_ns(tracer, ns);        ppid = task_tgid_nr_ns(rcu_dereference(p->real_parent), ns);        leader = p->group_leader;    }    //這個直接強制改tpid為0。    tpid=0;    cred = get_task_cred(p);    ...}
    

    當對方檢測/proc//status文件時,獲取到的TracerPid就會一直是0,并且應用的狀態不會出現stopped和tracing stop。下面是status的部分內容:

    Name:    .kanxue.crackmeState:    S (sleeping)Tgid:    12142Pid:    12142PPid:    545TracerPid:    0Uid:    10027    10027    10027    10027Gid:    10027    10027    10027    10027Ngid:    0FDSize:    128Groups:    9997 20027 50027VmPeak:     5657524 kBVmSize:     5210040 kB
    

    還有一處修改文件是kernel/google/marlin/fs/proc/base.c

    static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,              struct pid *pid, struct task_struct *task){    unsigned long wchan;    char symname[KSYM_NAME_LEN];     wchan = get_wchan(task);     if (lookup_symbol_name(wchan, symname) < 0)        if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))            return 0;        else            return seq_printf(m, "%lu", wchan);    else        if(strstr(symname,"trace")){        //這里是新增的,如果符號名稱包含trace,就固定改成sys_epoll_wait            return seq_printf(m,"%s","SyS_epoll_wait");        }        return seq_printf(m, "%s", symname);}
    

    2、sleep反調試

    通過sleep的反調試方式是在看雪高研班中學習到的,據說這個辦法可以過掉一些在前期檢測的native的反調試。

    思路就是需要調試的JNI函數執行前會調用JniMethodStart,那么我們只要在這里進行判斷,如果這個函數是目標函數,就sleep睡眠若干秒。在睡眠期間,我們再調試附加上即可。相關代碼如下:

    extern uint32_t JniMethodStart(Thread* self) {  JNIEnvExt* env = self->GetJniEnv();  DCHECK(env != nullptr);  uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie());  env->SetLocalRefCookie(env->GetLocalsSegmentState());  ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();  // TODO: Introduce special entrypoint for synchronized @FastNative methods?  //       Or ban synchronized @FastNative outright to avoid the extra check here?  DCHECK(!native_method->IsFastNative() || native_method->IsSynchronized());  const char* methodname=native_method->PrettyMethod().c_str();  if(ArtMethod::GetDebugMethod()!=nullptr && strlen(ArtMethod::GetDebugMethod())>0){    if(strstr(methodname,ArtMethod::GetDebugMethod())!=nullptr){                    std::ostringstream oss1;            oss1<< "fartext JniMethodStart methodname:"<" wait debug sleep 60...";            LOG(ERROR)<            sleep(60);        }  }  if (!native_method->IsFastNative()) {    // When not fast JNI we transition out of runnable.    self->TransitionFromRunnableToSuspended(kNative);  }  return saved_local_ref_cookie;}
    

    3、ROM級打樁

    這個其實沒什么難的,在關鍵的函數位置直接打印即可。我只講一下jni的打樁部分,我查資料的時候看到了兩種做法,第一種是打印jni部分是在jni函數中找比較通用的調用函數。

    例如InvokeVirtualOrInterfaceWithJValues、InvokeWithVarArgs、InvokeWithJValues,然后在里面加上打印。第二種就是像jnitracer一樣,通過偏移,找到所有的函數指針,然后再包裝處理,比如加一個外層函數調用。

    由于我c++非常爛,所以我采用最笨的方法,我寫了幾個較為通用的打印函數,jni_internal.cc中的所有我關心的jni函數,就調用各自對應的打印函數。下面貼上我的例子。

    void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,                                           const char* jniMethod,                                           jmethodID mid,                                           va_list args){     if(ArtMethod::IsJNIMethodPrint()){                 ArtMethod* method = jni::DecodeArtMethod(mid);               uint32_t shorty_len = 0;         const char* shorty =               method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);         JValue result;         ArgArray arg_array(shorty, shorty_len);         arg_array.VarArgsShowArg(soa, args,jniMethod,method->PrettyMethod().c_str());     }}void ShowJValue(const ScopedObjectAccessAlreadyRunnable& soa,                                           const char* jniMethod,                                           jmethodID mid,                                           const jvalue* args){    if(ArtMethod::IsJNIMethodPrint()){                ArtMethod* method = jni::DecodeArtMethod(mid);                uint32_t shorty_len = 0;                const char* shorty =                        method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);                JValue result;                ArgArray arg_array(shorty, shorty_len);                arg_array.JValuesShowArg(soa, args,jniMethod,method->PrettyMethod().c_str());    }} void ShowJniField(const char* jniMethodName,jfieldID field) {        if(ArtMethod::IsJNIMethodPrint()){            ArtField* f = jni::DecodeArtField(field);            std::ostringstream oss;            oss << "mikrom jni "<"\t"<            LOG(ERROR)<        }  } void ShowArgStr(const char* jniMethod,const char* str1,const char* str2){    if(ArtMethod::IsJNIMethodPrint()){            std::ostringstream oss;            if(str1!=nullptr && str2!=nullptr){                oss << "mikrom jni "<"\t"<"\t"<            }else if(str1!=nullptr ){                oss << "mikrom jni "<"\t"<            }else{                oss << "mikrom jni "<            }            LOG(ERROR)<  }}
    

    處理完判斷部分,接下來就是對于參數的打印部分了,這里我只處理了下將字符串參數打印出來,其他特殊的參數我就沒有處理了。

    void VarArgsShowArg(const ScopedObjectAccessAlreadyRunnable& soa,                                va_list ap,const char* jniMethodName,const char* methodName)      REQUIRES_SHARED(Locks::mutator_lock_) {    std::stringstream ss;        ss << "mikrom jni "<"\t"<"\t";    for (size_t i = 1; i < shorty_len_; ++i) {      switch (shorty_[i]) {        case 'Z':        case 'B':        case 'C':        case 'S':        case 'I':          ss<<"arg"<":"<"\t";          break;        case 'F':            ss<<"arg"<":"<"\t";          break;        case 'L':{            jobject obj=va_arg(ap, jobject);                      ObjPtr receiver =soa.Decode(obj);                      if(receiver==nullptr){                          LOG(ERROR)<<"mikrom  "<" receiver =nullptr";                          break;                      }                      ObjPtr cls=receiver->GetClass();                      if (cls->DescriptorEquals("Ljava/lang/String;")){                              ObjPtr argStr =soa.Decode(obj);                              ss<<"arg"<":"<<(const char*)argStr->GetValue();                      }else{                              ss<<"arg"<":"<"\t";                      }                      break;        }        case 'D':          ss<<"arg"<":"<"\t";          break;        case 'J':          ss<<"arg"<":"<"\t";          break;      }    }    LOG(ERROR) << ss.str();  }   void JValuesShowArg(const ScopedObjectAccessAlreadyRunnable& soa,                                const jvalue* args,const char * jniMethodName,const char* methodName)      REQUIRES_SHARED(Locks::mutator_lock_) {    std::stringstream ss;    ss << "mikrom jni "<"\t"<"\t";    for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {      switch (shorty_[i]) {        case 'Z':          ss<<"arg"<":"<"\t";          break;        case 'B':          ss<<"arg"<":"<"\t";          break;        case 'C':          ss<<"arg"<":"<"\t";          break;        case 'S':          ss<<"arg"<":"<"\t";          break;        case 'I':          FALLTHROUGH_INTENDED;        case 'F':          ss<<"arg"<":"<"\t";          break;        case 'L':{            jobject obj=args[args_offset].l;                      ObjPtr receiver =soa.Decode(obj);                      if(receiver==nullptr){                          LOG(ERROR)<<"mikrom  "<" receiver =nullptr";                          break;                      }                      ObjPtr cls=receiver->GetClass();                      if (cls->DescriptorEquals("Ljava/lang/String;")){                              ObjPtr argStr =soa.Decode(obj);                              ss<<"arg"<":"<<(const char*)argStr->GetValue();                      }else{                              ss<<"arg"<":"<"\t";                      }                      break;        }        case 'D':          FALLTHROUGH_INTENDED;        case 'J':          ss<<"arg"<":"<"\t";          break;      }    }    LOG(ERROR) << ss.str();  }
    

    搞定了封裝部分,就看看調用部分是怎么使用的:

    static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) {  CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);  CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);  ScopedObjectAccess soa(env);  //add  ShowJValue(soa,__FUNCTION__,mid,args);  //add end  return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetD();} static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {  va_list ap;  va_start(ap, mid);  ScopedVAArgs free_args_later(&ap);  CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);  CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);  ScopedObjectAccess soa(env);  //add  ShowVarArgs(soa,__FUNCTION__,mid,ap);  //add end  JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));  return result.GetF();}
    

    到這里就搞定了jni的打印以及參數的打印,大多數調用我都打印了,由于我實戰的少,所以可能會漏掉一些常用的打印,如果有發現什么比較有用的打印,也可以告訴我優化下。

    4、frida-gadget持久化

    這個frida持久化的問題,論壇里面也發過一遍又一遍了。我也是參考他們的文章,然后測試優化。先貼上參考文章。

    參考文章:ubuntu 20.04系統AOSP(Android 11)集成Frida(https://www.mobibrw.com/2021/28588)

    參考文章:從0開始實現一個簡易的主動調用框架(https://bbs.pediy.com/thread-270028.htm)

    不過我的加載時機和他的不太一樣,并且我擴展了可自行切換frida-gadget的版本,默認集成到系統中的版本是15.1,如果你喜歡用14.2的版本,可以自己上傳到手機進行切換,如果你想不root的情況下,也可以通過MikManager設置切換的版本。下面貼上加載的部分代碼。

    public static void loadGadget(){        String processName = ActivityThread.currentProcessName();        for(PackageItem item : mikConfigs){            if(item.packageName.equals(processName)){                try{                    boolean flag=true;                    if(item.fridaJsPath.length()<=0){                        continue;                    }                    String configPath="/data/data/"+processName+"/libfdgg.config.so";                    String configPath2="/data/data/"+processName+"/libfdgg32.config.so";                    if(item.fridaJsPath.equals("listen")||item.fridaJsPath.equals("listen_wait")){                        WriteConfig(configPath,item.fridaJsPath,item.port);                        WriteConfig(configPath2,item.fridaJsPath,item.port);                    }else{                        File file = new File(item.fridaJsPath);                        if(!file.exists()){                            file = new File( "/data/data/" + processName +"/"+file.getName());                        }                        if(!file.exists()){                            Log.e("mikrom", "initConfig package:" + processName+" frida js path:"+item.fridaJsPath+" not found");                            continue;                        }                        WriteConfig(configPath,item.fridaJsPath,item.port);                        WriteConfig(configPath2,item.fridaJsPath,item.port);                    }                    String tagPath = "/data/data/" + processName + "/libfdgg.so";//64位so的目錄                    String tagPath2 = "/data/data/" + processName + "/libfdgg32.so";//32位的so目錄                    //使用用戶自己設置的gadget路徑                    if(item.gadgetPath!=null&&item.gadgetPath.length()>0){                        mycopy(item.gadgetArm64Path, tagPath);//復制so到私有目錄                        mycopy(item.gadgetPath, tagPath2);                    }else{                        mycopy("/system/lib64/libfdgg.so", tagPath);//復制so到私有目錄                        mycopy("/system/lib/libfdgg.so", tagPath2);                    }                    int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;                    FileUtils.setPermissions(tagPath, perm, -1, -1);//將權限改為777                    FileUtils.setPermissions(tagPath2, perm, -1, -1);                    FileUtils.setPermissions(configPath, perm, -1, -1);                    FileUtils.setPermissions(configPath2, perm, -1, -1);                    File file1 = new File(tagPath);                    File file2 = new File(tagPath2);                    if (file1.exists()) {                        Log.e("mikrom", "app: " +System.getProperty("os.arch"));//判斷是64位還是32位                        if (System.getProperty("os.arch").indexOf("64") >= 0) {                            Log.e("mikrom", "initConfig package:" + processName+" frida js path:"+item.fridaJsPath+" load arch64");                            System.load(tagPath);                            file1.delete();//用完就刪否則不會更新                        } else {                            Log.e("mikrom", "initConfig package:" + processName+" frida js path:"+item.fridaJsPath+" load 32");                            System.load(tagPath2);                            file2.delete();                        }                    }                    Log.e("mikrom", "initConfig package:" + processName+" initConfig over");                }catch(Exception ex){                    Log.e("mikrom", "initConfig package:" + processName+" frida js path:"+item.fridaJsPath+" load err:"+ex.getMessage());                }                break;            }        }    }
    

    同時也是支持以三種模式加載gadget的,比如默認加載腳本,或者監聽模式,也就是可以frida連接上去,或者是監聽并阻塞等待。不過我留意到,有些自己寫的簡單app似乎使用gadget注入似乎并不成功。好像需要app對native有什么實現才行。

    5、so注入以及dobby的內置

    前面gadget的部分實際就是注入so了,但是前面的部分主要是針對gadget的注入相關處理的。我又特地單獨處理了一下對so的注入,如果我們自己寫了一個so想要單獨注入進去也是可以的。

    為了方便演示,我特地將dobby內置進去。由于dobby是一個hook框架,并且沒有注入功能,往往都是需要通過其他手段注入到應用中。所以我直接內置進來了。下面貼上相關代碼:

    public static void loadSo(String path){    String processName = ActivityThread.currentProcessName();    String fName = path.trim();    String fileName = fName.substring(fName.lastIndexOf("/")+1);    String tagPath = "/data/data/" + processName + "/"+fileName;//64位so的目錄    mycopy(path, tagPath);    int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;    FileUtils.setPermissions(tagPath, perm, -1, -1);//將權限改為777    File file = new File(tagPath);    if (file.exists()){        Log.e("mikrom", "load so:"+tagPath);        System.load(tagPath);        file.delete();//用完就刪否則不會更新    }} //注入sopublic static void loadConfigSo(){    String processName = ActivityThread.currentProcessName();    for(PackageItem item : mikConfigs){        if(!item.packageName.equals(processName))            continue;        if(item.soPath.length()<=0)            continue;         if(item.isDobby){            if(System.getProperty("os.arch").indexOf("64") >= 0) {                loadSo("/system/lib64/libdby_64.so");            }else{                loadSo("/system/lib/libdby.so");            }        }        String[] soList=item.soPath.split("");        for(String sopath :soList){            loadSo(sopath);        }    }}
    

    6、smali trace

    實際上這個功能在ROM中是有的,就是TraceExecution函數,只是需要控制打開就行了,但是必須走switch解釋器執行才能執行到便于我們修改的地方。

    所以這個就有兩個地方需要修改,第一個是判斷打印的部分,第二個是強制以解釋器執行的部分。下面看看我的修改部分:

    static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,                                  const uint32_t dex_pc)    REQUIRES_SHARED(Locks::mutator_lock_) {     if(shadow_frame.GetMethod()!=nullptr && ArtMethod::GetTraceMethod()!=nullptr&&strlen(ArtMethod::GetTraceMethod())>0){        const char* methodName=shadow_frame.GetMethod()->PrettyMethod().c_str();        if(strstr(methodName,ArtMethod::GetTraceMethod())) {            std::ostringstream oss;                    oss << android::base::StringPrintf("mikrom smaliTrace 0x%x: ", dex_pc)                            << inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\t//";                    for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {                        uint32_t raw_value = shadow_frame.GetVReg(i);                        ObjPtr ref_value = shadow_frame.GetVRegReference(i);                        oss << android::base::StringPrintf(" vreg%u=0x%08X", i, raw_value);                        if (ref_value != nullptr) {                            if (ref_value->GetClass()->IsStringClass() &&                                    !ref_value->AsString()->IsValueNull()) {                                oss << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";                            } else {                                oss << "/" << ref_value->PrettyTypeOf();                            }                        }                    }                    LOG(ERROR)<        }    }}
    

    另外就是強制以解釋器執行的部分:

    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_) {  DCHECK(!shadow_frame.GetMethod()->IsAbstract());  DCHECK(!shadow_frame.GetMethod()->IsNative());  ...  if(ArtMethod::GetTraceMethod()!=nullptr && strlen(ArtMethod::GetTraceMethod())>0){             if(strstr(method->PrettyMethod().c_str(),ArtMethod::GetTraceMethod())){                std::ostringstream oss;                oss<< "mikrom Execute strstr:"<PrettyMethod().c_str()<<"\t"<                LOG(ERROR) <                return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,false);            }  }   //add end  ...}
    

    除了這里,在LinkerCode的地方也需要修改:

    static void LinkCode(ClassLinker* class_linker,                     ArtMethod* method,                     const OatFile::OatClass* oat_class,                     uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {  ...  const void* quick_code = method->GetEntryPointFromQuickCompiledCode();  bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);  if(strlen(ArtMethod::GetTraceMethod())>0){          std::ostringstream oss;        oss<<"mikrom LinkCode method:"<PrettyMethod().c_str();        LOG(ERROR)<        enter_interpreter=true;  }  ...}
    

    變量enter_interpreter決定了是否使用解釋器執行,如果是quick快速模式執行,將無法執行到我們修改的函數。最后測試發現,函數第一次調用的時候,是走的解釋執行,成功打印出trace記錄。

    第二次調用時,又被優化到快速模式執行了。不過我們已經拿到想要的結果了,我就不繼續優化了。關于LinkCode這里的修改如果不理解的,可以看看下面的參考文章,里面有詳細的流程圖了。我就不重復了。

    參考文章:將FART和Youpk結合來做一次針對函數抽取殼的全面提升(https://bbs.pediy.com/thread-260052.htm)

    7、dex注入(未完成)

    這個功能我原本是想內置一個IO重定向,或者是內置一個Sandhook,然后直接通過配置勾選就能自動注入使用了。然后我們修改的dex直接注入進去即可使用。不過目前還沒想好怎么設計里面的一些交互部分。所以只是簡單的做了下注入的步驟。后續也不一定有時間繼續實現,先暫時擱置吧。

    11

    MikManager展示

    (圖片太大微信不支持顯示,可點擊文末閱讀原文查看)

    12

    使用說明

    操作起來非常簡單,選擇想要處理的應用,然后選擇對應功能即可。記得勾選完后,回到應用欄目點保存,如果是要使用frida腳本,或者是要選擇so,需要把文件放在sdcard中的對應應用的目錄中。如果sdcard中沒有對應目錄,可以打開一下應用會自動生成的。

    脫殼功能的保存結果在/sdcard/Android/data//dump中查看。

    查看logcat輸出日志統一搜索mikrom,jni調用的輸出的格式如下:

    2022-01-30 19:43:30.198 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni CallNonvirtualVoidMethodV    void java.lang.ClassNotFoundException.(java.lang.String, java.lang.Throwable)    arg1:android.widget.ViewStubarg2:0x6fcc6f50   2022-01-30 19:43:30.198 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetArrayLength2022-01-30 19:43:30.198 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetStringUTFChars    android.widget.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni CallNonvirtualVoidMethodV    void java.lang.ClassNotFoundException.(java.lang.String, java.lang.Throwable)    arg1:android.widget.ViewStubarg2:0x12c2a5e8   2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetStringUTFChars    android.webkit.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni NewStringUTF    android.webkit.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni CallObjectMethodV    java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String)    arg1:android.webkit.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetStringUTFChars    android.webkit.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetStringUTFChars    android.webkit.ViewStub2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni CallNonvirtualVoidMethodV    void java.lang.ClassNotFoundException.(java.lang.String, java.lang.Throwable)    arg1:android.webkit.ViewStubarg2:0x6fcc6f50   2022-01-30 19:43:30.199 4105-4105/com.kanxue.crackme E/.kanxue.crackm: mikrom jni GetArrayLength
    

    RegisterNative的輸出格式如下:

    2022-01-30 19:48:43.219 4576-4576/com.kanxue.crackme E/.kanxue.crackm: mikrom RomPrint RegisterNative name:boolean com.kanxue.crackme.MainActivity.jnicheck(java.lang.String) native_ptr:0x75a5735904 method_idx:5682022-01-30 19:48:43.219 4576-4576/com.kanxue.crackme E/.kanxue.crackm: mikrom RomPrint RegisterNative name:boolean com.kanxue.crackme.MainActivity.crypt2(java.lang.String) native_ptr:0x75a5739750 method_idx:2
    

    ArtInvoke的輸出格式如下:

    2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)---called:boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:void android.graphics.Paint.nSetFlags(long, int)---called:void android.graphics.Paint.nSetFlags(long, int)2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:int java.lang.String.hashCode()---called:int java.lang.String.hashCode()2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:void android.graphics.Paint.nSetTextLocalesByMinikinLocaleListId(long, int)---called:void android.graphics.Paint.nSetTextLocalesByMinikinLocaleListId(long, int)2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:void android.graphics.Paint.nSetDither(long, boolean)---called:void android.graphics.Paint.nSetDither(long, boolean)2022-01-30 19:50:16.247 4686-4686/com.kanxue.crackme E/.kanxue.crackm: mikrom [PerformCall] caller:void android.graphics.Paint.nSetDither(long, boolean)---called:void android.graphics.Paint.nSetDither(long, boolean)
    

    smali trace日志的輸出結果如下:

    2022-01-30 20:06:11.003 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x0: invoke-static {}, void com.mik.miktest.CommonTools.Test() // method@7    // vreg0=0x00000000 vreg1=0x00000000 vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.003 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x3: const-string v0, "MainActivity" // string@30    // vreg0=0x00000000 vreg1=0x00000000 vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.008 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x5: const-string v1, "TraceTest" // string@33    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000000 vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.008 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x7: invoke-static {v0, v1}, int android.util.Log.i(java.lang.String, java.lang.String) // method@0    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x12C896A8/java.lang.String "TraceTest" vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.008 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0xa: add-int v1, v5, v6    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x12C896A8/java.lang.String "TraceTest" vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.008 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0xc: new-instance v2, java.lang.StringBuilder // type@TypeIndex[16]    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x00000000 vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0xe: invoke-direct {v2}, void java.lang.StringBuilder.() // method@17    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x11: const-string v3, "TraceTest," // string@34    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x00000000 vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x13: invoke-virtual {v2, v3}, java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) // method@19    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x16: move-result-object v2    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x17: invoke-virtual {v2, v1}, java.lang.StringBuilder java.lang.StringBuilder.append(int) // method@18    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x1a: move-result-object v2    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x1b: invoke-virtual {v2}, java.lang.String java.lang.StringBuilder.toString() // method@20    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x1e: move-result-object v2    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C896C8/java.lang.StringBuilder vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x1f: invoke-static {v0, v2}, int android.util.Log.i(java.lang.String, java.lang.String) // method@0    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C89738/java.lang.String "TraceTest,16" vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x22: mul-int v2, v5, v6    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x12C89738/java.lang.String "TraceTest,16" vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x24: new-instance v4, java.lang.StringBuilder // type@TypeIndex[16]    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x00000000 vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x26: invoke-direct {v4}, void java.lang.StringBuilder.() // method@17    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x29: invoke-virtual {v4, v3}, java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) // method@19    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x2c: move-result-object v3    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89708/java.lang.String "TraceTest," vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x2d: invoke-virtual {v3, v2}, java.lang.StringBuilder java.lang.StringBuilder.append(int) // method@18    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89758/java.lang.StringBuilder vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.010 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x30: move-result-object v3    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89758/java.lang.StringBuilder vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.011 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x31: invoke-virtual {v3}, java.lang.String java.lang.StringBuilder.toString() // method@20    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89758/java.lang.StringBuilder vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.011 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x34: move-result-object v3    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C89758/java.lang.StringBuilder vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.011 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x35: invoke-static {v0, v3}, int android.util.Log.i(java.lang.String, java.lang.String) // method@0    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C897A8/java.lang.String "TraceTest,15" vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.011 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x38: add-int v0, v1, v2    // vreg0=0x12C89670/java.lang.String "MainActivity" vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C897A8/java.lang.String "TraceTest,15" vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F2022-01-30 20:06:11.011 5157-5157/com.mik.miktest E/com.mik.miktes: mikrom smaliTrace 0x3a: return v0    // vreg0=0x0000001F vreg1=0x00000010 vreg2=0x0000000F vreg3=0x12C897A8/java.lang.String "TraceTest,15" vreg4=0x12C89758/java.lang.StringBuilder vreg5=0x00000001 vreg6=0x0000000F
    

    so注入如果勾選自動注入dobby后,就會注入系統中自帶的,如果你想注入自己編譯的dobby,也可以在so注入里面導入dobby,注入順序會默認按照導入順序。

    13

    完結撒花

    整理一遍之后,感覺好像做了很多,又感覺好像也沒搞點啥。也碰到很多問題讓我一遍又一遍的編譯測試。不過總算大致實現了當時的想法。后續應該不會有啥升級了。調轉方向研究點其他東西了。

    如果以后內核功底深一些了,可能會回頭擴展把。重要的事情再提一次,不知道修改到了art部分的哪個點,導致了調試超級慢,如果有大佬知道啥原因的,麻煩指導一下。


    函數調用string
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    字符串是 JavaScript 中的重要數據類型
    本文是一篇關于《惡意代碼分析實戰》書中的實驗Lab12-02樣本的分析報告,這個樣本涉及到進程注入和XOR加密技術,樣本難度適中。
    上一篇文章介紹了xorstr的原理和最小化驗證概念的代碼,這篇文章來看下這種已經被廣泛應用于各惡意樣本以及安全組件中的技術如何還原,如果還沒看上篇建議先看下了解其實現后再看本篇文章。
    前置知識分析Transformer接口及其實現類。transform()傳入對象,進行反射調用。構造調用鏈調用鏈構造原則:找調用關系要找不同名的方法,如果找到同名,再通過find usages得到的還是一樣的結果。找到InvokerTransformer類中的transform(),右鍵,點 Find Usages,找函數調用關系,最好找不同名的方法,調用了transform()。因為transform()調用transform()不能換到別的方法里,沒有意義。如果有一個類的readObject()調用了get(),那我們就可能找到了調用鏈。最終選擇TransformedMap這個類,因為TransformedMap類中有好幾處都調用了transform()。
    Thinkphp是一個國內輕量級的開發框架,采用php+apache,在更新迭代中,thinkphp也經常爆出各種漏洞,thinkphp一般有thinkphp2、thinkphp3、thinkphp5、thinkphp6版本,前兩個版本已經停止更新
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    該漏洞發生的位置是在驅動文件Win32k.sys中的xxxHandleMenuMessage函數,產生的原因是沒有對該函數中調用的xxxMNFindWindowFromPoint函數的返回值進行合法性驗證,直接將其作為參數傳遞給后面的xxxSendMessage函數調用,從而造成了提權漏洞。
    因為程序肯定是病毒,我就不上傳殺毒網去查殺了。正常我們在分析一個未知惡意程序的時候,流程都是要先上傳殺毒網看看。 用PEID進行查殼,顯示未加殼,程序采用Delphi語言開發。
    ldap連接地址為:ldap://192.168.11.16 用戶為hack 密碼為test123.. 在域外我們需要指定ip地址,在域內我們只需要指定域名也行,例如測試環境的redteam,也就是ldap://redteam,這里就說明我們寫代碼的時候就需要考慮是在域內還是在域外。 在c#進行ldap連接的時候需要引入DirectoryServices.dll,這個是系統自帶的,自行尋找。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类