Apktoolソースコード解析——第一篇

21964 ワード

有名なapktoolはandroidの逆界で最も一般的なツールで、このプロジェクトの元の住所はここhttp://code.google.com/p/android-apktool/ですが、天朝のグーグルではアクセスできないことを知っていますので、githubに直接行きます.https://github.com/brutall/brut.apktool.
brut.apktoolパスは主なコードです.もちろんbrutもあります.apktool.smaliはsmaliを逆コンパイルするディレクトリで、目視とbrutがあります.j.common,brut.j.dir,brut.j.utilsはjavaに使用されるクラスです.
brut.apktoolパスの下のapktool-cliはまだ何をしているのか分かりませんが、とにかく次のapktool-libは私たちが最も理解しなければならないことです.
 
AndroidディレクトリはXmlPullParserを用いて実装されたandroidにおけるxml専用の復号ツールパッケージである.
brut/androlibディレクトリはまさに私たちの研究のテーマです.
com/mindprod/ledatastreamディレクトリはleデータサポートライブラリです.
org/xmlpull/mxp1_serializerディレクトリはXmlPullParserのXml解析ライブラリです.
Apktool源码解析——第一篇 androlibディレクトリ一覧.
まずApkDecoderというクラスを見てみましょう.
    public ApkDecoder(File apkFile, Androlib androlib) {

        mAndrolib = androlib;

        setApkFile(apkFile);

    }



    public void setApkFile(File apkFile) {

        mApkFile = new ExtFile(apkFile);

        mResTable = null;

    }
mResTable       ,        ,              ,      ,     。
   public void setOutDir(File outDir) throws AndrolibException {

        mOutDir = outDir;

    }



    public void setApi(int api) {

        mApi = api;

    }

この二つの方法は名前を見ると言うまでもなく、ここではくどくない.多く何行のコードを見て発見して、このコードの品質はかなり高くて、まったくあまり良質ではありませんて、何よりAxmlPrinter 2のコードはずっときれいで、ほほほ!
次は逆コンパイルの核心的な方法です.客観的に見てください.
 public void decode() throws AndrolibException, IOException,DirectoryException {

        File outDir = getOutDir();



        if (!mForceDelete && outDir.exists()) {//                     

            throw new OutDirExistsException();

        }



        if (!mApkFile.isFile() || !mApkFile.canRead()) {//  apk                  

            throw new InFileNotFoundException();

        }



        try {

            OS.rmdir(outDir);//   

        } catch (BrutException ex) {

            throw new AndrolibException(ex);

        }

        outDir.mkdirs();



        LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + mApkFile.getName());



        if (hasResources()) {//    mApkFile.getDir().contraincontainsFile("resources.arsc")

            setTargetSdkVersion();

            setAnalysisMode(mAnalysisMode, true);//        true,    mResTable   

            setCompressionMode();//mCompressResources  ,res.arsc  zip           ZipEntry.DEFLATED  



            switch (mDecodeResources) {//     FULL

                case DECODE_RESOURCES_NONE://          res  

                    mAndrolib.decodeResourcesRaw(mApkFile, outDir);

                    break;

                case DECODE_RESOURCES_FULL://  androilib.decodearesources  ,       AndrolibResources.decode()  

                    mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable());

                    break;

            }

        } else {//      

            // if there's no resources.asrc, decode the manifest without looking

            // up attribute references

            if (hasManifest()) {//    res.asrc      manifest       

                switch (mDecodeResources) {

                    case DECODE_RESOURCES_NONE:

                        mAndrolib.decodeManifestRaw(mApkFile, outDir);

                        break;

                    case DECODE_RESOURCES_FULL:// mAndRes.decodeManifest(resTable, apkFile, outDir)

                        mAndrolib.decodeManifestFull(mApkFile, outDir,

                                getResTable());

                        break;

                }

            }

        }



        if (hasSources()) {//      

            switch (mDecodeSources) {

                case DECODE_SOURCES_NONE://    classes.dex

                    mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex");

                    break;

                case DECODE_SOURCES_SMALI://    smali,SmaliDecoder.decode()

                    mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mDebug, mDebugLinePrefix, mBakDeb, mApi);

                    break;

                case DECODE_SOURCES_JAVA://   jarnew AndrolibJava().decode()

                    mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);

                    break;

            }

        }



        if (hasMultipleSources()) {//     dex

            // foreach unknown dex file in root, lets disassemble it

            Set<String> files = mApkFile.getDirectory().getFiles(true);

            for (String file : files) {//     dex

                if (file.endsWith(".dex")) {

                    if (! file.equalsIgnoreCase("classes.dex")) {

                        switch(mDecodeSources) {

                            case DECODE_SOURCES_NONE:

                                mAndrolib.decodeSourcesRaw(mApkFile, outDir, file);

                                break;

                            case DECODE_SOURCES_SMALI:

                                mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mDebug, mDebugLinePrefix, mBakDeb, mApi);

                                break;

                            case DECODE_SOURCES_JAVA:

                                mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);

                                break;

                        }

                    }

                }

            }

        }



        mAndrolib.decodeRawFiles(mApkFile, outDir);//  libs assets

        mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable);//      ,brutall       ,     https://github.com/iBotPeaches/Apktool

        mAndrolib.writeOriginalFiles(mApkFile, outDir);//      ,  AndroidManifest.xml     

        writeMetaFile();//      apktool.yml

    }
          
   public void setAnalysisMode(boolean mode, boolean pass) throws AndrolibException{

        mAnalysisMode = mode;



        // only set mResTable, once it exists

        if (pass) {

            if (mResTable == null) {

                mResTable = getResTable();

            }

            mResTable.setAnalysisMode(mode);

        }

    }



    public void setCompressionMode() throws AndrolibException, IOException {

        // read the resources.arsc checking for STORED vs DEFLATE

        // this will determine whether we compress on rebuild or not.

        ZipExtFile zef = new ZipExtFile(mApkFile.getAbsolutePath());

        ZipArchiveEntry ze = zef.getEntry("resources.arsc");

        if (ze != null) {

            int compression = ze.getMethod();

            mCompressResources = (compression == ZipEntry.DEFLATED);

        }

        zef.close();

    }



    public void setTargetSdkVersion() throws AndrolibException, IOException {

        if (mResTable == null) {

            mResTable = mAndrolib.getResTable(mApkFile);

        }



        Map<String, String> sdkInfo = mResTable.getSdkInfo();

        if (sdkInfo.get("targetSdkVersion") != null) {

            mApi = Integer.parseInt(sdkInfo.get("targetSdkVersion"));

        }

    }
public ResTable getResTable() throws AndrolibException {

        if (mResTable == null) {

            boolean hasResources = hasResources();

            boolean hasManifest = hasManifest();

            if (! (hasManifest || hasResources)) {//  apk      AndroidManifest.xml resource.arsc,        apk  

                throw new AndrolibException(

                        "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file");

            }

            AndrolibResources.sKeepBroken = mKeepBrokenResources;

            mResTable = mAndrolib.getResTable(mApkFile, hasResources);

        }

        return mResTable;

    }

 
End!
   :
decode() writeMetaFile() , 。
   private void writeMetaFile() throws AndrolibException {

        Map<String, Object> meta = new LinkedHashMap<String, Object>();

        meta.put("version", Androlib.getVersion());//    ?

        meta.put("apkFileName", mApkFile.getName());//apk  



        if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) {//                 manifest  

            meta.put("isFrameworkApk", mAndrolib.isFrameworkApk(getResTable()));

            putUsesFramework(meta);

            putSdkInfo(meta);

            putPackageInfo(meta);

            putVersionInfo(meta);

            putCompressionInfo(meta);

        }

        putUnknownInfo(meta);//  Unknown      
     mAndrolib.writeMetaFile(mOutDir, meta); // meta    apktool.yml       
  }
  private void putUsesFramework(Map<String, Object> meta) throws AndrolibException {

        Set<ResPackage> pkgs = getResTable().listFramePackages();

        if (pkgs.isEmpty()) {

            return;

        }



        Integer[] ids = new Integer[pkgs.size()];

        int i = 0;

        for (ResPackage pkg : pkgs) {

            ids[i++] = pkg.getId();

        }

        Arrays.sort(ids);



        Map<String, Object> uses = new LinkedHashMap<String, Object>();

        uses.put("ids", ids);//ids



        if (mAndrolib.apkOptions.frameworkTag != null) {//tag

            uses.put("tag", mAndrolib.apkOptions.frameworkTag);

        }



        meta.put("usesFramework", uses);

    }



    private void putSdkInfo(Map<String, Object> meta) throws AndrolibException {

        Map<String, String> info = getResTable().getSdkInfo();//ResTable    sdk  ?

        if (info.size() > 0) {

            meta.put("sdkInfo", info);

        }

    }



    private void putPackageInfo(Map<String, Object> meta) throws AndrolibException {

        String renamed = getResTable().getPackageRenamed();//     package  ?

        String original = getResTable().getPackageOriginal();

        int id = getResTable().getPackageId();



        HashMap<String, String> packages = new HashMap<String, String>();



        // only put rename-manifest-package into apktool.yml, if the change will be required

        if (!renamed.equalsIgnoreCase(original)) {

            packages.put("rename-manifest-package", renamed);

        }

        packages.put("forced-package-id", String.valueOf(id));

        meta.put("packageInfo", packages);

    }



    private void putVersionInfo(Map<String, Object> meta) throws AndrolibException {

        Map<String, String> info = getResTable().getVersionInfo();//      

        if (info.size() > 0) {

            meta.put("versionInfo", info);

        }

    }



    private void putUnknownInfo(Map<String, Object> meta) throws AndrolibException {

        Map<String,String> info = mAndrolib.mResUnknownFiles.getUnknownFiles();//Androlib   Unknwn  

        if (info.size() > 0) {

            meta.put("unknownFiles", info);

        }

    }



    private void putCompressionInfo(Map<String, Object> meta) throws AndrolibException {

        meta.put("compressionType", getCompressionType());//    

    }