【原】掌閱IREADER Android APK脫殼


原版APK內的classes.dex 只負責加載libDexHelper.so,在這個so的JNI_OnLoad() 函數中,通過跟蹤代碼,發現在第一運行時向

/data/data/com.chaozh.iReaderFree/.cache

這個目錄下面寫了3個文件:

這個是在調試過程中截的圖,因此 dex 文件還大小是0。

DVE文件不知道是干什么的,JAR文件就是 apk 里的 asset/class0.jar ,但用ZIP無法解壓,說明也是經過處理的。

跟蹤了一下,處理的方法如下:

1. 初始化一個長度為256的byte(unsigned char)數組,每個元素的值與其索引相等,即:

for (i = 0; i < 0x100; i++)
    a[i] = i;

2. 用到另外一個數組

unsigned char b[16] = {
    0x66, 0x97, 0x6C, 0xE8, 0x6D, 0x46, 0x38, 0xB0, 
    0x09, 0x5A, 0xA5, 0xD7, 0x0F, 0xCB, 0x9A, 0xA0,
};

3. 累加數組a中的元素和b中的元素,因為b只有16個元素,如果到末尾,就從頭再開始,將“和”表示的索引的值與當前元素的值互換

int sum = 0, temp;

for (i = 0, j = 0; i < 0x100; i++, j++)
{
    if (j > 15)
            j = 0;
    sum = (sum + a[i] + b[j]) & 0XFF;
    temp = a[sum];
    a[sum] = a[i];
    a[i] = temp;
}

4. 文件的前0x20000個字節,使用下面的算法解密:

unsigned int R1, R6, R7;
size_t i, j;

R1 = 0;

for (i = 0, j = 0; i < len; i++)
{
        j = (j+1) & 0xFF;
        R7 = a[j];
        R1 += R7;
        R1 = R1 & 0xFF; 

        R6 = a[R1];
        a[j] = R6;
        a[R1] = R7;
        //R6 = (R6 + R7) & 0xFF; 
        R6 = (a[j] + a[R1]) & 0xFF;

        R7 = src[i] ^ a[R6];
        dest[i] = R7;
}

5. 從0x20000以后的字節,簡單的與0xAC異或。

 

這樣處理之后,class0.jar 就變成了一個zip文件:

並且里面只有一個 classes.dex,用ZIP解壓出來就是原始的dex了。但在手機上,.cache 文件夾下面的 classes.dex 並不是直接解壓出來的,而是在解壓出來后對文件進行了處理,過程與上面處理 class0.jar 的完全一樣。

 

IREADER APK同時做了加殼、DEX混淆、資源混淆,但脫殼之后,其他的就無關緊要了。用未加固的classes.dex,可以刪除lib/armeabi下面的libDexHelper.so了。

但是這樣打包后,運行APK會發生錯誤:

05-12 09:34:48.064 W/dalvikvm(30980): JNI_OnLoad returned bad version (2147483647) in /data/app-lib/com.chaozh.iReaderFree1-1/libUiControl.so 0x41150990

05-12 09:34:48.064 W/dalvikvm(30980): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/zhangyue/iReader/JNI/Common;

05-12 09:34:48.064 D/dalvikvm(30980): threadid=13: exiting

 

在APP運行時,會對簽名進行驗證,很多APP會驗證簽名,但驗證的方式不盡相同,大部分是在JAVA里做的,干掉比較簡單,但IREADER的驗證是在libUiControl.so里面做的,而且這個SO使用的是THUMB指令集,分析起來稍微有點難度。驗證是在 sub_5F038FAE 函數中做的,我們改過簽名,肯定不能通過,函數返回 0x7FFFFFFF,然后把這個值放到了R6寄存器里。

 

JNI_OnLoad() 會返回JAVA版本,這個SO里的做法有點奇怪,大致是這樣的:

double d1 = (double) 0x7FFFFFFF; // R6 
double d2 = 10000, d3 = 65542, d4 = 2;
double d_result;
int result;

d_result = pow(d2, d1) * d3 - d4;
result = (int) d_result;

也就是將簽名驗證的結果放到 R6 中,計算 10000 的 R6 次方,然后乘以 65545,再減去2. 如果簽名驗證通過,R6里為0,那么結果是 0x10004,也就是 JNI_VERSION_1_4。

看到最后的簽名結果是從R6中取的,那么我們只需將R6的值清0:。在調用sub_5F038FAE函數之后,會把返回值R0保存到R6中:

OPCODE是0x1C06,在這里清R6,改為 EOR R6, R6. OPCODE為0x4076:

寫回到文件中

IREADER就可以運行起來了。修改 AndroidMenifest.xml ,把 LAUNCH Activity 改成 com.zhangyue.iReader.bookshelf.ui.ActivityBookShelf,刪除 WelcomeActivity,這樣就可以跳過歡迎界面直接進書架。

 

但是還遇到一個問題,在書架界面第一次按返回鍵,進入到“書城”界面,返回后再按返回鍵才能退出。看來 ActivityBookShelf 對返回鍵也做了處理,打開 smali 文件,找到 onKeyDown() 函數,觀察到應該調用 this.s() 函數(這里面有調用了this.j())是退出,前面有一些判斷,不必管它是干什么,直接干掉:

.line 1135
:cond_8

invoke-direct {p0}, Lcom/zhangyue/iReader/bookshelf/ui/ActivityBookShelf;->s()V

goto/16 :goto_0

.line 1159

在 this.j() 里面也有this.h的判斷,同樣干掉即可。用apktool重新打包,就搞定了。

 

重新生成APK放到了百度網盤,鏈接: http://pan.baidu.com/s/1i5wFTgp 密碼: vxxt

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM