freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

安卓逆向-APK结构到四大组件的分析
2021-08-19 16:58:28

前言

随着近年来的发展,移动设备的种类越来越多,移动应用程序也越来越多,存在的隐患也越来越多样,移动安全成为安全中的重要的一块。 Android系统内部非常复杂,经层层封装后。每一次看似简单的操作,背后所有的复杂工作都是交由系统来完成。正常情况程序启动后,首先需要依赖进程,那么就需要先创建进程,系统需要记录每个进程,这便产生了ProcessRecord。 在Android中,对于进程的概念被弱化,通过抽象后的四大组件,让开发者几乎感受不到进程的存在。 当应用退出时,进程也并非马上退出,而是调用onResume(),下次该应用再启动的时候;也可以不用 再创建进程直接初始化组件即可,提高启动速度。 接下来就让我们开始学习APK结构到四大组件的分析:

一、安卓基础知识

1、安卓历史版本

安卓5.0开始试用ART虚拟机,安卓系统也开始分32位和64位版本。

安卓4.4版本配置API19

android工具下载:
https://www.androiddevtools.cn/

2、安卓架构

linux:系统
Libraies:c代码库、函数库、本地数据库
WebKit:谷歌封装至CEF,CEF又封装至WebKit
application framework:安卓系统提供的API,方便做代码上的调整和操作,界面管理、包管理等等
applications:应用层,Java和安卓代码
Java:java代码
C/C++:so文件就是它写的,PC端上的线程文件,用java调用下C就可以用了,C反编译比较大,Java反编译比较容易;
JNI:如果java要调用C需要JNI做中间人,所以还需要了解JNI代码
Android:API

打包成APK后,是smali代码,动态调试也是smali代码,还需要了解smali代码,需要下断点。
如果调用的不是汇编的加密库,是自己写的,那么还需要了解ARM汇编知识,前提是SO里面是一个自定义算法就需要学习,高级知识点。

二、APK基本结构

1、APK解压

APK可以用zip打开,和Java的jar包类似。

image-20210805131848957

2、assets目录

这里面一般放的是资源,这里面的资源通常是没有编译过的,可以直接用,有图片、js、html等。

3、lib目录

目录放的一般是so文件,也就是本地代码,好几个文件夹一般...MIPS、x86、x64等,CPU的一些平台。

机器码

汇编:低级的符号语言

C语言:高级的符号语言这两个都是认机器的
都要经过CPU执行,意思就是认CPU,CPU架构不一样的话代码就不一样,或者就是编译后的机器码不一样。
armeabi:就是为了做兼容arm5的cpu

armeabi-v7a:是arm7的cpu,

大多数在用的有arm8、x64位的CPU......APK很少有64位的,在动态调试的时候会有静态分析和动态分析arm代码是不一样的,注意下即可。
libs:引用第三方的java包都会放入这里。

4、META-INF 目录

我们每次打包APK后都需要做一个签名,在系统里面是需要做验证的,不管是代码验证还是在安卓系统里面都需要签名验证,假设如果把APK文件改了,在放回去的话本身的签名和修改后放回去签名是不对应的,这时候怎么解决呢?
需要破解器破解系统核心,就算不重签名也可以放上去;或者安装在雷电等模拟器上面进行操作,模拟器支持不重签名也可以操作。

5、res目录

放的资源,程序的图标、样式、布局、XML等,编译之后的文件,直接查看是乱码,需要反编译的!APKtool即可反编译查看。

6、AndroidManifest.xml

清单文件,直接查看大部分是乱码,清单:APK需要使用系统的权限、包名是什么、APK是否支持调试等等内容。

7、resources.arsc**

编译之后的文件,语言包、程序内容等。

8、classes.dex

dex文件,运行在Dalivk虚拟机上的文件,是smali代码也就是源代码,需要反编译转换为smali代码,还可以把smali代码转换为java代码或者直接des转换为jar包也可以。
大型的apk里面会有好几个classes,本身是可以合成为一个的,为什么分开放,是因为文件大小是有限制的。

三、JVM、DVM、ART

1、JVM

就是java虚拟机,运行的是.java文件编译后的.class文件。

2、DVM

Dalvik虚拟机,在Android4.4及以前使用的都是Dalivk虚拟机,我们知道APK在打包的过程中会将java等源码通过javac编译成.class文件,但Dalvik虚拟机只会执行.dex文件,所以会用到dx工具会将.class文件打包成.dex文件在丢给Dalvik虚拟机执行,但是Dalivk虚拟机在启动时候会将.dex文件转换成快速与进行的看机器码,又因为65535这个问题,导致我们在应用冷启动的时候有一个合包的过程,最后导致的一个结果就是APP启动慢,这就是Dalvik虚拟机的JIT特性。
如果超过65535就会重新生成.dex文件!!

3、ART

ART虚拟机是在Android5.0才开始试用的Android虚拟机,ART虚拟机必须要兼容Dalvik虚拟机的特性,但是ART有一个很好的特性AOT(ahead of time),这个特性就是在安装APK的时候就将dex直接处理成可直接供ART虚拟机使用的机器码,ART虚拟机将.dex文件转换成可直接运行的.oat文件,ART虚拟机天生支持多dex,所以也不会有一个合包的过程,所以ART虚拟机会很大的提升APP冷启动速度。

四、重新理解APK打包流程

image-20210717003551792

1、前面三个是资源、源码、接口

2、然后从左往右理解,APP进化过程通过AAPT(绿色框都是工具),通过AAPT得到R.java和complied resources,R.java是java编辑器编译成的R文件。

3、然后.aidl Files通过AIDL工具编译成java文件,被处理的文件file文件,编译成java接口文件

4、然后左边和右边都指向了complier的java工具,然后全部编译变成.class文件

5、在右边还有一个第三方的jar包,和前面的.class都指向dex编译器编译为.dex文件,目前为第二次文件。

6、other resource是什么?静态、动态、第三方资源

7、三个全部指向了apkbuilder工具,apkbuilder是打包器,通过apkbuilder打包为apk文件!打包成APK后还是无法使用的。

8、通过jarsigner工具进行签名操作,签名就关联到了META-INF文件目录内的公钥和私钥,然后变成Signed.apk文件。

9、这个时候只需要通过zipalign压缩下即可使用了,zipalign为什么要改工具包呢,因为有对apk一个安装优化加速的过程提升操作。

五、APK中四个组件

1.活动(activity) :用于表现功能
2.服务(service):后台运行服务,不提供界面呈现
3.广播接收者(Broadcast recive) :用于接受广播
4、内容提供者(Content provider):支持多个应用中存储和读取数据,相当于数据库

1、Activity(活动)

Activity是一个界面,一个APP是由很多个Activity进行界面调用的,想要使用Activity需要在AndroidManifest中声明,只要调用的就需要声明!(AndroidKiller工具)

image-20210802105236152

如何创建Activity(活动)

新建一个类继承自Activity(活动)(eclipse工具)

image-20210802105738438

继承Activity才能被识别为一个活动界面MainActivity

2、Activity 运行过程

1)onCreate()

他会在活动第一次被创建的时候调用。在这个方法中完成活动的初始化操作,比如加载布局、绑定事件等。

image-20210802110039038

2)onStart()

这个方法在活动由不可见变为可见的时候进行调用。

界面呈现出来了

3)onResume()

这个方法在活动准备好和用户进行交互的时候进行调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。

3、Activity 销毁过程

1)onPause()

这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。

2) onStop()

这个方法在活动完全不可见的时候调用。他和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPuse()方法会得到执行,而onStop()方法并不会执行。

3)onDestroy()

这个方法在活动被销毁之前调用,之后活动的状态变为销毁状态。

4)onRestart()

这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。

image-20210802114207268

讲解:

Activity starts一个活动界面第一次被创建时,创建好后第一次会调用onCreate方法创建完成后会调用onStart,这时候界面呈现出来了用户是可见的,但是界面无法被用户所操作,因为还未调用
onResume,onResume一旦被调用界面上的控件可以被点击,获得焦点,onStart和onResume是创建被点击过程,所以只有去调用onResume活动界面才能成被点击的界面,然后到Activity is running开始运行后到onPause,onPause界面失去焦点后必须要去调用onResume获得焦点才能再次运行,那么暂停之后往下走还会去调用onStop方法,onStop这个时候用户是不可见时,如果是对话框则仍然可见的!直到onDestroy方法后活动界面才会消失!那么如果在onStop界面不想销毁了想反悔了,那么返回过去调用一个onRestart的方法重新让它直接到onStart方法,然后继续onStart -> onResume -> onPause ->onStop -> onDestroy -> onRestart -> Activity is down整个活动页面会被停止结束!

2、Service(服务)

1、Service概念

Android中的服务,他与Activity不同,他是不能与用户交互的,不能自己启动的,运行在后台的程序如果我们推出应用时,Service进程并没有结束,他仍然在后台运行,那么我们什么时候用到service呢?

比如我们播放音乐的时候,有可能想边听音乐边干些其他事情,当我们退出播放音乐的应用,如果不用Service,我们就听不到歌了,所以这时便就得用到Service了,又比如当我们一个应用的数据是通过网络获取的,不同时间(一段时间)的数据是不同的这时候我们可以用Service在后台定时更新,而不用每打开应用的时候在去获取。就像微信里面的语音一样!

2、Service生命周期

1、Service生命周期的介绍

Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用oncreate()和onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行oncreate()方法,而是直接执行onStart()方法

2、start Service启动Service的生命周期

执行start Service时,Service会经历onCreate -> onStart command(函数API)。当执行stop Service时,直接调用onDestroy方法。调用者如果没有stop Service,Service会一直在后台运行,下次调用者再起来仍然可以stopService。
如果不去触发,就一直在后台运行!

3、bondService启动Service的生命周期

执行bondservice时,Service会经历onCreate -> onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind-> onDestroy。这里所谓的绑定在一起就是说两者共存亡了。
就和音频一样概念!

3、广播接收者-Broadcast receive

1、介绍

BroadcastReceiver:广播接收者,及是接收来自android内部的广播,对接收到的广播进行选择处理,想要接收什么样的广播和内部定义的广播匹配,匹配则进行该做的处理操作,没有匹配则无操作,就比如在玩游戏的同时接收到短信事件,对此你要做什么操作,是想看短信内容还是不做什么处理继续玩游戏,这就是广播的用途,android内部的四大内置类之一:BroadcastReceiver!

下面介绍广播接收者的功能用途:

1、首先实现功能是:有一个发送给广播的按钮,编写的广播接收者对其处理。

则布局代码很简单,只需要一个按钮

2、编写广播接受类Reeiver,继承BrodcastReceiver

public class Receiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("哈哈,Received");
	}
}

image-20210802151131081

onReceive()方法是对接收匹配的处理操作,此时我只是输出一个文本,提示我已经接收到了广播信号。

3、因BroadcastReceiver是android内置类就如Activity类一样需要在AndroidManifest.xml文件注册

<receiver android:name=".Receiver">
	<intent-filter >
		<action android:name="android.intent.action.name"/>
	</intent-filter>
</receiver>

image-20210802151114907

其中action的name属性是自己定义的名字,想要匹配广播则发送的广播和此名称一致即可对其处理。

4、main.xml代码

点击按钮设置intent的action和上面设置一致即:android.intent.action.name

则接收者就是可接受的广播对其操作

sendBroadcast():发送广播

public class MainActivity extends Activity {
	//定义一个Button 变量send
	private Button send;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//绑定界面
		setContentView(R.layout.activity_main);
		 //绑定按钮
		send=(Button) this.findViewById(R.id.send);
		//监听send按钮
		send.setOnClickListener(new OnClickListener() {
		
			public void onClick(View v) {
				Intent i=new Intent();
				i.setAction("android.intent.action.name");
				//发送广播
				sendBroadcast(i);
			}
		});
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
 
}

image-20210802151159927

可想结果则会输出:哈哈,Received

2、Notfication(状态栏通知)的理解和使用

1、Notfication的类

在Android系统中,发一个状态栏通知还是很方便的。下面我们就来看一下,怎么发送状态栏通知,状态栏通知又有哪些参数可以设置?首先,发送一个状态栏通知必须用到两个类:NotificationManager、Notification。
1)NotificationManager:是状态栏通知的管理类,负责发通知、清除通知等。NotificationManager是一个系统Service,必须通过getSystemService()方法来获取。
2)Notification:是具体的状态栏通知对象,可以设置icon、文字、提示声音、振动等参数。

2、状态栏通知(Notifcation)的使用

1)创建Notification

通过NotificationManager的notify(int,Notifcation)方法来启动Notification.

第一个参数唯一的标识该Notification,第二个参数就是Notification对象。

2)更新Notifcation
调用Notification的 setLatestEventInfo方法来更新内容,然后再调用NotificationManager的notify()方法即可。(具体可以看下面的实例)
3)删除Notifcation
通过NotificationManager的cancel(int)方法,来清除某个通知。其中参数就是Notification的唯一标识ID。当然也可以通过cancelAlI()来清除状态栏所有的通知。

4)Notification设置(振动、铃声等)

3、状态栏通知(Notification)参数的设置

//新建状态栏通知
baseNF = new Notification();
//设置通知在状态栏显示的图标
baseNF .icon = R.drawable.icon;
//通知时在状态栏显示的内容
baseNF.tickerText = "You clicked BaseNF!";
//通知的默认参数DEFAULT_SOUND,DEFAULT_VIBRATE,DEFAULT_LIGHTS.
//如果要全部采用默认值,用DEFAULT_ALL.
//此处采用默认声音
baseNF.defaults = Notification.DEFAULT_SOUND;
//第二个参数︰下拉状态栏时显示的消息标题expanded message title
//第三个参数:下拉状态栏时显示的消息内容 expanded message text
//第四个参数:点击该通知时执行页面跳转
baseNF.setLatestEventInfo(Lesson_10.this,"Title01","Content01",pd);
//发出状态栏通知
nm.notify(Notification_ID_BASE, baseNF);

4、对PendingInten理解和使用

  1. PendingIntent 从名字上看起来就和Intent有些类似,它们之间也确实存在着不少共同点。比如它们
    都可以去指明某一个“意图”,都可以用于启动活动、启动服务以及发送广播等。不同的是,Intent 更加倾向于去立即执行某个动作,而PendingIntent 更加倾向于在某个合适的时机去执行某个动作。所以,也可以把 PendingIntent简单地理解为延迟执行的Intent。
    2)如何获取Pendinglntent的实例?
    通过三个静态的方法:

getActivity()方法
getBroadcast()方法
getService()方法

四、内容提供者-Content provider

1、内容提供者的使用详解

1、Content Providers的概念

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供器是Android实现跨程序共享数据的标准方式。不同于文件存储和 SharedPreferences 存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

2、Content Providers的两种方法

1)使用现有的内容提供器来读取和操作相应的程序中的数据

2)创建自己的内容提供器给我们程序的数据提供外部访问接口

3、访问其他程序中的数据

当一个应用程序通过内容提供器对其数据提供了外部访问接口,任何其他的应用程序就都可以对这部分数据进行访问。Android系统中自带的电话簿、短信、媒体库等程序都提供了类似的访问接口。
1) ContentResolver的使用
a)对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要助ContentResolve类,可以通过Context中的getContentResolver()方法获取到该类的实例。
b)ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中 insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。但不同于SQLiteDatabase,ContentResolver 中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。
c) 什么是URL?
内容URI给内容提供器中的数据建立了唯一标识符,它主要由两部分组成,权限(authority)和路径(path)。权限是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进
行命名。路径则是用于对同一应用程序中不同的表做区分的,通常都会添加到权限的后面

第一部分:content://相当于http://
第二部分:com.eyy5.app.provider相当于vip.qiyikt.com
第三部分: table1相当于forum

例如:content://com.eyy5.app.provider/table1
获取Uri对象:Uri uri = Uri.parse("content://com.eyy5.app.provider/table1")

d) 查询

uri:指定查询某个应用程序下的某一张表
projection:指定查询的列名
selection:指定where的约束条件
selectionArgs:为where中的占位符提供具体的值
sortOrder:指定查询结果的排序方式
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sort0rder);
if ( cursor != null) {
	while (cursor.moveToNext()){
		String column1 = cursor.getString(cursor.getColumnIndex("column1"));
		int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
	}
	cursor.close();
}

e) 添加

ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);

f) 修改

ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});

g) 删除

getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });

2) 读取系统联系人的步骤

a) 获取URL

通过ContactsContract.CommonDataKinds.Phone类已经把Uri做好了封装,并且提供了一个CONTENT_URI常量,这个常量就是通过Uri.parse()解析过的Uri。

b) 获取联系人名字的字段

ContactsContract.CommonDataKinds.Phone.DISplay\_NAME

c ) 获取联系人电话的字段

ContactsContract.CommonDatakinds.Phone.NUMBER

注意:

需要访问联系人的权限:android.permission.READ_CONTACTS

2、使用SharedPreferences存储数据

SharedPreferences的概念

**1)保存基于XML文件存储的key-value键值对数据,**通常用来存储一些简单的配置信息。通过DDMS的
File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储
在/data/shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。

**2) SharedPreferences本身是一个接口,**程序无法直接创建SharedPreferences实例,只能通过
Context提供的getSharedPreferences(String name,int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下:
**Context.MODE_PRIVATE:**指定该SharedPreferences数据只能被本应用程序读、写。
Context.MODE_WORLD_READABLE:指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE:指定该SharedPreferences数据能被其他应用程序读,写。

3)Editor有如下主要重要方法:

**SharedPreferences.Editor clear():**清空SharedPreferences里所有数据
**SharedPreferences.Editor putXxx(String key , xxx value):**向SharedPreferences存入指定key对应的数据,其中xxx可以是boolean,float,int等各种基本类型据
**SharedPreferences.Editor remove():**删除SharedPreferences中指定key对应的数据项

**boolean commit():**当Editor编辑完成后,使用该方法提交修改

**4) SharedPreferences是以键值对来存储应用程序的配置信息的一种方式,**它只能存储基本数据类型。一个程序的配置文件仅可以在本应用程序中使用,或者说只能在同一个包内使用,不能在不同的包之间使用。实际sharedPreferences是采用了XML格式将数据存储到设备中,在DDMS 中的File Explorer中的/data/shares_prefs下。

5)SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论
SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。

使用范围:

保存少量的数据,并且这些数据的格式非常简单:字符串型,基本类型的值。比如应用程序的各种配置(如是否打开音效,是否使用震动效果,小游戏的玩家积分等),解锁口令密码等

3、文件存储方法

1、文件存储数据的概念cookie
文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样方便于之后将数据从文件中重新解析出来。
2、文件存储数据的实现步骤
Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/fles/目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE 和MODE_APPEND:
MODE_PRIVATE 是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容。

MODE_APPEND则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。

其实文件的操作模式本来还有另外两种,MODE_WORLD_READABLE 和MODE_WORLD_WRITEABLE,这两种模式表示允许其他的应用程序对我们程序中的文件进行读写操作,不过由于这两种模式过于危险,很容易引起应用的安全性漏洞,现已在Android 4.2版本中被废弃。

除此之外,Context还提供了如下几个重要的方法:

getDir(String name,int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录File getFilesDir():获取该应用程序的数据文件夹的绝对路径
String] fileList():返回该应用数据文件夹的全部文件

读写sdcard上的文件

其中读写步骤按如下进行:
1、调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,且应用程序具有读写SD卡的权限,如下代码将返回true

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

2、调用Environment.getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录,或者使用"/mnt/sdcard/"目录
3、使用IO流操作SD卡上的文件
注意点∶手机应该已插入SD卡,对于模拟器而言,可通过mksdcard命令来创建虚拟存储卡必须在AndroidManifest.xml上配置读写SD卡的权限。

<uses-permission android:name= "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE"/>

记住文件夹,二次打包,宣传官方更新的版本,它只要用了,就会获得他本地的密码!

4、SQLite存储数据

1、SQLite数据库的概念
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百K的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL 语法,还遵循了数据库的ACID事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手SQLite。而SQLite又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。Android正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。
2、如何创建数据库

Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。
注意:
SQLiteOpenHelper是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个帮助类去继承SQLiteOpenHelper。

SQLiteOpenHelper中有两个抽象方法,**分别是onCreate()和 onUpgrade(),**我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。

2) SQLiteOpenHelper的构造方法

//context:上下文对象
//name:数据库名,创建数据库时使用的就是这里指定的名称
//factory:允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null
//version:当前数据库的版本号,可用于对数据库进行升级操作
public MyDatabaseHelper(Context context,String name , CursorFactory factory,int version) {
	super(context,name,factory,version);
}

这样,就构建出SQLiteOpenHelper的实例,再调用它的getReadableDatabase()或
getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data//databases/目录下。此时,重写的onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。
3)SQLiteOpenHelper的两个重要实例方法
getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已经存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而 getWritableDatabase()方法则将出现异常。
4)如何升级数据库
a) 数据库版本号的使用
b) 该onUpgrade()方法是用于对数据库进行升级的,它在整个数据库的管理工作当中起着非常重要的作用。
2、Android中提供了一系列的辅助性方法对数据的添加、更新、删除、查询数据

1)添加
SQLiteDatabase 中提供了一个insert()方法,这个方法就是专门用于添加数据的。它接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的 put()方法重载,用于向ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
2)更新
SQLiteDatabase 中也是提供了一个非常好用的update()方法用于对数据进行更新,这个方法接收四个参数,第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是ContentValues对象,要把更新数据在这里组装进去。第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有。
3)删除
SQLiteDatabase 中提供了一个delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数仍然是表名,这个已经没什么好说的了,第二、第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
4)查询
SQLiteDatabase中还提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。那我们就先来看一下这七个参数各自的含义吧,第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据。第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。

//table:指定查询的表名l/columns:指定查询的列名
//selection:指定where 的约束条件
//lselectionArgs:为where中的占位符提供具体的值
//groupBy:指定需要group by 的列
//having:对group by 后的结果进一步约束
//lorderBy:指定查询结果的排序方式
Cursor cursor=dp . query(table,columns,selection,selectionArgs,groupBy,having,orderBy);

3、使用SQL Sericve执行sql

1)添加

db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });

2)更新

db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99","The DaVinci Code" });

3)删除

db.execSQL("delete from Book where pages > ?",new String[{"500"});

4)查询

db.rawQuery("select * from Book ", null);

5、网络数据存储

工作原理:
就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数
据进行解析和处理就可以了。

1)使用HttpURLConnection
获取到HttpURLConnection 的实例:

URL url = new URL("http://ww.baidu.com");
HttpURLConnection conn = (HttpURLConnection) url. openConnection();

设置一下HTTP 请求所使用的方法。常用的方法主要有两个,GET和POST。GET表示希望从服务器那里获取数据,而POST则表示希望提交数据给服务器。

//记得get要大写
conn . setRequestMethod("GET");
//记得POST要大写
conn . setRequestMethod("POST");
//连接超时、读取超时的毫秒数
conn . setConnectTimeout(8000);
conn.setReadTimeout(8000);

调用getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是对输入流进行读取。

InputStream in =conn.getInputStream();
关闭HTTP连接:
conn.disconnect();

2)使用HttpClient
HttpClient是Apache 提供的HTTP网络访问接口,从一开始的时候就被引入到了AndroidAPI中。
HttpClient 是一个接口,因此无法创建它的实例,通常情况下都会创建一个 DefaultHttpClient 的实例:

HttpClient httpClient = new DefaultHttpClient();
GET请求:
HttpGet httpGet = new HttpGet("http://www.baidu.com");httpClient.execute(httpGet);

POST请求:
HttpPost httpPost = new HttpPost("http://www.baidu.com");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username ","admin");
params.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params,"utf-8");
httpPost.setEntity(entity);
httpClient.execute(httpPost);

执行execute()方法之后会返回一个HttpResponse对象,服务器所返回的所有信息就会包含在这里面。通常情况下我们都会先取出服务器返回的状态码,如果等于200就说明请求和响应都成功了。例如:

if (httpResponse.getStatusLine(.getStatusCode() == 200) {
	//请求和响应都成功了
}

在if 判断的内部取出服务返回的具体内容,可以调用getEntity()方法获取到一个HttpEntity 实例,然后再用EntityUtils.toString()这个静态方法将 HttpEntity转换成字符串即可。

HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity);

注意

如果服务器返回的数据是带有中文的,直接调用EntityUtils.toString()方法进行

转换会有乱码的情况出现,这个时候只需要在转换的时候将字符集指定成utf-8就可以了。

String response = EntityUtils.toString(entity,"utf-8");

需要添加网络权限:

<uses-permission android: name="android.permission.INTERNET"/>
# android安全 # Android # Android应用程序 # APK逆向分析 # Activity
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录