2020/8/10

2020-08-11 14:48:41

00:00-01:10 1.17h
09:23-09:46 0.38h
10:09-12:20 2.18h
15:05-16:27 1.37h
16:50-18:26 1.6h
20:58-23:51 2.88h
今日學習時間:9.85h
累計時間:9.85h

Native Hook

Got/PLT Hook

實現原理:修改延時系結表
粒度:方法級
作用域:窄
效能:高
難度:中

在實際環境中應用較多的是GOT/PLT Hook,由於只是在ELF動態鏈接的預設流程上稍作修改,這種方式侵入性較低,且能保證效能,可以方便地實現對so庫的方法hook,缺點是隻能作用於系結表中存在的方法,作用域有一定限制。

Trap Hook

實現原理:SIGTRAP斷點信號
粒度:指令級
作用域:廣
效能:低
難度:中

Trap hook由於使用系統中斷,在效能上表現不好。

Inline Hook

實現原理:執行時指令替換
粒度:指令級
作用域:廣
效能:高
難度:極高

Inline hook是終極hook手段,通過直接修改執行時記憶體的方式替換指令,完全手工的完成hook及跳回操作,理論上可以實現任意位置的hook,不過手寫指令時需要考慮abi相容等衆多因素,實現難度很高,實際的應用場景不多。

JNI數據型別

Jboolean
Jlong
jint
jobject
jclass

JNI介面函數

字串操作

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

將String型別的字串轉換成char*型。

Void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);

當我們確認某個char*型別不再需要使用時,需要手動釋放記憶體。

Jstring NewStringUTF(JNIEnv *env, const char *bytes);

將char*型別字串轉換成String型別,在呼叫Java函數時,作爲參數傳遞。

獲取Java類的範例

Jclass FindClass(JNIEnv *env, const char *name);

通過一個Java類的全限定名來獲取一個該類的類物件範例,如果沒有找到返回NULL。

Jclass GetObjectClass(JNIEnv *env,jobject obj);

根據一個Java物件找到對應的類的範例物件。

Jboolean IsInstanceOf(JNIEnv *env,jobject obj,jclass clazz);

判斷是否爲某類的物件。

Jboolean IsSamObject(JNIEnv *env, jobject ref1, jobject ref2);

判斷兩物件是否相同的函數。

操作某個Java類的欄位

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

操作一個Java類的欄位時,首先需要得到一個jfieldID型別的值來表示這個欄位,使用GetFieldID()來獲取。
參數env是JNI介面指針,clazz是該欄位所屬類的類範例,通過FindClass()獲得,name是該欄位在Java類中定義的名字,sig是該欄位在Java中定義的型別。

NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
Void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);

得到了某個Java欄位的jfieldID值後,我們可以通過JNI函數獲取它的值,或者給它賦值,型別值要拿我們在Java中類定義該欄位的型別來代替。

在这里插入图片描述

在这里插入图片描述

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
NativeType GetStaticField(JNIEnv *env, jclass clazz, jfieldID fieldID);
Void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);

獲取Java類的某靜態欄位的操作跟獲取非靜態欄位的方法基本類似,但是有兩點區別:

  1. 由於非靜態欄位是屬於某個物件的,所以進行賦值/取值操作時,函數接收的是jobject型別的範例物件,代表哪一個物件的欄位值;而靜態欄位是屬於類的,所以傳遞的是jclass物件,代表一個Java類的範例。
  2. 操作靜態欄位的JNI函數在函數名中新增了static標識,以示區分。

在这里插入图片描述
在这里插入图片描述

操作某個Java類的函數

jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

由於Java允許函數過載,所以要依賴簽名才能 纔能唯一定位到Java函數。Name是Java中定義的函數名,sig是Java中函數定義的參數列表和返回值資訊。

NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID,);
NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *ars);
NativeType Call<type>Mehod(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

得到某個Java函數的jmethodID值是操作的前提,根據參數傳遞情況的不同,在某個Java物件上呼叫成員函數有這三種方式,第一種呼叫方式是最常用的方式,參數直接一一傳遞。

在这里插入图片描述

jmethodID GetStaticMehodID(JNIEnv *env, jclass clazz, cosnt char *name, const char *sig);
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);

類似地,對於在JNI中呼叫Java的靜態方法,有一套類似的JNI函數,呼叫方法跟呼叫非靜態函數的過程基本一致。要注意的是呼叫非靜態函數和靜態函數時,一個傳遞的是jobject,一個是jclass。
在这里插入图片描述

建構函式

Jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID,)

對應的還有NewObjectA、NewObjectV兩種形式。
我們知道,在Java函數中,有一類特殊的成員函數:建構函式;當我們要在JNI中呼叫Java類別建構函式,建立一個物件該怎麼做呢?
有前面的內容可知,我們如果要獲取到一個Java函數對應的jmethodID值時,需要傳入函數名和函數的簽名資訊。對於建構函式,在JNI中它有一個特殊的函數名字:""。當我們需要獲取某個建構函式的jmethodID值時,函數名的參數填入""即可;函數簽名資訊的填寫規則則跟普通函數一樣。
JNI呼叫Java無參建構函式建立範例物件的例子:

    jclass event_class =
env->FindClass("android/hardware/location/ActivityRecognitionHardware$Event");
    jmethodID event_ctor = env->GetMethodID(event_class, "<init>", "()V");
jobject event_object = env->NewObject(event_class, event_ctor);

陣列操作

Jsize GetArrayLength(JNIEnv *env, jarray array);

獲取陣列的長度。

jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);

建立陣列,length爲建立的陣列大小,elementClass爲陣列元素型別,initialElement爲初始值,一般爲NULL。
例如,我們可以在JNI中建立一個數組,作爲參數去呼叫Java層的某個方法。建立一個長度爲array_size、元素型別爲String的陣列:

jclass string_class = env->FindClass("java/lang/String");
jobjectArray string_array = env->NewObjectArray(array_size, string_class, NULL);

jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);

獲取/設定陣列元素。

註冊JNI函數

Jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
Typedef struct{
	Char *name; //Java中定義的方法名
	Char *signature; //Java中方法的函數簽名
	Void *fnPtr; //函數簽名
} JNINativeMethod;

Clazz爲與該JNI檔案相關聯的Java類全限定名,methods爲需要註冊的JNI方法陣列,nMethods爲需要註冊的JNI方法的個數。

Jint UnregisterNatives(JNIEnv *env, jclass clazz);

取消註冊的函數。

c語言中const作用

const變數成爲常數(Constant),定義的值不能被改變,在整個作用域中都保持固定。

const和函數形參

在C語言中,單獨定義const變數沒有明顯優勢,完全可以使用#define命令代替。Const通常用在函數形參中,如果形參是一個指針,爲了防止在函數內部修改指針指向的數據,就可以用const來限制。

const和非const型別轉換

Const char *和char *是不同的型別,不能將const char *型別的數據賦值給char *類的變數,但是反過來是可以的,編譯器允許將char *型別的數據賦值給const char *型別的變數。

const在C與C++中的區別

在C中,const不是常數,只能說是一個不能改變的變數,C編譯器不能把const看成一個編譯期間的常數,因爲它在記憶體中有分配,C編譯器不知道他在編譯期間的值。所以不能作爲陣列定義的下標,因爲它必須爲常數。

在C中,const in a; 是可以的,因爲這只是宣告一個變數,告訴編譯器,我這裏是宣告,指明別的地方有記憶體分配。但是在C++中這樣寫是不正確的,C++中const預設是內部鏈接,C中預設是外部鏈接,爲了起到和C語言一樣的效果,C++語言需要將const修飾爲extern,因爲extern優先順序高於const,所以變數會被變爲extern外部鏈接,起到和C語言一樣的效果。

C++中是否爲const分配記憶體空間要看具體情況,如果被宣告爲extern或者取const變數地址,就需要爲const變數分配空間。