so 文件調用
隨着 Android 移動安全的高速發展,不論是爲了執行效率仍是程序的安全性等,關鍵代碼下沉 native 層已成爲基本操做。<br>native 層的開發就是通指的 JNI/NDK 開發,經過 JNI 能夠實現 java 層和 native 層(主要是 C/C++ )的相互調用,native 層經編譯後產生 so 動態連接庫,so 文件具備可移植性廣,執行效率高,保密性強等優勢。<br>那麼問題來了,如何調用 so 文件顯得異常重要,固然你也能夠直接分析 so 文件的僞代碼,利用強悍的編程功底直接模擬關鍵操做,可是我想對於普通人來講頭髮仍是比較重要的。 <br>當前調用 so 文件的主流操做應該是: <br>1,基於 Unicorn 的各類實現(還在學習中,暫且不表) <br>2,Android 服務器的搭建,在 App 內起 http 服務完成調用 so 的需求(固然前提是過了 so 的效驗等操做)<br>至於爲何選用 AndServer,好吧,不爲何,只是由於搜索到了它 <br>爲何結合 Service,在學習 Android 開發的時候瞭解到了 Service 的生命週期,我的理解用 Service 去建立 Http 服務比較好。 <br>固然也有 Application 的簡單使用,由於在正式環境中,大多數 so 文件的邏輯中都有 context 的一些包名了,簽名了的效驗等,自定義 Application 的話獲取 context 傳參就行了。java
libyemu.so 簡介
這是我編譯好的一個 so 文件,就是根據入參作下簡單的字符串拼接(如下是 native 層編譯前的 c 代碼)android
extern "C" JNIEXPORT jstring JNICALL Java_com_fw_myapplication_ndktest_NdkTest_stringFromUTF(JNIEnv *env, jobject instance, jstring str_) { jclass String_clazz = env->FindClass("java/lang/String"); jmethodID concat_methodID = env->GetMethodID(String_clazz, "concat", "(Ljava/lang/String;)Ljava/lang/String;"); jstring str = env->NewStringUTF(" from so --[NightTeam夜幕]"); jobject str1 = env->CallObjectMethod(str_, concat_methodID, str); const char *chars = env->GetStringUTFChars((jstring)str1, 0); return env->NewStringUTF(chars); }
這部分代碼仍是有必要貼一下的,簡單的靜態註冊使用了反射的思想,反射在逆向中相當重要<br>接下來是 java 代碼,定義了 native 函數編程
package com.fw.myapplication.ndktest; public class NdkTest { public static native String stringFromUTF(String str); static { System.loadLibrary("yemu"); } }
若是到這裏有點懵逼的同窗可能須要去補下 Android 開發基礎了json
Android 項目測試 so
先說下個人環境,由於這個環境影響太大了 <br>1,AndroidStudio 3.4 <br>2,手機 Android 6 架構 armeabi-v7a <br>打開 AndroidStudio 新建 project <br><br>在 module 的 build 中加這麼一句,而後 sync <br><br>把編譯好的 so 文件複製到 libs 文件夾下(和剛纔的 jniLibs.srcDirs 對應) <br><br>把 so 對應的 java 代碼也 copy 過來,注意包名類名的一致性 <br><br>打開 activity_main.xml 文件爲 TextView 添加 id <br><br>打開 MainActiviy.java 開始編碼 <br><br>這兩行的意思就是,先從佈局中找到對應 id 的 TextView,而後爲其設置 Text(調用 native 函數的返回值) <br>下面測試一下我們的 so 調用狀況 <br><br>能夠看到我們的 so 文件調用成功(這裏我們的 so 沒有效驗,只是測試 app 是否能夠正常調用)<br>瀏覽器
AndServer 代碼編寫
AndServer 官方文檔:https://yanzhenjie.com/AndServer/ <br>打開官方文檔,看看人家的入門介紹,新建 java 文件 <br><br>如圖經典 MVC 的 C 就寫好了,定義了一個 nightteam_sign 接口,請求方式爲 get,請求參數爲 sign,調用 native 函數,而後返回 json,可是這裏我想利用 Application 獲取下 context 對象,取下包名,接下來自定義 Applictaion<br>安全
package com.nightteam.httpso; import android.app.Application; public class MyApp extends Application { private static MyApp myApp; public static MyApp getInstance() { return myApp; } @Override public void onCreate() { super.onCreate(); myApp = this; } }
而後在 manifest 文件中指定要啓動的 Application <br><br>而後修改 MyController.java 的代碼 <br><br>接下來把官方文檔-服務器的代碼 copy 下來 <br>導入一些包,修改部分代碼以下 <br><br>新版本的 AndServer.serverBuilder 已經須要傳遞 context 了,這裏把網絡地址和端口號也修改成從構造參數中獲取,到這裏 AndServer 的東西基本完了,實際上我們就搭建一個調 so 的接口,並無過多的業務邏輯,因此代碼就是使用的最簡單的<br>服務器
Service 代碼編寫
我們這裏用按鈕的點擊事件啓動 Service,故在 activity_main.xml 中添加一個 button 並指定點擊事件 <br><br><br>接下來編寫自定義 Service 代碼<br>網絡
package com.nightteam.httpso.Service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; import com.nightteam.httpso.ServerManager; import java.net.InetAddress; import java.net.UnknownHostException; public class MyService extends Service { private static final String TAG = "NigthTeam"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: MyService"); new Thread() { @Override public void run() { super.run(); InetAddress inetAddress = null; try { inetAddress = InetAddress.getByName("0.0.0.0"); Log.d(TAG, "onCreate: " + inetAddress.getHostAddress()); ServerManager serverManager = new ServerManager(getApplicationContext(), inetAddress, 8005); serverManager.startServer(); } catch (UnknownHostException e) { e.printStackTrace(); } } }.start(); } @Override public IBinder onBind(Intent intent) { return null; } }
打上了幾個 log,在子線程中啓動 AndServer 的服務(什麼時候使用 UI 線程和子線程是 Android 基礎,這裏就不贅述了) <br>注意一下,這裏從 0.0.0.0 獲取 inetAddress,可不要寫錯了,localhost 和 0.0.0.0 的區別請移步搜索引擎 <br>而後就是向 ServerManager 的構造函數傳遞 context,inetAddress,port 用來 new 對象,隨後開啓服務<br>最後注意檢查下 manifest 文件中 Service 的聲明 <br>架構
開啓 Service,並獲取本機 ip
回到咱們的 MainActivity.java 的 operate( button 的點擊事件)編寫啓動 Service 代碼app
public void operate(View view) { switch (view.getId()){ case R.id.id_bt_index: //啓動服務:建立-->啓動-->銷燬 //若是服務已經建立了,後續重複啓動,操做的都是同一個服務,不會再從新建立了,除非你先銷燬它 Intent it1 = new Intent(this, MyService.class); Log.d(TAG, "operate: button"); startService(it1); ((Button) view).setText("服務已開啓"); break; } }
到這裏咱們的服務基本搭建好了,可是爲了方便起見,我想把我們的本機 ip 顯示在 App 上,這樣咱們就不用去設置再查看了 <br>我在網上找到了一個獲取 ip 地址的一個工具類,源碼以下:
package com.nightteam.httpso; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.regex.Pattern; public class NetUtils { private static final Pattern IPV4_PATTERN = Pattern.compile("^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"); private static boolean isIPv4Address(String input) { return IPV4_PATTERN.matcher(input).matches(); } //獲取本機IP地址 public static InetAddress getLocalIPAddress() { Enumeration<NetworkInterface> enumeration = null; try { enumeration = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } if (enumeration != null) { while (enumeration.hasMoreElements()) { NetworkInterface nif = enumeration.nextElement(); Enumeration<InetAddress> inetAddresses = nif.getInetAddresses(); if (inetAddresses != null) while (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) { return inetAddress; } } } } return null; } }
把工具類 copy 到咱們的 Android 項目中,繼續在 MainActivity.java 中編碼 <br><br>獲取了一下本機地址和 Android SDK 版本( Android 8 以後啓動 Service 方式不同)<br>
申請權限,啓動 App
最後一步就是爲 app 申請網絡權限了 <br><br>隨後鏈接咱們的手機,運行項目,測試一下,點擊開啓服務 <br><br>看下 AndroidStudio 日誌 <br><br>好像一切正常,在瀏覽器訪問下試試( ip 就是 App 中顯示的 ip 地址) <br><br>如圖正常訪問到了咱們想要的內容<br>回過頭來講下 Service,打開咱們手機的設置,找到應用程序管理-運行中的服務(手機不一樣,方式不一樣)<br><br>能夠看到咱們的程序,運行了一個服務,這個服務就是我們編碼的 MyService<br><br>接下來殺掉該 App進程,再次查看運行中的服務<br><br>我這裏在權限管理設置了自動運行,能夠保持服務的運行。(這個地方仍是根據系統有大小差別)<br>至此使用 App 起 http 服務調 so 就完成了
TiToData:專業的短視頻、直播數據接口服務平臺。 更多信息請聯繫: TiToData 覆蓋主流平臺:抖音,快手,小紅書,TikTok,YouTube