Android逆向小工具开发:快速查找应用XML数据第一步:获取应用的XML数据文件

小伙伴们大家好,又是一段时间过去了,在上面几篇的博客中我们主要学习了关于Xposed的进阶操作,还不知道大家学习的怎么样,那么在接下来的学习中,Xposed学习将会告一段落,接下来的学习正如标题所示:我将会带领大家来学习制作一款自己的逆向小工具,帮助大家在逆向分析中更好的掌握主动权,快速地进行逆向开发~

对于一个逆向工程师而言,什么才是最重要的东西?大家心里很快就会出现答案,没错!那就是逆向工具!一款好的逆向工具对逆向工程师而言那就犹如神兵利器,在逆向世界中叱咤风云的存在!要说鼎鼎大名的逆向工具,比如IDA,比如Apktool,还有Jadx,正是有了这些功能强大的逆向工具,逆向工程师才能在逆向分析中得心应手,如履平地。世界级的逆向工具博主自认是开发不出来,但是一些小儿灵巧的方便小工具,对于我们大家来说可为一试,通过一些小工具的开发,一方面能巩固加强我们代码的编写能力,另一方面开发完成后的小工具可以帮助辅助我们的逆向工作,可谓是一举两得!

那么相信你看到博文的题目就已经明白到我们要开发的小工具的功能,没错,那就是快速检索应用的XML数据,找出我们指定搜索内容的XML标签或者字段!关于XML数据,相比大家都很明白,在data/data/com.xxxx.xxxx/目录下的/shared_prefs文件夹中包含了这个应用所进行持久化处理的xml文件,这些xml文件正常情况下会使用SharedPreferences进行存取值操作,我们在应用层使用SharedPreferences进行数据的持久化处理,这些数据就是被放入了/shared_prefs文件夹下的xml文件内,在读取这些数值的时候说白了也是xml文件的解析操作,读取指定的字段值。

一般应用在XML文件中持久化处理的都会是一些比较重要的数据,比如说手机的一些基本参数数据,或者应用的一些配置数据等等。这些数据在我们逆向工作的时候也会是重要关注的对象之一,毕竟能够放进XML中持久化保存下来的数据都不是什么“泛泛之辈”!当需要我们去查看XML中保存下来的数据的时候,我们通过RE文件管理器进入data/data目录下进行查看,我相信那么多的XML文件,文件中那么多的字段值绝对会让你头大,想找到自己想要的东西无异于大海捞针。。。博主在翻看应用宝的XML数据简直生不如死,想想自己身为一名Android开发和逆向工程师,怎么能这么痛苦的折磨自己?!既然机器最擅长的事情就是查找的匹配,这个痛苦的工作还是交给机器好了!

于是博主立即着手进行相关的开发,具体开发的项目已经发布到博主个人的GitHub主页上,地址为:快速查找应用XML数据的小工具。大家可以去Github上下载项目源代码的压缩包,解压在Android Studio上运行即可,同时也可以配合本博客进行开发学习~好了,前言就到此为止,下面就让我们赶快进入今天的学习把!

首先开发的第一步还是进行需求分析。我们通过分析需求得到下面几条具体需求:

1:能够获取到某个应用的XML数据文件,即目录/data/data/com.xxx.xxx/shared_prefs

2:输入某一字符串,能够在获取的应用XML数据中查找出带有该字符串的标签或者文本

3:展示查询结果包含XML文件名,以及该XML文件内包含查询字段的标签文本

4:获取的应用XML数据文件可以重复利用,可以选择重新获取或者使用之前已经获取的XML文件

具体的需求分析就大致分为以上四条,我们大致分析一下开发流程就是:第一步获取应用的XML数据文件,第二步遍历XML数据文件进行查找,第三步展示查询之后的结果。一个查找的小工具,流程非常的清晰简单,那么我们就先来进行第一步:如何获取应用的XML数据文件?

我们这里已经知道的是应用的XML数据文件保存路径位于/data/data/...路径下,这个路径只允许本身的应用访问,其他的应用是无权访问的,那么这里我们如果想拿到别的应用的XML数据文件该怎么办?其实这里也很好解决,看过博主之前的博文的小伙伴大概就已经猜到了方法,没错,那就是利用adb命令啦!哎,不对,准确的来说,是利用adb命令和Liunx命令!

这里估计就蒙圈了,怎么还会有Liunx命令?!咱们这里又不是Linux虚拟机环境!这里可以提一下Liunx和Android的关系,简单的一句话:Android是基于Linux内核的操作系统,所以在安卓内你可以使用Liunx命令来操作~对这段历史感兴趣的小伙伴可以自行搜索相关资料,这不再做过多的叙述。首先先说一下具体的分工,adb命令负责进行权限的分配,Liunx命令负责进行操作文件。你可能会说,adb命令中也有很多文件的操作命令,为什么不使用adb命令来操作文件呢?这里就来解释一下,adb确实有相关的文件操作命令,但是在这里它并不能胜任我们的文件操作需求,因为这里我们需要操作的是整个文件夹内的所有XML文件,而不是单一的某一个文件!adb其实更多的被用于和电脑设备连接起来后的操作,进行设备之间文件的复制和删除都是非常的方便,这里从它的中文名字就可以看出来。但是在这里我们并不是进行两个设备之间的文件处理,而是手机内部中的文件操作!显然,Linux更能胜任这份工作!

下面我们就开始编写代码,开始尝试获取某一应用的XML数据文件:首先还是adb命令和Liunx命令的翻译者和执行者:

public class RootCmd {

    public static AppModel modelChange;
    private List appModels=new ArrayList<>();
    Context context;

    public RootCmd(Context mContext){
        context=mContext;
    }

    //翻译并执行相应的adb命令
    public static boolean exusecmd(String command) {
        Process process = null;
        DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();
            Log.e("updateFile", "======000==writeSuccess======");
            process.waitFor();
        } catch (Exception e) {
            Log.e("updateFile", "======111=writeError======" + e.toString());
            return false;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (process != null) {
                    process.destroy();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

}

 写一个方法moveFileToSD(String,String):

    //移动文件
    public void moveFileToSD(String filePath, String cpFilePath) {
        exusecmd("mount -o rw,remount "+cpFilePath);
        exusecmd("chmod 777 "+cpFilePath);
        exusecmd("cp -r "+filePath+" "+cpFilePath);
    }

我们通过moveFileToSD()方法来把某一应用的XML数据文件给复制到手机的SD卡内。上面两句adb命令想必大家很熟悉了,修改目的路径的权限为可读写,给目的路径777权限,然后执行Liunx的文件夹复制命令:cp -r xxxx xxxx;表示遍历该文件夹内所有的文件,并把它们全部复制到目的路径下。

命令方法已经写好,那么下面就要开始进行相关选择App操作然后进行路径组装,因为每一个应用的XML数据文件存放路径都不同,/data/data/com.xxx.xxx/shared_prefs路径中/data/data/后面需要填写该应用的包名,我们需要获取他们的包名才能组装读取路径!

写一个App信息Model:AppModel

package com.example.xmlfileparsing;

import android.graphics.drawable.Drawable;

/**
 * Created by 王将 on 2018/10/29.
 */

public class AppModel {
    private int id;
    private Drawable icon;
    private String appName;
    private String packageName;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setIcon(Drawable icon) {
        this.icon = icon;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public Drawable getIcon() {
        return icon;
    }

    public String getPackageName() {
        return packageName;
    }

    public String getAppName() {
        return appName;
    }
}

 在RootCmd类中写一个方法用来获取手机内所有App的相关信息:getAppInfo()

public List getAppInfo(){
        if (appModels.size()==0){
            PackageManager pm = context.getPackageManager();
            List list = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);

            int i=0;
            for (PackageInfo packageInfo:list) {
                AppModel appModel = new AppModel();
                appModel.setId(i);
                appModel.setAppName(packageInfo.applicationInfo.loadLabel(context.getPackageManager()).toString());
                appModel.setPackageName(packageInfo.packageName);
                appModel.setIcon(packageInfo.applicationInfo.loadIcon(context.getPackageManager()));
                appModels.add(appModel);
                i++;
            }
        }

        return appModels;
    }

下面我们需要弹出一个Dialog用来展示所获取的所有App信息,然后让用户选择需要获取哪一个App的XML数据文件:

public void appShowDialog(final boolean isNew, final ImageView imageView, final TextView textView, final List appModels){
        final Dialog dialog=new Dialog(context, R.style.app_style_dialog);

        View view= LayoutInflater.from(context).inflate(R.layout.show_package,null,false);
        dialog.setContentView(view);

        LinearLayout linearLayout=(LinearLayout) view.findViewById(R.id.scroll_appInfo);
        TextView title=(TextView) view.findViewById(R.id.title_show);

        title.setText("请选择你需要解析的应用:");
        for (final AppModel appModel:appModels){
            final View app= LayoutInflater.from(context).inflate(R.layout.appinfo_layout,linearLayout,false);

            ImageView icon=(ImageView) app.findViewById(R.id.icon_app);
            TextView name=(TextView) app.findViewById(R.id.name_app);
            TextView packageName=(TextView) app.findViewById(R.id.package_app);

            icon.setImageDrawable(appModel.getIcon());
            name.setText(appModel.getAppName());
            packageName.setText(appModel.getPackageName());

            app.setTag(appModel);
            app.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AppModel model=(AppModel) v.getTag();
                        if (isNew){
                            String dataFilePath="/data/data/"+model.getPackageName()+"/shared_prefs";
                            moveFileToSD(dataFilePath,getSDPath()+"/xmlTransit",model.getAppName()+"ID"+model.getId());
                        }

                        imageView.setVisibility(View.VISIBLE);
                        imageView.setImageDrawable(model.getIcon());
                        textView.setText(model.getAppName());
                        dialog.dismiss();
                    }
            });
            linearLayout.addView(app);
        }


        Window window = dialog.getWindow();
        window.setGravity(Gravity.BOTTOM);
        window.getDecorView().setPadding(0, 0, 0, 0);

        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
        lp.height = WindowManager.LayoutParams.MATCH_PARENT;
        window.setAttributes(lp);

        dialog.show();
    }

在点击的监听事件中,我们会拿到用户选择的App信息Model,组装它的XML数据文件路径和目的路径,调用命令执行方法moveFileToSD()开始进行XML文件的复制。这样我们基本下来就完成了第一步,XML数据文件的获取!

这里还需要特别的分析一下:细心分析过项目代码的小伙伴就会发现不明白的地方了,代码中具体获取XML数据文件的方法并没有上面说的那么简单!当然了,这里博主是挑主要代码进行大致的讲解,其实在代码的开发中,博主选择了两步走的获取战略!

什么是两步走战略?准确的来说就是,第一步使用命令复制XML文件到一个取名为“中转站”的文件夹内,这里中转站的路径为:/storage/emulated/0/xmlTransit/,第二步从中转站内使用代码复制中转站内所有的XML文件到最终的目的路径为:/storage/emulated/0/xmlTarget/xxxx/,xxx为具体的应用名称加上它在getInstalledPackages()方法获取到的List的下标值!复制成功后执行文件夹删除命令,删除中转站/storage/emulated/0/xmlTransit/。

为什么要这么处理?一步到位不好么?一步到位当然很好,简单方便快捷,但是没有办法通过命令去实现!博主起初目标是直接复制到目的路径/storage/emulated/0/xmlTarget/xxxx/下,注意,其中/xmlTarget/xxxx/是不存在的路径。博主在尝试了好多文件复制命令后,始终没有办法将XML文件直接复制到目的路径下,只能是复制到SD卡的一级目录内。所以博主想了一个办法,进行了两步走战略,先把文件复制到SD内的中转站路径下,然后执行文件操作代码,进行文件的读写操作,把中转站内的文件复制到最终的目的路径下,之后删除文件中转站即可!

两步走战略的第二步:方法writeTarget()

private void writeTarget(String path,String name){
        File file = new File(path);
        File[] subFile = file.listFiles();
        if (subFile!=null&&subFile.length>0){
            for (int iFileLength = 0; iFileLength < subFile.length; iFileLength++) {
                if (!subFile[iFileLength].isDirectory()) {
                    String filePath = subFile[iFileLength].getAbsolutePath();

                    List strList=readText(filePath);
                    writeTxtToFile(strList,getSDPath()+"/xmlTarget/"+name+"/"+subFile[iFileLength].getName());
                }
            }
            deleFile();

            showToast("XML文件获取成功!");
        }else {
            showToast("没有找到该应用的XML文件!");
        }

    }

 遍历中转站内所有的文件,读取文件的内容存放进一个List内,然后把文件内容写入最终目的路径。具体的文件读写代码不再贴出分析。后面执行deleFile()方法,把中转站文件夹删除。这里你可能会问,为什么不使用代码删除文件?那是因为此时执行代码删除是删除不掉的,博主猜测原因可能是此时文件夹所处的状态还是输入输出流状态,想关闭又找不到地方,于是万能的命令又来了,直接赋予管理员权限,执行文件夹强制删除命令,至此风平浪静~

好啦,第一部分的获取应用XML数据到此讲解完毕,我们主要学习到的是Liunx命令的相关处理和操作,以及针对顽固问题的解决思路!死磕命令不是最终之道,配合代码实现具体功能才是王道!

下面的博客中我们接着进行小工具的开发之旅第二部分:处理XML数据文件以及展示~大家要跟上脚步抓紧时间学习呀!

本篇博文到此结束,如有引用请标明出处,谢谢合作!

你可能感兴趣的:(Android逆向小工具开发:快速查找应用XML数据第一步:获取应用的XML数据文件)