内容纲要

Apktool

待完成

目前进度:对decode部分进行源码解析

源码

目录简介

img

  1. apktool主目录
  2. main函数入口
  3. 反编译和回编业务代码
  4. 常量声明,现在主要是异常
  5. 压缩文件处理
  6. 工具类、cmd
  7. apktool在各个系统中的执行脚本

源码分析

image-20220722180915898

反编译——cmdDecode

前面是一堆查看cmdline选项设置参数值

ApkDecoder decoder = new ApkDecoder();
decoder.decode();

ApkDecoder.decode()

if (hasResources()) {
    switch (mDecodeResources) {
        case DECODE_RESOURCES_FULL:
            if (hasManifest()) {
                mAndrolib.decodeManifestWithResources(mApkFile, outDir, getResTable());
            }
            mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable());
            break;
    }
}

ApkDecoder.hasResources()

//查看是否包含resources.arsc资源文件
public boolean hasResources() throws AndrolibException {
        try {
            return mApkFile.getDirectory().containsFile("resources.arsc");
        } catch (DirectoryException ex) {
            throw new AndrolibException(ex);
        }
    }

解析Manifest文件

ResFileDecoder.decodeManifest(Directory inDir, String inFileName,Directory outDir, String outFileName)

try (
                InputStream in = inDir.getFileInput(inFileName);
                OutputStream out = outDir.getFileOutput(outFileName)
        ) {
            ((XmlPullStreamDecoder) mDecoders.getDecoder("xml")).decodeManifest(in, out);

11

ResStreamDecoder有三个实现

image-20220722190310539

这里为XmlPullStreamDecoder

XmlPullStreamDecoder

//XmlPullStreamDecoder.decodeManifest调用了XmlPullStreamDecoder.decode
public void decodeManifest(InputStream in, OutputStream out)
        throws AndrolibException {
        decode(in, out);
}

decode主要包括了两个操作:parseManifest和parseAttr

if ("manifest".equalsIgnoreCase(pp.getName())) {
    try {
        hidePackageInfo = parseManifest(pp);
    } catch (AndrolibException ignored) {}
} else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
    try {
        hideSdkInfo = parseAttr(pp);
        if (hideSdkInfo) {
            return;
        }
    } catch (AndrolibException ignored) {}
}

总结

ASRCDecoder对resource.asrc进行解析,对各个区域的属性进行偏移量解析,一步步动调的,看的有点晕,不好说= =

image-20220726132659419

然后利用asrc文件对manifest进行解析,将解析完的arsc赋给resTable后,进行parseManifest和parseAttr

//parseManifest
                private boolean parseManifest(XmlPullParser pp)
                        throws AndrolibException {
                    String attr_name;

                    // read <manifest> for package:
                    for (int i = 0; i < pp.getAttributeCount(); i++) {
                        attr_name = pp.getAttributeName(i);

                        if (attr_name.equalsIgnoreCase(("package"))) {
                            resTable.setPackageRenamed(pp.getAttributeValue(i));
                        } else if (attr_name.equalsIgnoreCase("versionCode")) {
                            resTable.setVersionCode(pp.getAttributeValue(i));
                        } else if (attr_name.equalsIgnoreCase("versionName")) {
                            resTable.setVersionName(pp.getAttributeValue(i));
                        }
                    }
                    return true;
                }
//获得versionCode、versionName、package
//parseAttr
                private boolean parseAttr(XmlPullParser pp)
                        throws AndrolibException {
                    for (int i = 0; i < pp.getAttributeCount(); i++) {
                        final String a_ns = "http://schemas.android.com/apk/res/android";
                        String ns = pp.getAttributeNamespace(i);

                        if (a_ns.equalsIgnoreCase(ns)) {
                            String name = pp.getAttributeName(i);
                            String value = pp.getAttributeValue(i);
                            if (name != null && value != null) {
                                if (name.equalsIgnoreCase("minSdkVersion")
                                        || name.equalsIgnoreCase("targetSdkVersion")
                                        || name.equalsIgnoreCase("maxSdkVersion")
                                        || name.equalsIgnoreCase("compileSdkVersion")) {
                                    resTable.addSdkInfo(name, value);
                                } else {
                                    resTable.clearSdkInfo();
                                    return false; 
                                }
                            }
                        } else {
                            resTable.clearSdkInfo();

                            if (i >= pp.getAttributeCount()) {
                                return false;
                            }
                        }
                    }

                    return ! resTable.getAnalysisMode();
                }
//获得了minSdkVersion、targetSdkVersion、maxSdkVersion……

image-20220726140946866

对xml进行解析,在flush后manifest文件中有了内容,我没调出来,但感觉应该是这一段解析

switch (eventType) {
            case XmlPullParser.START_DOCUMENT:
                //use Boolean.TRUE to make it standalone
                Boolean standalone = (Boolean) pp.getProperty(PROPERTY_XMLDECL_STANDALONE);
                startDocument(pp.getInputEncoding(), standalone);
                break;

            case XmlPullParser.END_DOCUMENT:
                endDocument();
                break;

            case XmlPullParser.START_TAG:
                writeStartTag(pp);
                break;

            case XmlPullParser.END_TAG:
                endTag(pp.getNamespace (), pp.getName ());
                break;

            case XmlPullParser.IGNORABLE_WHITESPACE:
                //comment it to remove ignorable whtespaces from XML infoset
                String s = pp.getText ();
                ignorableWhitespace(s);
                break;

            case XmlPullParser.TEXT:
                if(pp.getDepth() > 0)  {
                    text(pp.getText ());
                } else {
                    ignorableWhitespace(pp.getText ());
                }
                break;

            case XmlPullParser.ENTITY_REF:
                entityRef (pp.getName ());
                break;

            case XmlPullParser.CDSECT:
                cdsect( pp.getText () );
                break;

            case XmlPullParser.PROCESSING_INSTRUCTION:
                processingInstruction( pp.getText ());
                break;

            case XmlPullParser.COMMENT:
                comment (pp.getText ());
                break;

            case XmlPullParser.DOCDECL:
                docdecl (pp.getText ());
                break;
        }

image-20220726135425095

解析res文件并在路径下生成

解析class.dex

            if (hasSources()) {
                switch (mDecodeSources) {
                    case DECODE_SOURCES_NONE:
                        mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex");
                        break;
                    case DECODE_SOURCES_SMALI:
                    case DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES:
                        mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mBakDeb, mApiLevel);
                        break;
                }
            }
    public boolean hasSources() throws AndrolibException {
        try {
            return mApkFile.getDirectory().containsFile("classes.dex");
        } catch (DirectoryException ex) {
            throw new AndrolibException(ex);
        }
    }

decodeSourcesSmali

            File smaliDir;
            if (filename.equalsIgnoreCase("classes.dex")) {
                smaliDir = new File(outDir, SMALI_DIRNAME);
            } else {
                smaliDir = new File(outDir, SMALI_DIRNAME + "_" + filename.substring(0, filename.indexOf(".")));
            }
            OS.rmdir(smaliDir);
            smaliDir.mkdirs();
            LOGGER.info("Baksmaling " + filename + "...");
            DexFile dexFile = SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel);
            int minSdkVersion = dexFile.getOpcodes().api;
            if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) {
                mMinSdkVersion = minSdkVersion;
            }

查看decode

一系列dex检查后,调用baksmali进行返汇编

image-20220726140748117