偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

一、前言

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

[[190216]]

在之前的一篇文章中,我介紹了如何從Android原生代碼中創(chuàng)建一個(gè)Java虛擬機(jī),但我沒(méi)有給出一個(gè)具體的例子。因此,我會(huì)在本文中給出一個(gè)具體的例子來(lái)說(shuō)明這一點(diǎn)。

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

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

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

我創(chuàng)建了一個(gè)示例應(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/”目錄。這篇文章中,我使用的是一個(gè)x86模擬器鏡像以及一個(gè)名為“app-universal-debug.apk”的應(yīng)用。

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

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

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

  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)是通過(guò)harness工具,開(kāi)啟一個(gè)socket服務(wù)器,讀取socket上傳輸?shù)膮?shù),使用這些參數(shù)來(lái)調(diào)用函數(shù)。這樣一來(lái),解密函數(shù)就會(huì)變成一個(gè)服務(wù),我們可以簡(jiǎn)單使用一個(gè)Python腳本與其通信。

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

在調(diào)用函數(shù)前,我們需要了解函數(shù)的簽名(即參數(shù)個(gè)數(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); 

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

  1. Java_org_cf_nativeharness_Cryptor_decryptString(JNIEnv *env, jstring encryptedString) 

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

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

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

  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) 

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

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

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

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

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

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

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

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

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

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

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

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

六、Harness工具的使用方法

你可以通過(guò)如下幾個(gè)步驟來(lái)使用Harness工具。

1、啟動(dòng)模擬器

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

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

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

5、運(yùn)行harness工具

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

7、運(yùn)行“decrypt_string.py”,祈禱一切順利

你可以使用以下命令將應(yīng)用及原生庫(kù)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 

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

現(xiàn)在,你可以運(yùn)行harness,將目標(biāo)庫(kù)路徑作為第一個(gè)參數(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 

為了測(cè)試工具是否正常工作,你可以在另一個(gè)終端上運(yùn)行如下命令:

  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ù)實(shí)際情況,修改harness工具源碼中的目標(biāo)函數(shù)。另外,實(shí)際場(chǎng)景中,目標(biāo)程序錯(cuò)綜復(fù)雜,我并不能保證這種方法100%有效。

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

2016-12-15 08:30:02

Linux命令

2012-02-08 16:37:36

ibmdw

2020-12-06 08:00:46

scanimage命令行Linux

2019-07-15 05:50:19

Linux命令行VirtualBox版

2019-08-27 08:00:10

OpenStack命令虛擬機(jī)

2014-06-06 10:00:56

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

2019-01-22 13:46:01

LinuxUnix系統(tǒng)命令行

2014-10-22 12:03:14

Linux嗅探HTTP

2018-06-19 16:05:27

LinuxStratis存儲(chǔ)

2023-08-01 13:31:18

模型Alpacaicuna

2020-10-31 08:20:39

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

2021-09-18 09:19:21

Linux

2018-05-24 14:20:01

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

2014-04-10 10:10:16

KVMDebianUbuntu

2022-06-30 12:19:22

Linux

2022-10-20 16:51:44

Linux命令行IP 地址

2010-11-24 15:33:59

mysql命令行參數(shù)

2010-03-10 17:23:37

Python 命令行參

2020-12-10 16:16:08

工具代碼開(kāi)發(fā)

2020-12-11 06:44:16

命令行工具開(kāi)發(fā)
點(diǎn)贊
收藏

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