自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何從命令行調(diào)用Android JNI函數(shù)并傳遞Java對象參數(shù)

安全 移動安全
當(dāng)我們對某個使用原生庫(native library)的惡意軟件或者應(yīng)用進(jìn)行分析或滲透測試時,如果能夠?qū)旌瘮?shù)進(jìn)行隔離和執(zhí)行是再好不過的事情,這樣做我們就可以使用其自身的代碼來調(diào)試對抗惡意軟件。

一、前言

當(dāng)我們對某個使用原生庫(native library)的惡意軟件或者應(yīng)用進(jìn)行分析或滲透測試時,如果能夠?qū)旌瘮?shù)進(jìn)行隔離和執(zhí)行是再好不過的事情,這樣做我們就可以使用其自身的代碼來調(diào)試對抗惡意軟件。舉個例子,如果惡意軟件包含加密字符串,并使用原生函數(shù)完成解密過程,你可以選擇花大量時間逆向分析算法來編寫自己的解密函數(shù),也可以選擇直接利用這個函數(shù)來處理任意輸入數(shù)據(jù)。如果使用后一種方法,即使惡意軟件作者完全改變了軟件的加密算法,你也可能不需要做任何修改即可完成任務(wù)。在這篇文章中,我將向讀者介紹如何利用并執(zhí)行原生庫函數(shù),即使調(diào)用這些函數(shù)時需要傳入JVM實例作為參數(shù)也沒問題。

[[190216]]

在之前的一篇文章中,我介紹了如何從Android原生代碼中創(chuàng)建一個Java虛擬機(jī),但我沒有給出一個具體的例子。因此,我會在本文中給出一個具體的例子來說明這一點。

我們至少可以使用兩種方法來調(diào)用原生函數(shù)。第一種方法是對應(yīng)用進(jìn)行修改,使應(yīng)用接受你的輸入數(shù)據(jù)并傳遞給原生函數(shù)。例如,你可以寫一個intent filter,將其轉(zhuǎn)化為Smali語言,將代碼添加到目標(biāo)應(yīng)用中,修改manifest文件,運行應(yīng)用,使用adb命令將帶有參數(shù)的intent發(fā)送給目標(biāo)應(yīng)用即可。另一種方法更好,你可以添加一個小型socket或web服務(wù)器,使用curl向其發(fā)送請求,這種方法不需要修改manifest文件。

第二種方法的目標(biāo)是創(chuàng)建一個通過命令行運行的小型原生可執(zhí)行工具,用來加載庫文件、調(diào)用目標(biāo)函數(shù)、傳遞我們輸入的任意參數(shù)。這樣我們就可以單獨運行一個可執(zhí)行文件,而不需要運行整個應(yīng)用程序,因此調(diào)試起來也就更為方便。

二、目標(biāo)應(yīng)用

我創(chuàng)建了一個示例應(yīng)用,方便讀者按照教程學(xué)習(xí),應(yīng)用名為“native-harness-target”。你可以使用以下命令將工程文件復(fù)制到本地并完成編譯(記得修改其中的“$ANDROID_*”變量)。

  1. git clone https://github.com/CalebFenton/native-harness-target.git 
  2. cd native-harness-target 
  3. echo 'ndk.dir=$ANDROID_NDK' > local.properties 
  4. echo 'sdk.dir=$ANDROID_SDK' >> local.properties 
  5. ./gradlew build 

APK文件最終生成在“app/build/outputs/apk/”目錄。這篇文章中,我使用的是一個x86模擬器鏡像以及一個名為“app-universal-debug.apk”的應(yīng)用。

該應(yīng)用程序包含一個加密字符串,并會在運行時使用原生庫對字符串進(jìn)行解密。以下是在Smail中字符串的解密過程:

  1. const/16 v3, 0x57 
  2. new-array v1, v3, [B 
  3. fill-array-data v1, :array_2a 
  4. .local v1, "encryptedStringBytes":[B 
  5. invoke-static {}, Lorg/cf/nativeharness/Cryptor;->getInstance()Lorg/cf/nativeharness/Cryptor; 
  6. move-result-object v0 
  7. .line 21 
  8. .local v0, "c":Lorg/cf/nativeharness/Cryptor; 
  9. # v3 contains a String made from encrypted bytes 
  10. new-instance v3, Ljava/lang/String; 
  11. invoke-direct {v3, v1}, Ljava/lang/String;-><init>([B)V 
  12. # Call the decryption method, move result back to v3 
  13. invoke-virtual {v0, v3}, Lorg/cf/nativeharness/Cryptor;->decryptString(Ljava/lang/String;)Ljava/lang/String; 
  14. move-result-object v3 

三、構(gòu)建Harness工具

我使用的是Tim 'diff' Strazzere開發(fā)的一款名為“native-shim”的工具(Tim是RedNaga的一名成員)作為整套利用工具的基礎(chǔ),我將這個工具命名為“Harness”。在Android中,shim就像一個中間墊片,作用是加載一個庫,并調(diào)用其“JNI_OnLoad”方法。它可以使調(diào)試工作更加簡單,我們只需要使用調(diào)試器啟動shim,并將具體路徑以參數(shù)形式傳遞給目標(biāo)庫即可。我們可以設(shè)置調(diào)試器的斷點,在庫加載時觸發(fā)斷點,這樣就能進(jìn)入“JNI_OnLoad”函數(shù)的處理流程。此外,native-shim還可以加載庫文件(.so文件)、獲取函數(shù)的引用并調(diào)用函數(shù),這一切對我們來說都非常實用。

首先,我添加了部分代碼以初始化一個Java虛擬機(jī)實例,并將該實例傳遞給JNI_OnLoad函數(shù),這樣處理可以使JNI的初始化過程更為準(zhǔn)確。如果沒有真實的虛擬機(jī)實例,JNI庫的內(nèi)部狀態(tài)看起來可能會有些奇怪。不同庫文件的JNI_OnLoad的實現(xiàn)可能不盡相同,但這并不重要,重要的是這些實現(xiàn)都會檢查JNI版本,如這段代碼所示。因此我們需要創(chuàng)建一個虛擬機(jī)實例。

  1. printf(" [+] Initializing JavaVM Instance\n"); 
  2. JavaVM *vm = NULL
  3. JNIEnv *env = NULL
  4. int status = init_jvm(&vm, &env); 
  5. if (status == 0) { 
  6.   printf(" [+] Initialization success (vm=%p, env=%p)\n", vm, env); 
  7. } else { 
  8.   printf(" [!] Initialization failure (%i)\n", status); 
  9.   return -1; 
  10. printf(" [+] Calling JNI_OnLoad\n"); 
  11. onLoadFunc(vm, NULL); 

我們的最終目標(biāo)是通過harness工具,開啟一個socket服務(wù)器,讀取socket上傳輸?shù)膮?shù),使用這些參數(shù)來調(diào)用函數(shù)。這樣一來,解密函數(shù)就會變成一個服務(wù),我們可以簡單使用一個Python腳本與其通信。

四、理解目標(biāo)函數(shù)

在調(diào)用函數(shù)前,我們需要了解函數(shù)的簽名(即參數(shù)個數(shù)和參數(shù)類型)及函數(shù)的返回類型。我們可以先看一下org.cf.nativeharness.Cryptor類的反編譯代碼,類中包含decryptString原生方法的聲明,如下所示:

  1. public class Cryptor { 
  2.     private static Cryptor instance = null
  3.     public static Cryptor getInstance() { 
  4.         if (instance == null) { 
  5.             instance = new Cryptor(); 
  6.         } 
  7.         return instance; 
  8.     } 
  9.     public native String decryptString(String encryptedString); 

從這段代碼中,我們可知該方法使用了一個String對象作為參數(shù),返回了一個String對象,看上去比較簡單?,F(xiàn)在我們將其轉(zhuǎn)化為原生函數(shù)形式,如下所示:

  1. Java_org_cf_nativeharness_Cryptor_decryptString(JNIEnv *env, jstring encryptedString) 

每個JNI原生方法都需要將JNIEnv對象作為第一個參數(shù)。這意味著定義我們函數(shù)的typedef語句應(yīng)該如下所示:

  1. typedef jstring(*decryptString_t)(JNIEnv *, jstring); 

不幸的是,如果你試圖使用上述typedef語句執(zhí)行這個函數(shù),你會得到一個錯誤信息,如下所示:

  1. E/dalvikvm: JNI ERROR (app bug): attempt to use stale local reference 0x1 
  2. E/dalvikvm: VM aborting 
  3. A/libc: Fatal signal 6 (SIGABRT) at 0x00000a9a (code=-6), thread 2714 (harness) 

這讓我困惑了好一陣子。我原先以為我可能在某個地方使用了空指針引用,因此我花了很多功夫,添加了許多printf語句,將內(nèi)存中所有相關(guān)指針的位置全部打印出來。這個錯誤信息貌似在提示我某個參數(shù)出現(xiàn)了問題,但我排查后發(fā)現(xiàn)所有指針都是正常的,沒有空引用情況。

我敢肯定我傳遞的參數(shù)沒有問題,問題可能出在JNI上。為了證實這一點,我使用了javah命令,它可以生成實現(xiàn)原生方法所需要的C語言頭文件以及源代碼文件。

為了完成這個工作,你需要安裝dex2jar,找到正確的類路徑,將“platforms/android-19”改為你已經(jīng)安裝的具體平臺,如下所示:

  1. $ d2j-dex2jar.sh app-universal-debug.apk 
  2. dex2jar app-universal-debug.apk -> ./app-universal-debug-dex2jar.jar 
  3. $ javah -cp app-universal-debug-dex2jar.jar:$ANDROID_SDK/platforms/android-19/android.jar org.cf.nativeharness.Cryptor 

上述命令可以生成“_org_cf_nativeharness_Cryptor.h_”文件,其中包含如下信息:

  1. JNIEXPORT jstring JNICALL Java_org_cf_nativeharness_Cryptor_decryptString(JNIEnv *, jobject, jstring); 

我們可以看到多了一個jobject作為第二個參數(shù),這究竟是為什么?如果你已經(jīng)知道了這個問題的答案,我敢打賭你已經(jīng)花了很多時間深入學(xué)習(xí)了Smali,特別是其中的invoke-virtual方法。無論你在何時調(diào)用虛擬方法(通常都是些非靜態(tài)方法),第一個參數(shù)總是某個對象的實例,這個實例負(fù)責(zé)方法的具體實現(xiàn)。對于這個例子來說,此時第一個參數(shù)應(yīng)該是“org.cf.nativeharness.Cryptor”類的一個實例。

當(dāng)然,你可以投機(jī)取巧,比如可以查看str-crypt.c代碼,找到函數(shù)的具體調(diào)用形式。但你要知道你是個逆向分析師(或滲透測試員),你不可能拿到源代碼。

因此正確的typedef語句中應(yīng)該包含Cryptor實例的一個jobject對象,如下所示:

  1. typedef jstring(*decryptString_t)(JNIEnv *, jobject, jstring); 

你可能會感到好奇,為什么我們不以靜態(tài)方法開始介紹?沒有特別的理由,主要是因為我在寫這篇博客時,所分析的原始應(yīng)用中目標(biāo)方法不是靜態(tài)方法,僅此而已。

這一部分內(nèi)容最大的收獲就是,如果你不確定函數(shù)的具體調(diào)用形式,你可以試一下javah命令,時刻牢記虛擬方法與Java中的Method#invoke()類似,使用某個實例對象作為第一個參數(shù)。

五、構(gòu)建Socket服務(wù)器

這是整個工作中最無趣的一個環(huán)節(jié),如果你不介意的話,我會跳過這一部分。你可以自行查看具體的實現(xiàn)源碼,如果愿意的話也可以提出修改意見。

六、Harness工具的使用方法

你可以通過如下幾個步驟來使用Harness工具。

1、啟動模擬器

2、將harness push到設(shè)備中

3、將目標(biāo)原生庫及其他依賴庫push到設(shè)備中(本文示例中不涉及到依賴庫)

4、將目標(biāo)應(yīng)用push到設(shè)備中

5、運行harness工具

6、將模擬器的端口轉(zhuǎn)發(fā)到宿主機(jī)上

7、運行“decrypt_string.py”,祈禱一切順利

你可以使用以下命令將應(yīng)用及原生庫push到設(shè)備中。

  1. $ adb push app/build/output/apk/app-universal-debug.apk /data/local/tmp/target-app.apk 
  2. $ unzip app/build/outputs/apk/app-universal-debug.apk lib/x86/libstr-crypt.so 
  3. Archive:  app/build/outputs/apk/app-universal-debug.apk 
  4.   inflating: lib/x86/libstr-crypt.so 
  5. $ adb push lib/x86/libstr-crypt.so /data/local/tmp 
  6. lib/x86/libstr-crypt.so: 1 file pushed. 1.5 MB/s (5476 bytes in 0.004s) 

使用如下命令將harness工具push到設(shè)備中。

  1. cd harness 
  2. make && make install 

注意:以上命令會將x86庫push到設(shè)備中,如果你確實想要使用其他的模擬器鏡像,你可以使用“adb push libs//harness /data/local/tmp”命令替換“make install”命令。

現(xiàn)在,你可以運行harness,將目標(biāo)庫路徑作為第一個參數(shù)傳入,如下所示:

  1. $ adb shell /data/local/tmp/harness /data/local/tmp/libstr-crypt.so 
  2. [*] Native Harness 
  3.  [+] Loading target: [ /data/local/tmp/libstr-crypt.so ] 
  4.  [+] Library Loaded! 
  5.  [+] Found JNI_OnLoad, good 
  6.  [+] Initializing JavaVM Instance 
  7. WARNING: linker: libdvm.so has text relocations. This is wasting memory and is a security risk. Please fix. 
  8.  [+] Initialization success (vm=0xb8e420a0env=0xb8e420e0
  9.  [+] Calling JNI_OnLoad 
  10.  [+] Found decryptString function, good (0xb761f4f0) 
  11.  [+] Finding Cryptor class 
  12.  [+] Found Cryptor class: 0x1d2001d9 
  13.  [+] Found Cryptor.getInstance(): 0xb27bc270 
  14.  [+] Instantiated Cryptor class: 0x1d2001dd 
  15.  [+] Starting socket server on port 5001 

為了測試工具是否正常工作,你可以在另一個終端上運行如下命令:

  1. $ ./decrypt_string.py 
  2. Sending encrypted string 
  3. Decrypted string: "Seek freedom and become captive of your desires. Seek discipline and find your liberty." 

如果你在輸出結(jié)果中看到解密后的字符串,表明一切順利,非常完美。

七、總結(jié)

你可以根據(jù)實際情況,修改harness工具源碼中的目標(biāo)函數(shù)。另外,實際場景中,目標(biāo)程序錯綜復(fù)雜,我并不能保證這種方法100%有效。

責(zé)任編輯:趙寧寧 來源: 安全客
相關(guān)推薦

2012-02-08 16:37:36

ibmdw

2020-12-06 08:00:46

scanimage命令行Linux

2016-12-15 08:30:02

Linux命令

2019-08-27 08:00:10

OpenStack命令虛擬機(jī)

2019-07-15 05:50:19

Linux命令行VirtualBox版

2014-10-22 12:03:14

Linux嗅探HTTP

2019-01-22 13:46:01

LinuxUnix系統(tǒng)命令行

2014-06-06 10:00:56

命令行監(jiān)控Nginx Web服務(wù)

2021-09-18 09:19:21

Linux

2020-10-31 08:20:39

curl命令命令行互聯(lián)網(wǎng)

2023-08-01 13:31:18

模型Alpacaicuna

2018-06-19 16:05:27

LinuxStratis存儲

2014-04-10 10:10:16

KVMDebianUbuntu

2018-05-24 14:20:01

數(shù)據(jù)庫MySQL命令行

2022-06-30 12:19:22

Linux

2010-03-10 17:23:37

Python 命令行參

2010-11-24 15:33:59

mysql命令行參數(shù)

2022-10-20 16:51:44

Linux命令行IP 地址

2011-08-22 11:51:13

Linuxconfigure

2010-07-20 14:02:38

Perl命令行參數(shù)
點贊
收藏

51CTO技術(shù)棧公眾號