-
-
[原创]Android Storage Access Framework(SAF)框架实现外置SD卡的写入(JAVA层与JNI层HOOK)
-
发表于:
2020-1-14 20:48
7049
-
[原创]Android Storage Access Framework(SAF)框架实现外置SD卡的写入(JAVA层与JNI层HOOK)
之前折腾了了一下MINE模拟器,发现SDL全是在JNI层fopen
操作的,而安卓的SAF则是JAVA层通过DocumentFile和docUri来实现写入的。一种方法是通过去的File Descriptor然后传给JNI层,通过fdopen
实现写入[1]。于是成功在MINE模拟器添加外置sd卡写入功能,详见我在贴吧布的 mine模拟器外置SD卡写入修复版。完整版源码我已经封装好了发布到github上,理论上通用。

通过DocumentFile来实现写入,Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
发送请求docUri
,然后onActivityResult
来得到并储存docUri
,通过SharedPreference来实现共享。已经封装在静态类SafFile.java
中。
这里用到了xhook架构,原理上是运行的时候来替换目标动态库的.got
表到自己编译的函数地址,通过JNI来调用JAVA层我们写好通过SAF机制得到的文件描述符。
[1] b17K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4U0K9$3!0$3k6i4u0X3L8r3!0%4i4K6u0W2j5$3!0E0i4K6u0r3M7i4g2W2M7%4c8A6L8$3&6K6i4K6u0r3x3K6l9#2z5e0x3&6y4U0c8Q4x3V1k6Z5L8%4N6Q4x3X3c8@1L8#2)9J5k6r3q4U0j5$3g2K6M7#2)9J5k6r3q4F1k6s2u0G2K9h3c8Q4x3X3c8D9L8$3I4D9K9i4m8G2M7q4)9J5k6r3c8G2j5%4g2E0k6h3&6@1k6X3W2D9k6g2)9J5k6r3k6A6L8r3g2K6i4K6u0V1N6X3W2S2i4K6u0V1L8X3c8C8i4K6u0r3x3K6p5$3y4K6M7J5z5o6M7`.
[2] b6bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2L8X3c8J5L8$3W2V1i4K6u0W2j5$3!0E0i4K6u0r3k6%4g2A6k6r3g2Q4x3V1k6@1L8%4m8A6j5%4y4Q4x3V1k6H3M7X3!0$3K9h3c8W2M7Y4y4Q4x3V1k6V1L8$3y4#2L8h3g2F1N6q4)9J5k6s2m8J5L8%4k6A6k6r3g2J5i4K6u0W2K9s2c8E0L8q4)9K6c8X3S2D9i4K6y4p5P5X3S2Q4x3X3c8U0L8R3`.`.
public 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;
}
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;
}
[培训]科锐逆向工程师培训第53期2025年7月8日开班!
最后于 2020-1-14 22:12
被devseed编辑
,原因: