请问pkg.apk下载这个密码是多少?

上一次我们反编译了手Q,并遇到了tool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之鱼不如授之以渔,只有知道一些基本原理,才能让我们以后能自行解决更多问题。那么,你知道么?从我们在 Studio中,点击run,到app运行在手机上,之间究竟发生了什么,代码和资源是怎么变成APK的,而APK又是怎么安装上去,并能执行的呢。build-simple-overview我们或许都能说出来像上图这样一个简单的过程:Android工程编译打包为APK,签名后通过 push到设备或者模拟器上安装。但是再深入就蒙了。希望看完下文,大家能对整个过程有一定了解。源码:资源部分为Android 4.4,后半段改为了6.0_r2APK是Android Package的缩写,实际上APK就是一个压缩包,使用zip解压软件直接就能对其进行解压,解压后会发现就是由各种资源文件、一或多个文件(odex过的apk除外)、Android.xml、ources.arsc以及其他一些文件组成的。我们先看看从Android在线文档找来的APK文件构建流程图,如下(方形为对象,圆形为动作)。apk-build从该图来看,整个打包过程可以分为以下七个步骤:第1步:打包资源文件,生成R.和编译后的资源。aapt的可执行文件位于sdk的build-tools下,而源码则在frameworks\base\tools\aapt目录下。打包过程主要是调用了Resources.cpp下的buildResources路径为Main.cpp下的handleCommand(Bundle* bundle)到Command.cpp下的doPackage(Bundle* bundle),经过一些初始化和检查后调用了我们要深入看的buildResources(Bundle* bundle, &AaptAssets&& assets)。因为代码都比较长,这里不贴了,主要说一下大概的逻辑和流程。检查AndroidManifest.xmlsp&AaptGroup& androidManifest = assets-&getFiles.valueFor(String8(&AndroidManifest.xml&)); if (androidManifestFile == NULL) { (, &: No AndroidManifest.xml file found.\n&); return UNKNOWN_ERROR; } status_t
= parsePackage(bundle, assets, androidManifestFile); if (err != NO_ERROR) { } NOISY(printf(&Creating resources for package %s\n&, assets-&getPackage.string));主要做一些检查并使用parsePackage初始化并设置一些attribute,比如。使用table.addIncludedResources(bundle, assets)添加被引用资源包,比如系统的那些android:命名空间下的资源。收集资源文件收集资源文件: void collect_files(const sp&AaptDir&& dir, KeyedVector&String8, sp&ResourceTypeSet& &* resources);处理overlay(重叠包,如果指定的重叠包有和当前编译包重名的资源,则使用重叠包的):// apply the overlay files to the base set if (!applyFileOverlay(bundle, assets, &drawables, &drawable&) || !applyFileOverlay(bundle, assets, &layouts, &layout&) || !applyFileOverlay(bundle, assets, &anims, &anim&) || !applyFileOverlay(bundle, assets, &animators, &animator&) || !applyFileOverlay(bundle, assets, &interpolators, &interpolator&) || !applyFileOverlay(bundle, assets, &transitions, &transition&) || !applyFileOverlay(bundle, assets, &xmls, &xml&) || !applyFileOverlay(bundle, assets, &raws, &raw&) || !applyFileOverlay(bundle, assets, &colors, &color&) || !applyFileOverlay(bundle, assets, &menus, &menu&) || !applyFileOverlay(bundle, assets, &mipmaps, &mipmap&)) { return UNKNOWN_ERROR; }将收集到的资源文件加到资源表(对res目录下的各个资源子目录进行处理,函数为makeFileResourcesstatic status_t makeFileResources(Bundle* bundle, const sp&AaptAssets&& assets, ResourceTable* table, const sp&ResourceTypeSet&& set, * resType);编译values资源并添加到资源表在上一步添加过程中,其实并没有对values资源进行处理,因为values比较特殊,需要经过编译之后,才能添加到资源表中。编译会调用的进行:status_t compileResourceFile(Bundle* bundle, const sp&AaptAssets&& assets, const sp&AaptFile&& in,
ResTable_config& defParams, const bool overwrite, ResourceTable* outTable);给bag资源分配id在继续编译其他资源之前,我们需要先给bag资源(attrs,比如orientation这种属性的取值范围定义的子元素)分配id,因为其他资源可能对它们有引用。status_t ResourceTable::assignResourceI最后我们终于可以编译xml文件了,因为我们已经为它准备好了一切可能引用到的东西(value, drawable等)。程序会对layouts, anims, animators等逐一调用ResourceTable.cpp的:status_t compileXmlFile(const sp&AaptAssets&& assets, const sp&AaptFile&& target, ResourceTable* table, int options);进行编译,内部流程又可以分为:解析xml文件,赋予属性名称资源id,解析属性值,扁平化为二进制文件(调用flatten(Bundle* bundle, const sp&AaptFile&& dest)编译AndroidManifest.xml文件该步骤其实也可以归为上一步,但由于manifest文件的特殊,所以姑且抽了出来。// 拿到AndroidManifest.xml文件 const sp&AaptFile& manifestFile(androidManifestFile-&getFiles.valueAt(0)); String8 manifestPath(manifestFile-&getPrintableSource); // 生成最终编译后的manifest文件 // 清空原来的数据,重新解析 manifestFile-&clearD sp&XMLNode& manifestTree = XMLNode::parse(manifestFile); if (manifestTree == NULL) { return UNKNOWN_ERROR; } // 马杀鸡manifest(从bundle读取必要的tag并写到manifestTree, // 处理package name重载,把各种相对路径的名字改为绝对路径) err = massageManifest(bundle, manifestTree); if (err & NO_ERROR) { } // 编译manifest xml文件 err = compileXmlFile(assets, manifestTree, manifestFile, &table); if (err & NO_ERROR) { }生成最终资源表生成我们解压后看到的那个if (table.hasResources) { // 生成资源符号表 sp&AaptSymbols& symbols = assets-&getSymbolsFor(String8(&R&)); err = table.addSymbols(symbols); if (err & NO_ERROR) { } // 生成资源索引表resources.arsc resFile = getResourceFile(assets); if (resFile == NULL) { fprintf(stderr, &Error: unable to generate entry for resource data\n&); return UNKNOWN_ERROR; } err = table.flatten(bundle, resFile); if (err & NO_ERROR) { } // 可能有一些同学用到过,这就是public.xml里定义的固定资源id if (bundle-&getPublicOutputFile) { FILE* fp = fopen(bundle-&getPublicOutputFile, &w+&); if (fp == NULL) { fprintf(stderr, &ERROR: Unable to open public definitions output file %s: %s\n&, (const char*)bundle-&getPublicOutputFile, strerror(errno)); return UNKNOWN_ERROR; } if (bundle-&getVerbose) { printf(& Writing public definitions to %s.\n&, bundle-&getPublicOutputFile); } table.writePublicDefinitions(String16(assets-&getPackage), fp); fclose(fp); } // 把资源读回来到最终资源表 finalResTable.add(resFile-&getData, resFile-&getSize, NULL); // 生成完毕 }而具体的resources.arsc生成则在// 一个400行的函数,具体的生成实现在这里 status_t ResourceTable::flatten(Bundle* bundle, const sp&AaptFile&& dest);写入顺序为 索引资源表头部(ResourceTypes:ResTable_header) -& 资源项的值字符串资源池 -& Package数据块。验证AndroidManifest.xml文件验证manifest各个属性对应值的合法性,即value中能出现的字符,完成后资源正式处理完毕,添加到AaptAssetsif (resFile != NULL) { // These resources are now considered to be a part of the included resources, for others to reference. err = assets-&addIncludedResources(resFile); if (err & NO_ERROR) { fprintf(stderr, &ERROR: Unable to parse generated resources, aborting.\n&); } }生成R.java终于,我们已经读取并处理好了需要的一切,是时候开始写文件了,于是又回到了Command.cpp的// 更新那些需要被作为Java符号的符号 assets-&applyJavaS if (SourcePos::hasErrors) { } // 如果需要则在这里生成.d依赖文件 ... // 写R.java常量 if (!assets-&havePrivateSymbols) { if (bundle-&getCustomPackage == NULL) { // 将R.java文件写到恰当的目录下 // 如 gen/com/foo/app/R.java err = writeResourceSymbols(bundle, assets, assets-&getPackage, true); } else { const String8 customPkg(bundle-&getCustomPackage); err = writeResourceSymbols(bundle, assets, customPkg, true); } if (err & 0) { } // 如果有library文件,则需要把R.java也写到那些libraries的对应class目录下 // 如 gen/com/foo/app/lib/R.java if (bundle-&getExtraPackages != NULL) { // 冒号分割 String8 libs(bundle-&getExtraPackages); char* packageString = strtok(libs.lockBuffer(libs.length), &:&); while (packageString != NULL) { err = writeResourceSymbols(bundle, assets, String8(packageString), true); if (err & 0) { } packageString = strtok(NULL, &:&); } libs.unlockB } } else { // 有不对外开放的私有符号 err = writeResourceSymbols(bundle, assets, assets-&getPackage, false); if (err & 0) { } err = writeResourceSymbols(bundle, assets, assets-&getSymbolsPrivatePackage, true); if (err & 0) { } }生成ProGuard文件err = writeProguardFile(bundle, assets); if (err & 0) { }而writeProguardFile(bundle, assets)则会调用writeProguardForAndroidManifest(&keep, assets);writeProguardForLayouts(&keep, assets);将规则更新到ProguardKeepSet中,然后打开proguard文件进行写入(proguard文件由-G命令指定)。生成apk又是一个洋洋洒洒150多行的函数,浓缩一下看看删减版Package.cppstatus_t writeAPK(Bundle* bundle, &Assets&& assets, const String8& outputFile) { ... 计时,初始化状态变量 // so,apk就是作为一个zip包创建的 ZipFile* zip = NULL; /* * 准备Zip文件,如果文件存在,则根据bundle设置的&update&或者&force&进行对应处理 */ FileType fileType = getFileType(outputFile.string); ... // 准备完毕,new ZipFile并打开准备开始写 status_ zip = new ZipF status = zip-&open(outputFile.string, ZipFile::kOpenReadWrite | ZipFile::kOpenCreate); if (status != NO_ERROR) { fprintf(stderr, &ERROR: unable to open '%s' as Zip file for writing\n&, outputFile.string); } // 先把assets丢进去(即第10步我们准备好的东西,包括了assets目录, res目录下values以外的子目录 - 因为values已经被编译到资源索引表了, resources.arsc) count = processAssets(bundle, zip, assets); if (count & 0) { fprintf(stderr, &ERROR: unable to process assets while packaging '%s'\n&, outputFile.string); result = } // 再把用-j指定的包含classes的jar或者zip包给丢进去 count = processJarFiles(bundle, zip); if (count & 0) { (, &ERROR: unable to process jar files while packaging '%s'\n&, outputFile.string); result = } // 走到这儿算是处理成功了 result = NO_ERROR; /* * 各种标记删除,然后flush zip */ ... /* 没东西要你何用,删 */ if (zip-&getNumEntries == 0) { ... } // 如果被要求生成依赖文件,则在这里进行,最后写到指定apk输出目录下,如bin/resources.ap_.d ... assert(result == NO_ERROR); // 上面任何一部出错则goto跑来这儿,删除文件 bail: // must close before remove in Win32 if (result != NO_ERROR) { ...删除并unlink } if (result == NO_ && bundle-&getVerbose) printf(&Done!\n&); }第2步:处理aidl文件,调用build-tools下的aidl可执行文件生成对应的Java文件。该工具的源码位于frameworks\base\tools\aidl。第3步:Java源码编译我们有了R.java和生成的Java文件,再加上工程的源代码,现在可以使用javac进行正常的java编译生成class文件了。第4步:dex调用dx.bat将所有的class文件(上一步生成的以及第三方库的)转化为classes.dex文件,实际调用的是build-tools\lib\dx.jar,其源码位于libcore\dex(描述Dex文件的格式)及dalvik\dx(包含dx及multidex打包)下。dx会将class转换为Dalvik字节码,生成常量池,消除冗余数据等。关于dex,我们下一篇会单独去细说。第5步:apkbuilder打包生成APK文件。旧的apkbuilder脚本已经废弃,现在都已经通过sdklib.jar的ApkBuilder类进行打包了。输入为我们之前生成的包含resources.arcs的.ap_文件,上一步生成的dex文件,以及其他资源如jni、jar包内的资源。大致步骤为以包含resources.arcs的.ap_文件为基础,new一个ApkBuilder,设置debugModeapkBuilder.addZipFile(f);apkBuilder.addSourceFolder(f);apkBuilder.addResourcesFromJar(f);apkBuilder.addNativeLibraries(nativeFileList);apkBuilder.sealA // 关闭apk文件generateDependencyFile(depFile, inputPaths, outputFile.getAbsolutePath);第6步:Jarsigner对apk文件进行签名。APK需要签名才能在设备上进行安装,源码在build\tools\signapk下。很多时候我们在逆向改完后,会因为没有签名文件导致最后的apk无法正常使用,又细分为本地验证和服务器验证。第7步:zipalign调用buildtools\align,对签名后的apk文件进行对齐处理,使apk中所有资源文件距离文件起始偏移为4字节的整数倍,从而在通过映射访问apk文件时会更快。这样我们的最终apk就生成完毕了,对gradle是如何在输入gradle assembleDebug之后打包的,可以参见aosp下builder/src/main/java/com/android/builder目录,这样你可以更了解整个流程和每个gradle子任务做了什么(像是BuildConfig是怎么生成的)。ADB, 全名 Android Debug Bridge,不仅仅是命令行我们输入的adb xxx命令,DebugDevice MonitorDDMS也都是通过adb来完成设备与我们的开发机器的通信的。比如当我们在命令行输入adb daemon start实际上就会有2个进程被起起来(这就是下文提到的组件中的client和了)adb ps角色ADB扮演了2个角色传输。host和设备间的通信路径。可能是USB,也可能是TCP,但host不需要关心。服务。通过传输提供服务,在目标设备上执行指定命令。组件ADB中有3个组件adb clients。其实就是那个子命令的可执行文件。比如起了3个adb shell,那就是3个clients。adb server(就是那个动不动卡死要restart的东西)。在开发机器的后台运行,扮演着adb clients和adbd之间的中介,让彼此可以通信。adb daemon(adbd)。在目标设备上运行的后台进程;由init启动,死掉后会由init重启。server的启动当启动adb client的时候,client首先会检查是否有adb server进程在运行中,如果没有则启动进程。server启动后会绑定到TCP端口5037,并监听来自adb clients的命令。接着server会通过扫描之间的奇数端口(被模拟器和物理设备所使用),建立到所有运行中设备实例的连接。一旦server找到adb daemon,就会建立到那个端口的连接(而未开启USB调试的设备则没有adb daemon运行)。每个设备实例都需要一对连续的端口(这就是为什么刚才只扫描奇数端口),一个偶数端口用于控制连接,一个奇数端口用于adb连接,例如:模拟器1,控制: 5554模拟器2,adb: 5555Nexus6,控制: 5556Nexus6, adb: 5557...如上,其实都是被同一台设备所使用。内部实现源码位于aosp的system/core/adb目录下,adb和adbd都是从这儿编译出来的。有一部分文件是共用的:adb.c, fdevent.c, transort.c, transport_local.c, tansport_usb.c, service.c, sockets.c, util.c。举个例子std::string get_trace_setting { #if ADB_HOST return get_trace_setting_from_ #else return get_trace_setting_from_ #endif }通过ADB_HOST这个宏编译不同的代码。其他大部分文件则由server和client后缀可以区分。跟我们的主题息息相关的主要就是系列的命令了,先看看命令使用:adb install [-lrtsdg] &file& - 把安装包文件push到设备并安装 (-l: forward lock application) (-r: replace existing application) (-t: allow test packages) (-s: install application on sdcard) (-d: allow version code downgrade) (-g: grant all runtime permissions) adb install-multiple [-lrtsdpg] &file...& - 把安装包文件push到设备并安装 (-l: forward lock application) (-r: replace existing application) (-t: allow test packages) (-s: install application on sdcard) (-d: allow version code downgrade) (-p: partial application install) (-g: grant all runtime permissions) adb uninstall [-k] &package& - 从设备上卸载该app ('-k' means keep the data and cache directories)分别对应static int install_app(transport_type t, const char* serial, int argc, const char** argv); static int install_multiple_app(transport_type t, const char* serial, int argc, const char** argv); static int uninstall_app(transport_type t, const char* serial, int argc, const char** argv);adb install这里以install命令为例看看adb做了什么:static int install_app(transport_type transport, const char* serial, int argc, const char** argv) { static const char *const DATA_DEST = &/data/local/tmp/%s&; static const char * SD_DEST = &/sdcard/tmp/%s&; const char* where = DATA_DEST; // 解析-s参数,有则把where改成SD_DEST ... // 找到最后的APK参数,检查是否是APK,不是则提示&Invalid APK file&,然后直接退出 ... const char* apk_file = argv[last_apk]; char apk_dest[PATH_MAX]; // apk_dest这样就是最终要拷贝过去的完整路径了,比如&/data/local/tmp/app.apk& snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); // 把文件push上去 int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */); if (err) {
cleanup_ } else { argv[last_apk] = apk_ /* destination name, not source location */ } // 通过pm安装 err = pm_command(transport, serial, argc, argv); // push出错了就删了文件 cleanup_apk: delete_file(transport, serial, apk_dest); } // pm命令
int pm_command(transport_type transport, const char* serial, int argc, ** argv) { std::string
= &shell:pm&; while (argc-- & 0) { cmd += & & + escape_arg(*argv++); } return send_shell_command(transport, serial, cmd); }彩蛋还有几个有趣的命令# 跟adb shell差不多,不过颜色很hell adb hell # 笑你妹 adb lolcat为什么有时候会安装不上apk呢?安装的界面是怎么弹出来的?抱着这些疑问,我们看下去。安装方式大致上有四种系统程序安装,开机时安装,没有安装界面。由开机时启动的PackageManagerService服务完成,会在启动时扫描/system/appvender/app/data/app/data/app-private并安装。通过Android市场安装,Google Play可以直接安装,其他市场除非root,否则需要自己点击安装(除非定制rom),即和第4种一样。ADB安装,即上一节说的,也没有安装界面。shell:pm是的Shell客户端,源码位于/frameworks/base/cmds/pm执行路径大致是从main -& run -& runInstall,挑一段最后的核心代码Pm.javaprivate int runInstall { .... try { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); // 通过IPackageManager mPm.installPackageAsUser(apkFilePath, obs.getBinder, installFlags, installerPackageName, verificationParams, abi, userId); synchronized (obs) { while (!obs.finished) { try { obs. } catch (InterruptedException e) { } } // 我们看到的成功失败返回信息 if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println(&Success&); return 0; } else { System.err.println(&Failure [& + installFailureToString(obs) + &]&); return 1; } } } catch (RemoteException e) { ... } }手机自行通过文件浏览器打开安装,有安装界面。PackageInstaller当我们在手机的文件管理器或者notification点击apk文件,就会出现如下图所示(Nexus6 Android 6.0.1)的界面,点击安装按钮即可开始安装,点击取消按钮返回。这个安装界面是Android系统程序PackageInstaller的PackageInstallerActivity,dump一下可以看到如下图信息PackageInstallerActivity当Android系统请求安装apk程序时,会启动这个Activity,并通过Intent读取传来的apk信息,我们来简单看看该Activty onCreate的代码:public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener { ... @Override protected void onCreate() { super.onCreate(icicle); // 拿到pm和installer mPm = getPackageM mInstaller = mPm.getPackageI mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); final Intent intent = getI // 检查permission,初始化读取mSessionId,mPackageURI,mOriginatingURI,mReferrerURI ... // 检查是否允许未知来源 ... // 检查是否支持,不支持则直接结束 final String scheme = mPackageURI.getS if (scheme != null && !&file&.equals(scheme) && !&package&.equals(scheme)) { Log.w(TAG, &Unsupported scheme & + scheme); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME); } final PackageUtil.AppS if (&package&.equals(mPackageURI.getScheme)) { // package scheme mInstallFlowAnalytics.setFileUri(false); try { mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart, PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException e) { } // 无法获得PackageInfo,直接退出 if (mPkgInfo == null) { ... } as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo), mPm.getApplicationIcon(mPkgInfo.applicationInfo)); } else { // file scheme mInstallFlowAnalytics.setFileUri(true); final File sourceFile = new File(mPackageURI.getPath); PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile); // 检查解析错误,显示错误对话框,直接退出 if (parsed == null) { ... } // 生成PackageInfo mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_PERMISSIONS, 0, 0, null, new PackageUserState); // manifest校验 mPkgDigest = parsed.manifestD // 设置apk的程序名和图标 as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile); } mInstallFlowAnalytics.setPackageInfoO setContentView(R.layout.install_start); mInstallConfirm = findViewById(R.id.install_confirm_panel); mInstallConfirm.setVisibility(View.INVISIBLE); PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet); mOriginatingUid = getOriginatingUid(intent); // 如果必要则禁止来自未知来源的安装 if (!requestFromUnknownSource) { // 进行一些其他的初始化工作 initiateI } // 未知来源检查,如果admin禁止则直接提示错误退出。否则显示选项提示用户去设置里修改该设置。 final boolean isManagedProfile = mUserManager.isManagedP if (!unknownSourcesAllowedByAdmin || (!unknownSourcesAllowedByUser && isManagedProfile)) { showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING); } else if (!unknownSourcesAllowedByUser) { showDialogInner(DLG_UNKNOWN_SOURCES); mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING); } else { initiateI } } }整个方法有2个重点函数。1)PackageUtil.getPackageInfo(sourceFile)getPackageInfo会构造PackageParser,调用Package parseMonolithicPackage(File apkFile, int flags)去解析该apk程序包,然后记录下manifest的校验码。parseMonolithicPackage对于我们普通的app又会调用parseBaseApk(File apk, AssetManager assets, int flags)去做真正的解析并获得Package对象(该类里有很多给split apk用的方法和逻辑)。解析过程会首先读取AndroidManifest.xml获取程序包名以构建Package对象,然后再处理的其他标签包括四大组件,并把信息全都存到Package对象里面。首先检测该程序是否已安装,是则弹框提示是否替换程序,否则直接调用startInstallConfirm,做UI初始化和事件绑定,于是当我们点击安装的时候则会触发onClick下的OK按钮事件:if (mOkCanInstall || mScrollView == null) { mInstallFlowAnalytics.setInstallButtonC if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, true); // We're only confirming permissions, so we don't really know how the // assume success. mInstallFlowAnalytics.setFlowFinishedWithPackageManagesult( PackageManager.INSTALL_SUCCEEDED); } else { startI } } else { mScrollView.pageScroll(View.FOCUS_DOWN); }对于本地app则会继续走的逻辑,开启一个新的activity,InstallAppProgress,该activity判断scheme进行不同的安装:if (&package&.equals(mPackageURI.get)) { try { pm.installExistingPackage(mAppInfo.packageName); observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_SUCCEEDED); } catch (PackageManager.NameNotFoundException e) { observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_FAILED_INVALID_APK); } } else { pm.installPackageWithVerificationAndEncryption(mPackageURI, ob, installFlags, installerPackageName, verificationParams, null); }installPackageWithVerificationAndEncryption其实还是会调用installPackage,结果和adb安装殊途同归,整个转的路径为-&(这儿会先检查调用者是否有安装的权限) -& processPendingInstall -& installPackageLI:private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { .... if (replace) { // 替换已有程序 replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, installerPackageName, volumeUuid, res); } else { // 安装新程序 installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res); } .... }无论是替换还是新安装,都会调用,然后跑去,它会判断是否为系统程序,解析apk程序包,检查依赖库,验证签名,检查sharedUser签名、权限冲突、ContentProvider冲突,更新native库目录文件(检测abi),进行dexopt,杀掉现有进程(仅对覆盖安装的场景)等等,最后调用createDataDirsLI进行实际安装:private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) { int users = sUserManager.getUserI int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo); if (res & 0) { } for (int user : users) { if (user != 0) { res = mInstaller.createUserData(volumeUuid, packageName, UserHandle.getUid(user, uid), user, seinfo); if (res & 0) { } } } }mInstaller为Installer类的实例,但实际安装并不是在做的,而会通过InstallerConnection把命令使用socket通信发到/system/bin/installd。在这里第一次call的install对应命令为install uuid name uid gid seinfo而第二次call的createUserData则会使用命令mkuserdata uuid name uid userId seinfoinstalld是一个常驻进程,可以在 shell通过ps | grep installd查看进程信息。源码位于frameworks/native//installd/installd.cpp下(dexopt也在这里哦),处理install命令的函数为do_install, do_调用了Command.cpp的执行完毕后,通过socket回传结果,而根据返回结果做对应处理并显示给用户,至此为止,整个apk安装过程结束。我们了解了一个android工程是怎么变成apk的,是怎么跑到设备上,而最后又是如何安装的。下一次我们来看看dex和odex,art上的elf和oat都是什么,而dexopt又做了什么优化。dex加壳技术大多就是在上面做了手脚。《软件安全与逆向分析》,作者:丰生强,人民邮电出版社写的途中还不慎看到csdn上某排名500多的百度大V声称自己看老罗的博客并结合参考资料写的整理,实则完全就是照抄书上的,连错误的地方都照抄了,也没有说是人家的,我也是呵呵哒。更有趣的是该作者还在新的文章里提到觉得网上的文章内容重复太多。恩恩,不愧是伟大的百度公司的开发。实在忍不住喷一下。果然还是太年轻。将为您减少类似内容我要收藏358个赞不感兴趣分享到分享到:相关文章还可以输入140字热门频道195.6万人订阅18.4万人订阅15.3万人订阅119.2万人订阅16.8万人订阅你还可用第三方账号来登录请输入你注册的电子邮件地址绑定密保手机*您可用使用此密保手机找回密码及登录*请勿随意泄露手机号,以防被不法分子利用,骗取帐号信息手机号码发送验证码确定电子邮件请输入您的意见和建议请您输入正确的邮箱地址,以便我们和您联系,帮您解决问题。扫描下载手机客户端热门搜词

我要回帖

更多关于 wifi密码查看器apk 的文章

 

随机推荐