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解析ライブラリです.
androlibディレクトリ一覧.
まずApkDecoderというクラスを見てみましょう.
この二つの方法は名前を見ると言うまでもなく、ここではくどくない.多く何行のコードを見て発見して、このコードの品質はかなり高くて、まったくあまり良質ではありませんて、何よりAxmlPrinter 2のコードはずっときれいで、ほほほ!
次は逆コンパイルの核心的な方法です.客観的に見てください.
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解析ライブラリです.
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());//
}