Android平臺dalvik模式下java Hook框架ddi的分析(2)--dex文件的注入和調用

本文博客地址:http://blog.csdn.net/qq1084283172/article/details/77942585

前面的博客《Android平臺dalvik模式下java Hook框架 ddi 的分析(1)》中,已經分析了dalvik模式下 ddi 框架Hook java方法的原理和流程,這裏來學習一下ddi框架代碼中涉及到的 dex文件的注入和調用。將一個Android的so庫文件跨進程注入到另一個進程中,在so庫文件的實現裏,我們可以做很多的事情,例如:inline Hook,java方法的Hook,dex文件的注入和調用,ndk的jni函數的Hook等等。


1.ddi框架在進行dex文件的注入和調用是在原來dalvik模式下java方法Hook的基礎上修改過來的,在 hijack注入工具將android so庫文件注入到目標pid進程時實現android的inline Hook操作,爲了保證android的inline Hook操作的順利執行,需要爲注入到目標pid進程中android so庫文件定義 .init段或者.init_array段的構造函數,如下圖中的my_init構造函數;在inline Hook操作的自定義函數my_epoll_wait裏進行dalvik虛擬機模式下的java方法Hook操作。




代碼的流程梳理如下:

1. 在被注入到目標pid進程中動態庫文件 libsmsdispatch.so 的Android.mk配置文件。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libsmsdispatch
LOCAL_SRC_FILES := smsdispatch.c.arm
LOCAL_C_INCLUDES := ../../../../adbi/instruments/base/ ../../../dalvikhook/jni/
LOCAL_LDLIBS    := -L../../../dalvikhook/jni/libs -ldl -ldvm 
LOCAL_LDLIBS    := -Wl,--start-group ../../../../adbi/instruments/base/obj/local/armeabi/libbase.a ../../../dalvikhook/obj/local/armeabi/libdalvikhook.a -Wl,--end-group
LOCAL_CFLAGS    := -g

# 生成動態庫文件libsmsdispatch.so
include $(BUILD_SHARED_LIBRARY)

2. 動態庫文件 libsmsdispatch.so 的.init段或者.init_array段的構造函數的實現,在Android so庫文件的構造函數中實現inline Hook操作。

// set my_init as the entry point
void __attribute__ ((constructor)) my_init(void);

void my_init(void)
{
	log("libsmsdispatch: started\n")
 
 	debug = 1;

 	// set log function for  libbase (very important!)
	set_logfunction(my_log2);
	// set log function for libdalvikhook (very important!)
	dalvikhook_set_logfunction(my_log2);

	// 對libc.so庫文件的epoll_wait函數進行inline Hook操作
	hook(&eph, getpid(), "libc.", "epoll_wait", my_epoll_wait, 0);
}

3. 在inline Hook操作的自定義實現函數裏實現dalvik模式下的java方法Hook。

// 自定義的inline Hook替換函數
static int my_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
{
	int (*orig_epoll_wait)(int epfd, struct epoll_event *events, int maxevents, int timeout);
	orig_epoll_wait = (void*)eph.orig;
	// remove hook for epoll_wait
	hook_precall(&eph);

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	// 從加載的libdvm.so庫文件中,獲取需要的導出函數的地址
	dexstuff_resolv_dvm(&d);
	
	// 對com.android.internal.telephony.SMSDispatcher類的dispatchPdus函數進行dalvik Hook操作
	dalvik_hook_setup(&dpdu, "Lcom/android/internal/telephony/SMSDispatcher;", "dispatchPdus", "([[B)V", 2, my_dispatch);
	dalvik_hook(&d, &dpdu);

	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	        
	// call original function
	int res = orig_epoll_wait(epfd, events, maxevents, timeout);    
	return res;
}

2.在dalvik模式下的java方法Hook實現的自定義函數裏進行dex文件的加載和調用,先來複習一下dalvik模式下java方法的Hook實現步驟。




dalvik模式下java方法Hook的代碼流程梳理:

1. dexstuff_resolv_dvm函數--動態加載"libdvm.so"庫文件並獲取該加載的動態庫中dalvik Hook實現需要的導出函數的調用地址和導出全局變量。

// 源碼文件 dexstuff.c

// 獲取加載的動態庫中導出函數的調用地址
static void* mydlsym(void *hand, const char *name)
{
	void* ret = dlsym(hand, name);
	log("%s = 0x%x\n", name, ret)

	return ret;
}


// 動態加載"libdvm.so"庫文件並獲取該加載的動態庫中dalvik Hook實現需要的導出函數的調用地址和導出全局變量
void dexstuff_resolv_dvm(struct dexstuff_t *d)
{
	// 動態加載"libdvm.so"庫文件並保存文件句柄
	d->dvm_hand = dlopen("libdvm.so", RTLD_NOW);
	log("dvm_hand = 0x%x\n", d->dvm_hand)
	
	// 獲取加載的文件句柄成功的情況
	if (d->dvm_hand) {

		// 獲取加載的"libdvm.so"庫文件中導出函數dvm_dalvik_system_DexFile的調用地址
		d->dvm_dalvik_system_DexFile = (DalvikNativeMethod*) mydlsym(d->dvm_hand, "dvm_dalvik_system_DexFile");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvm_java_lang_Class的調用地址
		d->dvm_java_lang_Class = (DalvikNativeMethod*) mydlsym(d->dvm_hand, "dvm_java_lang_Class");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmThreadSelf的調用地址
		d->dvmThreadSelf_fnPtr = mydlsym(d->dvm_hand, "_Z13dvmThreadSelfv");
		if (!d->dvmThreadSelf_fnPtr)
			d->dvmThreadSelf_fnPtr = mydlsym(d->dvm_hand, "dvmThreadSelf");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmStringFromCStr的調用地址
		d->dvmStringFromCStr_fnPtr = mydlsym(d->dvm_hand, "_Z32dvmCreateStringFromCstrAndLengthPKcj");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmGetSystemClassLoader的調用地址
		d->dvmGetSystemClassLoader_fnPtr = mydlsym(d->dvm_hand, "_Z23dvmGetSystemClassLoaderv");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmIsClassInitialized的調用地址
		d->dvmIsClassInitialized_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmIsClassInitializedPK11ClassObject");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmInitClass的調用地址
		d->dvmInitClass_fnPtr = mydlsym(d->dvm_hand, "dvmInitClass");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmFindVirtualMethodHierByDescriptor的調用地址
		d->dvmFindVirtualMethodHierByDescriptor_fnPtr = mydlsym(d->dvm_hand, "_Z36dvmFindVirtualMethodHierByDescriptorPK11ClassObjectPKcS3_");
		if (!d->dvmFindVirtualMethodHierByDescriptor_fnPtr)
			d->dvmFindVirtualMethodHierByDescriptor_fnPtr = mydlsym(d->dvm_hand, "dvmFindVirtualMethodHierByDescriptor");

		// 獲取加載的"libdvm.so"庫文件中導出函數dvmFindDirectMethodByDescriptor的調用地址
		d->dvmFindDirectMethodByDescriptor_fnPtr = mydlsym(d->dvm_hand, "_Z31dvmFindDirectMethodByDescriptorPK11ClassObjectPKcS3_");
		if (!d->dvmFindDirectMethodByDescriptor_fnPtr)
			d->dvmFindDirectMethodByDescriptor_fnPtr = mydlsym(d->dvm_hand, "dvmFindDirectMethodByDescriptor");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmIsStaticMethod的調用地址
		d->dvmIsStaticMethod_fnPtr = mydlsym(d->dvm_hand, "_Z17dvmIsStaticMethodPK6Method");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmAllocObject的調用地址
		d->dvmAllocObject_fnPtr = mydlsym(d->dvm_hand, "dvmAllocObject");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmCallMethodV的調用地址
		d->dvmCallMethodV_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmCallMethodA的調用地址
		d->dvmCallMethodA_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmCallMethodAP6ThreadPK6MethodP6ObjectbP6JValuePK6jvalue");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmAddToReferenceTable的調用地址
		d->dvmAddToReferenceTable_fnPtr = mydlsym(d->dvm_hand, "_Z22dvmAddToReferenceTableP14ReferenceTableP6Object");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmSetNativeFunc的調用地址
		d->dvmSetNativeFunc_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmSetNativeFuncP6MethodPFvPKjP6JValuePKS_P6ThreadEPKt");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmUseJNIBridge的調用地址
		d->dvmUseJNIBridge_fnPtr = mydlsym(d->dvm_hand, "_Z15dvmUseJNIBridgeP6MethodPv");
		if (!d->dvmUseJNIBridge_fnPtr)
			d->dvmUseJNIBridge_fnPtr = mydlsym(d->dvm_hand, "dvmUseJNIBridge");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmDecodeIndirectRef的調用地址
		d->dvmDecodeIndirectRef_fnPtr =  mydlsym(d->dvm_hand, "_Z20dvmDecodeIndirectRefP6ThreadP8_jobject");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmLinearSetReadWrite的調用地址
		d->dvmLinearSetReadWrite_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmLinearSetReadWriteP6ObjectPv");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmGetCurrentJNIMethod的調用地址
		d->dvmGetCurrentJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z22dvmGetCurrentJNIMethodv");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmFindInstanceField的調用地址
		d->dvmFindInstanceField_fnPtr = mydlsym(d->dvm_hand, "_Z20dvmFindInstanceFieldPK11ClassObjectPKcS3_");
		
		//d->dvmCallJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmCheckCallJNIMethodPKjP6JValuePK6MethodP6Thread");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmCallJNIMethod的調用地址
		d->dvmCallJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmDumpAllClasses的調用地址
		d->dvmDumpAllClasses_fnPtr = mydlsym(d->dvm_hand, "_Z17dvmDumpAllClassesi");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmDumpClass的調用地址
		d->dvmDumpClass_fnPtr = mydlsym(d->dvm_hand, "_Z12dvmDumpClassPK11ClassObjecti");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmFindLoadedClass的調用地址
		d->dvmFindLoadedClass_fnPtr = mydlsym(d->dvm_hand, "_Z18dvmFindLoadedClassPKc");
		if (!d->dvmFindLoadedClass_fnPtr)
			d->dvmFindLoadedClass_fnPtr = mydlsym(d->dvm_hand, "dvmFindLoadedClass");
		
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmHashTableLock的調用地址
		d->dvmHashTableLock_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmHashTableLockP9HashTable");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmHashTableUnlock的調用地址
		d->dvmHashTableUnlock_fnPtr = mydlsym(d->dvm_hand, "_Z18dvmHashTableUnlockP9HashTable");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmHashForeach的調用地址
		d->dvmHashForeach_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmHashForeachP9HashTablePFiPvS1_ES1_");
		// 獲取加載的"libdvm.so"庫文件中導出函數dvmInstanceof的調用地址
		d->dvmInstanceof_fnPtr = mydlsym(d->dvm_hand, "_Z13dvmInstanceofPK11ClassObjectS1_");
		
		// 獲取加載的"libdvm.so"庫文件中導出全部變量gDvm的調用地址
		d->gDvm = mydlsym(d->dvm_hand, "gDvm");
	}
}

2. dalvik_hook_setup函數--記錄java層目標函數被dalvik Hook操作需要的相關信息結構體,也就是預先設置好dalvik模式下java方法的Hook需要的參數值,方便後面進行java方法的Hook操作。

// 源碼文件 dalvik_hook.c
/*
 * dalvik_hook_t用於記錄被dalvik Hook的java類成員方法的有關信息
 * cls爲被dalvik Hook的java類成員方法所在的類的簽名
 * meth爲被dalvik Hook的java類成員方法的名稱
 * sig爲被dalvik Hook的java類成員方法的函數簽名
 * ns爲java成員方法被修改爲native層實現的方法後調用,傳參需要的寄存器的個數
 * func爲被dalvik Hook的java類成員方法的替換自定義Hook函數
 * 調用實例:dalvik_hook_setup(&sb1, "Ljava/lang/StringBuffer;",  "toString",  "()Ljava/lang/String;", 1, sb1_tostring);
 *
 * 注意:
 * 1.Android的dalvik虛擬機是基於寄存器的,因此java語言實現的函數在執行的時候需要預先計算出函數調用時傳遞參數需要的寄存器數量insSize以及函數內部
 * 局部變量要用到的寄存器的數量outsSize,需要的寄存器總數量爲registersSize=outsSize+insSize。
 * 2.java的類成員方法中非靜態的成員方法的第一個函數參數是指向本身的對象指針然後是其他的傳入參數,靜態成員方法不存在這樣的問題。
 * 3.當java層的類成員方法被修改爲native屬性的成員方法後,由於native屬性方法的局部變量的內存申請是通過堆棧來完成的,因此在計算該方法的寄存器數量時,
 * 局部變量用到的寄存器的數量outsSize爲0,並且總的寄存器數量與傳參寄存器的數量相同即registersSize=insSize。
 *
 */
// 記錄java層目標函數被dalvik Hook操作需要的相關信息結構體
int dalvik_hook_setup(struct dalvik_hook_t *h, char *cls, char *meth, char *sig, int ns, void *func)
{
	if (!h)
		return 0;

	// 保存被dalvik Hook的類成員方法所在的類的名稱
	strcpy(h->clname, cls);
	// 保存被dalvik Hook的類成員方法所在的類的協議名稱,如:"java/lang/StringBuffer"
	strncpy(h->clnamep, cls+1, strlen(cls)-2);

	// 保存被dalvik Hook的類成員方法的名稱
	strcpy(h->method_name, meth);
	// 保存被dalvik Hook的類成員方法的函數簽名
	strcpy(h->method_sig, sig);

	// 提示:下面的部分變量值主要用於該java層的類成員方法meth被修改爲native層後寄存器記錄變量值的修改
	// dalvik Hook的實現其實就是將java層實現的類成員函數修改爲native層實現的自定義Hook函數
	// 保存被dalvik Hook後java層類成員方法的寄存器數量

	// 被dalvik Hook後該native層的函數傳參需要的寄存器數量
	h->n_iss = ns;
	// 被dalvik Hook後該native層的函數需要的總的寄存器的數量
	h->n_rss = ns;
	// 被dalvik Hook後該native層的函數局部變量需要的寄存器的數量
	h->n_oss = 0;
	// 被dalvik Hook後該native層的函數被替換的自定義Hook函數
	h->native_func = func;

	// 記錄被dalvik Hook的函數是否是靜態成員方法的標誌,默認爲非靜態成員方法即0
	h->sm = 0; // set by hand if needed

	// java類成員函數的類型屬性標誌accessFlags,其中accessFlags=0x0100表示該函數爲native層實現的函數
	h->af = 0x0100; // native, modify by hand if needed

	// 記錄是否需要保存查找到,將被dalvik Hook的目標函數所在類的指針和方法結構體指針的標誌,默認爲0-需要
	h->resolvm = 0; // don't resolve method on-the-fly, change by hand if needed

	// debug調試打印Log日誌的開關,默認爲0-不打印日誌
	h->debug_me = 0;

	return 1;
}

3. dalvik_hook函數--修改java層目標函數爲native屬性的jni函數並修正該函數正確調用相關的Method結構體成員的值,主要通過修改java類方法的Method結構體成員的相關值達到實現dalvik模式下java方法的Hook操作。

// 修改java層目標函數爲native屬性的jni函數並修正該函數正確調用相關的Method結構體成員的值
void* dalvik_hook(struct dexstuff_t *dex, struct dalvik_hook_t *h)
{
	if (h->debug_me)
		log("dalvik_hook: class %s\n", h->clname)
	
	// 調用dalvik虛擬機的函數dvmFindLoadedClass獲取被dalvik hook的目標函數所在類的指針
	void *target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname);
	if (h->debug_me)
		log("class = 0x%x\n", target_cls)

	// print class in logcat
	if (h->dump && dex && target_cls){

		// 打印找到被dalvik hook的目標函數所在類的名稱信息
		dex->dvmDumpClass_fnPtr(target_cls, (void*)1);
	}

	// 判斷被dalvik hook的目標函數所在類的指針是否爲null並打印日誌
	if (!target_cls) {

		if (h->debug_me)
			log("target_cls == 0\n")

		return (void*)0;
	}

	// 在被dalvik hook的目標函數所在類的 非靜態成員函數 中查找將被dalvik hook的java層目標函數
	h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
	// 如果查找失敗
	if (h->method == 0) {

		// 在被dalvik hook的目標函數所在類的 靜態成員函數 中查找將被dalvik hook的java層目標函數
		h->method = dex->dvmFindDirectMethodByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
	}

	// constrcutor workaround, see "dalvik_prepare" below
	// 保存查找到的,將被dalvik hook的java層目標函數所在類指針和方法結構體指針的信息,用以dalvik Hook後的還原
	if (!h->resolvm) {

		// 保存查找到將被dalvik hook的java層目標函數所在類的指針
		h->cls = target_cls;
		// 保存查找到將被dalvik hook的java層目標函數的方法體結構指針
		h->mid = (void*)h->method;
	}

	// 打印查找到被dalvik hook的java層目標函數的信息
	if (h->debug_me)
		log("%s(%s) = 0x%x\n", h->method_name, h->method_sig, h->method)

	// 進行目標函數被dalvik Hook操作的修改
	if (h->method) {

/*
 * dalvik虛擬機中方法結構體Method的提示:
 * -----------------------------------------------------------------------------------------------------------
 * 1.method->insns:
 * 如果這個方法不是Native層實現即java層實現的函數,則這裏存放了指向該方法具體的Dalvik指令的指針
 * (這個變量指向的是實際加載到內存中的Dalvik指令代碼,而不是在Dex文件中的);
 * 如果這個方法是一個Dalvik虛擬機自帶的Native函數(即Internal Native函數),則這個變量會是Null。
 * 如果這個方法是一個普通的Native函數即jni實現的自定義函數,則這裏存放了指向jni實際函數機器碼的首地址;
 * -----------------------------------------------------------------------------------------------------------
 * 2.method->jniArgInfo:
 * 這個變量記錄了一些預先計算好的函數參數信息,從而不需要在函數調用的時候再通過方法的參數和返回值實時計算了,
 * 方便了JNI的調用,提高了調用的速度。如果第一位爲1(即0x80000000),則Dalvik虛擬機會忽略後面的所有信息,強制在調用時實時計算;
 * -----------------------------------------------------------------------------------------------------------
 * 3.method->nativeFunc:
 * 如果這個方法是一個Dalvik虛擬機自帶的Native函數(Internal Native)的話,則這裏存放了指向JNI實際函數機器碼的首地址。
 * 如果這個方法是一個普通的Native函數,則這裏將指向jni跳轉橋接函數;
 * -----------------------------------------------------------------------------------------------------------
 * 4.通過method->accessFlags可以判斷一個方法是不是Native的(和0x00100相與),如果是Native的話,就直接執行nativeFunc所指向的本地代碼,
 * 如果不是Native的話,就執行insns所指向的Dalvik代碼。
 *
 * <---------------參考自:http://blog.csdn.net/roland_sun/article/details/38640297--------------->
 * 有關Method結構體每一項的實現具體可以參考android的源碼
 */
		// 保存被dalvik Hook的java層目標函數的dalvik字節碼指針method->insns
		h->insns = h->method->insns;
		if (h->debug_me) {

			// 打印被dalvik Hook操作的java層目標函數的原始信息
			log("nativeFunc %x\n", h->method->nativeFunc)
			log("insSize = 0x%x  registersSize = 0x%x  outsSize = 0x%x\n", h->method->insSize, h->method->registersSize, h->method->outsSize)
		}

		// 保存被dalvik Hook的java層目標函數的寄存器數量信息,用以後面dalvik Hook的恢復還原

		// 被dalvik Hook的java層目標函數的傳參寄存器的個數
		h->iss = h->method->insSize;
		// 保存被dalvik Hook的java層目標函數的局部變量使用的寄存器個數
		h->oss = h->method->outsSize;
		// 保存被dalvik Hook的java層目標函數的總寄存器個數
		h->rss = h->method->registersSize;
	
		// 修改被dalvik Hook的java層目標函數的寄存器個數爲該目標函數爲native屬性時的正確個數
		h->method->insSize = h->n_iss;
		h->method->registersSize = h->n_rss;
		h->method->outsSize = h->n_oss;

		if (h->debug_me) {

			log("shorty %s\n", h->method->shorty)
			log("name %s\n", h->method->name)
			log("arginfo %x\n", h->method->jniArgInfo)
		}

		// 修改被dalvik Hook的目標函數的jni參數爲運行時實時計算
		// 原本函數可能並不是Native的,現在被偷偷改成了Native的,所以肯定不能使用這個域進行優化
		h->method->jniArgInfo = 0x80000000; // <--- also important
		if (h->debug_me) {

			log("noref %c\n", h->method->noRef)
			log("access %x\n", h->method->a)
		}

		// 保存被dalvik Hook的java層目標函數的原始函數屬性標誌值
		h->access_flags = h->method->a;
		// 修改被dalvik Hook的java層目標函數的爲native層實現的jni函數
		h->method->a = h->method->a | h->af; // make method native

		if (h->debug_me)
			log("access %x\n", h->method->a)
	
		// 調用libdvm.so中的dvmUseJNIBridge函數來實現將method->nativeFunc域改成指向一個JNI橋跳轉函數地址(dvmCallJNIMethod)
		// 並將method->insns域改成指向真正的jni函數代碼即我們自定義實現的dalvik Hook函數代碼首地址處
		dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
		
		if (h->debug_me)
			log("patched %s to: 0x%x\n", h->method_name, h->native_func)

		// 到這裏,java層目標函數的dalvik Hook實現完成
		return (void*)1;

	} else {

		// 查找被dalvik hook的java層目標函數失敗的情況
		if (h->debug_me)
			log("could NOT patch %s\n", h->method_name)
	}

	return (void*)0;
}


// 恢復還原被dalvik Hook的java層目標函數
int dalvik_prepare(struct dexstuff_t *dex, struct dalvik_hook_t *h, JNIEnv *env)
{

	// this seems to crash when hooking "constructors"
	// 判斷需要被恢復還原的dalvik Hook目標函數是否存在
	if (h->resolvm) {

		// 查找指定的目標類
		h->cls = (*env)->FindClass(env, h->clnamep);
		if (h->debug_me)
			log("cls = 0x%x\n", h->cls)
		if (!h->cls)
			return 0;

		// 查找指定的目標函數
		if (h->sm)
			h->mid = (*env)->GetStaticMethodID(env, h->cls, h->method_name, h->method_sig);
		else
			h->mid = (*env)->GetMethodID(env, h->cls, h->method_name, h->method_sig);
		if (h->debug_me)
			log("mid = 0x%x\n", h-> mid)
		if (!h->mid)
			return 0;

	}

	// 恢復被dalvik Hook的java層目標函數的原始寄存器數量
	h->method->insSize = h->iss;
	h->method->registersSize = h->rss;
	h->method->outsSize = h->oss;

	// 恢復被dalvik Hook的java層目標函數的原始函數類型屬性
	h->method->a = h->access_flags;
	h->method->jniArgInfo = 0;
	// 恢復被dalvik Hook的java層目標函數的dalvik字節碼指針
	h->method->insns = h->insns; 

	return 1;
}


// 再次對java層實現的目標函數進行dalvik Hook操作
void dalvik_postcall(struct dexstuff_t *dex, struct dalvik_hook_t *h)
{

	// 修改被dalvik Hook的java層目標函數的寄存器數量值
	h->method->insSize = h->n_iss;
	h->method->registersSize = h->n_rss;
	h->method->outsSize = h->n_oss;

	//log("shorty %s\n", h->method->shorty)
	//log("name %s\n", h->method->name)
	//log("arginfo %x\n", h->method->jniArgInfo)

	// 修改被dalvik Hook的java層目標函數的參數的計算方法爲運行時實時計算
	h->method->jniArgInfo = 0x80000000;
	//log("noref %c\n", h->method->noRef)
	//log("access %x\n", h->method->a)

	// 修改被dalvik Hook的java層目標函數的類型屬性爲native
	h->access_flags = h->method->a;
	h->method->a = h->method->a | h->af;
	//log("access %x\n", h->method->a)

	// 修改被dalvik Hook的目標函數的insns爲我們自定義的dalvik Hook函數的代碼首地址
	// 修改nativeFunc爲函數正確調用需要的jni跳轉橋代碼的地址
	dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
	
	if (h->debug_me)
		log("patched BACK %s to: 0x%x\n", h->method_name, h->native_func)
}

3.在dalvik模式下java方法Hook的自定義實現函數裏進行dex文件的加載和調用。




代碼實現流程梳理:

1. 調用libdvm.so庫文件中導出函數Dalvik_dalvik_system_DexFile_openDexFileNative加載dex文件到目標pid進程的內存中,該函數對應java層的openDexFile函數。

// 調用libdvm.so庫文件中Dalvik_dalvik_system_DexFile_openDexFileNative函數加載dex文件到內存中
int dexstuff_loaddex(struct dexstuff_t *d, char *path)
{
	jvalue pResult;
	jint result;
	
	log("dexstuff_loaddex, path = 0x%x\n", path)

	// 調用libdvm.so庫文件中函數dvmStringFromCStr將C語言形式的字符串轉換成dvmString字符串
	void *jpath = d->dvmStringFromCStr_fnPtr(path, strlen(path), ALLOC_DEFAULT);
	u4 args[2] = { (u4)jpath, (u4)NULL };
	
	/***
	typedef struct DalvikNativeMethod_t {

    	const char* name; //函數名稱
    	const char* signature; // 函數簽名
    	DalvikNativeFunc  fnPtr; // 函數指針

	} DalvikNativeMethod;

	// dvm_dalvik_system_DexFile是native層函數指針信息的結構體
	const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
		{ "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)I",
				Dalvik_dalvik_system_DexFile_openDexFileNative },
		{ "openDexFile",        "([B)I",
				Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
		{ "closeDexFile",       "(I)V",
				Dalvik_dalvik_system_DexFile_closeDexFile },
		{ "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
				Dalvik_dalvik_system_DexFile_defineClassNative },
		{ "getClassNameList",   "(I)[Ljava/lang/String;",
				Dalvik_dalvik_system_DexFile_getClassNameList },
		{ "isDexOptNeeded",     "(Ljava/lang/String;)Z",
				Dalvik_dalvik_system_DexFile_isDexOptNeeded },
		{ NULL, NULL, NULL },
	};
	***/

	// 調用libdvm.so庫文件中函數Dalvik_dalvik_system_DexFile_openDexFileNative實現加載dex文件到內存中,
	// 調用DexClassLoader將dex文件加載到內存中最終也是調用native層的Dalvik_dalvik_system_DexFile_openDexFileNative實現dex加載的
	d->dvm_dalvik_system_DexFile[0].fnPtr(args, &pResult);

	// 函數Dalvik_dalvik_system_DexFile_openDexFileNative被調用後返回的是映射底層DvmDex數據的DexClassLoader.mDexs[0].mCookie值
	result = (jint) pResult.l;
	log("cookie = 0x%x\n", pResult.l)

	return result;
}

2. 調用libdvm.so庫文件中導出函數Dalvik_dalvik_system_DexFile_defineClassNative實現對指定名稱java類的加載。

// 調用Dalvik_dalvik_system_DexFile_defineClassNative函數實現指定名稱類的加載
void* dexstuff_defineclass(struct dexstuff_t *d, char *name, int cookie)
{
	u4 *nameObj = (u4*) name;
	jvalue pResult;
	
	log("dexstuff_defineclass: %s using %x\n", name, cookie)
	
	// 調用libdvm.so庫文件中的dvmGetSystemClassLoader函數獲取當前進程的SystemClassLoader即當前進程的類加載器
	void* cl = d->dvmGetSystemClassLoader_fnPtr();
	log("sys classloader = 0x%x\n", cl)

	// 調用libdvm.so庫文件中的dvmGetCurrentJNIMethod函數獲取當前方法的Method結構體信息
	Method *m = d->dvmGetCurrentJNIMethod_fnPtr();
	// 打印當前方法所在的classLoader的地址
	log("cur m classloader = 0x%x\n", m->clazz->classLoader)
	
	// 調用dvmStringFromCStr函數將c語言形式的類名稱字符串轉換成dvmString形式的字符串
	void *jname = d->dvmStringFromCStr_fnPtr(name, strlen(name), ALLOC_DEFAULT);
	//log("called string...\n")
	
	u4 args[3] = { (u4)jname, (u4) m->clazz->classLoader, (u4) cookie };
	// 調用libdvm.so庫文件中的(Dalvik_dalvik_system_DexFile_defineClassNative函數)實現目標類name的加載
	// 具體就是將cookie中的目標類name加載到內存中並和當前方法的classLoader聯繫起來
	d->dvm_dalvik_system_DexFile[3].fnPtr( args , &pResult );
	jobject *ret = pResult.l;
	log("class = 0x%x\n", ret)

	return ret;
}

3. 通過 jni方法反射調用dex文件中的java類方法。

// 替換Android系統原始的dispatchPdus函數的自定義dalvik Hook函數
static void my_dispatch(JNIEnv *env, jobject obj, jobjectArray pdu)
{
	// load dex classes
	// 調用libdvm.so庫文件中Dalvik_dalvik_system_DexFile_openDexFileNative函數加載dex文件到內存中
	int cookie = dexstuff_loaddex(&d, "/data/local/tmp/ddiclasses.dex");
	log("libsmsdispatch: loaddex res = %x\n", cookie)
	if (!cookie)
		log("libsmsdispatch: make sure /data/dalvik-cache/ is world writable and delete data@local@[email protected]\n")

	// 調用libdvm.so庫文件中Dalvik_dalvik_system_DexFile_defineClassNative函數實現指定名稱類org/mulliner/ddiexample/SMSDispatch的加載
	void *clazz = dexstuff_defineclass(&d, "org/mulliner/ddiexample/SMSDispatch", cookie);
	log("libsmsdispatch: clazz = 0x%x\n", clazz)

    // call constructor and passin the pdu
	// 上面的步驟中已經實現了org.mulliner.ddiexample.SMSDispatch類的加載
	// 在當前進程中查找獲取目標類org.mulliner.ddiexample.SMSDispatch
	jclass smsd = (*env)->FindClass(env, "org/mulliner/ddiexample/SMSDispatch");
	// 獲取目標類org.mulliner.ddiexample.SMSDispatch構造函數的Method結構體的信息
	jmethodID constructor = (*env)->GetMethodID(env, smsd, "<init>", "([[B)V");
	if (constructor) { 

		jvalue args[1];
		args[0].l = pdu;

		// 調用目標類org.mulliner.ddiexample.SMSDispatch的構造函數實現對象org.mulliner.ddiexample.SMSDispatch的實例化
		jobject objj = (*env)->NewObjectA(env, smsd, constructor, args);
		log("libsmsdispatch: new obj = 0x%x\n", obj)
		if (!objj)
			log("libsmsdispatch: failed to create smsdispatch class, FATAL!\n")

	} else {

		// 獲取目標類org.mulliner.ddiexample.SMSDispatch的構造函數失敗
		log("libsmsdispatch: constructor not found!\n")
	}

	// call original SMS dispatch method
	jvalue args[1];
	args[0].l = pdu;

	// 恢復還原被dalvik Hook的java層com.android.internal.telephony.SMSDispatcher類的目標函數dispatchPdus
	dalvik_prepare(&d, &dpdu, env);
	
	// 調用原始的java層目標函數dispatchPdus(即調用原來的函數)
	(*env)->CallVoidMethodA(env, obj, dpdu.mid, args);
	log("success calling : %s\n", dpdu.method_name)

	// 再次對java層目標函數dispatchPdus進行dalvik Hook操作
	dalvik_postcall(&d, &dpdu);

}

4.Android dex文件注入和調用的總結

1. root權限下實現跨進程注入android so庫文件到指定pid目標進程中,編寫實現注入so庫文件的.init段或者.init_array段的構造函數。

2. 在注入so庫文件的代碼中採用類似於android加固的方法,在目標pid進程中通過 DexClassLoader來加載dex文件並進行dex文件中指定目標java類方法的調用。


5. 被注入的dex文件的代碼 SMSDispatch.java 的實現。

爲了實現Android dex文件的注入和調用,因此在進行Android dex文件注入之前需要先將 SMSDispatch.java文件編譯爲dex文件格式。

/*
 *  Collin's Dynamic Dalvik Instrumentation Toolkit for Android
 *  Collin Mulliner <collin[at]mulliner.org>
 *
 *  (c) 2012,2013
 *
 *  License: LGPL v2.1
 *
 */

package org.mulliner.ddiexample;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;

import android.app.Application;
import android.content.Intent;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsMessage;

// takes incoming SMS message, reverses the body message and injects it back into the system (will appear as a 2nd SMS message)
public class SMSDispatch 
{	
	public SMSDispatch(byte pdus[][])
	{
		System.out.println("org.mulliner.ddiexample.SMSDispatch(pdu)");
		
		SmsMessage s1 = SmsMessage.createFromPdu(pdus[0]);
		if (s1 != null) {
			System.out.println("ddiexample: incoming SMS");
			System.out.println("ddiexample: " + s1.getMessageBody());
			System.out.println("ddiexample: " + s1.getOriginatingAddress());
		
			if (s1.getMessageBody() != null) {
		
				String rs = "";
				// reverse message
				for (int i = s1.getMessageBody().length() - 1; i >= 0; i--)
					rs = rs + s1.getMessageBody().charAt(i);
				
				byte p[] = createFakeSms(s1.getOriginatingAddress(), rs);
				byte sp[][] = new byte[1][p.length];
				System.arraycopy(p, 0, sp[0], 0, p.length);
				
				System.out.println("ddiexample: fake SMS");
				System.out.println("ddiexample: " + rs);
				
				Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED");
				intent.putExtra("pdus", sp);
				intent.putExtra("format", "3gpp");
				
				System.out.println(intent.toString());
		
				// get a context
				Application a = getcon();
				// send intent
				a.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
				System.out.println("ddiexample: appname: " + a.toString());
			}
		}
	}
	
	public Application getcon() 
	{
		try {
		    final Class<?> activityThreadClass =
		            Class.forName("android.app.ActivityThread");
		    if (activityThreadClass == null)
		    	System.out.println("activityThreadClass == null");
		    final Method method = activityThreadClass.getMethod("currentApplication");
		    Application app = (Application) method.invoke(null, (Object[]) null);
		    if (app == null) {
		    	System.out.println("getcon app == null");
		    	final Method method2 = activityThreadClass.getMethod("getApplication");
		    	if (method2 == null)
		    		System.out.println("method2 == null");
		    	if (app == null) {
		    		System.out.println("getcon 2 app == null");
		    	try {
		    	Field f = activityThreadClass.getField("mInitialApplication");
		    	app = (Application) f.get(activityThreadClass);
		    	} catch (Exception e) {}
		    	}
		    	if (app == null)
		    		System.out.println("getcon 3 app == null");
		    }
		    return app;
		} catch (final ClassNotFoundException e) {
		    // handle exception
			System.out.println(e.toString());
		} catch (final NoSuchMethodException e) {
		    // handle exception
			System.out.println(e.toString());
		} catch (final IllegalArgumentException e) {
		    // handle exception
			System.out.println(e.toString());
		} catch (final IllegalAccessException e) {
		    // handle exception
			System.out.println(e.toString());
		} catch (final InvocationTargetException e) {
		    // handle exception
			System.out.println(e.toString());
		}
		System.out.println("getcon == null :-(");
		return null;
	}
	
	//
	// reverseBytes and createFakesSms code taken from Thomas Cannon's SMSSpoof.java
	// https://github.com/thomascannon/android-sms-spoof/blob/master/SMSSpoofer/src/net/thomascannon/smsspoofer/SMSSpoof.java
	//
	private static byte reverseByte(byte b) 
	{
        return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);
    }
	
	private static byte[] createFakeSms(String sender, String body)
	{
		//Source: http://stackoverflow.com/a/12338541
		//Source: http://blog.dev001.net/post/14085892020/android-generate-incoming-sms-from-within-your-app
        byte[] scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD("0000000000");
        byte[] senderBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(sender);
        int lsmcs = scBytes.length;
        byte[] dateBytes = new byte[7];
        Calendar calendar = new GregorianCalendar();
        dateBytes[0] = reverseByte((byte) (calendar.get(Calendar.YEAR)));
        dateBytes[1] = reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));
        dateBytes[2] = reverseByte((byte) (calendar.get(Calendar.DAY_OF_MONTH)));
        dateBytes[3] = reverseByte((byte) (calendar.get(Calendar.HOUR_OF_DAY)));
        dateBytes[4] = reverseByte((byte) (calendar.get(Calendar.MINUTE)));
        dateBytes[5] = reverseByte((byte) (calendar.get(Calendar.SECOND)));
        dateBytes[6] = reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET) + calendar
                .get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));
        try {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            bo.write(lsmcs);
            bo.write(scBytes);
            bo.write(0x04);
            bo.write((byte) sender.length());
            bo.write(senderBytes);
            bo.write(0x00);
            bo.write(0x00); // encoding: 0 for default 7bit
            bo.write(dateBytes);
            try {
                String sReflectedClassName = "com.android.internal.telephony.GsmAlphabet";
                Class cReflectedNFCExtras = Class.forName(sReflectedClassName);
                Method stringToGsm7BitPacked = cReflectedNFCExtras.getMethod("stringToGsm7BitPacked", new Class[] { String.class });
                stringToGsm7BitPacked.setAccessible(true);
                byte[] bodybytes = (byte[]) stringToGsm7BitPacked.invoke(null, body);
                bo.write(bodybytes);
            } catch (Exception e) {
            }

            return bo.toByteArray();
        } catch (IOException e) {}
       return null;
	}
}

完全註釋版ddi Hook框架的代碼學習下載地址:http://download.csdn.net/download/qq1084283172/9975180


6.參考連接:

https://github.com/crmulliner/ddi

Android平臺下Dalvik層hook框架ddi的研究

Android平臺dalvik模式下java Hook框架ddi的分析(1)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章