安卓xml乱码/加密转换:abx2xml和xml2abx使用及源码介绍

背景:

上一篇文章
android系统中data下的xml乱码无法查看问题剖析及解决方法

发布后,想要寻找一个可以直接把二进制xml和普通xml进行相互转换的,当时还写了相关的方案,但是当时没有找到现成的开源工具,后来经过相关粉丝提醒找到了和方法2一模一样思路的开源工具那就是abx2xml和xml2abx。

转化命令使用介绍abx2xml和xml2abx

转换前
属于二进制乱码

使用命令转换,命令:

bash 复制代码
abx2xml ./system/users/0/appwidgets.xml ./system/users/0/appwidgets-read.xml

转化后的./system/users/0/appwidgets-read.xml变成我们常见的普通xml

同样普通xml也可以转成二进制xml,转化命令

bash 复制代码
xml2abx  ./system/users/0/appwidgets-read.xml ./system/users/0/appwidgets-binary.xml

然后看看./system/users/0/appwidgets-binary.xml是不是变得二进制不可读了

abx2xml和xml2abx命令解析

abx2xml和xml2abx其实代码都是一样的,本质就是个sh脚本而已,运行的

bash 复制代码
#!/system/bin/sh
export CLASSPATH=/system/framework/abx.jar
exec app_process /system/bin com.android.commands.abx.Abx "$0" "$@"

可以看出本质上都是调用到了Abx这个java类,参数就是一个$0,这个代表命令本身,比如使用使用是:

abx2xml input.xml output.xml

那么这里的 0 就是 a b x 2 x m l ,后面 i n p u t . x m l o u t p u t . x m l 就是 0就是abx2xml,后面input.xml output.xml就是 0就是abx2xml,后面input.xmloutput.xml就是@

源码剖析Abx类

java 复制代码
public class Abx {
    private static final String USAGE = "" +
            "usage: abx2xml [-i] input [output]\n" +
            "usage: xml2abx [-i] input [output]\n\n" +
            "Converts between human-readable XML and Android Binary XML.\n\n" +
            "When invoked with the '-i' argument, the output of a successful conversion\n" +
            "will overwrite the original input file. Input can be '-' to use stdin, and\n" +
            "output can be '-' to use stdout.\n";

    private static InputStream openInput(String arg) throws IOException {
        if ("-".equals(arg)) {
            return System.in;
        } else {
            return new FileInputStream(arg);
        }
    }

    private static OutputStream openOutput(String arg) throws IOException {
        if ("-".equals(arg)) {
            return System.out;
        } else {
            return new FileOutputStream(arg);
        }
    }

    private static void mainInternal(String[] args) {
        if (args.length < 2) {
            throw new IllegalArgumentException("Missing arguments");
        }

        final XmlPullParser in;
        final XmlSerializer out;
        if (args[0].endsWith("abx2xml")) {//这里根据传递近来参数看看是否要二进制xml转普通还是逆过来转
            in = Xml.newBinaryPullParser(); //二进制转普通,那么输入就是BinaryPullParser,输出就是普通的
            out = Xml.newSerializer();
        } else if (args[0].endsWith("xml2abx")) {
            in = Xml.newPullParser();//普通转二进制,那么输入就是普通的,输出就是newBinarySerializer
            out = Xml.newBinarySerializer();
        } else {
            throw new IllegalArgumentException("Unsupported conversion");
        }

        final boolean inPlace = "-i".equals(args[1]);
        final String inputArg = inPlace ? args[2] : args[1];
        final String outputArg = inPlace ? args[2] + ".tmp" : args[2];

        try (InputStream is = openInput(inputArg);
                OutputStream os = openOutput(outputArg)) {
            in.setInput(is, StandardCharsets.UTF_8.name());//输入设置对应的流InputStream
            out.setOutput(os, StandardCharsets.UTF_8.name());//输出设置对应的流OutputStream
            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
            Xml.copy(in, out);//再调用 Xml.copy,重点就是在这
            out.flush();
        } catch (Exception e) {
            // Clean up failed output before throwing
            if (inPlace) {
                new File(outputArg).delete();
            }
            throw new IllegalStateException(e);
        }

        // Successful in-place conversion of a file requires a rename
        if (inPlace) {
            if (!new File(outputArg).renameTo(new File(inputArg))) {
                throw new IllegalStateException("Failed rename");
            }
        }
    }

    public static void main(String[] args) {
        try {
            mainInternal(args);
            System.exit(0);
        } catch (Exception e) {
            System.err.println(e.toString());
            System.err.println();
            System.err.println(USAGE);
            System.exit(1);
        }
    }
}

上面代码核心部分有注释,其实整体算比较简单,核心方法就剩下一个Xml.copy方法

XML的相关copy方法如下:

cpp 复制代码
 /**
     * Copy the first XML document into the second document.
     * <p>
     * Implemented by reading all events from the given {@link XmlPullParser}
     * and writing them directly to the given {@link XmlSerializer}. This can be
     * useful for transparently converting between underlying wire protocols.
     *
     * @hide
     */
    public static void copy(@NonNull XmlPullParser in, @NonNull XmlSerializer out)
            throws XmlPullParserException, IOException {
        // Some parsers may have already consumed the event that starts the
        // document, so we manually emit that event here for consistency
        if (in.getEventType() == XmlPullParser.START_DOCUMENT) {
            out.startDocument(in.getInputEncoding(), true);
        }

        while (true) {
            final int token = in.nextToken();//不断循环xml的内容节点等,简单说就是in读出什么就往out中写什么
            switch (token) {
                case XmlPullParser.START_DOCUMENT:
                    out.startDocument(in.getInputEncoding(), true);
                    break;
                case XmlPullParser.END_DOCUMENT:
                    out.endDocument();
                    return;
                case XmlPullParser.START_TAG:
                    out.startTag(normalizeNamespace(in.getNamespace()), in.getName());
                    for (int i = 0; i < in.getAttributeCount(); i++) {
                        out.attribute(normalizeNamespace(in.getAttributeNamespace(i)),
                                in.getAttributeName(i), in.getAttributeValue(i));
                    }
                    break;
                case XmlPullParser.END_TAG:
                    out.endTag(normalizeNamespace(in.getNamespace()), in.getName());
                    break;
                case XmlPullParser.TEXT:
                    out.text(in.getText());
                    break;
                case XmlPullParser.CDSECT:
                    out.cdsect(in.getText());
                    break;
                case XmlPullParser.ENTITY_REF:
                    out.entityRef(in.getName());
                    break;
                case XmlPullParser.IGNORABLE_WHITESPACE:
                    out.ignorableWhitespace(in.getText());
                    break;
                case XmlPullParser.PROCESSING_INSTRUCTION:
                    out.processingInstruction(in.getText());
                    break;
                case XmlPullParser.COMMENT:
                    out.comment(in.getText());
                    break;
                case XmlPullParser.DOCDECL:
                    out.docdecl(in.getText());
                    break;
                default:
                    throw new IllegalStateException("Unknown token " + token);
            }
        }
    }

更多framework详细代码和资料参考如下链接

hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg

其他课程七件套专题:

点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw

视频试看:
https://www.bilibili.com/video/BV1wc41117L4/

更多framework假威风耗:androidframework007

相关推荐
阿巴斯甜6 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95277 小时前
Andorid Google 登录接入文档
android
黄林晴9 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab21 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android