安卓實(shí)戰(zhàn)開(kāi)發(fā)之JNI入門及高效的配置(android studio一鍵生成.h,so及方法簽名)

前言

以前也講過(guò)NDK開(kāi)發(fā),但是開(kāi)始是抱著好玩的感覺(jué)去開(kāi)始的垄琐,然后呢會(huì)helloWord就覺(jué)得大大的滿足赶袄,現(xiàn)在靜下來(lái)想這NDK開(kāi)發(fā)到底是干什么呢?

NDK開(kāi)發(fā)猴娩,其實(shí)是為了項(xiàng)目需要調(diào)用底層的一些C/C++的一些東西阴幌;另外就是為了效率更加高效些但是在java與C相互調(diào)用時(shí)平白又增大了開(kāi)銷(其實(shí)效率不見(jiàn)得有所提高),然后呢勺阐,基于安全性的考慮也是為了防止代碼被反編譯我們?yōu)榱税踩鹨?jiàn),使用C語(yǔ)言來(lái)編寫這些重要的部分來(lái)增大系統(tǒng)的安全性矛双,最后呢生成so庫(kù)便于給人提供方便渊抽。

好了,我們來(lái)看一下qq的結(jié)構(gòu)议忽,我們就能理解任何有效的代碼混淆對(duì)于會(huì)smail語(yǔ)法反編譯你apk是分分鐘的事腰吟,即使你加殼也不能幸免高手的攻擊,當(dāng)然你的apk沒(méi)有什么機(jī)密和交易信息就沒(méi)有人去做這事了徙瓶。

分析qq的apk架構(gòu):
1.使用ClassyShark.jar來(lái)打開(kāi)qq.apk


2.點(diǎn)開(kāi)Archive我們來(lái)查看架構(gòu)

從上圖我們可以看出qq里面是一堆的so庫(kù)是嗎毛雇,所以呢so庫(kù)可見(jiàn)比代碼混淆安全系數(shù)高的多。

JNI與NDK的關(guān)系

  • NDK:
    NDK是一系列工具的集合侦镇。它提供了一系列的工具灵疮,幫助開(kāi)發(fā)者快速開(kāi)發(fā)C(或C++)的動(dòng)態(tài)庫(kù),并能自動(dòng)將so和java應(yīng)用一起打包成apk壳繁。這些工具對(duì)開(kāi)發(fā)者的幫助是巨大的震捣。它集成了交叉編譯器,并提供了相應(yīng)的mk文件隔離CPU闹炉、平臺(tái)蒿赢、ABI等差異,開(kāi)發(fā)人員只需要簡(jiǎn)單修改mk文件(指出“哪些文件需要編譯”渣触、“編譯特性要求”等)羡棵,就可以創(chuàng)建出so。它可以自動(dòng)地將so和Java應(yīng)用一起打包嗅钻,極大地減輕了開(kāi)發(fā)人員的打包工作皂冰。
  • JNI:
    JavaNative Interface (JNI)標(biāo)準(zhǔn)是java平臺(tái)的一部分,JNI是Java語(yǔ)言提供的Java和C/C++相互溝通的機(jī)制养篓,Java可以通過(guò)JNI調(diào)用本地的C/C++代碼秃流,本地的C/C++的代碼也可以調(diào)用java代碼。JNI 是本地編程接口柳弄,Java和C/C++互相通過(guò)的接口舶胀。Java通過(guò)C/C++使用本地的代碼的一個(gè)關(guān)鍵性原因在于C/C++代碼的高效性。
    現(xiàn)在明白了吧碧注,NDK就是為我們生成了c/c++的動(dòng)態(tài)鏈接庫(kù)而已嚣伐,jni呢只不過(guò)是java和c溝通而已,兩者與android沒(méi)有半毛錢關(guān)系应闯,只因?yàn)榘沧渴莏ava程序開(kāi)發(fā)然后jni又能與c溝通纤控,所以使“Java+C”的開(kāi)發(fā)方式終于轉(zhuǎn)正挂捻。

Android是JVM架設(shè)在Linux之上的架構(gòu)碉纺。所以無(wú)論如何,在Linux OS層面,都應(yīng)該可以跑C/C++程序骨田。

Android Native C就是使用C/C++程序直接跑到Linux OS層面上的程序耿导。與其它平臺(tái)類似,只需要交叉編譯后态贤。并得到Linux OS root權(quán)限舱呻,就可以直接跑起來(lái)了。

android studio 中簡(jiǎn)單的jni開(kāi)發(fā)

Let’s GoS破O渎馈!

準(zhǔn)備工作不再需要什么cgwin來(lái)編譯ndk(太特么操蛋了),現(xiàn)在只需要你下載一下NDK的庫(kù)就ok了柿冲,然后你也可以去離線下載http://www.androiddevtools.cn最新版茬高,這里吐槽一下android studio對(duì)NDK的支持還有待提高。

效果看下今天的效果:(安卓jni獲取 apk的包名及簽名信息)

這里寫圖片描述

必須的步驟

1.配置你的ndk路徑(local.properties)

ndk.dir=E:\Android\sdk\android-ndk-r11b-windows-x86_64\android-ndk-r11b

2.grale配置使用ndk(gradle.properties)

android.useDeprecatedNdk=true

3.在module下的build.gradle添加ndk以及jni生成目錄

ndk{ moduleName "JNI_ANDROID" abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種abi體系結(jié)構(gòu)下的so庫(kù)假抄,目前可有可無(wú)怎栽。 } sourceSets.main{ jniLibs.srcDirs = ['libs'] }

準(zhǔn)備工作做好了開(kāi)始寫代碼:(jni實(shí)現(xiàn)獲取應(yīng)用的包名和簽名信息)

步驟1:先寫要實(shí)現(xiàn)本地方法的類,及加載庫(kù)(JNI_ANDROID也就是ndk 里面配的moduleName)

package com.losileeya.getapkinfo;
/** 
* User: Losileeya (847457332@qq.com)
 * Date: 2016-07-16 
* Time: 11:09 
* 類描述: * * @version : 
*/
public class JNIUtils {
 /**
 * 獲取應(yīng)用的簽名 
* @param o 
* @return 
*/ 
public static native String getSignature(Object o);
 /** * 獲取應(yīng)用的包名 
* @param o 
* @return 
*/ 
public static native String getPackname(Object o); 
/** 
* 加載so庫(kù)或jni庫(kù) 
*/
 static { 
    System.loadLibrary("JNI_ANDROID");
 }}

注意我們 的加載c方法都加了native關(guān)鍵字宿饱,然后要使用jni下的c/c++文件就必須使用System.loadLibrary()熏瞄。

步驟2:使用javah命令生成.h(頭文件)

javah -jni com.losileeya.getapkinfo.JNIUtils

執(zhí)行完之后你可以在module下文件夾app\build\intermediates\classes\debug下看見(jiàn)生成的 .h頭文件為:

com_losileeya_getapkinfo_JNIUtils.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_losileeya_getapkinfo_JNIUtils */
#ifndef _Included_com_losileeya_getapkinfo_JNIUtils
#define _Included_com_losileeya_getapkinfo_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_losileeya_getapkinfo_JNIUtils_getPackname(JNIEnv *, jobject, jobject);JNIEXPORT jstring JNICALL Java_com_losileeya_getapkinfo_JNIUtils_getSignature(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif

在工程的main目錄下新建一個(gè)名字為jni的目錄,然后將剛才的.h文件剪切過(guò)來(lái)谬以,當(dāng)然文件名字是可以修改的

步驟3:根據(jù).h文件生成相應(yīng)的c/cpp文件

//// Created by Administrator on 2016/7/16.//
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
#include "appinfo.h"
JNIEXPORT jstring JNICALL Java_com_losileeya_getapkinfo_JNIUtils_getPackname(JNIEnv *env, jobject clazz, jobject obj){
jclass native_class = env->GetObjectClass(obj);
jmethodID mId = env->GetMethodID(native_class, "getPackageName", "()Ljava/lang/String;");
jstring packName = static_cast<jstring>(env->CallObjectMethod(obj, mId));
return packName;
}
JNIEXPORT jstring JNICALL Java_com_losileeya_getapkinfo_JNIUtils_getSignature(JNIEnv *env, jobject clazz, jobject obj){
jclass native_class = env->GetObjectClass(obj);
jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
jobject pm_obj = env->CallObjectMethod(obj, pm_id);
jclass pm_clazz = env->GetObjectClass(pm_obj);
// 得到 getPackageInfo 方法的 
IDjmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
jstring pkg_str = Java_com_losileeya_getapkinfo_JNIUtils_getPackname(env, clazz, obj);// 獲得應(yīng)用包的信息
jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
// 獲得 PackageInfo 類
jclass pi_clazz = env->GetObjectClass(pi_obj);
// 獲得簽名數(shù)組屬性的 IDjfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures", "[Landroid/content/pm/Signature;");
jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
jobjectArray signaturesArray = (jobjectArray)signatures_obj;
jsize size = env->GetArrayLength(signaturesArray);
jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
jclass signature_clazz = env->GetObjectClass(signature_obj);
jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString", "()Ljava/lang/String;");
jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
char *c_msg = (char*)env->GetStringUTFChars(str,0);
return str;
}

注意:要使用前得先聲明强饮,方法名直接從h文件考過(guò)來(lái)就好了,studio目前還是很操蛋的为黎,對(duì)于jni的支持還是不很好胡陪。

步驟4:給項(xiàng)目添加Android.mk和Application.mk

此步驟顯然也是不必要的,如果你需要生成so庫(kù)添加一下也好碍舍,為什么不呢考過(guò)去改一下就好了柠座,如果你不寫這2文件也是沒(méi)有問(wèn)題的,因?yàn)閐ebug下也是有這些so庫(kù)的片橡。好吧妈经,勉強(qiáng)看一下這2貨:

Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := JNI_ANDROID
LOCAL_SRC_FILES =: appinfo.cpp
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_MODULES := JNI_ANDROID
APP_ABI := all

android studio下External Tools的高級(jí)配置NDK一鍵javah,ndk生成so

庫(kù)

eclipse開(kāi)發(fā)ndk的時(shí)候你可能就配置過(guò)javah,所以android studio也可以配置,是不是很興奮:Settings--->Tools---->External Tools就可以配置我們的終端命令了捧书,別急一個(gè)一個(gè)來(lái):

  • javah -jni 命令的配置(一鍵生成h文件)

我們先來(lái)看參數(shù)的配置:

1.Program:$JDKPath$\bin\javah.exe 這里配置的是javah.exe的路徑(基本一致)
2.Parametes: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$這里指的是定位在Module的jni文件你指定的文件執(zhí)行jni指令
3.Working:$ModuleFileDir$\src\main\java * ndk-build(一鍵生成so庫(kù))


我們同樣來(lái)看參數(shù)的配置:

1.Program:E:\Android\sdk\android-ndk-r11b-windows-x86_64\android-ndk-r11b\ndk-build.cmd 這里配置的是ndk下的ndk-build.cmd的路徑(自己去找下)
2.Working:$ModuleFileDir$\src\main\ * javap-s(此命令用于c掉java方法時(shí)方法的簽名)


我們同樣來(lái)看參數(shù)的配置:

1.Program:$JDKPath$\bin\javap.exe 這里配置的是javap.exe的路徑(基本一致)
2.Parametes: -classpath $ModuleFileDir$/build/intermediates/classes/debug -s $FileClass$ 這里指的是定位到build的debug目錄下執(zhí)行 javap -s class文件
3.Working:$ModuleFileDir$

這里介紹最常用的3個(gè)命令吹泡,對(duì)你的幫助應(yīng)該還是很大的來(lái)看一下怎么使用:

  • javah -jni的使用:選中native文件--->右鍵--->External Tools--->javah -jni效果如下:


是不是自動(dòng)生成了包名.類名的.h文件。

  • ndk-build的使用:選中jni文件--->右鍵--->External Tools--->ndk-build效果如下:


是不是一鍵生成了7種so庫(kù)经瓷,你還想去debug目錄下面去找嗎

  • javap-s的使用:選中native文件--->右鍵--->External Tools--->javap-s效果如下:



    看見(jiàn)了每個(gè)方法下的descriptor屬性的值就是你所要的方法簽名爆哑。

3種一鍵生成的命令講完了,以后你用到了什么命令都可以這樣設(shè)置舆吮,是不是很給力揭朝。

新實(shí)驗(yàn)版Gradle插件與AS下NDK開(kāi)發(fā)

近期的 AS 與 Gradle 版本的快速更新對(duì) NDK 開(kāi)發(fā)又有了更加牛叉的體驗(yàn)队贱,因?yàn)樗耆С质褂?GDB 和 LLDB (不清楚這兩是啥的請(qǐng)自行腦部Unix編程基礎(chǔ))來(lái) GUI 化 debug 我們得 native 代碼了(以前真的好蛋疼,命令行巴拉巴拉的潭袱,淚奔爸印!)屯换。
總之現(xiàn)在的 AS 和 Gradle 已經(jīng)趨于實(shí)驗(yàn)完善 NDK 開(kāi)發(fā)了编丘,主要表現(xiàn)在如下方面:

  • AS 完全支持 GUI 模式的 GDB/LLDB 調(diào)試 native 代碼(LLDB調(diào)試引擎需要gradle-experimental plugin的支持)。
  • AS 可以很好的直接編寫 native 代碼彤悔。
  • Java 層代碼聲明好以后 AS 可以自動(dòng)幫我們生成 JNI 接口規(guī)范代碼嘉抓。
    *推出了幾乎針對(duì) NDK 的實(shí)驗(yàn)版 Gradle 插件≡我ぃ可以看見(jiàn)掌眠,現(xiàn)在 NDK 開(kāi)發(fā)已經(jīng)漸漸的變得越來(lái)越方便了,牛叉的一逼幕屹!
    因?yàn)槭菍?shí)驗(yàn)版本蓝丙,所以我就試了下,不作為今后開(kāi)發(fā)的主要方向望拖,但是還是需要了解下渺尘。
    區(qū)別如下:
    1.情況1
//Project的build.gradle文件
buildscript { 
     repositories { 
               jcenter() 
     }  
   dependencies {
 classpath 'com.android.tools.build:gradle-experimental:0.7.0-alpha1' 
   }}
  allprojects { 
     repositories { jcenter() }
}

2.情況2

//Module的build.gradle文件
apply plugin: 'com.android.model.application'
      model { 
          android {
          compileSdkVersion = 23 
          buildToolsVersion = "23.0.2"
         defaultConfig.with { 
         applicationId = "com.losileeya.getapkinfo"  
          minSdkVersion.apiLevel = 8   
         targetSdkVersion.apiLevel = 23 
        }
    } 

/* * native build settings */
 android.ndk { 
   moduleName = "JNI_ANDROID" 
 } 
android.productFlavors { 
 // for detailed abiFilter descriptions, refer to "Supported ABIs" @  // https://developer.android.com/ndk/guides/abis.html#sa create("arm") { ndk.abiFilters.add("armeabi") } 
create("arm7") { ndk.abiFilters.add("armeabi-v7a") } 
create("arm8") { ndk.abiFilters.add("arm64-v8a") } 
create("x86") { ndk.abiFilters.add("x86") }
 create("x86-64") { ndk.abiFilters.add("x86_64") } 
create("mips") { ndk.abiFilters.add("mips") } 
create("mips-64") { ndk.abiFilters.add("mips64") }  
// To include all cpu architectures, leaves abiFilters empty create("all") 
}

可以明顯感覺(jué)到 Project 和 Module 的 build.gradle 文件編寫閉包都有了變化。入門就講完了说敏,你也可以刪掉jni目錄鸥跟,把so庫(kù)放入jniLibs下,效果還是一模一樣的盔沫,很晚了医咨,睡覺(jué)。

總結(jié)

初步使用ndk的技巧已經(jīng)說(shuō)完了架诞,后續(xù)還會(huì)介紹jni中c調(diào)用java以及java調(diào)用c和相關(guān)一系列ndk開(kāi)發(fā)中所要注意的拟淮。
注意事項(xiàng)

1.jni調(diào)用前記得申明,比如:#include stdio.h谴忧,#include jni.h很泊,#include stdlib.h,方法被調(diào)用者寫前面或者頭文件里面>
2.c中env調(diào)方法時(shí)(*env)->但是cpp中就得這樣env->,原因是cpp中是一級(jí)指針沾谓,所以指針特別注意

demo 傳送門:GetApkInfo.rar
我的個(gè)人站點(diǎn):https://zilianliuxue.github.io/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末委造,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子均驶,更是在濱河造成了極大的恐慌昏兆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妇穴,死亡現(xiàn)場(chǎng)離奇詭異爬虱,居然都是意外死亡隶债,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門饮潦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燃异,“玉大人携狭,你說(shuō)我怎么就攤上這事继蜡。” “怎么了逛腿?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵稀并,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我单默,道長(zhǎng)碘举,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任搁廓,我火速辦了婚禮引颈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘境蜕。我一直安慰自己蝙场,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布粱年。 她就那樣靜靜地躺著售滤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪台诗。 梳的紋絲不亂的頭發(fā)上完箩,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音拉队,去河邊找鬼弊知。 笑死,一個(gè)胖子當(dāng)著我的面吹牛粱快,可吹牛的內(nèi)容都是我干的吉捶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼皆尔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呐舔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起慷蠕,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤珊拼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后流炕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體澎现,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仅胞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剑辫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片干旧。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妹蔽,靈堂內(nèi)的尸體忽然破棺而出椎眯,到底是詐尸還是另有隱情,我是刑警寧澤胳岂,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布编整,位于F島的核電站,受9級(jí)特大地震影響乳丰,放射性物質(zhì)發(fā)生泄漏掌测。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一产园、第九天 我趴在偏房一處隱蔽的房頂上張望汞斧。 院中可真熱鬧,春花似錦什燕、人聲如沸粘勒。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仲义。三九已至,卻和暖如春剑勾,著一層夾襖步出監(jiān)牢的瞬間埃撵,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工虽另, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暂刘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓捂刺,卻偏偏與公主長(zhǎng)得像谣拣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子族展,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容