美文网首页
一种可能可行的修改源码dump dex的方案

一种可能可行的修改源码dump dex的方案

作者: 修符道人 | 来源:发表于2019-12-20 10:44 被阅读0次

本来想通过https://blog.csdn.net/qq1084283172/article/details/78003184这篇文章来dump出dex,后来失败了。

可以知道Dalvik模式的设备,可以从DvmDex.cpp的dvmDexFileOpenPartial方法将dex dump出来。但是有一个问题:系统那么多apk都会走这个方法,如何确定执行当前方法的应用是我想要的应用?
我想到了一种办法:在dvmDexFileOpenPartial获取调用当前方法的进程id和包名,但是发现有很大困难,而且
调用当前方法的进程也并不是apk对应的进程,所以此方法不可行。

后来监听日志(adb logcat > logcat.txt会监听到所有日志)发现“Running dexopt on 包名",紧接着就会执行dvmDexFileOpenPartial方法。源码搜索"Running dexopt on“(参照我的另外一篇博客https://blog.51cto.com/4259297/1955867),找到日志输出的文件位置:PackageManagerService.java

image.png image.png

所以我诞生一种思路:

在PackageManagerService.java的"Running dexopt on"将包名记录起来,然后在dvmDexFileOpenPartial获取包名,如果是想要的包名就将dex写入到文件。

这种方案就涉及到PackageManagerService.java和DvmDex.cpp之间的一个通讯的问题了,我尝试了2种方案,都遇到了困难。

  • 通过JNI
    既然涉及到C和Java文件的交互,自然会想到JNI技术。我的设想是这样的:在PackageManagerService.java使用一个静态常量,记住包名。然后DvmDex.cpp调用PackageManagerService.java的一个方法获取包名。
    但是PackageManagerService.java属于“frameworks/base/services/java”模块,有一个对应的Android.mk文件。而DvmDex.cpp位于“dalvik/vm”模块,有一个对应的Android.mk文件。使用过Eclipse应用JNI的都知道,通过JNI关联的C和Java属于同一个工程,有着相同的Android.mk文件。
    所以上面的PackageManagerService.java和DvmDex.cpp之间的通讯并非简单的JNI技术即可解决。

  • 文件共享
    选择对“/data/local/tmp”目录创建一个文件来记录包名,为什么使用这个目录?https://blog.csdn.net/weixin_30787531/article/details/95132119

PackageManagerService.java源码(4.4)修改

源码目录:frameworks/base/services/java/com/android/server/pm
最小编译目录:frameworks/base/services/java
修改目的:记住安装的应用的包名,为后面dump dex作准备。

 ...
 Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
            boolean test = write("/data/local/tmp/test.txt",pkg.applicationInfo.packageName);
            Log.e(TAG,"write result:" + test);  

            //CZAdd
              /**if("com.chinalwb.are.demo".equals(pkg.applicationInfo.packageName)){
                        killerAppName = pkg.applicationInfo.packageName;
                  }else{
                        killerAppName = null;
                  }*/   

                        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
                        ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg));
                        pkg.mDidDexOpt = true;
                        performed = true;
 ...

 /*
     *写文件
     */
    private boolean write(String fileName, String fileContent) {
        boolean result = false;
        File f = new File(fileName);
        File parentFile = f.getParentFile();
        if(!parentFile.exists()){
            parentFile.mkdirs();
        }
        if(!f.exists()){
            try {
                f.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            FileOutputStream fs = new FileOutputStream(f,false);
            byte[] b = fileContent.getBytes();
            fs.write(b);
            fs.flush();
            fs.close();
            result = true;
        } catch (Exception e) {
           Log.e(TAG,"write failure:" + e.getMessage());
        }
        return result;
    }

调试发现framework属于systemServer进程,对于“/data/local/tmp/”目录,没有写的权限。受高人指点,发现“/data/system"目录可以写文件。

boolean test = write("/data/system/tmp/test.txt",pkg.applicationInfo.packageName);
Log.e(TAG,"write result:" + test);  

DvmDex.cpp源码(4.4)修改

源码目录:dalvik/vm
最小编译目录:dalvik/vm
修改目的:读dump的配置文件(保存用户想要dump的包名),读当前安装的应用的包名,然后判断两个包名相同就去dump dex。

int findTargetPName(){
    ALOGE("try to find the target pname");
    FILE *fp = NULL;
    fp = fopen("/data/system/tmp/want_dump_pname.txt", "r");

    if (fp==NULL){
        // fclose(fp);      fp==NULL不需要close。
        fp = NULL;
        ALOGE("find the target pname failure");
        return -1;
    }else{
         // 读取脱壳apk的内存dex文件dump后的文件名称字符串
        fgets(dexDumpName, 128, fp);
       
        if( dexDumpName[strlen(dexDumpName) -1 ] == '\a'){
            dexDumpName[strlen(dexDumpName)-1] = 0;           //有回车符才要减1  
        }else{
            dexDumpName[strlen(dexDumpName)] = 0;  
        }        
        

        fclose(fp);
        fp = NULL;

        ALOGE("find the target pname:%s,len=%d",dexDumpName,strlen(dexDumpName));
        return 0;
    }
} 

int readCurrentInstalledPName(){
    ALOGE("try to read current installed pname");
    FILE *fp = NULL;
    fp = fopen("/data/system/tmp/test.txt", "r");

    if (fp==NULL){
        // fclose(fp);      fp==NULL不需要close。
        fp = NULL;
        ALOGE("read current installed pname failure");
        return -1;
    }else{
         // 读取当前安装的APK的包名
        fgets(currentInstalledPName, 128, fp);
       
        if( currentInstalledPName[strlen(currentInstalledPName) -1 ] == '\a'){
            currentInstalledPName[strlen(currentInstalledPName)-1] = 0;          //有回车符才要减1  
        }else{
            currentInstalledPName[strlen(currentInstalledPName)] = 0;  
        }        

        fclose(fp);
        fp = NULL;

        ALOGE("read current installed pname:%s,len=%d",currentInstalledPName,strlen(currentInstalledPName));
        return 0;
    }
} 

int dumpDexFile(const void* addr, int len){
  FILE *fp = NULL;
  
  fp =  fopen("/data/system/tmp/dump.dex", "wb+");
   if (fp==NULL){
         ALOGE("open dex output file failure");
          return -1;
  }else{
        fwrite(addr,sizeof(char),len,fp);
        fclose(fp);
         fp = NULL;
          ALOGE("dump dex success,dex file path:%s","/data/system/tmp/dump.dex");
         return 0;
   }
}


int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
    //CZAdd-----------------------------------------------------------------start
    // 读取当前安装的应用的包名。
    int findTargetPNameResult = findTargetPName();
    if(findTargetPNameResult >= 0){
        int findInstalledPNameResult = readCurrentInstalledPName();
        if(findInstalledPNameResult >= 0){
            if (strcmp(dexDumpName, currentInstalledPName) == 0){
                ALOGE("the target pname is same as the current installed pname,prepare to dump");
                dumpDexFile(addr,len);
            }else{
                ALOGE("the target pname is diffrent with the current installed pname,findTargetPNameResult:%s,findInstalledPNameResult:%s",dexDumpName,currentInstalledPName);
            }
        }
    }
    //CZAdd-----------------------------------------------------------------end
    ...
}

findTargetPName():自定义方法,获取用户想要dump的应用的包名
readCurrentInstalledPName():自定义方法,读取当前最近安装的应用的包名,就是上面修改PackageManagerService.java保存的包名。
dumpDexFile():自定义方法,dump出相应的应用的dex文件。
dvmDexFileOpenPartial():系统源码的方法

开始dump

系统源码修改烧到系统后,就可以开始按照下面的步骤来dump了。

1.编辑并推送用户dump配置文件

编辑文件

want_dump_pname.txt


image.png

注意:不要带空格或者回车符。

推送文件

adb push 你电脑绝对路径/want_dump_pname.txt   /data/system/tmp

如果没有tmp路径,则自己创建,如果创建没有权限,chmod 777 相应的目录或者文件。

2.安装要dump的apk

在framework创建的test.txt文件,由于权限的问题,在dalvik里无法读取。有两种解决办法:
1.在test.txt文件创建后,可以chomd 777这个文件,然后再安装apk.
2.framework在创建test.txt的时候, 用shell脚本创建(Runtime.execute),给其足够的权限。(没有验证)

3.pull dex文件

adb pull /data/system/tmp/dump.dex

可能会提示” remote Permission denied“,chmod 777这个文件所在的目录即可解决。

4.打开dex文件

用jadx-gui打开dex文件


image.png

遗憾的是仍然是360的壳。

不过不要灰心,只要找到真正的内部dex加载的源码位置,就能dump成功。

相关文章

网友评论

      本文标题:一种可能可行的修改源码dump dex的方案

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