Android下hook實現(xiàn)文件讀寫透明加解密

發(fā)一篇好幾年的文章。。因為最近公司要求我研究研究hook怜森,想起來我以前做的這部分工作 >_<

實現(xiàn)原理:

修改write函數(shù)所對應的got表項中的地址殿雪,修改成自己定義的函數(shù)暇咆,則每當系統(tǒng)調(diào)用write函數(shù)時,會執(zhí)行我們自定義函數(shù)丙曙,從而只需在自定義函數(shù)中添加加密或者解密算法爸业,就能實現(xiàn)對應用開發(fā)者透明的加解密。

幾個問題:

Question 1:Android如何實現(xiàn)文件讀寫亏镰?

編寫Android應用程序時扯旷,遇到文件讀寫一般使用FileInputStream類中的read()方法和FileOutputStream類中的write方法。我們以調(diào)用FileOutputStream.write()為例索抓。
http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/java/io/FileOutputStream.java

源代碼如下所示钧忽,系統(tǒng)會繼續(xù)調(diào)用IoBridge中的write方法。

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

接著逼肯,系統(tǒng)調(diào)用final類Libcore中的static field OS的write方法耸黑。
static field os轉(zhuǎn)型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();
        }
    }

最終調(diào)用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);
    }

系統(tǒng)繼續(xù)調(diào)用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;

最終,我們可以發(fā)現(xiàn)洲拇,系統(tǒng)將會調(diào)用native方法writeBytes.繼續(xù)深入:
http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/native/libcore_io_Posix.cpp

native方法writeBytes的具體實現(xiàn)在/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函數(shù)。

    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)));
    }

可以發(fā)現(xiàn)赋续,writeBytes最終還是調(diào)用了write函數(shù)男翰。這個write函數(shù)應該是系統(tǒng)提供的API,在libc.so中纽乱。

Question 2 :Posix_writeBytes函數(shù)在哪一個共享庫中蛾绎?

如第一部分的分析,系統(tǒng)在Posix_writeBytes函數(shù)中調(diào)用write函數(shù)。如果我們能夠修改掉這個write函數(shù)在got中的地址租冠,那么當系統(tǒng)運行至Posix_writeBytes函數(shù)中鹏倘,并企圖調(diào)用write函數(shù)時,將進入我們自定義函數(shù)顽爹。

但是纤泵,Android APP在運行時會加載許多的.so庫文件,我們需要知道Posix_writeBytes函數(shù)在哪一個庫中镜粤,才能夠修改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函數(shù)地址的表項循狰?

ELF文件有執(zhí)行視圖和鏈接視圖的區(qū)分。以下捻浦,我們按照執(zhí)行視圖去尋找表項晤揣。
大致流程是:

  1. 通過ELF文件頭找到Program Header
  2. 通過Program Header找到.dynamic節(jié)
  3. 通過.dynamic節(jié)找到重定位表和符號表以及必需的附加信息
  4. 查找符號表,得出"write"函數(shù)在符號表中的索引
  5. 遍歷重定位表朱灿,計算每一個重定位項在符號表中的索引
  6. 比較第四步與第五步得出的索引昧识,若相等,則表明找到了"write"的重定位項盗扒,讀出offset即可跪楞。

需要注意的是,第4步查找符號表侣灶。實際上甸祭,ELF文件完成這一步是依靠HASH表來完成的。
HASH表的結構是這樣子的:

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

bucket 數(shù)組包含 nbucket 個項目褥影, chain 數(shù)組包含 nchain 個項目池户, 下標都是從
0 開始。 bucket 和 chain 中都保存符號表索引凡怎。 Chain 表項和符號表存在對應校焦。 符號
表項的數(shù)目應該和 nchain 相等,所以符號表的索引也可用來選取 chain 表項统倒。哈希
函數(shù)能夠接受符號名并且返回一個可以用來計算 bucket 的索引寨典。

因此,如果哈希函數(shù)針對某個名字返回了數(shù)值 X房匆,則 bucket[X%nbucket] 給出了
一個索引 y耸成, 該索引可用于符號表报亩, 也可用于 chain 表。 如果符號表項不是所需要的井氢,
那么 chain[y] 則給出了具有相同哈希值的下一個符號表項弦追。我們可以沿著 chain 鏈
一直搜索,直到所選中的符號表項包含了所需要的符號毙沾,或者 chain 項中 包含值
STN_UNDEF骗卜。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市左胞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌举户,老刑警劉巖烤宙,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俭嘁,居然都是意外死亡躺枕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門供填,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拐云,“玉大人,你說我怎么就攤上這事近她〔娲瘢” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵粘捎,是天一觀的道長薇缅。 經(jīng)常有香客問我,道長攒磨,這世上最難降的妖魔是什么泳桦? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮娩缰,結果婚禮上灸撰,老公的妹妹穿的比我還像新娘。我一直安慰自己拼坎,他們只是感情好浮毯,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著演痒,像睡著了一般亲轨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸟顺,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天惦蚊,我揣著相機與錄音器虾,去河邊找鬼。 笑死蹦锋,一個胖子當著我的面吹牛兆沙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莉掂,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼葛圃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了憎妙?” 一聲冷哼從身側(cè)響起库正,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厘唾,沒想到半個月后褥符,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡抚垃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年喷楣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹤树。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡铣焊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罕伯,到底是詐尸還是另有隱情曲伊,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布捣炬,位于F島的核電站熊昌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏湿酸。R本人自食惡果不足惜婿屹,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望推溃。 院中可真熱鬧昂利,春花似錦、人聲如沸铁坎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硬萍。三九已至扩所,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朴乖,已是汗流浹背祖屏。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工助赞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人袁勺。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓雹食,卻偏偏與公主長得像,于是被迫代替她去往敵國和親期丰。 傳聞我的和親對象是個殘疾皇子群叶,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容