分析Android程序

分析Android程序

当拿到一个apk之后,我们应该怎样分析它?刚刚看完了Android软件安全权威指南前面一部分,尝试着梳理一下知识,那么接下来就开始吧

1 安装apk

由于只有一个手机,而后面可能会涉及到恶意软件分析,所以在这里,着重讨论的是使用电脑上的Android虚拟机,目前使用的是Win 10系统加上模拟器。接下来开始吧。

1.1 安装&测试

在我目前使用的Win10电脑上进行arm环境的模拟会出现闪退,而这个问题我暂时并没有解决掉,所以对于arm 环境的模拟,我使用的是模拟器。

1.1.1 emulator

官方文档:从命令行启动模拟器

常用的命令主要有两个

1
2
3
$ emulator -list-avds # 查看当前可用的avd
Nexus_6P_API_25
$ emulator -avd Nexus_6P_API_25 -port 5555 # 启动avd

1.1.2 adb(Android 调试桥)

adb官方文档在这

我目前使用的是网易mumuAndroid模拟器

网易mumu官方文档在这

打开模拟器之后使用adb 手动连接模拟器端口

常用指令

1
2
3
4
5
6
7
8
9
10
11
$ adb connect ip # 连接到设备
$ adb devices # 列出已经连接的设备
$ adb kill-server # 重置adb主机
$ adb install %path_to_apk% # 从主机上安装软件到虚拟机
$ adb uninstall %path_to_apk% # 从虚拟机中卸载软件
$ adb shell dumpsys window | findstr mCurrentFocus # 正在运行应用包名
$ adb pull remote local # 将文件从虚拟机复制到主机
$ adb pull locak remote # 将文件从主机复制到虚拟机

$ adb am commadn # Activity Manager
$ adb pm command # packages Manager

对于文件夹中 已经存在的文件 a.apk,我想安装它,使用的命令如下:

1
2
$ adb connect 127.0.0.1:7555 # 使用adb连接到虚拟机
$ adb install a.apk # 安装 apk文件

2 反编译apk

将apk安装进虚拟机之后能大概查看下app的功能,便于我们后面进一步的分析,之后我们需要做的是分析apk,并将它反编译。

1
2
3
4
> $ file  *.apk
> .\a.apk: Zip archive data, at least v2.0 to extract
> $ unzip *.apk
>

将apk 解压缩之后我们可以得到:

  1. original目录下的META_INF目录:包含了apk的签名信息
  2. AndroidManifest.xml: 编译好的AXML二进制格式文件
  3. res 目录,程序中使用的资源信息
  4. resources.arsc:编译好的二进制资源信息
  5. assets: 资源文件
  6. classes.dex程序的可执行代码,如果开启了MutliDex,会有多个dex文件

对于apk的详细分析我们之后再说明,现在假定我们拿到的apk文件可以直接反编译:

可以使用的工具有

  • apktools :官方文档在这
  • jd-jui :查看工具
  • jadx: 查看工具
  • jeb: 提供了方法交叉引用和重命名
  • ida pro

反编译之后得到的文件有:

1
2
3
4
5
6
7
Mode                LastWriteTime         Length Name
---- ------------- ------ ----
d----- 2020/3/16 15:38 original
d----- 2020/3/16 15:38 res
d----- 2020/3/16 15:38 smali
-a---- 2020/3/16 15:38 936 AndroidManifest.xml
-a---- 2020/3/16 15:38 425 apktool.yml

接下来就是对反编译之后的samli文件分析过程:

smali文件时Dalvik字节码文件,官方网站是Dalvik字节码

对于Dalvik字节码的一个总结如下:

2.1 Dalvik 字节码文件

编写的.java文件经过编译之后得到java字节码文件.class.class文件再编译得到.dex文件,.dex文件在虚拟机上能够执行,.dex文件经过反编译之后就得到了Dalvik字节码文件,完整的过程以及流程如下:

Dalvik虚拟机和Java虚拟机区别

  1. Java虚拟机运行.class java字节码文件,Dalvik虚拟机运行的是.dexDalvik字节码文件
  2. 传统的java文件通过编译生成java字节码保存在.class文件中,java虚拟机通过解码.class文件来运行程序,而 Dalvik虚拟机运行的是Dalvik字节码文件,它都是通过java字节码转换而来,并被打包到一个.dex可执行文件中,Dalvik虚拟机通过解释DEX文件来执行字节码
  3. Java虚拟机基于架构,而Dalvik虚拟机基于寄存器架构,数据间访问更快。

2.2 虚拟机的启动流程

img

Android系统启动加载内核后—-> 执行init进程 —-> 启动Zygote进程 —-> 初始化Dalvik虚拟机 —> 启动system_server进入Zygote模式,用socket等待命令 —> Zygote收到命令后fork一个Dalvik虚拟机实例来执行程序入口函数。

其中Zygote有三种创建进程的方法:

  • fork()创建Zygote进程
  • forkAndSpecialize()创建非Zygote进程
  • forkSystemServer()创建系统服务进程

fork后 —> 虚拟机通过loadClassFromDex完成装载(用gDvm.loadedClass全局哈希表存储查询类) —> dvmVerifyCodeFlow对代码检验 —> FindClass查找装载main方法类 —> dvmInterpret初始化解释器并执行字节码流。

3 一个简单ctf题解

题目来源

反编译之后,有三个文件

1584367107017

1584367142346

1584367163371

可以看到实现的是一个 enigma密码机 ,直接根据最后的密码解了即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from collections import deque#双端队列
alpha = deque("abcdefghijklmnopqrstuvwxyz")
t1 = deque([8, 25, 17, 23, 7, 22, 1, 16, 6, 9, 21, 0, 15, 5, 10, 18, 2, 24, 4, 11, 3, 14, 19, 12, 20, 13])
t2 = deque([7, 14, 16, 21, 4, 24, 25, 20, 5, 15, 9, 17, 6, 13, 3, 18, 12, 10, 19, 0, 22, 2, 11, 23, 1, 8])

ss = 'wigwrkaugala'
for _ in range(2):
t1.append(t1.popleft()) #实现转动
for _ in range(3):
t2.append(t2.popleft())
print(t1)
print(t2)

def dec(s):
i = t2[(ord(s) - ord('a'))] #ord(s) - ord('a')就可以得到在那个a到z的字符中的索引值
i = t1[(i)]
print(alpha[i], end='')
t1.append(t1.popleft())
alpha.append(alpha.popleft())
for s in ss: dec(s)