1. 概述
众所周知,Android是分层架构的。顶层应用层,Android会随系统一起打包一些核心应用(日历、短信、电话等),均是使用java语言编写;Java框架层也就是我们所说的Framework,里面主要为一些ActivityManagerService、PackageManageService等等,我们所编写的android代码能够正常的识别和动作都要依赖这一层的支持,由java语言实现;而Native层为一些本地服务和链接库,例如我们需要一个复杂的运算如果使用java去编写效率会很低,该层一般用c++开发,因语言的差异此时要与上层编写的java代码互通,就需要使用android中的jni机制;Android底层是在linux内核基础上做一些裁剪和定制,并运行了一个Dalvik/Art虚拟机,所有的应用都需要运行在虚拟机上。当程序代码趋向底层时对其的破译、逆向分析难度也就会增大从而安全性和隐蔽性也就有了良好的保障,而黑客也正是利用这一点对其开发的程序加上了一层保护伞。
近期,暗影实验室发现了一款app,此类恶意程序启动后,隐藏启动图标,防止用户主动查杀,而其真正目的是在后台窃取用户隐私。经过对该app的挖掘分析,发现其将自身的敏感且关键的信息写到了本地层,该篇文章即通过此示例讲述了Native层的逆向以及对ARM汇编代码的解析,最后提取出我们所需要的关键信息。
2. 样本信息
恶意程序基本信息如下表:
应用名称 | 样本md5 | 包名 |
相片 | 762B9874707BA9305914F4528D08CD2C | com.swift.a45 |
表2-1 样本基本信息
恶意程序安装信息如下图:
表2-2 样本安装信息
恶意程序签名信息如下表:
所有者 | CN=8y9u43,OU=ht97uig,O=tiug543,L=yf54543,ST=uyfh543,C=86 |
发布者 | CN=8y9u43,OU=ht97uig,O=tiug543,L=yf54543,ST=uyfh543,C=86 |
序列号 | 6BDF7DA9 |
证书MD5 | 8C63C8499087821D567D2EE16B8C57C3 |
证书SHA1 | 53760FC838C5C4DD7FCD825FBDAAA49A0B42EC0D |
表2-3 样本签名信息
3. Native层逆向逻辑分析流程
4. 代码分析
4.1 启动项与加载项
该程序采用传统的启动方式,即MainActivity的onCreate方法,随后通过Intent跳转到后台服务执行恶意操作。
图4-1-1 启动项
启动服务-关键信息切入点:
图4-1-2 加载项
4.2 NativeMethod
该应用为了提高隐蔽性已将个人数据隐藏至本地层,在启动服务后加载指定函数,调用本地方法,从本地拿取所需的关键数据。
图4-2-1 接入本地方法
图4-2-2加载so与本地方法
4.3 通过Jni完成函数的调用
JNI作为与本地数据传递的桥梁,通过其可以实现Java代码里调用C、C++等语言的代码,如下为加载到本地层的关键函数信息
图4-3-1 JNI函数调用模块
此时需注意通过JNI调用本地方法的书写方式:
- 格式 = Java_包名_类名_需要调用的方法名
- Java必须大写
- 对于包名,包名里的.要改成_,_要改成_1。
如包名为scut.carson_ho.ndk_demo,则需要改成scut_carson_1ho_ndk_1demo
4.4 解析ARM指令
找到调用的函数后,我们可以将其转换成汇编伪指令,进一步解读其含义,此时应注意code位数与指令模式以便进行偏移的计算。
4.4.1 ARM关键函数信息
邮箱信息指令逻辑
图4-4-1-1 arm-邮箱账号函数块
密码信息指令逻辑
图4-4-1-2 arm-邮箱密码函数块
号码信息指令逻辑
图4-4-1-3 arm-手机号信息
众所周知,25端口为SMTP协议主要用于发送邮件,也被很多木马程序所利用。
图4-4-1-4 arm-端口号及主机信息
4.4.2 ARM指令解析
因结构与计算方式大同小异,仅以上述邮箱和密码为例
图4-4-2-1 邮箱信息
图4-4-2-2 加密数据信息
首先,通过指令地址的对应关系及指令长度可知该指令为Thumb指令,关于指令模式也可根据T标志位得知,T=0为ARM模式,T=1为Thumb模式;
若要解析ARM指令,首先要明确其指令模式,Thumb指令可以是16位也可以是32位,ARM指令为32位,而新版本的ARM已经64位了,寄存器也从R改为了X。ARM程序和Thumb程序可相互调用,相互之间的状态切换开销几乎为零。
通过分析可知,该应用为Thumb与arm指令结合使用,而关键的函数调用则为Thumb指令。
图4-4-2-3 函数指令模式
PUSH {R3,LR}与POP{R3,PC}用于开辟空间,申请了多少空间,当程序执行完毕后就需要释放多少空间,堆栈平衡原理,否则会造成地址或内存混乱程序崩溃;
使用LDR指令通过寄存器的寻址,加载R1寄存器。
邮箱账号:
此时R1寄存器取址为:R1,=(a100ab104ab108a - 0xDB0)
当前PC取址:00000DAC
a100ab104ab108a取址:rodata:00001FD0
取得如上字符串后,通过Bl指令跳转到JNIEnv,进行字符串转化,将值返回到调用者。
邮箱密码:
此时R1寄存器取址为:R1, =(a164ab164ab100a - 0xDC0)
当前PC取址:00000DAC
a100ab104ab108a取址:rodata:00001FD0
当前PC取址:00000DBC
取得如上字符串后,通过Bl指令跳转到JNIEnv,进行字符串转化,将值返回到调用者。
4.5 指令计算
因PC指令用来存放当前欲执行指令的地址,ARM是冯诺依曼结构,同时采用流水线工作原理,对于三级流水线(最少也是三级),有取址、译码、执行阶段。那么当执行当前语句时,PC的值就是指向下第2条指令(以当前指令为参考系),这就是我们所说的地址重定位,即地址重定位地址=偏移地址+当前PC地址因如上指令采用的为Thumb指令,当执行到PC时实际地址需+4,ARM指令模式需+8。所以得到如下计算
邮箱账号模块指令计算流程:
R1+PC=(a100ab104ab108a - 0xDB0)+DAC+4=1FD0-0xDB0+DAC+4=1FD0
即关键信息就在地址1FD0处,值的加密信息如下图
邮箱密码模块指令计算流程:
R1+PC=(a164ab164ab100a - 0xDC0)+DBC+4=202F-0xDC0+DBC+4=202F
即关键信息就在地址202F处,值的加密信息如下图
图4-5-1 寻址到的加密数据信息
4.6 解密
将拿到的本地数据通过拟写好的解密算法解密,该算法即是将拿到的字符串以“AB”字符分割,随后将分割出的字符逐个解析成整型进行算数运算(将所有值-51)最后再重组成新的字符串得以返回。
图4-6-1 解密算法封装
解密算法如下:
图4-6-2 解密算法
StringTokenizer是java中object类的一个子类,属于 java.util 包,用于分割字符串,该算法即是将拿到的字符串以“AB”字符分割。
图4-6-3 解密算法
解密后得到我们想要的本地关键数据,邮箱和密码
图4-6-4 解密结果
也可通过码表得出上述结果
图4-6-5 通过码表匹配结果
5. 修复方法
当用户不慎安装恶意app后,处理方式有四个步骤,用户可以通过以下方法卸载:
(1)立即关闭所有网络连接(断开手机移动网络和wlan),在未删除app前,建议禁止网络连接;
(2)在手机设置的应用信息中找到应用图标,点击卸载;
(3)当恶意程序已经运行且隐藏了图标,此时应在手机正在运行的程序中,找到该应用停止并卸载,若已激活了设备管理器需找到管理器界面停止授权并卸载。
(4)如果以上方法都无法删除,备份一些重要数据到电脑,然后恢复出厂设置。如果不放心,也可选择重置手机,以此规避已经投放到手机上的恶意负载的攻击。
6. 安全建议
恒安嘉新暗影移动安全实验室在此提醒广大用户,不轻易相信陌生人,不轻易点击陌生人发送的链接,不轻易下载不安全应用,不安装非正规途径来源的APP。用户可以采用以下方式来阻止恶意软件攻击:
- 谨慎打开未知短信的下载链接。
- 避免点击网页中的链接或下载附件。
- 仅从受信任的来源下载应用程序,建议去正规的应用市场或官方下载。
- 适时升级系统,常做对手机进行扫描查杀