上篇博文中淺析了從手機淘寶中提煉出商品搜索接口,不少人有個疑惑,x-sign怎麼來的?目前不少網友表示是經過xposed hook用模擬器做服務器中轉的方式。下面咱們經過逆向so文件的方式取得這個x-sign的算法。java
##找到x-sign的計算點 通過一系列跳轉後,咱們看到了com.taobao.wireless.security.adapter.a接口的a方法。算法
private String a(String[] arg4, String arg5, int arg6, String arg7) { return this.a.getRouter().doCommand(10401, new Object[]{arg4, arg5, Integer.valueOf(arg6), arg7}); }
在接下來的跳轉鏈以後,咱們又找到了實現RouterComponent接口以及doCommand方法的一個類:服務器
package com.alibaba.wireless.security.mainplugin; import com.alibaba.wireless.security.framework.IRouterComponent; import com.taobao.wireless.security.adapter.JNICLibrary; public class a implements IRouterComponent { public a() { super(); } public Object doCommand(int arg2, Object[] arg3) { return JNICLibrary.doCommandNative(arg2, arg3); } }
還有一個JNICLibrary類,其中聲明瞭doCommandNative方法:架構
package com.taobao.wireless.security.adapter; public class JNICLibrary { public static native Object doCommandNative(int arg0, Object[] arg1); }
所以,咱們須要在原生代碼中找到doCommandNative方法。less
在libsgmain.so文件中包含一個原生庫(libsgmain.so其實是一個.JAR文件,其中實現了與加密有關的接口):libsgmainso-6.xx.x。在IDA中加載該庫後,咱們看到了一堆錯誤消息提示框,問題在於section頭表無效。函數
經過elf查看工具咱們能夠看到 但咱們並不須要這個信息,程序頭表對咱們而言已經足夠,能夠正確加載並分析ELF文件。所以咱們能夠簡單刪除section頭表,將頭部中對應的字段置空。工具
而後再次在IDA中打開該文件。this
咱們有兩種方法能告訴Java虛擬機哪一個原生庫包含代碼中聲明的原生代碼的具體實現。第一種方法就是採用Java_package_name_ClassName_methodName之類的名字,第二種方法是調用RegisterNatives函數,在加載庫的時候進行註冊(在JNI_OnLoad函數中)。對於這個案例,若是咱們使用第一種方法,那麼函數名應該相似於Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative。在導出函數中咱們找不到這個名字,這意味着咱們須要查找RegisterNatives。所以,咱們轉到JNI_OnLoad函數,看到以下代碼:編碼
這裏代碼執行了哪些邏輯?初步分析時,函數頭以及函數尾都是典型的ARM架構。第一條指令會將函數須要使用的寄存器值push到棧中(這裏爲R0、R一、R2以及LR,用來保存函數返回地址)。最後一條指令恢復已保存的寄存器值,將返回地址存到PC寄存器中,而後返回函數。但若是咱們仔細分析,可能會注意到倒數第二條指令改變了返回地址。來計算一下代碼執行後返回地址的值。該地址加載自R1(0xB130),減去5,而後被mov到R0,再加上0x10,最後這個值等於0xB13B。所以,IDA認爲最終指令執行的是正常的函數返回操做,然而實際上會跳轉到0xB13B這個地址。加密
這裏須要注意的是,ARM處理器有兩個型號以及兩組指令:ARM以及Thumb。地址的低位用來決定處理器會使用哪一組指令集。這裏地址爲0xB13A,所以對應的是Thumb模式。
在這個庫中,每一個函數開頭處都添加了相似的語句以及某些垃圾代碼,這裏咱們不會詳細分析這些內容,只要記住幾乎全部函數的實際代碼都離函數開頭有一段距離。
因爲已有代碼中沒有顯式轉換到0xB13A,所以IDA沒法識別該地址處的代碼。一樣,IDA也沒有將庫中的大部分數據識別爲代碼,這樣咱們分析起來須要稍微用點技巧 所以,咱們手動告訴IDA代碼位置,而後獲得以下結果: 接下來咱們採用腳原本patch代碼。(鑑於篇幅 腳本內容略) patch完成後,咱們能夠指引IDA找到函數的真實代碼。IDA會逐一收集全部函數代碼,而後咱們就可使用HexRays來反編譯代碼。 咱們已經找到加密算法和密鑰,如今讓咱們嘗試解密類名。咱們獲得的結果爲com/taobao/wireless/security/adapter/JNICLibrary
如今咱們須要找到哪裏調用了RegisterNatives,這將咱們指引到doCommandNative函數。通過一系列分析還原得出具體邏輯:
int __fastcall doCommandNative(JNIEnv *env, jobject obj, int command, jarray args) { int v5; // r5 struc_2 *a5; // r6 int v9; // r1 int v11; // [sp+Ch] [bp-14h] int v12; // [sp+10h] [bp-10h] v5 = 0; v12 = *(_DWORD *)off_8AC00; v11 = 0; a5 = (struc_2 *)malloc(0x14u); if ( a5 ) { a5->field_0 = 0; a5->field_4 = 0; a5->field_8 = 0; a5->field_C = 0; v9 = command % 10000 / 100; a5->field_0 = command / 10000; a5->field_4 = v9; a5->field_8 = command % 100; a5->field_C = env; a5->field_10 = args; v5 = sub_9D60(command / 10000, v9, command % 100, 1, (int)a5, &v11); } free(a5); if ( !v5 && v11 ) sub_7CF34(env, v11, &byte_83ED7); return v5; }
函數名錶示這是開發者將全部函數轉到原生庫的統一入口點,咱們的目標函數編號爲10401。 從代碼中咱們能夠經過命令編號生成3個子編號:command / 10000、command % 10000 / 100以及command % 10(這裏咱們對應的是一、4以及1)。這3個子編號、指向JNIEnv的指針以及傳給該函數的其餘參數共同組成一個結構體,以便後續使用。 這棵樹會在JNI_OnLoad中動態建立,其中3個子編號共同編碼了整棵樹的路徑。樹中每一個節點都包含相應函數通過異或處理後的地址,祕鑰位於父節點中。