JAVA层SAF核心代码

通过DocumentFile来实现写入,Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)发送请求docUri,然后onActivityResult来得到并储存docUri,通过SharedPreference来实现共享。已经封装在静态类SafFile.java中。

获取外置sd卡根目录DocumentFilepublic static DocumentFile getBaseDocumentFile(final Context context, final SharedPreferences share) {

if(context==null) {

Log.e(LOGTAG, "SafFile.getBaseDocumentFile context is null!");

return null;

}

if(share==null){

Log.e(LOGTAG, "SafFile.getBaseDocumentFile share is null!");

return null;

}

DocumentFile base = null;

Uri docUri = null;

final String p = share.getString("docUri", null);

if (p != null)

docUri = Uri.parse(p);

base = DocumentFile.fromTreeUri(context, docUri);

return base;

}注意DocumentFile CreateFile好像不只能在本目录下创建,要一层一层往里面走

public static DocumentFile getTargetDirDocumentFile(final DocumentFile base, String path) {

DocumentFile target = null;

if (base == null) {

Log.e(LOGTAG, "SafFile.getTargetDirDocumentFile base is null!");

return null;

}

if(path==null) path="";

path = path.replace("\", "/");

final String paths[] = path.split("/");

int i;

final int end = paths[paths.length - 1].length() > 0 ? paths.length - 1 : paths.length - 2;

for (i = 0; i < end; i++) {

// Log.i(LOGTAG, "getTar... path["+String.valueOf(i)+"], "+paths[i]);

if (paths[i].equals(base.getName())) {

if (i >= end - 1) {

// Log.i(LOGTAG, "getTar... "+path+" end="+paths[paths.length-1]+" "+ paths[end]);

return base;

}

i++;

break;

}

}

// Log.i(LOGTAG, "getTarget... "+base.getName()+" "+path);

target = base.findFile(paths[i++]);

// Log.i(LOGTAG, "target, "+ target.getName());

for (; i < end; i++) {

if (target == null)

break;

// Log.i(LOGTAG, "getTar..., "+path+" "+ target.getName());

target = target.findFile(paths[i]);

}

return target;

}获得OutputStream

public static OutputStream getOutputStreamSaf(final Context context, final DocumentFile base, final String path,

final boolean append) {

if(context==null) {

Log.e(LOGTAG, "SafFile.getOutputStreamSaf context is null!");

return null;

}

if(base==null){

Log.e(LOGTAG, "SafFile.getOutputStreamSaf base is null!");

return null;

}

OutputStream out = null;

final String mode = append ? "wa" : "w";

// Log.i(LOGTAG, "getOut.. "+ path +" "+mode);

final DocumentFile df2 = createFileSaf(base, path, append);

if (df2 == null) {

return null;

}

try {

out = context.getContentResolver().openOutputStream(df2.getUri(), mode);

} catch (final Exception e) {

Log.e(LOGTAG, "SafFile.getOutputStreamSaf " + e.getClass().getName());

}

return out;

}获取文件描述符

public static int getFdSaf(final Context context, final DocumentFile base, final String path, final String mode) {

if(context==null) {

Log.e(LOGTAG, "SafFile.getFdSaf context is null!");

return 0;

}

if(base==null){

Log.e(LOGTAG, "SafFile.getFdSaf base is null!");

return 0;

}

ParcelFileDescriptor pfd = null;

boolean append = false;

DocumentFile df2 = null;

if (mode.indexOf('+') != -1 || mode.indexOf('a') != -1)

append = true;

if (mode.indexOf('w') == -1)

append = true;

df2 = createFileSaf(base, path, append);

if (df2 == null) {

Log.e(LOGTAG, "SafFile.getFdSaf, " + path + " error!");

return 0;

}

try {

pfd = context.getContentResolver().openFileDescriptor(df2.getUri(), mode);

} catch (final Exception e) {

Log.e(LOGTAG, "SafFile.getFdSaf " + e.getClass().getName());

}

if (pfd == null)

return 0;

return pfd.detachFd();

}3.JNI层hook核心代码

这里用到了xhook架构,原理上是运行的时候来替换目标动态库的.got表到自己编译的函数地址,通过JNI来调用JAVA层我们写好通过SAF机制得到的文件描述符。

初始化要JNI中要调用的JAVA方法,class为"com/yurisizuku/utils/SafFile"

void nativeInitSafJavaCallbacks(JNIEnv* env, jclass clazz)

{

LOGI("In nativeInitSafJavaCallbacks start!");

g_javaGetFD=(*env)->GetStaticMethodID(env, clazz, "getFD", "(Ljava/lang/String;Ljava/lang/String;I)I");

g_javaMkdir=(*env)->GetStaticMethodID(env, clazz, "mkdir", "(Ljava/lang/String;Ljava/lang/String;I)I");

g_javaRemove = (*env)->GetStaticMethodID(env, clazz, "remove", "(Ljava/lang/String;Ljava/lang/String;)I");

LOGI("In nativeInitSafJavaCallbacks finished!");

}xhook架构的hook fopen等函数

void nativeHookFile(JNIEnv* env, jclass clazz, jstring hooksoStr, jstring soPath)

{

char buf[100];

char *cstr_hooksoStr = jstr2cstr(env, hooksoStr);

LOGI("nativeHookFile, %s n", cstr_hooksoStr);

char *cstr_soPath = jstr2cstr(env, soPath);

if(cstr_soPath && strlen(cstr_soPath))

{

if (!dlopen(cstr_soPath, RTLD_LAZY)) //dlopen in advance

LOGE("dlopen(%s,%d) error!n", cstr_soPath,RTLD_LAZY);

else LOGI("dlopen(%s,%d) success !n", cstr_soPath,RTLD_LAZY);

}

if (xhook_register(cstr_hooksoStr, "fopen", fopen_saf, NULL))

LOGE("xhook fopen register failed!");

else LOGI("xhook fopen register successed!");

if (xhook_register(cstr_hooksoStr, "mkdir", mkdir_saf, NULL))

LOGE("xhook mkdir register failed!n");

else LOGI("xhook mkdir register successed!");

if (xhook_register(cstr_hooksoStr, "remove", remove_saf, NULL))

LOGE("xhook remove register failed!n");

else LOGI("xhook remove register successed!");

xhook_refresh(0);

free(cstr_hooksoStr);

LOGI("nativeHookFile xhook finished!");

if(cstr_soPath) free(cstr_soPath);

}```

## fopen的hook, 调用java层我们写好的getFD再用fdopen文件可写

```C

FILE *fopen_saf(const char *pathname, const char *mode)

{

FILE* fp=NULL;

JNIEnv* env = NULL;

(*g_vm)->AttachCurrentThread(g_vm, &env, NULL);

if(!env)

{

LOGE("fopen_asf, env AttachCurrentThread failed!n");

return fopen(pathname, mode);

}

int mode2=0;

if(mode[0] == 'w') mode2=1;

fp = fopen(pathname, mode);

if(!(fp || mode2 == 0 || errno != EACCES))

{

char buf[PATH_MAX_LEN];

getcwd(buf, PATH_MAX_LEN);

//LOGI("before fopen(%s, %s), cwd=%sn", pathname, mode, buf);

jstring s_pathname = (*env)->NewStringUTF(env, pathname);

jstring s_curdir = (*env)->NewStringUTF(env, buf);

int fd = (*env)->CallStaticIntMethod(env, g_javaClass, g_javaGetFD, s_curdir, s_pathname, mode2 );

(*env)->DeleteLocalRef(env, s_curdir);

(*env)->DeleteLocalRef(env, s_pathname);

fp = fdopen(fd, mode);

//LOGI("after fopen_saf(%s, %s),fp=%x, cwd=%sn", pathname, mode, (unsigned int)fp,buf);

}

return fp;

}

saf java_Android SAF实现外置SD卡的写入JAVA层与JNI层hook相关推荐

  1. saf java_[原创]Android Storage Access Framework(SAF)框架实现外置SD卡的写入(JAVA层与JNI层HOOK)...

    1. 前言 之前折腾了了一下MINE模拟器,发现SDL全是在JNI层fopen操作的,而安卓的SAF则是JAVA层通过DocumentFile和docUri来实现写入的.一种方法是通过去的File D ...

  2. saf java_Android SAF实现外置SD卡的写入(JAVA层与JNI层hook)

    1. 前言 之前折腾了了一下MINE模拟器,发现SDL全是在JNI层fopen操作的,而安卓的SAF则是JAVA层通过DocumentFile和docUri来实现写入的.一种方法是通过去的File D ...

  3. Android9.0外置sd卡无法写入的问题

    记录:https://blog.csdn.net/qq_36467463/article/details/88691726

  4. android 使用SAF框架操作外置sd卡

    android 使用SAF框架操作外置sd卡 在 Android 4.4中,Google 对 SD卡 的访问已经做了严格的限制,在 Android 5.0中,开发者可以使用 新API 要求用户对某个指 ...

  5. Android9.0中应用如何通过SAF框架写入外置SD卡

    背景介绍 Overview 基于SAF框架写入外置SD卡网上相关资料比较少,现整理一下具体实现方法,如果是访问主存储,弹出授权后即可正常写入,如果是副卡,在Android9.0上必须要使用SAF框架. ...

  6. 安卓手机的加密设备和加密外置SD卡

    手机一旦被偷或者遗失,那么隐私就会泄露了,安卓手机中有"加密设备"和"加密外置sd卡"的功能.那么这两项功能有什么用有什么区别呢? 加密设备 加密设备后,内置S ...

  7. android 获取默认存储路径,Android获取外置SD卡存储路径的方法

    在开发应用的过程中,经常会遇到需要获取设备存储路径的问题.而从网上看到的很多方法获取到的都是内置存储位置,并非外置SD卡路径,因此我推荐使用反射的机制来获取外置存储的路径. 通常,使用Environm ...

  8. android sd卡挂载广播,Android--检测内置/外置SD卡存储卡,枚举所有挂载点(通过反射实现),监听SD卡广播...

    直接上重点: 1:获取内置SD卡的路径, 但是判断是否有效(是否挂载), 需要用到下面检测挂载点的方法 /** * 获取内置SD卡路径 * * @return */ public String get ...

  9. Android 外置 SD 卡写入权限问题

    https://busy.im/post/android-sdcard-write/ 最近升级到 Android 9.0 后,发现文件管理器在写入外置 SD 卡时出现了写入失败的问题,定位到 File ...

最新文章

  1. Linux内核初期内存管理---memblock(转)
  2. 实现数组(java)
  3. MySQL修改和删除索引(DROP INDEX)
  4. Java客户端操作elasticsearch--查询索引库
  5. 在Xcode中制作.a文件
  6. MySQL---数据库基础入门
  7. autojs人像变换
  8. Microsoft KMS Client Setup Keys ( Windows + Office )
  9. 如何承接软件外包项目
  10. 在线ssd测试软件,AS SSD Benchmark测试
  11. 前端炫酷登录页,拿来就能用
  12. Unity3d 周分享(9期 2018.12.31)
  13. 字符串常量池,看这篇就够了(一)
  14. 《Effective Java》阅读体会之四--通用方法(建议覆盖toString,compare排序的比较)
  15. Echarts中国地图各个省市自治区自定义颜色
  16. c语言 银行取号排队队列程序,银行排队队列问题则么解决
  17. 程序员就非要科班出身
  18. centos7.6 安装Ambari-2.7.1.0搭建HDP-3.1.0
  19. 三星A7计算机,5.5寸大屏纤薄金属机 三星GALAXY A7评测
  20. 剑灵合服后服务器位置,剑灵合服名单整理及各服情况简介 回归玩家和萌新的福音...

热门文章

  1. linux下的工具移植到windows下
  2. 第十三章 失业、通货膨胀和经济周期
  3. 你管这叫操作系统源码(五)
  4. 【从0到1搭建LoRa物联网】3、国产LoRa终端ASR6505软硬件
  5. pythonset是什么意思_Python 中 set 是什么?为何要是用它?
  6. 为什么把人称呼为“总”?
  7. 从睡眠期间的大脑活动检测痴呆症
  8. 火星存在大型地下水系统,火星或曾是一片海洋
  9. 如何在 React Component 之外获取 Redux Store
  10. WEB项目系统添加redis缓存逻辑和功能