androidSDK中並沒有鎖定文件相關的api.
但是android是基於linux操作系統的,linux比較底層,靈活性也更大,為了實現鎖定文件的效果,大概有以下幾種辦法:
- 用chmod命令修改文件讀寫權限
- 利用linux中的多線程獨占鎖,啟動一個長期占用文件的后台線程
- 使用文件IO流,對文件的前1K字節進行加密,使其不能被識別為文件,或者讀不出有意義的數據
這三種方法中最優雅的是第三種方法,下面結合金山文件鎖的源碼和技術文章等來詳解第三種方法.
==================================技術分割線===================================
金山文件鎖會在SD卡下生成一個.ksbox文件夾,這個文件夾下會保存加密后的文件.這個文件夾下有一個db.sqlite數據庫文件,使用SQLite Database Browser打開后可以看到被加密文件的列表.
接下來去反編譯金山的apk,最開始我使用的工具是dex2jar和java decompiler然而並沒有看到加密核心代碼.之后使用Apk IDE來反編譯.
相關的核心代碼在com/ijinshan/mPrivacy/c/j.smali文件中,里面是一些類似匯編的代碼:
method public final read([BII)I
.locals 7
.parameter
.parameter
.parameter
.prologue
const/4 v6, 0x0
const/16 v5, 0x400
.line 61
iget-object v0, p0, Lcom/ijinshan/mPrivacy/c/j;->a:Ljava/io/FileInputStream;
invoke-virtual {v0, p1, p2, p3}, Ljava/io/FileInputStream;->read([BII)I
move-result v0
.line 63
const/4 v1, -0x1
if-ne v0, v1, :cond_0
.line 103
:goto_0
return v0
.line 70
:cond_0
iget-wide v1, p0, Lcom/ijinshan/mPrivacy/c/j;->f:J
const-wide/16 v3, 0x400
cmp-long v1, v1, v3
if-gtz v1, :cond_5
.line 73
iget-boolean v1, p0, Lcom/ijinshan/mPrivacy/c/j;->e:Z
if-nez v1, :cond_1
.line 75
iget-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->c:Lcom/ijinshan/mPrivacy/c/g;
iget-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->b:Ljava/lang/String;
invoke-static {v1}, Lcom/ijinshan/mPrivacy/c/g;->b(Ljava/lang/String;)[B
move-result-object v1
iput-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
.line 76
iget-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
if-eqz v1, :cond_1
.line 77
const/4 v1, 0x1
iput-boolean v1, p0, Lcom/ijinshan/mPrivacy/c/j;->e:Z
.line 80
:cond_1
if-ge v0, p3, :cond_3
move v1, v0
.line 82
:goto_1
add-int v2, p2, v1
if-gt v2, v5, :cond_4
.line 84
iget-object v2, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
if-eqz v2, :cond_2
.line 85
iget-object v2, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
invoke-static {v2, p2, p1, v6, v1}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
.line 100
:cond_2
:goto_2
iget-wide v1, p0, Lcom/ijinshan/mPrivacy/c/j;->f:J
int-to-long v3, v0
add-long/2addr v1, v3
iput-wide v1, p0, Lcom/ijinshan/mPrivacy/c/j;->f:J
goto :goto_0
:cond_3
move v1, p3
.line 80
goto :goto_1
.line 89
:cond_4
if-ge p2, v5, :cond_2
.line 91
iget-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
if-eqz v1, :cond_2
.line 92
iget-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
sub-int v2, v5, p2
invoke-static {v1, p2, p1, v6, v2}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
goto :goto_2
.line 98
:cond_5
const/4 v1, 0x0
iput-object v1, p0, Lcom/ijinshan/mPrivacy/c/j;->d:[B
goto :goto_2
.end method
還有一些和它相關聯的函數,代碼比較長就不全貼上來了,參考着文檔對其進行分析的大概了解到金山的做法:
首先創建類繼承InputStream,使用decodeStream函數得到輸入流,重寫read()方法,在方法體中對輸入流的前1K字節進行解密,后面的字節直接從filename文件中讀取.
解密的方法就是讀取filename_e文件,每個字節異或0x6b
這樣就得到了加密前的文件.
===========================技術分割線================================
這種方法算是所有方法中最優雅的方法了,雖然也有缺點(文件被誤刪),但是加解密計算小,只計算前1K字節.文件移動代價小,只要改變一下文件指針,就可以移動文件.速度快穩定性高.是業內主流的解決方案!