PS: 最近没工作,没工作就没需求,没需求就没什么技术总结的灵感,那就没更新什么。但是两个月不更新了,要是三个月不更新就会出大事,所以这次打算做一件有意思又不难的事。
之前有发文章写过反编译,今天就来试试反编译之正编译,开玩笑的,就是试试手动编译的过程, 平时我们在项目中编译出包都是使用Gradle直接执行assemble任务就能解决,我打算试试手动模拟整个过程。当然我也是第一次这样搞,所以如果有写得不对的地方,还望指出。
众所周知,apk实质上就是一个压缩包。复习一下,我们写个最简单的Demo,然后打包,然解压,注意是解压,不是反编译,意义是不同的。
注意我这个Demo很简单,什么都不引入
然后我们看看整个出包的过程,随便从网上拿张图
然后这里我们用Android SDK给我们提供的工具来完成整个流程,工具在sdk文件夹下的build-tools文件夹下,有什么aapt.exe、dx.bat,用的就是这些
这步应该是整个流程最简单的吧,我感觉,所以从最简单的开始。
我们先看看生成的dex有什么
对比项目,我是一开始最基本的项目,什么都没动,所以只有一个MainActivity.clas,所以这里肯定是要先想办法得到BuildConfig.class和R.class。
输入命令:
aapt p -f -m -J <输出路径> -S <res路径> -I <android.jar路径> -M <Manifest路径>
下一步,我们需要BuildConfig.class
这个BuildConfig.java是由gradle在我们配置好gradle之后自己帮我们生成的,所以我们直接拿来用,然后再javac就得到class文件了
然后我们再编译我们的MainActivity.java并将它们放到同一个文件夹下, MainActivity因为引用了Android.jar和R文件,所以编译时注意点,我为此被动好好的复习了一遍javac,都是泪
最后一步,我们用dx工具就能打出dex文件了
然后执行命令就得到一个Dex文件,看看这个文件里面和上面直接打出的apk中的Dex文件有什么不同:
看图,我们上一步已经生成.dex了,那么我们需要和compiled Resource 还有 Other Resource 一起生成APK。
我们先来生成compiled Resource,也就是resources.arsc
发现之前使用aapt生成R文件的时候没写完整,当时可以加一个-F参数直接生成arsc和Manifest
导出的abc.zip里面就有resources.arsc和AndroidManifest.xml。
因为之前写漏了,所以肯定要重新编一次MainActivity.java和Dex
我们把刚才的dex文件和aapt生成的resources.arsc、AndroidManifest.xml和res放到一个文件夹里面。
PS:res文件夹也是上面aapt的命令生成的
然后我们对比这个文件夹和之前apk解压的文件夹
最后运行
看来是成功了。
再说说遇到的还有两个问题,并说下我解决问题的思路
(1)我把它们都放到一个文件夹之后,我压缩成压缩包,然后改后缀成.apk,然后发现安装不了,我就直接反编译,发现发编译失败,提示包有问题,以我多点玩包的经验,我感觉就是压缩工具出了问题,然后我去下个“好压”(这不是广告啊),然后就能正常反编译了。
(2)但是还是安装不了,再根据我多年的玩包经验,我感觉是签名问题,然后我随便给这个包上一个签名,就能正常安装得到上图的结果了。
总体来说,还真挺好玩的,这整个过程,就是翻车了几次。做完之后感觉非常牛逼,为什么这样说,因为我知道这整个过程,我就可以做到,我不经过gradle来打包,我自己写个python脚本来调用aapt和dx来打包也是能做到的。
当然上面纯属异想天开,因为这是个什么都没有的Demo所以觉得简单,要是一个真实的项目,我感觉肯定要有很多坑,别的先不说,一个项目那么多依赖关系,我这javac要搞死人。
最后如果有不对的地方,希望有大佬能够指出,毕竟能运行也不能证明完全没问题。然后我使用的build-tools是28的,不敢保证其它版本包括以后版本的玩法都一样。
② 如何单独编译Android源码中的模块
1.make 模块名称
需要查看Android.mk文件的LOCAL_PACKAGE_NAME变量。
2.mmm命令
用于在源码根目录编译指定模块,参数为模块的相对路径。只能在第一次编译后使用。比如要编译Phone部分源码,需要在终端中执行以下命令:
$mmm packages/apps/phone
3.mm命令
用于在模块根目录编译这个模块。只能在第一次编译后使用。例如要编译Phone部分源码,需要在终端中执行以下命令:
$cd packages/apps/phone
$mm
注:mmm和mm命令必须在执行“.build/envsetup.sh”之后才能使用,并且只编译发生变化的文件。如果要编译模块的所有文件,需要-B选项,例如mm -B。
③ 如何编译一个精简的Android系统
本次试验使用的android源码是4.2,编译的架构是mini-mips。
一、所做的工作
1、修改build/target/proct/mini.mk,去掉一些不必要的模块(例如Phone、DownloadManager等)
2、修改SystemServer.java,屏蔽一些service,让系统能够启动起来(例如,Location Manager、Telephony Registry)
3、修改dalvik/vm/native/dalvik_system_Zygote.cpp,注释掉因为检查不到外部存储而导致dalvik abort的地方 (这是googel的一个bug,在2013年1月份已解决,如果用这以后的代码不用修改此处)
4、修改WindowManagerService.java,把发送BOOT_TIMEOUT消息的时间改为0(之前为30秒)
二、系统优化后的效果(验证工作均在mips模拟器上进行)
1、节省运行内存,下面是全编译与mini编译的内存使用状态的对比
1)full build
MemTotal: 499360 kB
MemFree: 242064 kB
2)mini build
MemTotal: 499360 kB
MemFree: 395192 kB
2、缩短开机启动时间
在虚拟机上的启动时间
1)full build-29秒
2)mini build-14秒
3、只启动home程序,其余的应用程序均被移除
三、保留android的开发环境
1、adb,ddms,apkinstall等,都能正常工作
2、在eclipse中编写的android应用程序能够运行在该mini-android之上
四、开机自动启动指定应用程序
本次测试使用Gallery.apk应用程序,修改其源码后可以实现随系统的启动而自动启动的功能。
④ 如何把应用程序app编译进android系统
准备工作:
一、开启ROOT权限。
ROOT方法:下载一键ROOT之类的软件,根据操作步骤进行,就可以获取ROOT权限了。
二、安装RE文件管理器。
清理系统自带应用
一、用RE打开系统根目录下的system/app,进行精简系统自带应用,注意,要对照一些总结可删减程序的文件,以免删错了导致系统故障。
二、根目录:preload目录下,可能会有系统的预装应用,不用的也可以全部删掉。
移动APK到system/app 目录
一、移动apk:用RE管理器把要安装的apk软件移动到/system/app 目录下。
在操作前,将/system/app 目录挂载为可读写;
注意软件名不要有中文 、空格及其他特殊字符等;
如果无法移动,显示空间不足时,可以先移动到data/app/下,再移动到/system/app;
二、修改apk权限:对移动进来的apk软件更改权限,即改为:用户权限为 读+写,分组权限为 读,其他权限为 读。
三、提取 .so文件:用RE提取apk软件中/lib/目录下的所有 .so文件,将其移动到/system/lib中。
点击apk软件,用RE查看文件内容,即可找到 ,so文件。
四、修改 .so文件权限:方法同上。
五、重启手机。在此过程中,可能加载的时间稍长点,请耐心等待即可。
完成以上操作后,手机的可用内存空间就会增大一些,同时,/system的剩余空间也可以有效的利用,机器运行时的速度也会有所提升了。
注意:在以后的恢复出厂设置中,还原的系统就会变为现在修改过的系统了。
操作截图如下:
⑤ 怎么样将自己开发的Android应用程序编译到系统Image中
1. 搭建编译环境
编译环境: Ubuntu 10.10
Android版本:Android 2.2
编译过程中可能需要在Ubuntu上安装必要的一些软件,我安装过的包含如下软件,不同的系统可能会有差别:
jdk6(Android官方建议装jdk5,但是我在编译时会遇到Java override问题,改用6没有任何问题), bison, lib64z1-dev, libasound2-dev, flex, gperf, libncurses5-dev
2. 应用程序存放目录
SimpleJNI是采用Android NDK和Java编写的程序,包含apk和so库文件,它的源代码在source tree的development/samples/目录下。
实际上package在编译时所放的目录并没有明确限定,编译后apk和so存放的位置是根据目录下Android.mk所指定的编译类型所决定的,例如:
SimpleJNI根目录下的Android.mk中有一行include $(BUILD_PACKAGE),表示将该目录下的模块编译成package,即apk文件,默认存放的位置为/system/app。
SimpleJNI/jni目录下的Android.mk中有一行为include $(BUILD_SHARED_LIBRARY),表示将该目录下的native.cpp编译为共享库文件,即so文件,默认存放的位置为/system/lib
因此,如果我们想要将自己编写的程序编译至image中,只需要将Eclipse下完成的整个工程到source tree下的某个目录即可,我一般习惯放到packages/apps下。
3. 添加Android.mk
完成了上一步,可以知道,Android.mk在编译中起着至关重要的作用,这其实就是Android编译环境中的make file。为了完成我们的工作,需要在源代码中添加Android.mk。添加自己的Android.mk可以仿照SimpleJNI中的Android.mk,稍微修改即可。我们首先看看SimpleJNI目录下的两个Android.mk的内容:
根目录下的Android.mk
TOP_LOCAL_PATH:= $(call my-dir)
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SimpleJNI
LOCAL_JNI_SHARED_LIBRARIES := libsimplejni
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
# ============================================================
# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))
根目录下的Android.mk决定了整个工程编译的配置,其中,
LOCAL_PATH 定义了当前的目录
LOCAL_MUDULE_TAGS 定义了当前模块的类型,编译器在编译时会根据类型的不同有些差别,有些tags的mole甚至不会被编译至系统中。LOCAL_MUDULE_TAGS主要有如下几种:user debug eng tests optional samples shell_ash shell_mksh。optional表示在所有版本的编译条件下都被编译至image中,剩下的表示在该版本中才会被编译只image中,如user表示在user版本下才会被编译至image中。
对于包含LOCAL_PACKAGE_NAME的mk文件,该项默认为optinal,具体可以参看build/core/package.mk。SimpleJNI中定义为samples的具体作用我也不太清楚,为了保险起见,我自己的apk一般定义为optional。
LOCAL_SRC_FILES 定义了编译apk所需要的java代码的目录
LOCAL_PACKAGE_NAME 这里需要改成自己的package的名字
LOCAL_JNI_SHARED_LIBRARIES 定义了要包含的so库文件的名字,如果你的程序没有采用JNI,这行不需要。
LOCAL_PROGUARD_ENABLED 定义了Java开发中的ProGuard压缩方法,主要用来分析压缩程序的,在我自己的应用中我没有加这行。
include $(BUILD_PACKAGE) 这行是build的关键,表示当前java代码build成apk
include $(call all-makefiles-under,$(LOCAL_PATH)) 表示需要build该目录下的子目录的文件,这样编译系统就会在当前目录下的子目录寻找Android.mk来编译so等其它程序。
根据上述所写,创建我自己的Android.mk如下:
TOP_LOCAL_PATH:= $(call my-dir)
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := TestJniApp
LOCAL_JNI_SHARED_LIBRARIES := libtestjniapp
include $(BUILD_PACKAGE)
# ============================================================
# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))
看起来很简单吧,基本不需要改动即可。
Jni目录下的Android.mk
由于我们的TestJniApp是用JNI完成的,包含C源代码,因此也需要一个jni目录下的Android.mk。同样首先看看SimpleJNI中jni目录下的Android.mk的内容:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
# This is the target being built.
LOCAL_MODULE:= libsimplejni
# All of the source files that we will compile.
LOCAL_SRC_FILES:= /
native.cpp
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := /
libutils
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Also need the JNI headers.
LOCAL_C_INCLUDES += /
$(JNI_H_INCLUDE)
# No special compiler flags.
LOCAL_CFLAGS +=
# Don't prelink this library. For more efficient code, you may want
# to add this library to the prelink map and set this to true. However,
# it's difficult to do this for applications that are not supplied as
# part of a system image.
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE 当前模块的名字,即编译后的so文件的名字
LOCAL_SRC_FILES 所要编译的文件
LOCAL_SHARED_LIBRARIES, LOCAL_STATIC_LIBRARIES 该模块要链接的动态库和静态库。
LOCAL_C_INCLUDES 要包含的头文件
LOCAL_CFLAGS C语言编译选项
LOCAL_PRELINK_MODULE 定义是否使用prelink工具,它用事先链接代替运行时链接的方法来加速共享库的加载,不仅可以加快起动速度,还可以减少部分内存开销。
经过修改后,我自己的TestJniApp中jni目录下的Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtestjniapp
LOCAL_SRC_FILES := com_test_app_Jni.c
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
这里有一点需要注意,如果要将so文件编译入image,必须要修改LOCAL_MODULE_TAGS,将原有的值samples修改为user,或者可以直接删掉 。删掉是因为对于包含LOCAL_MODULE的mk文件,如果没有指定LOCAL_MODULE_TAGS,该项默认为user,而只有定义为user的情况下,才会将so文件编译入image,具体定义可以参看build/core/base_rule.mk。
4. 修改/bulid/target/proct/generic.mk 把工程编译到系统中
至此,还有最后一部工作。为了将工程编译进入image,还需要在/bulid/target/proct/generic.mk文件中将package name添加进去
PRODUCT_PACKAGES := /
AccountAndSyncSettings /
CarHome /
DeskClock /
……
SyncProvider /
TestJniApp
完成上面这些步骤后,在source tree根目录下编译image就可以了。