打造 Android 服務器實現 so 文件調用 AndServer+Service

作者:妄為寫代碼 / 公眾號:wangweicodeing 發布時間:2019-10-17

"NightTeam",一個值得加星標的公眾號。
正文共:5623 字 26 圖
預計閱讀時間:15 分鐘
作者: 妄為so 文件調用
隨著 Android 移動安全的高速發展,不管是為了執行效率還是程序的安全性等,關鍵代碼下沉 native 層已成為基本操作。native 層的開發就是通指的 JNI/NDK 開發,通過 JNI 可以實現 java 層和 native 層(主要是 C/C++ )的相互調用,native 層經編譯后產生 so 動態鏈接庫,so 文件具有可移植性廣,執行效率高,保密性強等優點。那么問題來了,如何調用 so 文件顯得異常重要,當然你也可以直接分析 so 文件的偽代碼,利用強悍的編程功底直接模擬關鍵操作,但是我想對于普通人來說頭發還是比較重要的。當前調用 so 文件的主流操作應該是:1,基于 Unicorn 的各種實現(還在學習中,暫且不表) 2,Android 服務器的搭建,在 App 內起 http 服務完成調用 so 的需求(當然前提是過了 so 的效驗等操作)至于為什么選用 AndServer,好吧,不為什么,只是因為搜索到了它 為什么結合 Service,在學習 Android 開發的時候了解到了 Service 的生命周期,個人理解用 Service 去創建 Http 服務比較好。當然也有 Application 的簡單使用,因為在正式環境中,大多數 so 文件的邏輯中都有 context 的一些包名了,簽名了的效驗等,自定義 Application 的話獲取 context 傳參就好了。libyemu.so 簡介
這是我編譯好的一個 so 文件,就是根據入參做下簡單的字符串拼接(以下是 native 層編譯前的 c 代碼)
extern"C"JNIEXPORTjstringJNICALLJava_com_fw_myapplication_ndktest_NdkTest_stringFromUTF(JNIEnv*env,jobjectinstance,jstringstr_){jclassString_clazz=env->FindClass("java/lang/String");jmethodIDconcat_methodID=env->GetMethodID(String_clazz,"concat","(Ljava/lang/String;)Ljava/lang/String;");jstringstr=env->NewStringUTF("fromso--[NightTeam夜幕]");jobjectstr1=env->CallObjectMethod(str_,concat_methodID,str);constchar*chars=env->GetStringUTFChars((jstring)str1,0);returnenv->NewStringUTF(chars);}
這部分代碼還是有必要貼一下的,簡單的靜態注冊使用了反射的思想,反射在逆向中至關重要接下來是 java 代碼,定義了 native 函數
packagecom.fw.myapplication.ndktest;publicclassNdkTest{publicstaticnativeStringstringFromUTF(Stringstr);static{System.loadLibrary("yemu");}}
如果到這里有點懵逼的同學可能需要去補下 Android 開發基礎了Android 項目測試 so
先說下我的環境,因為這個環境影響太大了 1,AndroidStudio 3.4 2,手機 Android 6 架構 armeabi-v7a 打開 AndroidStudio 新建 project 在 module 的 build 中加這么一句,然后 sync 把編譯好的 so 文件復制到 libs 文件夾下(和剛才的 jniLibs.srcDirs 對應) 把 so 對應的 java 代碼也 copy 過來,注意包名類名的一致性 打開 activity_main.xml 文件為 TextView 添加 id 打開 MainActiviy.java 開始編碼 這兩行的意思就是,先從布局中找到對應 id 的 TextView,然后為其設置 Text(調用 native 函數的返回值) 下面測試一下咱們的 so 調用情況 可以看到咱們的 so 文件調用成功(這里咱們的 so 沒有效驗,只是測試 app 是否可以正常調用)
AndServer 代碼編寫
AndServer 官方文檔:https://yanzhenjie.com/AndServer/ 打開官方文檔,看看人家的入門介紹,新建 java 文件 如圖經典 MVC 的 C 就寫好了,定義了一個 nightteam_sign 接口,請求方式為 get,請求參數為 sign,調用 native 函數,然后返回 json,但是這里我想利用 Application 獲取下 context 對象,取下包名,接下來自定義 Applictaion
packagecom.nightteam.httpso;importandroid.app.Application;publicclassMyAppextendsApplication{privatestaticMyAppmyApp;publicstaticMyAppgetInstance(){returnmyApp;}@OverridepublicvoidonCreate(){super.onCreate();myApp=this;}}
然后在 manifest 文件中指定要啟動的 Application 然后修改 MyController.java 的代碼 接下來把官方文檔-服務器的代碼 copy 下來 導入一些包,修改部分代碼如下 新版本的 AndServer.serverBuilder 已經需要傳遞 context 了,這里把網絡地址和端口號也修改為從構造參數中獲取,到這里 AndServer 的東西基本完了,實際上咱們就搭建一個調 so 的接口,并沒有過多的業務邏輯,所以代碼就是使用的最簡單的
Service 代碼編寫
咱們這里用按鈕的點擊事件啟動 Service,故在 activity_main.xml 中添加一個 button 并指定點擊事件 接下來編寫自定義 Service 代碼
packagecom.nightteam.httpso.Service;importandroid.app.Service;importandroid.content.Intent;importandroid.os.IBinder;importandroid.util.Log;importcom.nightteam.httpso.ServerManager;importjava.net.InetAddress;importjava.net.UnknownHostException;publicclassMyServiceextendsService{privatestaticfinalStringTAG="NigthTeam";@OverridepublicvoidonCreate(){super.onCreate();Log.d(TAG,"onCreate:MyService");newThread(){@Overridepublicvoidrun(){super.run();InetAddressinetAddress=null;try{inetAddress=InetAddress.getByName("0.0.0.0");Log.d(TAG,"onCreate:"+inetAddress.getHostAddress());ServerManagerserverManager=newServerManager(getApplicationContext(),inetAddress,8005);serverManager.startServer();}catch(UnknownHostExceptione){e.printStackTrace();}}}.start();}@OverridepublicIBinderonBind(Intentintent){returnnull;}}
打上了幾個 log,在子線程中啟動 AndServer 的服務(何時使用 UI 線程和子線程是 Android 基礎,這里就不贅述了) 注意一下,這里從 0.0.0.0 獲取 inetAddress,可不要寫錯了,localhost 和 0.0.0.0 的區別請移步搜索引擎 然后就是向 ServerManager 的構造函數傳遞 context,inetAddress,port 用來 new 對象,隨后開啟服務最后注意檢查下 manifest 文件中 Service 的聲明 開啟 Service,并獲取本機 ip
回到我們的 MainActivity.java 的 operate( button 的點擊事件)編寫啟動 Service 代碼
publicvoidoperate(Viewview){switch(view.getId()){caseR.id.id_bt_index://啟動服務:創建-->啟動-->銷毀//如果服務已經創建了,后續重復啟動,操作的都是同一個服務,不會再重新創建了,除非你先銷毀它Intentit1=newIntent(this,MyService.class);Log.d(TAG,"operate:button");startService(it1);((Button)view).setText("服務已開啟");break;}}
到這里我們的服務基本搭建好了,但是為了方便起見,我想把咱們的本機 ip 顯示在 App 上,這樣我們就不用去設置再查看了 我在網上找到了一個獲取 ip 地址的一個工具類,源碼如下:
packagecom.nightteam.httpso;importjava.net.InetAddress;importjava.net.NetworkInterface;importjava.net.SocketException;importjava.util.Enumeration;importjava.util.regex.Pattern;publicclassNetUtils{privatestaticfinalPatternIPV4_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])$");privatestaticbooleanisIPv4Address(Stringinput){returnIPV4_PATTERN.matcher(input).matches();}//獲取本機IP地址publicstaticInetAddressgetLocalIPAddress(){Enumeration<NetworkInterface>enumeration=null;try{enumeration=NetworkInterface.getNetworkInterfaces();}catch(SocketExceptione){e.printStackTrace();}if(enumeration!=null){while(enumeration.hasMoreElements()){NetworkInterfacenif=enumeration.nextElement();Enumeration<InetAddress>inetAddresses=nif.getInetAddresses();if(inetAddresses!=null)while(inetAddresses.hasMoreElements()){InetAddressinetAddress=inetAddresses.nextElement();if(!inetAddress.isLoopbackAddress()&&isIPv4Address(inetAddress.getHostAddress())){returninetAddress;}}}}returnnull;}}
把工具類 copy 到我們的 Android 項目中,繼續在 MainActivity.java 中編碼 獲取了一下本機地址和 Android SDK 版本( Android 8 之后啟動 Service 方式不一樣)
申請權限,啟動 App
最后一步就是為 app 申請網絡權限了 隨后連接我們的手機,運行項目,測試一下,點擊開啟服務 看下 AndroidStudio 日志 好像一切正常,在瀏覽器訪問下試試( ip 就是 App 中顯示的 ip 地址) 如圖正常訪問到了我們想要的內容回過頭來說下 Service,打開我們手機的設置,找到應用程序管理-運行中的服務(手機不同,方式不同)可以看到我們的程序,運行了一個服務,這個服務就是咱們編碼的 MyService接下來殺掉該 App進程,再次查看運行中的服務我這里在權限管理設置了自動運行,可以保持服務的運行。(這個地方還是根據系統有大小差異)至此使用 App 起 http 服務調 so 就完成了
好了,上面就是利用 AndServer 打造 Android 服務器調 so 文件的整體思路和流程,如果你懶得看的話,直接用我寫好的 App 修修補補也是可以的,只需要發送消息【AndServer搭建Web服務調so】到公眾號【NightTeam】即可。
文章作者:「夜幕團隊 NightTeam 」- 妄為
夜幕團隊成立于 2019 年,團隊成員包括崔慶才、周子淇、陳祥安、唐軼飛、馮威、蔡晉、戴煌金、張冶青和韋世東。
涉獵的主要編程語言為 Python、Rust、C++、Go,領域涵蓋爬蟲、深度學習、服務研發和對象存儲等。團隊非正亦非邪,只做認為對的事情,請大家小心。

關注妄為寫代碼微信公眾號,獲取更多圖文精彩內容


其他欄目
足球规则