Android 安装包常以apk(android Package)、xapk为后缀,本质是一个压缩包,包内存存放运行着应用程序安装必要的所有资源、代码。可直接修改后缀为zip解包。
解包后目录大致如下:
assets |
资源文件,通过context.getAssets();获得,通常存放扩展资源。 |
lib |
JNI二进制动态库,用于与C++互相调用。通过 System.loadLibrary("")加载。 |
META-INF |
签名文件夹,应用程序打包后需要签名 |
CERT.RSA CERT.SF MANIFEST.MF
|
|
res |
应用程序资源,区别于Assets,一般为应用程序内资源,每个资源会在R类内被标注一个ID,通过getResources().*(R.*.资源名)、findViewByID等可获得响应的资源对象。 |
drawable layout menu |
图片 布局 菜单 |
classes.dex |
可执行文件 |
AndroidManifest.xml |
配置文件 |
resources.arsc |
R类会编译成这个文件,它负责将资源ID与资源文件联系起来 |
对于逆向我们更关心AndroidManifest、classes.dex、lib这三个。
1.AndroidManifest.xml文件描述了该app如何被安装与运行。
对于漏洞挖掘,逆向分析有着重要的引导作用,这里引入一个CTF的manifest文件。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="com.zhuotong.kanxuectf2" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="16"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<application android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:label="@string/app_name" android:icon="@drawable/icon" android:allowBackup="true" android:supportsRtl="true">
<activity android:name="android.support.p001v4.app.o000000o" android:excludeFromRecents="true" android:launchMode="singleInstance" android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity-alias android:label="@string/second_activity_name" android:icon="@drawable/baah" android:name="android.support.v4.app.Q496512831" android:enabled="false" android:excludeFromRecents="true" android:launchMode="singleInstance" android:targetActivity="android.support.v4.app.o000000o">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<provider android:name="android.support.p001v4.app.OO0OOOO0" android:authorities="com.zhuotong.net"/>
</application>
</manifest>
根节点为manifest,其中package属性描述着应用程序的包名。
uses-sdk 节点描述着所使用的sdk版本信息。
uses-permission描述应用程序申请使用的权限。当使用android中的权限时,都以android.permission开头
application 节点描述程序内部功能,如四大组件:
activity、provider、receiver、service。
他们都拥有intent-filter子节点,用于使用intent激活它们时对intent进行过滤。
其中category为android.intent.category.LAUNCHER时,表示被描述的Activity为主界面。且name为程序入口点。
此处为android.support.p001v4.app.o000000o。
2.classes.dex是一个dalvik可执行程序,内部存放着app的Java代码编译后的dalvik字节码
他被Android 虚拟机所识别、执行。是逆向中最重要的文件。
通过jadx等逆向软件查看到的代码就出自于这些classes.dex中。dex有时不止一个,当代码够多时按照classes2.dex、classes3.dex以此类推。
Android app静态分析工具都是对这些dex文件进行反编译,分析。可以理解为Android系统中的汇编。
Android 虚拟机有两种,分别是dalvik与ART。
Dalvik是Google公司自己设计用于Android平台的虚拟机。是一个翻译并执行dalvik指令的虚拟机。
在Android 4.4后新加入ART,在Dalvik基础上进行优化,将dalvik代码编译成本地机器码,执行效率大大提升。同时不需要更改dex的格式,对dalvik指令兼容。
注意:dalvik与jvm不一样且不互相兼容。
3.lib内会存放app中使用的二进制动态链接库文件。比如System.loadLibrary
它们是linux中的so库,是标准的ELF文件,不过它们对导出函数的命名规则与定义有特殊的要求。
如果想通过so库实现Java中的native函数,在c中需要将导出函数名按照类到方法的全路径写,并且用"_"替换路径中的"."
比如com.example.test.ctf03.JNI.getResult方法导出函数名需要写成Java_com_example_test_ctf03_JNI_getResult。
而且该方法会有一个固定的参数:JNIEnv *env。这个参数表示着Java的环境并且提供了许多调用Java代码与反射的方法。
比如:
jobject (JNICALL *NewObject)
(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
jclass (JNICALL *GetObjectClass)
(JNIEnv *env, jobject obj);
jmethodID (JNICALL *GetMethodID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject (JNICALL *CallObjectMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject (JNICALL *GetObjectField)
(JNIEnv *env, jobject obj, jfieldID fieldID);
void (JNICALL *SetObjectField)
(JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
至于jfieldID、jmethodID有对应的方法可以通过名称来获得。(GetxxxID)
同时从java的类型到C里变成了
typedef unsigned char jboolean;
typedef unsigned short jchar;
typedef short jshort;
typedef float jfloat;
typedef double jdouble;
typedef jint jsize;
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};
简单的实战:
通过JADX逆向APK:
拖入apk后的,有源代码与资源文件,源代码中的代码是通过DEX文件反编译而来,而资源文件是整个apk解压后的目录。
第一件事就是打开Manifest文件查找入口点与主界面
看到只有com.example.test.ctf02.MainActivity下有android.intent.category.LAUNCHER。
找到对应代码,其中onCreate方法是在创建时初始化的函数,没有构造函数时它将会是第一个执行的函数。
接下来就是对Java代码的审计工作了。
原文始发于微信公众号(锋刃科技):Android逆向基础——APK的格式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论