Android逆向小工具开发:快速查找应用XML数据第二步:分析查找XML数据文件!

经过上一篇的博客学习中,我们已经熟悉了如何对应用XML数据文件进行获取,当然获取只是第一步,今天我们就接着学习,如何分析获取到的XML数据文件。

只是拿到了XML文件并不是目的,我们真正的目的是分析到手的文件数据,找出我们想要的东西!那么既然是分析XML文件,聪明的小伙伴们肯定立刻想到了那几种解析XML文件的方式,广为熟知的有:pull方式,dom方式。既然有现成的解析框架,那解析几个XML文件还不是易如反掌~不过这里我可就要给你泼泼冷水了,起先博主也是想着使用现成的XML解析方法来获取XML文件数据,可是当我去尝试解析XML数据的时候,才发现事情远远没有这么简单!

首先先给大家随便找一个XML文件看一下,此文件来自于应用宝应用程序目录下,文件名为:cn.jiguang.wakesdk.preferences.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="device_registered_appkey">1ecde9eb73693e4a792360bd</string>
    <string name="key_password">5854816379630256128</string>
    <long name="app_running_last_collect_time" value="1545010664631" />
    <long name="last_collection_location" value="1545013663444" />
    <boolean name="lbs_report_now" value="false" />
    <long name="last_report_location" value="1545010685225" />
    <long name="key_device_info_time" value="1544858232812" />
    <long name="whitelist_wakeup_time" value="1545014264" />
    <long name="services_launched_time" value="1545014263" />
    <long name="jpush_awake_app_pk" value="1545010667" />
    <long name="key_uid" value="14633523015" />
    <long name="last_check_applist_time" value="1545011096880" />
    <string name="device_registration_id">100d85590963424f050</string>
    <string name="devcie_id_generated">eccc95fb8aa98f8262ad4f8b0df5fe83</string>
</map>

能够看出这个XML的结构还是挺复杂的,并不是那种在网络传输之间的XML文件结构那么严谨,想想也是合理,毕竟这个XML文件只是用来持久化某些数据,并不进行网络传输,所以随意一点还是有情可原的。但是这样的随意导致我们在进行XML文件解析的时候比较棘手,这里并不是说无法使用pull或者dom进行XML文件解析,而是在使用pull解析的时候,代码的逻辑上你要写的比较复杂,工作量会比较巨大,毕竟你要考虑到很多情况,解析到的标签元素,标签值,值类型等等。这样势必就需要我们要对XML文件结构有比较熟悉的了解,但是请你想想,我们开发这个小工具为了什么啊?!不就是为了自己能够轻松一点,不想查看这么多的XML文件数据嘛!如果还需要过来看看XML文件结构,我宁愿选择狗带!

于是博主在尝试了一下pull解析后就果断放弃了,既然没法用现成的XML文件解析框架,那么干脆自己写一个解析XML文件数据的算法好了!你可能会觉得我擦这么高大上好厉害~等我给你说完算法你就知道多么简单了!

其实具体的算法很简单,逐行读取XML文件数据,这样我们得到的一行String字符串的值就为一个标签内容,然后针对这一行的String进行字符串查找,发现特定的String存在,那么就把这一行的String给记录下来,遍历所有的XML文件中所有的标签行,遍历结束这样我们就获取到所有存在特定String的标签字符串,然后把记录通过TextView展示出来,这样就完成了整个查找的流程!很简单的算法,主要利用了文件的按行读取,以及字符串匹配,并没有什么特别厉害的代码存在!

既然如此,那么我们就赶快开始写代码吧!首先还是RootCmd类中,写一个方法用来按行读取文件:readText()

public List<String> readText(String filePath1) {
        List<String> txtStr=new ArrayList<>();
        Log.i("++++++",filePath1);
        try {
            File file1 = new File(filePath1);

            int count = 0;
            if (file1.isFile() && file1.exists()) {
                InputStreamReader isr = new InputStreamReader(new FileInputStream(file1));
                BufferedReader br = new BufferedReader(isr);
                String lineTxt = null;
                while ((lineTxt = br.readLine()) != null) {
                    if (!"".equals(lineTxt)) {
                        String reds = lineTxt.split("\\+")[0];  //java 正则表达式
                        txtStr.add(count, reds);
                        count++;
                    }
                }
                isr.close();
                br.close();
            }else {
                Log.i("++++++++","文件不存在!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return txtStr;
    }

这个并没什么好说的,简单的文件读取操作,把文件的每一行读取出来存放进一个List内,最后返回该List。

下面就是具体的查找算法实现了,还是RootCmd类,写一个查找方法:startFind()

public String startFind(String resultFind,TextView textView){
        if (resultFind.length()>0){
            resultFind="";
        }

        boolean isHaveFileName=false;

        List<String> names=getFolderName();
        if (names.size()>0){
            String name="";
            for (String str:names){
                if (str.contains(textView.getText())){
                    name=str;
                    break;
                }
            }

            File file = new File(getSDPath()+"/xmlTarget/"+name);
            File[] subFile = file.listFiles();
            for (int iFileLength = 0; iFileLength < subFile.length; iFileLength++) {
                // 判断是否为文件夹
                if (!subFile[iFileLength].isDirectory()) {
                    String filePath = subFile[iFileLength].getAbsolutePath();

                    List<String> strList=readText(filePath);
                    for (String str:strList){
                        String allString=str.toLowerCase();
                        String findString=MainActivity.findStr.toLowerCase();

                        if (allString.contains(findString)){
                            if (!isHaveFileName){
                                resultFind=resultFind+"文件名称:"+subFile[iFileLength].getName()+"\r\n";
                                isHaveFileName=true;
                            }
                            resultFind=resultFind+str+"\r\n";
                        }
                    }

                    isHaveFileName=false;
                }
            }

            if (resultFind.length()==0){
                resultFind="没有找到相关字段信息!";
            }
            showToast("查找完成!");
        }else {
            showToast("无可供查找的XML文件!");
        }

        return resultFind;
    }

这里就是主要的查找算法了。首先我们可以看到对传入的搜索结果字符串进行了长度判断,如果长度大于0,那么就赋值为null,这里主要的目的是为了多次查询的时候保证结果都是本次查询的内容。下面有一个布尔值isHaveFileName,这个布尔值的主要作用就是用于判断是否已经添加了XML文件的名字。继续向下看,一个循环遍历,这里是为了找到用户选择查找的应用XML文件保存下的文件夹名称,下面就是遍历该文件夹下所有的XML文件,外层的循环是遍历XML文件,内层循环是遍历一个XML文件的内容,主要的查找逻辑在内层循环内。我们看内层循环,首先就是前两句代码都调用了String.toLowerCase()方法,熟悉该方法的小伙伴就会明白,这个方法的作用就是把一条字符串改为小写,那么这里的作用就很明白,主要是为了不区分大小写查找!String中并没有不区分大小写匹配两个字符串,所有这里就采用了把待匹配的两条字符串先转换成小写,然后再比较的策略。可以看到后面调用了contains()方法进行两个字符串的匹配,如果发现查找的字符串中存在指定查找关键字符,那么就把它加入查询结果字符串中,同时还有一个布尔值判断,即之前提及的isHaveFileName,通过这个布尔值程序决定是否要把该XML文件名字加进查询结果。遍历完所有的XML数据文件后,就接下来就判断查询结果字符串的长度,如果为0说明没有查询到指定的字符串信息,就为查询结果字符串赋值“没有找到相关字段信息!”,方法的最后返回查询结果字符串。

一大串看下来我们可以发现其实并没有什么特别复杂的算法,简单的文件读取和字符串比较就行轻松满足我们的查找需求!下面就运行一下看看结果:

这里我们还是选择了查找应用宝下的XML数据,首先指定查找的应用是应用宝,然后在输入框内输入查询的关键字符为“Imei”,点击查询按钮,我们就看到在应用宝下的XML数据中包含关键字“imei”的内容全都被查找出来,注意这里查找并不区分大小写。展示的查找结果很清晰,首先是DENGTA_META.xml文件中存在四条含有指定查询内容“imei”的标签,标签名和值都一目了然,下面还有一个xml文件也存在“imei”相关内容!

是不是感觉这个小工具很方便呢!有了它从此查看应用的XML数据轻轻松松,想知道应用在xml中持久化了什么数据你就查什么数据,简单方便,快捷省事~

关于这个小工具还有一点别的东西没有说,关于已经获取的应用XML文件处理。这里博主采用的是读取SD卡路径下的/xmlTarget文件夹,因为获取到的应用XML数据文件都会在这个文件夹下面相对应的文件夹内,看看/xmlTarget文件夹下是否存在文件夹,如果不存在,说明没有已经获取的应用XML文件,如果有文件夹,那么就把该文件夹的名字存放入一个list中。具体操作代码为:getFolderName():

private List<String> getFolderName(){
        File file = new File(getSDPath()+"/xmlTarget");
        File[] subFile = file.listFiles();

        List<String> strs=new ArrayList<>();
        if (subFile!=null&&subFile.length>0){
            for (File f:subFile){
                strs.add(f.getName());
            }
        }
        return strs;
    }

为什么要获取文件夹的名字,那是因为该名字包含重要的信息,即应用的名称和ID。例如这里博主这里获取的是应用宝:

可以看到文件夹的名字为“应用宝ID137”,这个137其实是通过 getInstalledPackages()方法获取手机内所有的应用信息的list中,应用宝所在的ist下标值!当你进行选择App的时候,如果存在已经获取的XML文件,那么会弹出Dialog提醒用户进行相关的选择,方法为:toastDialog():

public void toastDialog(final ImageView imageView, final TextView textView){
        final List<String> strList=getFolderName();
        if (strList.size()==0){
            appShowDialog(true,imageView,textView,getAppInfo());
        }else {
            AlertDialog.Builder dialog=new AlertDialog.Builder(context);
            dialog.setTitle("发现已经存在的应用XML文件");
            dialog.setMessage("你想再重新获取新的应用XML文件吗?");
            dialog.setCancelable(false);
            dialog.setPositiveButton("是的", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    appShowDialog(true,imageView,textView,getAppInfo());
                    dialog.dismiss();
                }
            });
            dialog.setNegativeButton("不需要", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    List<AppModel> appModels=new ArrayList<>();
                    for (String str:strList){
                        String []s=str.split("ID");
                        appModels.add(getAppInfo().get(Integer.valueOf(s[1])));
                    }
                    appShowDialog(false,imageView,textView,appModels);
                }
            });
            dialog.show();
        }
    }

 可以很清楚的看到逻辑处理为:首先判断存放文件夹名字的List大小是否为0,为0说明没有已经获取的XML文件,那么就直接弹出所有App列表让用户选择,如果不为0那么就弹出提示Dialog提醒用户存在已经获取的XML文件,让用户选择是否重新获取。如果点击不需要,那么就弹出已经获取的XML文件应用列表让用户选择,这样就保证了不会频繁获取XML文件问题,让用户更快的进行查找工作。

好了,本次逆向小工具的开发到此结束,关于本片博客你有什么不明白的地方请评论留言,我会及时为你解答。后续我会继续为大家分享逆向有关的知识,希望大家好好学习呀~

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值