43.安卓逆向2-補環境-使用unidbg(使用Smali語法調用方法和使用方法地址調用方法)

免責聲明:內容僅供學習參考,請合法利用知識,禁止進行違法犯罪活動!

內容參考于:圖靈Python學院

工具下載:

鏈接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89

提取碼:zy89

復制這段內容后打開百度網盤手機App,操作更方便哦

上一個內容:42.安卓逆向2-補環境-unidbg安裝和簡單使用

開始之前先說明一下,unidbg是一個系統虛擬環境,它并不會去找某個app,它只是模擬一個運行環境,unidbg用到app的類、包名全部是假的(并不是我們使用反編譯工具看到的那樣,unidbg它只是通過類名、方法去構建一個假的類,然后去調用so文件),unidbg它只是模擬的安卓系統(模型安卓系統標準庫函數),如果so文件或java代碼依賴了開發者自定義的邏輯,unidbg是沒辦法模擬的,需要我們自己手動處理

然后先設置一下ida的目錄顯示,如下圖把壓縮空的中間軟件包取消勾選

首先創建一個目錄(或者叫包,包和目錄是一個東西),如下圖紅框的目錄,在com路面選擇新加軟件包

目錄名mmmm,輸入完按回車

按完回車目錄就創建好了,如下圖紅框

然后再創建一個java類(這里又創建了一個叫dac的目錄,在它里面創建java類)

如下圖java類名,輸入完按回車

然后就創建好了

然后開始寫unidbg的代碼,首先是初始結構和模擬器

package com.mmmm.dac;// 導入Unidbg框架的核心類
// AndroidEmulator:Android模擬器的核心類,用于模擬Android運行環境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加載的SO文件(動態鏈接庫)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模擬器構建器,用于創建不同配置的模擬器
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系統庫,模擬系統庫調用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象類,用于處理SO中的JNI調用(如Java方法調用)
import com.github.unidbg.linux.android.dvm.AbstractJni;
// DalvikModule:Dalvik虛擬機中的模塊類,用于加載和處理SO文件
import com.github.unidbg.linux.android.dvm.DalvikModule;
// VM:Dalvik虛擬機類,模擬Android的Java虛擬機環境
import com.github.unidbg.linux.android.dvm.VM;
// Memory:內存操作接口,用于管理模擬器的內存
import com.github.unidbg.memory.Memory;// 用于文件操作的Java標準類
import java.io.File;/*** DcTest類:繼承自AbstractJni,用于測試和分析目標SO文件* 作用:模擬Android環境,加載指定的SO文件,并獲取其中的函數地址等信息*/
public class DcTest extends AbstractJni {// 成員變量聲明// 模擬器實例:整個模擬環境的核心private final AndroidEmulator emulator;// 虛擬機實例:模擬Android的Java虛擬機private final VM vm;// 模塊實例:代表加載的SO文件,用于操作其中的函數和符號private final Module module;/*** 構造方法:初始化模擬器、虛擬機和加載SO文件* 當創建DcTest對象時,會自動執行這些初始化操作*/public DcTest(){// 1. 創建Android模擬器實例// for64Bit():指定創建64位模擬器(如果目標SO是32位,需改為for32Bit())// setProcessName():設置模擬的進程名,通常設為目標SO所在的APP包名// build():完成模擬器構建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 獲取模擬器的內存操作接口// 內存接口用于管理模擬器的內存分配、庫解析等final Memory memory = emulator.getMemory();// 3. 設置系統庫解析器// AndroidResolver(23):指定模擬的Android系統版本為API 23(Android 6.0)// 作用:當SO調用系統庫(如libc.so)時,模擬器能正確解析并模擬這些調用memory.setLibraryResolver(new AndroidResolver(23));// 4. 創建Dalvik虛擬機(Android的Java虛擬機)vm = emulator.createDalvikVM();// 5. 設置JNI處理器// 將當前類(DcTest)作為JNI調用的處理器// 當SO中調用JNI函數(如調用Java方法)時,會由當前類處理vm.setJni(this);// 6. 加載目標SO文件// 第一個參數:SO文件的路徑(這里是相對路徑,實際使用時需確保文件存在)// 第二個參數:false表示不自動調用JNI_OnLoad(后續會手動調用)// DalvikModule:用于在Dalvik虛擬機中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dc1127/libwtf.so"), false);// 7. 手動調用SO中的JNI_OnLoad函數// JNI_OnLoad是SO被加載時的初始化函數,通常用于注冊JNI方法// 動態注冊的JNI方法需要調用此函數才會生效,靜態注冊可以省略dm.callJNI_OnLoad(emulator);// 8. 獲取Module實例// Module是操作SO文件的主要接口,通過它可以查找函數、獲取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加載到內存中的起始地址,函數的實際地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函數的地址(C++函數,經過名稱修飾)// _ZN3MD56updateEPKhj:是C++函數MD5::update(const unsigned char*, unsigned int)的名稱修飾后的結果// findSymbolByName:通過函數名查找符號// getAddress():獲取函數在內存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函數地址(十六進制):" + Long.toHexString(address));// 11. 查找并打印Java native方法對應的C函數地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java類cn.thecover.lib.common.manager.SignManager中的getSign()本地方法對應的C函數名// 這是JNI靜態注冊的命名規則:Java_包名_類名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函數地址(十六進制):" + Long.toHexString(funaddr));}
}

然后為了方便引入so文件,這里創建一個目錄專門用來存放so文件,如下圖鼠標右擊根目錄,選擇新建目錄

然后輸入utils,然后按回車

然后在utils目錄再創建一個目錄,這個目錄是為了區分不同app的so文件(比如兩個app一個叫測試1一個叫測試2,然后創建一個測1目錄,然后測試1里用到的so文件全部都放到測1目錄里,然后創建一個測2目錄,用來存放測試2app中使用的so文件)

如下圖的目錄和文件

如下圖代碼中的使用

如下圖上圖紅框的so文件中存在getSign方法

然后可以得到一個類路徑,如下圖紅框

然后使用unidbg獲取這個SignManager類(可以理解為讓unidbg使用cn/thecover/lib/common/manager/SignManager這個類路徑創建一個虛擬的類),這里用的smail語法

vm.resolveClass("cn/thecover/lib/common/manager/SignManager");

獲取方法名,首先要把下圖紅框的代碼轉成smail代碼

如下圖利用大模型轉

# 聲明一個公共靜態 native 方法
.method public static native getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
.end method

然后復制下圖紅框的代碼

然后unidbg代碼

//要調用的方法
String method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 調用方法時傳的參數
String 參數1 = "1";
String 參數2 = "2";
String 參數3 = "3";//調用方法和獲取返回值
StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 參數1, 參數2, 參數3);

調用方法的完整代碼

/*** 調用目標Java類的靜態native方法getSign,并返回結果* 功能:通過Unidbg模擬調用SO中實現的getSign方法,傳遞三個字符串參數并獲取返回值*/
public String getSign(){// 1. 加載并獲取目標Java類的虛擬表示(DvmClass)// vm.resolveClass:讓Unidbg的虛擬機(vm)查找并加載指定的Java類// 參數是類的全限定名(用斜杠分隔),對應真實Java類:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass對象相當于這個類在虛擬環境中的"代言人",通過它可以操作這個類的靜態方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定義要調用的方法簽名(方法的"身份證")// 格式:方法名(參數類型列表)返回值類型// 這里的簽名對應Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示參數類型為String(Smali語法,所有引用類型都用這種格式)// - 三個Ljava/lang/String; 對應三個String參數// - 最后的Ljava/lang/String; 表示返回值為StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 準備調用方法時需要傳遞的參數String 參數1 = "1";  // 第一個字符串參數,實際使用中可能是具體業務數據(如時間戳、設備ID等)String 參數2 = "2";  // 第二個字符串參數,可能是加密鹽值、用戶ID等String 參數3 = "3";  // 第三個字符串參數,可能是隨機數、簽名類型等// 4. 調用目標類的靜態native方法// dvmClass.callStaticJniMethodObject:通過虛擬類對象調用靜態方法// 參數說明:// - emulator:當前的Android模擬器實例(提供運行環境)// - method:上面定義的方法簽名(指定要調用的具體方法)// - 參數1/2/3:傳遞給方法的實際參數// 返回值:StringObject(Unidbg中對String的包裝類,包含方法調用的結果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 參數1, 參數2, 參數3);// 5. 提取返回結果并返回// value.getValue():將Unidbg的StringObject轉換為Java原生Stringreturn value.getValue();
}

運行代碼

package com.mmmm.dac;// 導入Unidbg框架的核心類
// AndroidEmulator:Android模擬器的核心類,用于模擬Android運行環境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加載的SO文件(動態鏈接庫)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模擬器構建器,用于創建不同配置的模擬器
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系統庫,模擬系統庫調用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象類,用于處理SO中的JNI調用(如Java方法調用)
import com.github.unidbg.linux.android.dvm.*;
// DalvikModule:Dalvik虛擬機中的模塊類,用于加載和處理SO文件
// VM:Dalvik虛擬機類,模擬Android的Java虛擬機環境
// Memory:內存操作接口,用于管理模擬器的內存
import com.github.unidbg.memory.Memory;// 用于文件操作的Java標準類
import java.io.File;/*** DcTest類:繼承自AbstractJni,用于測試和分析目標SO文件* 作用:模擬Android環境,加載指定的SO文件,并獲取其中的函數地址等信息*/
public class DcTest extends AbstractJni {// 成員變量聲明// 模擬器實例:整個模擬環境的核心private final AndroidEmulator emulator;// 虛擬機實例:模擬Android的Java虛擬機private final VM vm;// 模塊實例:代表加載的SO文件,用于操作其中的函數和符號private final Module module;/*** 構造方法:初始化模擬器、虛擬機和加載SO文件* 當創建DcTest對象時,會自動執行這些初始化操作*/public DcTest(){// 1. 創建Android模擬器實例// for64Bit():指定創建64位模擬器(如果目標SO是32位,需改為for32Bit())// setProcessName():設置模擬的進程名,通常設為目標SO所在的APP包名// build():完成模擬器構建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 獲取模擬器的內存操作接口// 內存接口用于管理模擬器的內存分配、庫解析等final Memory memory = emulator.getMemory();// 3. 設置系統庫解析器// AndroidResolver(23):指定模擬的Android系統版本為API 23(Android 6.0)// 作用:當SO調用系統庫(如libc.so)時,模擬器能正確解析并模擬這些調用memory.setLibraryResolver(new AndroidResolver(23));// 4. 創建Dalvik虛擬機(Android的Java虛擬機)vm = emulator.createDalvikVM();// 5. 設置JNI處理器// 將當前類(DcTest)作為JNI調用的處理器// 當SO中調用JNI函數(如調用Java方法)時,會由當前類處理vm.setJni(this);// 6. 加載目標SO文件// 第一個參數:SO文件的路徑(這里是相對路徑,實際使用時需確保文件存在)// 第二個參數:false表示不自動調用JNI_OnLoad(后續會手動調用)// DalvikModule:用于在Dalvik虛擬機中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dcs/libwtf.so"), false);// 7. 手動調用SO中的JNI_OnLoad函數// JNI_OnLoad是SO被加載時的初始化函數,通常用于注冊JNI方法// 動態注冊的JNI方法需要調用此函數才會生效,靜態注冊可以省略dm.callJNI_OnLoad(emulator);// 8. 獲取Module實例// Module是操作SO文件的主要接口,通過它可以查找函數、獲取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加載到內存中的起始地址,函數的實際地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函數的地址(C++函數,經過名稱修飾)// _ZN3MD56updateEPKhj:是C++函數MD5::update(const unsigned char*, unsigned int)的名稱修飾后的結果// findSymbolByName:通過函數名查找符號// getAddress():獲取函數在內存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函數地址(十六進制):" + Long.toHexString(address));// 11. 查找并打印Java native方法對應的C函數地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java類cn.thecover.lib.common.manager.SignManager中的getSign()本地方法對應的C函數名// 這是JNI靜態注冊的命名規則:Java_包名_類名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函數地址(十六進制):" + Long.toHexString(funaddr));}/*** 調用目標Java類的靜態native方法getSign,并返回結果* 功能:通過Unidbg模擬調用SO中實現的getSign方法,傳遞三個字符串參數并獲取返回值*/public String getSign(){// 1. 加載并獲取目標Java類的虛擬表示(DvmClass)// vm.resolveClass:讓Unidbg的虛擬機(vm)查找并加載指定的Java類// 參數是類的全限定名(用斜杠分隔),對應真實Java類:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass對象相當于這個類在虛擬環境中的"代言人",通過它可以操作這個類的靜態方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定義要調用的方法簽名(方法的"身份證")// 格式:方法名(參數類型列表)返回值類型// 這里的簽名對應Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示參數類型為String(Smali語法,所有引用類型都用這種格式)// - 三個Ljava/lang/String; 對應三個String參數// - 最后的Ljava/lang/String; 表示返回值為StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 準備調用方法時需要傳遞的參數String 參數1 = "1";  // 第一個字符串參數,實際使用中可能是具體業務數據(如時間戳、設備ID等)String 參數2 = "2";  // 第二個字符串參數,可能是加密鹽值、用戶ID等String 參數3 = "3";  // 第三個字符串參數,可能是隨機數、簽名類型等// 4. 調用目標類的靜態native方法// dvmClass.callStaticJniMethodObject:通過虛擬類對象調用靜態方法// 參數說明:// - emulator:當前的Android模擬器實例(提供運行環境)// - method:上面定義的方法簽名(指定要調用的具體方法)// - 參數1/2/3:傳遞給方法的實際參數// 返回值:StringObject(Unidbg中對String的包裝類,包含方法調用的結果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 參數1, 參數2, 參數3);// 5. 提取返回結果并返回// value.getValue():將Unidbg的StringObject轉換為Java原生Stringreturn value.getValue();}public static void main(String[] args) {DcTest dcTest = new DcTest();String sign = dcTest.getSign();System.out.println(sign);}}

運行后會報錯,這個錯誤是調用 getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; so方法,它里面又調用了cn/thecover/lib/common/utils/LogShutDown類里的getAppSign方法,這個getAppSign方法找不到就會出下圖中的錯誤

如下圖還有一個Please.vm.setJni,這個與上方的錯誤一樣,都是so方法中調用了java方法,然后找不到調用的java方法就報錯了

這就需要補環境,再補環境之前,還有一個東西,上方是通過smail語法去調用的方法,接下來寫通過so文件的方法地址來調用方法,如下圖使用ida打開so文件,接下來就通過調用MD5的update方法做實例

上圖紅框并不是真正的函數名,真正的函數名是_ZN3MD56updateEPKhj

函數的地址43834

通過下圖紅框的代碼也可以獲取_ZN3MD56updateEPKhj方法地址

獲取java方法的地址

使用地址調用方法

/*** 通過Unidbg調用原生函數獲取簽名結果* 功能:調用指定地址的原生函數,傳入三個字符串參數,返回函數處理后的簽名字符串*/
public String getSignAdd(){// 定義要調用的原生函數在目標模塊中的內存地址(偏移量)// 0x45B48是通過逆向分析(如IDA、Ghidra)得到的函數地址long functionAddress = 0x45B48;/** 準備JNI環境相關對象* JNI(Java Native Interface)是Java與原生代碼交互的接口*/// 獲取JNI環境指針(JNIEnv*),這是調用任何JNI函數的第一個參數Pointer jniEnv = vm.getJNIEnv();// 創建三個字符串對象作為函數參數// StringObject是Unidbg中用于表示Java字符串的包裝類StringObject data1 = new StringObject(vm, "1");  // 第一個字符串參數值為"1"StringObject data2 = new StringObject(vm, "2");  // 第二個字符串參數值為"2"StringObject data3 = new StringObject(vm, "3");  // 第三個字符串參數值為"3"// 構建函數調用的參數列表List<Object> args = new ArrayList<>();// 添加第一個參數:JNI環境指針(JNIEnv*),這是JNI函數的標準第一個參數args.add(jniEnv);// 添加后續參數:將字符串對象轉換為DVM本地引用// vm.addLocalObject()會將對象添加到Dalvik虛擬機的本地引用表,返回引用ID// 原生函數通過這個引用ID可以訪問到對應的Java對象args.add(vm.addLocalObject(data1));args.add(vm.addLocalObject(data2));args.add(vm.addLocalObject(data3));// 調用目標原生函數// module.callFunction():通過模塊調用指定地址的函數// 參數說明:模擬器實例、函數地址、參數數組// 返回值:原生函數的返回結果(這里是一個對象引用ID)Number numbers = module.callFunction(emulator, functionAddress, args.toArray());// 將返回的引用ID轉換為DVM中的對象// 原生函數返回的是Java對象引用,需要通過vm.getObject()獲取實際對象DvmObject<?> object = vm.getObject(numbers.intValue());// 從DVM對象中提取字符串值// 假設原生函數返回的是String類型對象,通過getValue()獲取其字符串內容String value = (String) object.getValue();// 返回獲取到的簽名結果return value;
}

完整代碼

package com.mmmm.dac;// 導入Unidbg框架的核心類
// AndroidEmulator:Android模擬器的核心類,用于模擬Android運行環境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加載的SO文件(動態鏈接庫)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模擬器構建器,用于創建不同配置的模擬器
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系統庫,模擬系統庫調用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象類,用于處理SO中的JNI調用(如Java方法調用)
import com.github.unidbg.linux.android.dvm.*;
// DalvikModule:Dalvik虛擬機中的模塊類,用于加載和處理SO文件
// VM:Dalvik虛擬機類,模擬Android的Java虛擬機環境
// Memory:內存操作接口,用于管理模擬器的內存
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;// 用于文件操作的Java標準類
import java.io.File;
import java.util.ArrayList;
import java.util.List;/*** DcTest類:繼承自AbstractJni,用于測試和分析目標SO文件* 作用:模擬Android環境,加載指定的SO文件,并獲取其中的函數地址等信息*/
public class DcTest extends AbstractJni {// 成員變量聲明// 模擬器實例:整個模擬環境的核心private final AndroidEmulator emulator;// 虛擬機實例:模擬Android的Java虛擬機private final VM vm;// 模塊實例:代表加載的SO文件,用于操作其中的函數和符號private final Module module;/*** 構造方法:初始化模擬器、虛擬機和加載SO文件* 當創建DcTest對象時,會自動執行這些初始化操作*/public DcTest(){// 1. 創建Android模擬器實例// for64Bit():指定創建64位模擬器(如果目標SO是32位,需改為for32Bit())// setProcessName():設置模擬的進程名,通常設為目標SO所在的APP包名// build():完成模擬器構建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 獲取模擬器的內存操作接口// 內存接口用于管理模擬器的內存分配、庫解析等final Memory memory = emulator.getMemory();// 3. 設置系統庫解析器// AndroidResolver(23):指定模擬的Android系統版本為API 23(Android 6.0)// 作用:當SO調用系統庫(如libc.so)時,模擬器能正確解析并模擬這些調用memory.setLibraryResolver(new AndroidResolver(23));// 4. 創建Dalvik虛擬機(Android的Java虛擬機)vm = emulator.createDalvikVM();// 5. 設置JNI處理器// 將當前類(DcTest)作為JNI調用的處理器// 當SO中調用JNI函數(如調用Java方法)時,會由當前類處理vm.setJni(this);// 6. 加載目標SO文件// 第一個參數:SO文件的路徑(這里是相對路徑,實際使用時需確保文件存在)// 第二個參數:false表示不自動調用JNI_OnLoad(后續會手動調用)// DalvikModule:用于在Dalvik虛擬機中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dcs/libwtf.so"), false);// 7. 手動調用SO中的JNI_OnLoad函數// JNI_OnLoad是SO被加載時的初始化函數,通常用于注冊JNI方法// 動態注冊的JNI方法需要調用此函數才會生效,靜態注冊可以省略dm.callJNI_OnLoad(emulator);// 8. 獲取Module實例// Module是操作SO文件的主要接口,通過它可以查找函數、獲取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加載到內存中的起始地址,函數的實際地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函數的地址(C++函數,經過名稱修飾)// _ZN3MD56updateEPKhj:是C++函數MD5::update(const unsigned char*, unsigned int)的名稱修飾后的結果// findSymbolByName:通過函數名查找符號// getAddress():獲取函數在內存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函數地址(十六進制):" + Long.toHexString(address));// 11. 查找并打印Java native方法對應的C函數地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java類cn.thecover.lib.common.manager.SignManager中的getSign()本地方法對應的C函數名// 這是JNI靜態注冊的命名規則:Java_包名_類名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函數地址(十六進制):" + Long.toHexString(funaddr));}/*** 調用目標Java類的靜態native方法getSign,并返回結果* 功能:通過Unidbg模擬調用SO中實現的getSign方法,傳遞三個字符串參數并獲取返回值*/public String getSign(){// 1. 加載并獲取目標Java類的虛擬表示(DvmClass)// vm.resolveClass:讓Unidbg的虛擬機(vm)查找并加載指定的Java類// 參數是類的全限定名(用斜杠分隔),對應真實Java類:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass對象相當于這個類在虛擬環境中的"代言人",通過它可以操作這個類的靜態方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定義要調用的方法簽名(方法的"身份證")// 格式:方法名(參數類型列表)返回值類型// 這里的簽名對應Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示參數類型為String(Smali語法,所有引用類型都用這種格式)// - 三個Ljava/lang/String; 對應三個String參數// - 最后的Ljava/lang/String; 表示返回值為StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 準備調用方法時需要傳遞的參數String 參數1 = "1";  // 第一個字符串參數,實際使用中可能是具體業務數據(如時間戳、設備ID等)String 參數2 = "2";  // 第二個字符串參數,可能是加密鹽值、用戶ID等String 參數3 = "3";  // 第三個字符串參數,可能是隨機數、簽名類型等// 4. 調用目標類的靜態native方法// dvmClass.callStaticJniMethodObject:通過虛擬類對象調用靜態方法// 參數說明:// - emulator:當前的Android模擬器實例(提供運行環境)// - method:上面定義的方法簽名(指定要調用的具體方法)// - 參數1/2/3:傳遞給方法的實際參數// 返回值:StringObject(Unidbg中對String的包裝類,包含方法調用的結果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 參數1, 參數2, 參數3);// 5. 提取返回結果并返回// value.getValue():將Unidbg的StringObject轉換為Java原生Stringreturn value.getValue();}/*** 通過Unidbg調用原生函數獲取簽名結果* 功能:調用指定地址的原生函數,傳入三個字符串參數,返回函數處理后的簽名字符串*/public String getSignAdd(){// 定義要調用的原生函數在目標模塊中的內存地址(偏移量)// 0x45B48是通過逆向分析(如IDA、Ghidra)得到的函數地址long functionAddress = 0x45B48;/** 準備JNI環境相關對象* JNI(Java Native Interface)是Java與原生代碼交互的接口*/// 獲取JNI環境指針(JNIEnv*),這是調用任何JNI函數的第一個參數Pointer jniEnv = vm.getJNIEnv();// 創建三個字符串對象作為函數參數// StringObject是Unidbg中用于表示Java字符串的包裝類StringObject data1 = new StringObject(vm, "1");  // 第一個字符串參數值為"1"StringObject data2 = new StringObject(vm, "2");  // 第二個字符串參數值為"2"StringObject data3 = new StringObject(vm, "3");  // 第三個字符串參數值為"3"// 構建函數調用的參數列表List<Object> args = new ArrayList<>();// 添加第一個參數:JNI環境指針(JNIEnv*),這是JNI函數的標準第一個參數args.add(jniEnv);// 添加后續參數:將字符串對象轉換為DVM本地引用// vm.addLocalObject()會將對象添加到Dalvik虛擬機的本地引用表,返回引用ID// 原生函數通過這個引用ID可以訪問到對應的Java對象args.add(vm.addLocalObject(data1));args.add(vm.addLocalObject(data2));args.add(vm.addLocalObject(data3));// 調用目標原生函數// module.callFunction():通過模塊調用指定地址的函數// 參數說明:模擬器實例、函數地址、參數數組// 返回值:原生函數的返回結果(這里是一個對象引用ID)Number numbers = module.callFunction(emulator, functionAddress, args.toArray());// 將返回的引用ID轉換為DVM中的對象// 原生函數返回的是Java對象引用,需要通過vm.getObject()獲取實際對象DvmObject<?> object = vm.getObject(numbers.intValue());// 從DVM對象中提取字符串值// 假設原生函數返回的是String類型對象,通過getValue()獲取其字符串內容String value = (String) object.getValue();// 返回獲取到的簽名結果return value;}public static void main(String[] args) {DcTest dcTest = new DcTest();String sign = dcTest.getSignAdd();System.out.println(sign);}}

補自定義環境在下一節中


img

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95849.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95849.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95849.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Kubernetes知識點問答題】Pod 調度

1. 如何將特定 Pod 調度到指定的節點&#xff1f;可以使用下列方法中的任何一種來選擇 K8s 對特定 Pod 的調度&#xff1a;① 與節點標簽匹配的 nodeSelector&#xff1a;在 Pod 的規范中使用 nodeSelector 字段來指定節點標簽&#xff0c;以便將 Pod 調度到具有特定標簽的節點…

wordpress顯示時間日期的幾種常見的方式

在WordPress中&#xff0c;顯示時間日期有多種常見方式&#xff0c;包括使用默認設置、模板標簽、插件等&#xff0c;以下是詳細介紹&#xff1a; 使用默認設置 WordPress的默認設置允許你在文章列表中顯示文章的發布時間。登錄到WordPress后臺&#xff0c;在“設置”中找到“…

基于飛算JavaAI實現布隆過濾器防止緩存穿透:原理、實踐與全流程解析

引言&#xff1a;當緩存失效時&#xff0c;系統如何避免“雪崩式崩潰”&#xff1f; 在互聯網高并發場景中&#xff08;如電商秒殺、社交平臺熱點新聞&#xff09;&#xff0c;緩存是提升系統性能的核心手段——將頻繁訪問的數據&#xff08;如商品詳情、用戶信息&#xff09;存…

DeepResearch開源與閉源方案對比

在這個AI不再只是聊天工具的時代&#xff0c;"深度研究"已經成為大語言模型&#xff08;LLM&#xff09;的一項新能力。先進的LLM不再只是給出快速的一次性回答&#xff0c;而是可以像研究助手一樣工作——搜索網上信息&#xff0c;調用各種工具&#xff08;比如搜索…

UniApp 頁面傳參方式詳解

在 UniApp 開發中&#xff0c;頁面間參數傳遞是核心功能之一。以下是 8 種常用的傳參方式&#xff0c;每種方式都有其適用場景和特點&#xff1a;一、URL 拼接傳參&#xff08;最常用&#xff09; 適用場景&#xff1a;簡單數據傳遞&#xff0c;如 ID、狀態值等基礎類型數據 實…

音頻分類標注工具

pyqt 分類標注工具&#xff1a;import glob import sys import json import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem,QSplitter, QVBoxLayout, QWidget, QPushButton, QRadioButton,QButtonGroup, QLabel, QHBoxLayout, Q…

云計算-Kubernetes+Istio 實現金絲雀發布:流量管理、熔斷、流量鏡像、ingreess、污點及pv案例實戰

介紹 在微服務架構中,如何安全、高效地實現服務發布與流量管理是保障業務穩定性的核心挑戰。金絲雀發布(Canary Release)、灰度發布等策略通過精細化的流量控制,可有效降低新版本上線風險, Istio 作為主流的服務網格(Service Mesh)工具。 此次Istio 在 Kubernetes 集群…

12.web api 3

定時器-間歇函數

ComfyUI進階:EchoMimic插件全解析,讓靜態肖像實現音頻驅動的精準口型動畫

在數字內容創作中&#xff0c;讓靜態肖像“開口說話”并做出自然表情&#xff0c;是提升交互感與沉浸感的關鍵。傳統動畫制作需專業人員逐幀調整口型與表情&#xff0c;成本高且效率低。ComfyUI的EchoMimic插件通過音頻驅動技術&#xff0c;實現了“輸入音頻→自動生成匹配口型…

鏈式前向星、vector存圖

場景設定 想象你是一個社交達人&#xff0c;要記錄你和所有朋友的關系&#xff08;這就是“圖”&#xff09;。每個朋友是一個節點&#xff0c;關系是一條邊。你需要快速回答&#xff1a;“我有哪些朋友&#xff1f;”&#xff08;遍歷鄰居&#xff09;。方式1&#xff1a;鏈式…

YAML 中定義 List 的幾種方式

在 YAML 配置文件中定義 List 并在 Spring 應用中注入是非常常見的操作&#xff0c;下面詳細介紹具體寫法和注入方式。一、YAML 中定義 List 的幾種方式1. 縮進式寫法&#xff08;推薦&#xff09;最常用的方式&#xff0c;通過短橫線 - 加空格表示列表項&#xff1a;yaml# app…

C# 反射和特性(自定義特性)

自定義特性 你或許已經注意到了&#xff0c;應用特性的語法和之前見過的其他語法有很大不同。你可能會覺得特 性是一種完全不同的結構類型&#xff0c;其實不是&#xff0c;特性只是一種特殊的類。 有關特性類的一些要點如下。 用戶自定義的特性類叫作自定義特性。所有特性類都…

科目二的四個電路

一.K21電動機單連續運轉接線(帶點動控制)1.電路圖2.主線路這可很明了,是一條直線,從上接到下就OK了,然后從熱繼電器出來,接到SB3按鈕的常閉觸點上接著往下走一端接到SB2的常閉觸點上,接著往下走&#xff0c;走到接觸器的線圈上,從L2借一條火線出來,從熔斷器的上端接入,另一端接…

【位運算】查詢子數組最大異或值|2693

本文涉及知識點 位運算、狀態壓縮、枚舉子集匯總 3277. 查詢子數組最大異或值 給你一個由 n 個整數組成的數組 nums&#xff0c;以及一個大小為 q 的二維整數數組 queries&#xff0c;其中 queries[i] [li, ri]。 對于每一個查詢&#xff0c;你需要找出 nums[li…ri] 中任…

HTML DOM 方法

HTML DOM 方法 引言 HTML DOM&#xff08;文檔對象模型&#xff09;是HTML文檔的編程接口&#xff0c;它允許開發者通過JavaScript來操作HTML文檔中的元素。DOM 方法是DOM編程的核心&#xff0c;它提供了豐富的操作手段來改變網頁的結構、樣式和行為。本文將詳細介紹HTML DOM中…

w嵌入式分享合集68

自己的原文哦~ https://blog.51cto.com/whaosoft/14133002 一、一鍵開關機電路的設計方案 方案一&#xff1a;電路圖 一鍵開關機電路分析如下&#xff1a; 電路工作流程如下&#xff1a; Key按下瞬間&#xff0c;Q2、Q1導通&#xff0c;7805輸入電壓在8.9V左右&…

FFmpeg QoS 處理

FFmpeg 中的 QoS (服務質量) 處理主要關注于實時流媒體傳輸中的時序控制、丟幀策略和網絡適應等方面。以下是 FFmpeg 中 QoS 相關的關鍵機制和配置方法。1. 基本 QoS 機制丟幀策略 (Frame Dropping)cAVDictionary *options NULL; av_dict_set(&options, "framedrop&q…

TexStudio中的Latex,PDFLatex,XeLatex和LuaLatex的區別

多種LaTeX編譯器一、多種LaTeX編譯器 1.1 PDFLaTeX&#xff08;1994年&#xff09; 默認、最常用的引擎。 輸入文件通常是 ASCII 或 UTF-8 編碼&#xff08;但中文需要 CJK 宏包或 ctex 宏包支持&#xff09;。 字體選擇受限&#xff1a;只能使用 TeX 自帶的字體或者 Type 1…

容器化部署:用Docker封裝機器翻譯模型與服務詳解

文章目錄一、機器翻譯容器化的技術棧選型1.1 為什么需要容器化MT模型&#xff1f;1.2 基礎鏡像選擇對比1.3 典型依賴分層方案1.4 性能對比&#xff08;容器化 vs 原生部署&#xff09;二、關鍵部署模式2.1 輕量級API服務封裝2.2 模型熱更新策略三、Docker鏡像構建3.1 編寫Docke…

leetcode_42 接雨水

1. 題意 給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖&#xff0c;計算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 2. 題解 這個題不會做&#xff0c;全部是看得題解捏。 不過能看懂題解感覺自己也很棒了&#xff01; 看完題解后感覺最難的是如何求出有多少…