-
基于Xposed框架截取安卓手机应用数据信息
2017-04-07 10:29:26首先介绍的是Xposed只是一个安卓手机的框架,并不具有任何功能,为了实现个人的需求,我们需要自己编写模块加入到这个框架中去。现如今比较主流的Xposed框架包括微信运动步数的作弊模块、微信的虚拟位置定位模块等、...首先介绍的是Xposed只是一个安卓手机的框架,并不具有任何功能,为了实现个人的需求,我们需要自己编写模块加入到这个框架中去。现如今比较主流的Xposed框架包括微信运动步数的作弊模块、微信的虚拟位置定位模块等、Xposed的框架十分地强大,可以实现截取信息和修改信息的功能。要想实现你心中所想要实现的功能,你首先要去知道这个功能的方法在哪个应用包中的哪个类中的哪个方法中的某个参数,那么你的模块就要指明哪个包,哪个类,哪个方法,当系统重启加载目标应用包时,加载了你的模块的Xposed框架会识别到这个进程,接下来,如果你指明的应用中的某一方法被系统执行了,那么Xposed也会识别到,然后你的模块会进行Hook这个方法(Hook就是钩子陷阱的意思,也可以说是拦截)这个方法,并可以利用模块的接口让方法的参数和返回值结果暴露出来甚至是进行修改。
现在的网络教程中有很多是模拟写一个用户登录的界面,之后在利用Xposed框架进行截取信息以及修改用户信息的教程我在这里就不再过多的罗嗦了,大致的流程我给大家简单的介绍一下:
1. private boolean isOK(String account, String password){
2. return account.equals(ACCOUNT) && password.equals(PASSWORD);
3. }
这个是模拟写一个登录界面的APP,上方的代码是判断用户ID和PassWord的一个方法,假如ID和PassWord相符,返回turn反之false;那么假如我们想要hook到这个App中的登录界面的用户账号和密码,并且我们通过反编译知道了这个判断账号密码的类和方法了,那我们就可以利用Xposed框架来进行截取工作了,截取的方法如下所示:
1. new XC_MethodHook() {
2. @Override
3. protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
4. XposedBridge.log("账号:"+(String)param.args[0]+" 密码:"+(String)param.args[1]);
5. Log.d("zz","账号:"+(String)param.args[0]+" 密码:"+(String)param.args[1]);
6. }
7.
8. @Override
9. protected void afterHookedMethod(MethodHookParam param) throws Throwable {
10. Log.d("zz", param.getResult().toString());
11. }
12. });
通过param.arg就可以获取目标方法中的参数,通过log的方式打印出来,而param.getResult()方法就是获取返回结果的打印结果必然是true或是false;当然了如果你比较坏想替换他的账号密码也是可以的,在beforeHookMethod方法内对param.arg直接进行赋值,这样他在登录的App中无论如何填写账号密码,都会被替换到如下的账号密码。
1. //修改参数
2. param.args[0]="ssssss";
3. param.args[1]="111111";
以上是对别人的博客进行一个总结,也是为读者说明这个安卓Xposed框架的功能说明进行简单的介绍讲解,在下面我会详细进行说明和对某一款使用量很大的App的数据库进行hook操作,当然我是只是一个小新手Android开发经验只有短短的不到一年时间,只能够hook到加密过的乱码也没有仔细研究,只为大家提供一个思路和Xposed的简单使用教程。话不多说进入正题:
一、手机环境的要求:
Root过的手机一部,并且安装了Xposed框架。(Root教程和Xposed插件请百度搜索也可以qq加一些Xposed群,针对不
同机型版本,里面有很多详细的介绍)二、导入jar包
XposedBridgeApi.jar包导入到lib中,在Add as Library,这里必须注意两点细节,一是,放jar包的文件夹名字一定是“lib”或“jar”(自己创建lib文件与libs同级)亲测放到“libs”里没用;二是,需要在build.gradle里的dependencies将compile改为provided,不然可能会报错。
三、新建一个类(Xposed模块类),命名为Module,代码如下:
public class Module implements IXposedHookLoadPackage{ @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { hookQuery(loadPackageParam); } private void hookQuery(XC_LoadPackage.LoadPackageParam loadPackageParam) { try{ Class hookClass = loadPackageParam.classLoader.loadClass("com.tencent.mmdb.database.SQLiteDatabase"); XposedBridge.hookAllMethods(hookClass, "query", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); StringBuilder sb = new StringBuilder(); for (Object arg : param.args) { if (arg instanceof String) { sb.append("<==string==>"); sb.append(arg.toString()); } else if (arg instanceof String[]) { String []subArgs = (String []) arg; for (String str : subArgs) { sb.append("<==[ ]==>"); sb.append(str); } } } XposedBridge.log("hookQuery的数据为:" + sb.toString()); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } }); }catch (Exception e){ } } }
使用hookAllMethod是因为我将该APP下载反编译APK后,查看目标包下的同类中有多个重载的方法(方法名相同而参数列表不同的同名方法)而通常情况下可以直接使用XposedHelpers.findAndHookMethod()方法进行hook操作,在beforeHookedMethod方法中对param.args进行判断,因为在目标方法中的参数列表有数组存在,我们要打印结果就要进行判断在添加到一个字符串中。
四、配置清单文件,其中内容复制粘贴就可以,不存在Activity展示界面,此项目只作为一个Xposed模块插件而存在,代码如下所示:
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <meta-data android:name="xposedmodule" android:value="true"/> <meta-data android:name="xposeddescription" android:value="Hook WeiXin Query"/> <meta-data android:name="xposedminversion" android:value="54"/> </application>
五、在main文件夹下创建一个assets文件,并在里面创建一个普通的文件,,命名为“xposed_init”,在里面添加一段字符串,内容为包名 + 模块类名(注意在最后没有分号!!不然会不识别!!!)
com.sq.xp_rawquery_test.Module
六、使用说明
如果代码被混淆过,即使你明知道代码中要hook的类名和方法名,但都不一定能用,必须以smali中的名字为准,比如:queryMethod()混淆之后再smali中的函数名为a,那么hook的时候就必须写a,而不是queryMethod,第一个参数类名同理。
七、安装模块及使用
最后模块已经完成,接下来就是对其进行打包 Build -> Generate Signed APK,并安装在手机上。因为没有MainActivity,所以安装后不会弹出任何界面,安装了Xposed的手机会弹出消息,提示模块已经更新,此时可以选择Xposed菜单中的“框架” -> “软重启”(软重启手机不会断电,相当于电脑重启,比硬重启要快),之后正常启动APP,登录账号密码就会被拦截,在Xposed的日志中可以查看到账号密码,在Android Studio中也可以查看到log日志。最后窃取到的信息多为一些乱码数据,但是确实实现了数据的截取,只是怪自己代码经验不足,和人家TX的WX团队太厉害在加上反编译后的源码真的好难看懂,所以就不再进一步的深究了。最下方是自己的项目GitHub的下载地址,XposedBridgeApi.jar包可以到项目中的lib包中直接复制拿来用。(微信版本不一致是无法hook到数据的,因为反编译后的包名类名以及方法名都有可能发生改变)
结语:
Xposed的框架对于安卓手机来说就是一个神器,可以使用到的地方只要你能力够强,你的手机就可以变得有多高大上,对于一个程序员界的新手来说,希望通过这篇文章可以让更多人知道Xposed框架,可以设计出更多的强大模块来改变你的手机,如果以上文章有何错误希望可以被指出,请原谅我这个萌新写的博客的一些不足之处,如果写的不好还请谅解,可以在评论区给我一些改进的意见
。
https://github.com/shiqiangdva/xposed.git
-
安卓跨应用数据共享-ContentProvider和ContentResolver
2020-11-20 10:43:15ContentProvider 一、预备知识 uri对象 ...使用通配符 *:表示匹配任意长度的任意字符 #:表示匹配任意长度的数字 uri字符串转uri对象:Uri.parse("字符串") ... //以当前位置为参考,移动到指定行 c.moveContentProvider
一、预备知识
uri对象
content://com.example.app.provider/table1
content://com.example.app.provider/table1/1
使用通配符
*
:表示匹配任意长度的任意字符
#
:表示匹配任意长度的数字uri字符串转uri对象:
Uri.parse("字符串")
Cursor对象
常用方法:来自Carson_Ho
c.move(int offset); //以当前位置为参考,移动到指定行 c.moveToFirst(); //移动到第一行 c.moveToLast(); //移动到最后一行 c.moveToPosition(int position); //移动到指定行 c.moveToPrevious(); //移动到前一行 c.moveToNext(); //移动到下一行 c.isFirst(); //是否指向第一条 c.isLast(); //是否指向最后一条 c.isBeforeFirst(); //是否指向第一条之前 c.isAfterLast(); //是否指向最后一条之后 c.isNull(int columnIndex); //指定列是否为空(列基数为0) c.isClosed(); //游标是否已关闭 c.getCount(); //总数据项数 c.getPosition(); //返回当前游标所指向的行数 c.getColumnIndex(String columnName);//返回某列名对应的列索引值 c.getString(int columnIndex); //返回当前行指定列的值
遍历数据
c.moveToFirst(); while (!c.isAfterLast()) { int id=result.getInt(c.getColumnIndex("id")); // ... c.moveToNext(); } c.close();
二、提供方Provider
-
写一个ContentProvider的子类,重写方法
- onCreate:初始化一个SQLiteOpenHelper对象,通过getReadableDatabase和getWritableDatabase获得一个SQLiteDatabase对象,用来操作数据库,给下面的方法使用。(此SQLiteOpenHelper对象可以使用内部类方式定义子类)
- query
- insert
- upadte
- delete
- getType:根据uri返回一个MIME
@Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = mDbOpenHelper.getWritableDatabase(); String[] param = matchParam(uri); if (!TextUtils.isEmpty(param[0]) && !TextUtils.isEmpty(param[1])) { return db.delete(param[0], "id=?", new String[]{param[1]}); } else if (TextUtils.isEmpty(param[1])) { return db.delete(param[0], selection, selectionArgs); } return 0; } //用来从uri中判断查询的表,和分离路径参数(路径中的id) private String[] matchParam(Uri uri) { String[] param = new String[]{null, null}; switch (uriMatcher.match(uri)) { case USER_DIR: param[0] = "user"; break; case USER_ITEM: param[0] = "user"; // “/”为分隔符,第一个“/”右边元素下标为0;“//”不算 param[1] = uri.getPathSegments().get(1); break; } return param; } @Override public String getType(@NonNull Uri uri) { switch (uriMatcher.match(uri)) { case USER_DIR: // 多条记录vnd.android.cursor.dir开头 return "vnd.android.cursor.dir/vnd.cn.adolf.db.provider.user"; case USER_ITEM: // 一条记录vnd.android.cursor.item开头 return "vnd.android.cursor.item/vnd.cn.adolf.db.provider.user"; } return null; }
-
在manifest.xml中注册provider
<provider android:name="provider类路径名" android:authorities="一般是 包名.provider" android:enabled="true" android:exported="true" />
github源代码: AdolfDbProvider.java
三、调用方ContentResolver
- 使用
context.getContentResolver();
获得一个ContentResolver对象 - 调用ContentResolver对象的
insert, delete, update, query
- 要传入一个uri:
content://provider注册的authorities/表名/id
可以使用一个Manager来管理ContentResolver对象的调用,用来装配ContentResolver所调用方法的参数
public class DbResolverManager { private static final String ADOLF_URI = "content://cn.adolf.db.provider/"; private static DbResolverManager instance; // 懒汉,在需要使用时才实例化 // private static DatabaseManager instance= new DatabaseManager(context); // 饿汉,初始化时就实例化 private Context mContext; private ContentResolver mResolver; private DbResolverManager(Context context) { this.mContext = context; this.mResolver = context.getContentResolver(); } public static synchronized DbResolverManager getInstance(Context context) { if (instance == null) { instance = new DbResolverManager(context); } return instance; } public Uri addUser(UserBean userBean) { Uri uri = Uri.parse(ADOLF_URI + "user"); ContentValues values = new ContentValues(); values.put("username", userBean.getUsername()); values.put("sex", userBean.getSex()); values.put("motto", userBean.getMotto() + System.currentTimeMillis()); Uri newUri = mResolver.insert(uri, values); return newUri; } public int deleteUserById(int id) { if (findUserById(id) == null) return 0; Uri uri = Uri.parse(ADOLF_URI + "user/" + id); int delete = mResolver.delete(uri, null, null); return delete; } public Cursor findUserById(int id) { Uri uri = Uri.parse(ADOLF_URI + "user/" + id); Cursor cursor = mResolver.query(uri, null, null, null, null); return cursor; } //...省略其他方法 }
-
-
安卓应用用户数据_3分钟短文:Laravel应用跟用户打交道,就从拿到他们的数据开始!...
2020-11-23 15:02:16而laravel是偏重后端的,所以为了给后端的开发同学缓冲的时间,我们跳过视图,先来说说用户数据的获取和处理,这几乎是任何应用必备之功能。用户数据同时又是危险的!本文不会教你规避危险!代码时间我们在讲路由...引言
本来讲完路由和控制器,应该接着讲一讲视图的,但是鉴于视图的功能实在太枯燥,况且是前端页面,免不了写一些js和css。而laravel是偏重后端的,所以为了给后端的开发同学缓冲的时间,我们跳过视图,先来说说用户数据的获取和处理,这几乎是任何应用必备之功能。
用户数据同时又是危险的!本文不会教你规避危险!
代码时间
我们在讲路由规划的时候,说了如何使用url的位置参数绑定的方式进行导向,其实那也是一种获取用户输入数据的方式, 只不过,传入的位置参数一般都人畜无害,公开访问,任你来来往往。
如果是单个用户的私密数据,必然是不能直接url内嵌传输的。
laravel把用户的输入存储在 Input 对象内,而从逻辑上看,用户输入应该归属于请求项的,所以 Request 也继承了 Input 的方法和数据。 比如有下面的路由:
Route::post('form', function (IlluminateHttpRequest $request) { // $request->method() });
模拟的是一个表单地址,通过post方法传送数据,然后使用 Request 方法。可供使用的获取方法,我们一一道来。
先说说 $request->all(),这一个是打印所有的输入数据,比如表单内可能有下面这些字段。HTML 内容你们将就看一下哈!
<form method="post" action="/post-route?utm=12345"> {{ csrf_field() }} <input type="text" name="firstName"> <input type="submit"> </form>
为了调试数据,我们的路由这样注册:
Route::post('/post-route', function (Request $request) { var_dump($request->all()); });
大家猜猜会输出些什么内容呢?
/** * [ * '_token' => 'CSRF token here', * 'firstName' => 'value', * 'utm' => 12345 * ] */
没错儿,就是看到的post表单的字段 firstName,还有 querystring 的查询参数 utm,还有一个是用于 CSRF 防护的laravel内置函数,默认的表单字段就是 __token,所以大可不必费心。
但是有代码洁癖的同学会觉得受不了,这个不是我的字段,我在代码里也用不到,我不想看到这个 __token,这个陌生的变量。可以,我们在请求参数内直接过滤掉就好了:
Route::post('/post-route', function (Request $request) { var_dump($request->except('_token')); });
其中,except() 方法就是用来过滤某些字段,不需要使用的。与之对应的就是 only() 方法,是指定哪些是用的字段。这样用:
$request->only(['firstName', 'utm']);
还有些时候逻辑判断有些键是否存在,laravel提供了两种方式判断是否存在,一个是 has ,一个是 exists。有同学会疑问,为什么有了 has还要 exists呢?因为他们的作用不同。
has方法不仅会判断该键存在,如果该键的值是 空(比如 null,空字符串,false,0,空数组,空对象等等),也是判断为false的。所以就有必要使用 exists 单纯地判断键的存在与否,大家用的时候一定要注意这个细微的差别。
用法很简单,直接调用方法即可:
if ($request->has('utm')) { // Do some work }
如果对于输入值键不存在,我们也可以为其设置一个默认值,这就是 input 方法的第二个参数,使用如下调用:
Route::post('/post-route', function (Request $request) { $userName = $request->input('name', 'anonymous'); });
如果用户没有传入 name 字段,那么就用 anonymous 替代。
上面说的都是简单的键值对,还有一种表单,传入的字段可能会是数组,比如下面这个:
<form method="post" action="/post-route"> {{ csrf_field() }} <input type="text" name="employees[0][firstName]"> <input type="text" name="employees[0][lastName]"> <input type="text" name="employees[1][firstName]"> <input type="text" name="employees[1][lastName]"> <input type="submit"> </form>
laravel内获取数组可以使用点式方式读取,这是因为laravel解析的时候使用了助手类 Arr 的通用方法。比如获取某个键:
$employeeZeroFirstName = $request->input('employees.0.firstName');
明确地指定键名,并对应好层级即可。还可以使用星号匹配某一层级所有的键:
$allLastNames = $request->input('employees.*.lastName');
或者仅指定某个键,后面不管多少层级原样返回:
$employeeOne = $request->input('employees.1');
具体用法读者可以查看源码或文档,深入研究一下Arr操作类的用法。
写在最后
本文几乎涵盖了用户输入input方法的最为常用的一些方法,我们只讲了怎么正常地获取用户数据,没有讲如何验证数据有效性。因为前端验证几乎形同虚设,最后能写到数据库的数据,还是要应用程序把关。
那就是验证器的工作了,验证器,我们放到后续章节再详细地讲。
Happy coding :-)
我是@程序员小助手,专注编程知识,圈子动态的IT领域原创作者
-
安卓应用安全指南 4.5.2 使用 SQLite 规则书
2018-03-22 18:41:57安卓应用安全指南 4.5.2 使用 SQLite 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 使用 SQLite 时,遵循以下规则: 4.5.2.1 正确设置...安卓应用安全指南 4.5.2 使用 SQLite 规则书
原书:Android Application Secure Design/Secure Coding Guidebook
译者:飞龙
使用 SQLite 时,遵循以下规则:
4.5.2.1 正确设置 DB 文件位置和访问权限(必需)
考虑到 DB 文件数据的保护,DB 文件位置和访问权限设置是需要一起考虑的非常重要的因素。 例如,即使正确设置了文件访问权,如果 DB 文件位于无法设置访问权的位置,则任何人可以访问 DB 文件,例如, SD 卡。 如果它位于应用目录中,如果访问权限设置不正确,它最终将允许意外访问。 以下是正确分配和访问权限设置的一些要点,以及实现它们的方法。 为了保护数据库文件(数据),对于位置和访问权限设置,需要执行以下两点。
1) 位置
位于可以由
Context#getDatabasePath(String name)
获取的文件路径,或者在某些情况下,可以由Context#getFilesDir11
获取的目录。2) 访问权限
设置为
MODE_PRIVATE
(只能由创建文件的应用访问)模式。通过执行以下2点,即可
创建其他应用无法访问的 DB 文件。 以下是执行它们的一些方法。- 使用
SQLiteOpenHelper
- 使用
Context#openOrCreateDatabase
创建 DB 文件时,可以使用
SQLiteDatabase#openOrCreateDatabase
。 但是,使用此方法时,可以在某些 Android 智能手机设备中创建可从其他应用读取的 DB 文件。 所以建议避免这种方法,并使用其他方法。 上述量种方法的每个特征如下 [11][11] 这两种方法都提供了(包)目录下的路径,只能由指定的应用读取和写入。
使用
SQLiteOpenHelper
当使用
SQLiteOpenHelper
时,开发人员不需要担心很多事情。 创建一个从SQLiteOpenHelper
派生的类,并为构造器的参数指定DB名称(用于文件名)[12],然后满足上述安全要求的 DB 文件会自动创建。[12] (未在 Android 参考中记录)由于可以在
SQLiteOpenHelper
实现中,将完整文件路径指定为数据库名称,因此需要注意无意中指定不能控制访问权限的地方(路径)(例如 SD 卡)。对于如何使用,请参阅“4.5.1.1 创建/操作数据库”的具体使用方法。
使用
Context#openOrCreateDatabase
使用
Context#openOrCreateDatabase
方法创建数据库时,文件访问权应由选项指定,在这种情况下,请明确指定MODE_PRIVATE
。对于文件安排,数据库名称(用于文件名)可以像
SQLiteOpenHelper
一样指定,文件将在满足上述安全要求的文件路径中自动创建。 但是,也可以指定完整路径,因此有必要注意指定 SD 卡时,即使指定MODE_PRIVATE
,其他应用也可以访问。MainActivity.java(显式设定 DB 访问权的示例)
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //Construct database try { //Create DB by setting MODE_PRIVATE db = Context.openOrCreateDatabase("Sample.db", MODE_PRIVATE, null); } catch (SQLException e) { //In case failed to construct DB, log output Log.e(this.getClass().toString(), getString(R.string.DATABASE_OPEN_ERROR_MESSAGE)); return; } //Omit other initial process }
访问权限有三种可能的设置:
MODE_PRIVATE
,MODE_WORLD_READABLE
和MODE_WORLD_WRITEABLE
。 这些常量可以由或运算符一起指定。 但是,除API_PRIVATE
之外的所有设置,都将在 API 级别 17 和更高版本中被弃用,并且会在 API 级别 24 和更高版本中导致安全异常。 即使对于 API 级别 15 及更早版本的应用,通常最好不要使用这些标志 [13]。[13]
MODE_WORLD_READABLE
和MODE_WORLD_WRITEABLE
的更多信息,以及其使用的注意事项,请参见“4.6.3.2 访问目录的权限设置”。MODE_PRIVATE
只有创建者应用可以读写MODE_WORLD_READABLE
创建者应用可以读写,其他人只能读MODE_WORLD_WRITEABLE
创建者应用可以读写,其他人只能写
4.5.2.2 与其它应用共享 DB 数据时,将内容供应器用于访问控制(必需)
与其他应用共享 DB 数据的方法是,将 DB 文件创建为
WORLD_READABLE
,WORLD_WRITEABLE
,以便其他应用直接访问。 但是,此方法不能限制访问或操作数据库的应用,因此数据可以由非预期的一方(应用)读或写。 因此,可以认为数据的机密性或一致性方面可能会出现一些问题,或者可能成为恶意软件的攻击目标。如上所述,在 Android 中与其他应用共享数据库数据时,强烈建议使用内容供应器。 内容供应器存在一些优点,不仅从安全的角度来实现对 DB 的访问控制,而且从设计角度来看, DB 纲要结构可以隐藏到内容中。
4.5.2.3 在 DB 操作期间处理变量参数时,必需使用占位符(必需)
在防止 SQL 注入的意义上,将任意输入值并入 SQL 语句时,应使用占位符。 下面有两个方法用占位符执行 SQL。
- 使用
SQLiteDatabase#compileStatement()
,获取SQLiteStatement
,然后使用SQLiteStatement#bindString()
或bindLong()
等,将参数放置到占位符之后。 - 在
SQLiteDatabese
类上调用execSQL()
,insert()
,update()
,delete()
,query()
,rawQuery()
和replace()
时,使用具有占位符的 SQL 语句。
另外,通过使用
SQLiteDatabase#compileStatement()
执行SELECT
命令时,存在“仅获取第一个元素作为SELECT
命令的结果”的限制,所以用法是有限的。在任何一种方法中,提供给占位符的数据内容最好根据应用要求事先检查。 以下是每种方法的进一步解释。
使用
SQLiteDatabase#compileStatement()
:数据以下列步骤提供给占位符:
- 使用
SQLiteDatabase#compileStatement()
获取包含占位符的 SQL 语句,如SQLiteStatement
。 - 使用
bindLong()
和bindString()
方法为创建的SQLiteStatement
对象设置占位符。 - 通过
ExecSQLiteStatement
对象的execute()
方法执行 SQL。
DataInsertTask.java(占位符的用例):
//Adding data task public class DataInsertTask extends AsyncTask<String, Void, Void> { private MainActivity mActivity; private SQLiteDatabase mSampleDB; public DataInsertTask(SQLiteDatabase db, MainActivity activity) { mSampleDB = db; mActivity = activity; } @Override protected Void doInBackground(String... params) { String idno = params[0]; String name = params[1]; String info = params[2]; //*** POINT 3 *** Validate the input value according the application requirements. if (!DataValidator.validateData(idno, name, info)) { return null; } // Adding data task //*** POINT 2 *** Use place holder String commandString = "INSERT INTO " + CommonData.TABLE_NAME + " (idno, name, info) VALUES (?, ?, ?)"; SQLiteStatement sqlStmt = mSampleDB.compileStatement(commandString); sqlStmt.bindString(1, idno); sqlStmt.bindString(2, name); sqlStmt.bindString(3, info); try { sqlStmt.executeInsert(); } catch (SQLException e) { Log.e(DataInsertTask.class.toString(), mActivity.getString(R.string.UPDATING_ERROR_MESSAGE)); } finally { sqlStmt.close(); } return null; } [...] }
这是一种类型,它预先创建作为对象执行的 SQL 语句,并将参数分配给它。 执行的过程是固定的,所以没有发生 SQL 注入的可能。 另外,通过重用
SQLiteStatement
对象可以提高流程效率。使用
SQLiteDatabase
提供的每个方法:SQLiteDatabase
提供了两种类型的数据库操作方法。 一种是使用 SQL 语句,另一种是不使用 SQL 语句。 使用 SQL 语句的方法是SQLiteDatabase#execSQL()
/rawQuery()
,它以以下步骤执行。1) 准备包含占位符的 SQL 语句。
2) 创建要分配给占位符的数据。
3) 传递 SQL 语句和数据作为参数,并为每个过程执行一个方法。
另一方面,
SQLiteDatabase#insert()/update()/delete()/query()/replace()
是不使用 SQL 语句的方法。当使用它们时,数据应该按照以下步骤来准备。1) 如果有数据要插入/更新到数据库,请注册到
ContentValues
。2) 传递
ContentValues
作为参数,并为每个过程执行一个方法(例如,SQLiteDatabase#insert()
)SQLiteDatabase#insert()
(每个过程的方法的用例):private SQLiteDatabase mSampleDB; private void addUserData(String idno, String name, String info) { //Validity check of the value(Type, range), escape process if (!validateInsertData(idno, name, info)) { //If failed to pass the validation, log output Log.e(this.getClass().toString(), getString(R.string.VALIDATION_ERROR_MESSAGE)); return; } //Prepare data to insert ContentValues insertValues = new ContentValues(); insertValues.put("idno", idno); insertValues.put("name", name); insertValues.put("info", info); //Execute Inser try { mSampleDb.insert("SampleTable", null, insertValues); } catch (SQLException e) { Log.e(this.getClass().toString(), getString(R.string.DB_INSERT_ERROR_MESSAGE)); return; } }
在这个例子中,SQL 命令不是直接写入,而是使用
SQLiteDatabase
提供的插入方法。 SQL 命令没有直接使用,所以在这种方法中也没有 SQL 注入的可能。 - 使用
-
安卓应用测试指南 pdf_2020好应用盘点——S级性价比:安卓苹果电脑同时测试:游戏平台...
2020-12-08 13:45:03这几年随着互联网、云计算、5G通信和云服务器等技术和硬件支持的发展与普及,特别是云计算,它最大优点就是可以在任意位置、使用各种终端获取应用服务。所请求的资源来自“云”,而不是固定的有形的实体,只需要一台... -
防患于未然:如何检测恶意的安卓应用程序
2012-04-28 21:36:39对于许多人来说,在得到智能手机或平板电脑之后的第一件事情就是访问该设备的应用程序商店,开始下载...基于安卓的恶意程序能够通过GPS秘密跟踪用户的物理位置,发送文本消息,利用广告网络,甚至可以收集敏感数据。... -
魅族手机使用应用沙盒一键修改位置数据
2019-08-10 16:38:31之前文章介绍了如何在安卓手机上安装激活XPOSED框架,XPOSED框架的强大功能大家都介绍过,能不修改APK的前提下,修改系统内部的参数,打个比方在某些应用情景,大家需要修改手机的某个系统参数,这时就需要使用... -
红米手机使用应用沙盒动态修改位置数据
2019-07-29 16:20:10之前文章介绍了怎么样在安卓手机上安装激活xposed框架,xposed框架的极强的功能各位都清楚,能不修改apk的前提下,修改系统内部的参数,比如在某些应用需要,各位需要修改手机的某个系统参数,此时就需要使用xposed... -
获取位置_最新研究:关闭位置权限 安卓App仍能获取位置
2021-01-14 17:26:08中关村在线消息:近日,有国外研究团队发现,当我们在安卓手机中禁掉某APP的位置权限后,这些应用会悄悄将手机的唯一标识码和定位数据发送到自己的服务器,从而实现对用户的定位。Android Q将会默认隐藏用户位置信息... -
Android应用源码 - 基于安卓的个人隐私监控项目
2014-02-19 17:28:06隼翼安卓权限动态监控系统是一套基于安卓的软件权限和个人应用数据监控系统,实现了敏感权限访问记录生成、网络流量和短信的监控、软件行为分析、用户交互模块采用数据报表可视化的方式,让用户直观地了解到手机中各... -
移动终端应用程序开发各章代码安卓
2019-03-28 19:29:41移动终端应用程序开发各章代码 1,2章为概述无代码 第3章《第一个Android程序》 ...第9章《位置服务与地图应用》 第10章《Widget组件开发》 第11章 《Android NDK开发》 第12章 《综合示例设计与开发》 -
安卓中五种数据存储方式
2016-11-14 23:41:59分别是: --SharedPreferences存储; --文件存储;...应用场景: 适用于存储一些键值对,一般用来存储配置信息。 存储位置: /data/data/包名/shared_prefs 目录下,以xml格式进行保存。 可存储的数 -
Android应用源码大神级高仿安卓陌陌项目
2016-05-20 21:45:11个人和分组使用的模拟数据,支持下拉刷新,个人详细资料支持照片的查看、照片支持手势放大等基本操作;支持个人动态的查看,可进行虚拟的评论,并能发送表情内容。个人资料中的对话界面目前只是支持简单的发送本地... -
安卓调试精灵 简介
2020-08-05 23:14:15安卓调试精灵 是一款功能全面,简单易用的辅助开发和调试工具。...5,安卓设备上已安装应用列表展示,卸载应用,打开应用,安装应用,已安装应用所在位置,已安装应用大小,版本号,应用的界面名。 6,获取安卓. -
安卓系统下安装完apk程序后,具体的文件夹位置在哪里呢
2019-01-12 08:25:00分析如下: android程序安装后存储的目录介绍: 1、android应用安装涉及到如下几个目录 ①system/app 这系统自带的应用程序,无法删除 ...③data/data 存放应用程序的数据 ④Data/dalvik-ca... -
安卓帧数监测软件_电声联合检测法在变压器局部放电监测中的应用
2020-12-06 12:32:59声联合检测法的变压器局部放电在线监测系统,系统采用罗科夫斯基线圈采集脉冲电流信号,利用磁浮式高抗干扰超声探头采集局部放电声波信号,通过上位机软件进行放电脉冲幅值、频次和放电源位置等参数的计算,从而实现... -
初学者对SharePreferences数据存储应用
2018-03-20 08:29:47SharePreferences是安卓平台上的一个轻量级存储类,用来存储少量数据时简单、便捷(如记住密码)。 SharePreferences以键值对形式存储数据。 SharePreferences的存储位置在/data/data/<包名>/... -
初学者对于SharePreferences数据存储的应用
2018-03-19 23:21:54SharePreferences是安卓平台上的一个轻量级存储类,用来存储少量数据时简单、便捷(如记住密码)。 SharePreferences以键值对形式存储数据。 SharePreferences的存储位置在/data/data/<包名>/... -
安卓的五种存储方式
2020-12-04 11:02:18安卓的存储方式有5种,分别为: SharedPrefences; SQLite数据库 文件存储 网络存储 ContentProvider ...应用数据的存放位置: com.xxx.xxx 为自定义的包名 /data/data/com.xxx.xxx/cac... -
安卓如何实现图文混排
2018-12-26 16:31:36安卓如何实现图文混排一、安卓实现图文混排的几种方式1. EditText with Spannable2. RecyclerView3. WebViewF. 最终方案二、图片保存的几种方案1. 保留在原来位置2. 复制到应用的文件夹3. 复制到笔记的文件夹F. 最终... -
安卓开发复习笔记(一)
2019-07-18 00:10:00安卓应用开发特色: • 四大组件Activity,Service,Broadcast Receiver,Content Provider ️• SQLite 数据库轻量级,运算速度极快的嵌入式关系型数据库,不仅支持sql语句,还可以通过安卓封装好的API进行操作,... -
安卓_文件存储
2020-04-09 14:20:03存储位置:将数据以文件的形式存储到应用中。 存储路径:data/data/< pakagename >/目录下 其它应用操作该文件时:需要设置权限 删除文件:当应用被卸载时,该文件也会被删除 操作数据:通过openFileOutput()... -
Formula-One-Android-App:使用来自Ergast API托管的一级方程式API的数据的一级方程式android应用程序-源码
2021-02-10 02:30:16公式一安卓应用 一级方程式android应用程序使用来自Ergast API托管的数据。 该应用程序的目标是使用集成的Google Maps查看和搜索有关Formula 1的信息,包括驾驶员,构造函数,季节,赛道和赛道位置。 建筑 具有... -
信息发布系统(安卓+PC).zip
2021-01-27 14:27:48适应餐饮、零售等行业单机或局域网内应用 术语解释 终端: 播放多媒体广告内容的硬件设备 场景: 记录了媒体文件播放顺序和在屏幕中播放的位置, 也可以设定时间定时播放 节目单: 由一个或多个场景组成, 每个场景... -
安卓流式布局——标签墙
2017-07-13 16:28:21安卓标签墙也是一个常见的应用场景,所以最近学习写了一个流式布局,并学习一下自定义控件的一种方法,继承ViewGroup. 思路: 1.流式布局1行只能放下固定数量的标签,所以用一个类来标明一行的数据,这样在... -
安卓学习2-Intent
2012-07-04 20:53:27在安卓系统中占有十分重要的位置,负责连接两个组件。 例如:应用程序的多个Activity之间的跳转时通过Intent来进行跳转的 2,Intent还可以启动一个Service服务,也可以发起一个Broadcast ===== Intent由Action... -
黑马安卓52期视频教程
2015-06-24 22:15:4801、安卓基础+JNI(14天)-------------------------- day01_Android应用开发-快速入门 01_网络制式的概念 02_android简单历史 03_Android体系结构 04_JVM和DVM的区别 05_下载SDK 06_SDK目录结构 07_模拟器的创建 ... -
文件存储——安卓存储方案之二
2016-01-14 16:25:15→安卓提供了相关API,让你可以以流的方式去读写数据,这跟原来java里的流操作的方式基本一致,如果你对java流操作很熟悉,其实安卓里的文件操作没有什么特别新鲜的东西的。 →根据存储位置不同,文件存储可以分为... -
安卓开发学习——第五天
2018-01-10 16:19:321. Android中的数据存储方式: 2. SharedPrefrence存储 1). 位置 /data/data/packageName/shared_prefs/xxx.xml 2).... 应用卸载时会自动删除此数据 3). 相关API 接口/类: 1). 它是