美文网首页
Android下hook实现文件读写透明加解密

Android下hook实现文件读写透明加解密

作者: 羊角包 | 来源:发表于2019-01-23 16:40 被阅读0次

    发一篇好几年的文章。。因为最近公司要求我研究研究hook,想起来我以前做的这部分工作 >_<

    实现原理:

    修改write函数所对应的got表项中的地址,修改成自己定义的函数,则每当系统调用write函数时,会执行我们自定义函数,从而只需在自定义函数中添加加密或者解密算法,就能实现对应用开发者透明的加解密。

    几个问题:

    Question 1:Android如何实现文件读写?

    编写Android应用程序时,遇到文件读写一般使用FileInputStream类中的read()方法和FileOutputStream类中的write方法。我们以调用FileOutputStream.write()为例。
    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/java/io/FileOutputStream.java

    源代码如下所示,系统会继续调用IoBridge中的write方法。

        @Override
        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
            IoBridge.write(fd, buffer, byteOffset, byteCount);
        }
    

    接着,系统调用final类Libcore中的static field OS的write方法。
    static field os转型BlockGuard对象。
    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/Libcore.java

        public final class Libcore {
        private Libcore() { }
        public static Os os = new BlockGuardOs(new Posix());
    }
    

    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/IoBridge.java

        public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
            Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
            if (byteCount == 0) {
                return;
            }
            try {
                while (byteCount > 0) {
                    int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
                    byteCount -= bytesWritten;
                    byteOffset += bytesWritten;
                }
            } catch (ErrnoException errnoException) {
                throw errnoException.rethrowAsIOException();
            }
        }
    

    最终调用BlackGuardOs中的write方法。
    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/BlockGuardOs.java

        public BlockGuardOs(Os os) {
            super(os);
        }
        @Override 
        public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
            BlockGuard.getThreadPolicy().onWriteToDisk();
            return os.write(fd, bytes, byteOffset, byteCount);
        }
    

    系统继续调用Posix类中的write方法。
    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/Posix.java

        public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
            // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
            return writeBytes(fd, bytes, byteOffset, byteCount);
        }
    
        private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
    

    最终,我们可以发现,系统将会调用native方法writeBytes.继续深入:
    http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/native/libcore_io_Posix.cpp

    native方法writeBytes的具体实现在/libcore/luni/src/main/native/libcore_io_Posix.cpp
    查看本地方法注册

        NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I")
    
        void register_libcore_io_Posix(JNIEnv* env) {
            jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
        }
    

    宏定义为

        #define NATIVE_METHOD(className, functionName, signature, identifier) \
        { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) }
    

    结合上面的代码,可以得知:本地方法writeBytes被注册成了:Posix_writeBytes函数。

        static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) {
            ScopedBytesRO bytes(env, javaBytes);
            if (bytes.get() == NULL) {
            return -1;
            }
        int fd = jniGetFDFromFileDescriptor(env, javaFd);
        return throwIfMinusOne(env, "write", TEMP_FAILURE_RETRY(write(fd, bytes.get() + byteOffset, byteCount)));
        }
    

    可以发现,writeBytes最终还是调用了write函数。这个write函数应该是系统提供的API,在libc.so中。

    Question 2 :Posix_writeBytes函数在哪一个共享库中?

    如第一部分的分析,系统在Posix_writeBytes函数中调用write函数。如果我们能够修改掉这个write函数在got中的地址,那么当系统运行至Posix_writeBytes函数中,并企图调用write函数时,将进入我们自定义函数。

    但是,Android APP在运行时会加载许多的.so库文件,我们需要知道Posix_writeBytes函数在哪一个库中,才能够修改write在该库文件中got表项的地址。
    http://androidxref.com/4.4_r1/xref/libcore/Android.mk

    Android.mk文件是标示如何编译源代码的说明书,查看Android.mk得知详情在http://androidxref.com/4.4_r1/xref/libcore/NativeCode.mk中。

        include $(CLEAR_VARS)
        LOCAL_CFLAGS += $(core_cflags)
        LOCAL_CPPFLAGS += $(core_cppflags)
        LOCAL_SRC_FILES += $(core_src_files)
        LOCAL_C_INCLUDES += $(core_c_includes)
        LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libexpat   libicuuc libicui18n libnativehelper libz
        LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
        LOCAL_MODULE_TAGS := optional
        LOCAL_MODULE := libjavacore
        LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
        include external/stlport/libstlport.mk
        include $(BUILD_SHARED_LIBRARY)
    

    得知/libcore/luni/src/main/native/libcore_io_Posix.cpp被编译成了libjavacore.so。

    所以,我们得出思路:修改libjavacore.so中write的got表项的值。

    Question 3 :如何找到got表中存储write函数地址的表项?

    ELF文件有执行视图和链接视图的区分。以下,我们按照执行视图去寻找表项。
    大致流程是:

    1. 通过ELF文件头找到Program Header
    2. 通过Program Header找到.dynamic节
    3. 通过.dynamic节找到重定位表和符号表以及必需的附加信息
    4. 查找符号表,得出"write"函数在符号表中的索引
    5. 遍历重定位表,计算每一个重定位项在符号表中的索引
    6. 比较第四步与第五步得出的索引,若相等,则表明找到了"write"的重定位项,读出offset即可。

    需要注意的是,第4步查找符号表。实际上,ELF文件完成这一步是依靠HASH表来完成的。
    HASH表的结构是这样子的:

    ||nbucket
    ||nchain
    ||bucket[0]~ bucket[nbucket - 1]
    ||chain [0]~ chain[nchain - 1]

    bucket 数组包含 nbucket 个项目, chain 数组包含 nchain 个项目, 下标都是从
    0 开始。 bucket 和 chain 中都保存符号表索引。 Chain 表项和符号表存在对应。 符号
    表项的数目应该和 nchain 相等,所以符号表的索引也可用来选取 chain 表项。哈希
    函数能够接受符号名并且返回一个可以用来计算 bucket 的索引。

    因此,如果哈希函数针对某个名字返回了数值 X,则 bucket[X%nbucket] 给出了
    一个索引 y, 该索引可用于符号表, 也可用于 chain 表。 如果符号表项不是所需要的,
    那么 chain[y] 则给出了具有相同哈希值的下一个符号表项。我们可以沿着 chain 链
    一直搜索,直到所选中的符号表项包含了所需要的符号,或者 chain 项中 包含值
    STN_UNDEF。

    相关文章

      网友评论

          本文标题:Android下hook实现文件读写透明加解密

          本文链接:https://www.haomeiwen.com/subject/tkhsjqtx.html