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

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傳參就行了。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);
}
複製代碼

這部分代碼仍是有必要貼一下的,簡單的靜態註冊使用了反射的思想,反射在逆向中相當重要 接下來是java代碼,定義了native函數git

package com.fw.myapplication.ndktest;

public class NdkTest {
    public static native String stringFromUTF(String str);

    static {
        System.loadLibrary("yemu");
    }
}
複製代碼

若是到這裏有點懵逼的同窗可能須要去補下Android開發基礎了github

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官方文檔:yanzhenjie.com/AndServer/
打開官方文檔,看看人家的入門介紹,新建java文件
json

如圖經典MVC的C就寫好了,定義了一個nightteam_sign接口,請求方式爲get,請求參數爲sign,調用native函數,而後返回json,可是這裏我想利用Application獲取下context對象,取下包名,接下來自定義Applictaion

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
瀏覽器

而後修改MyController的代碼
接下來把官方文檔-服務器的代碼copy下來
導入一些包,修改部分代碼以下
新版本的AndServer.serverBuilder已經須要傳遞context了,這裏把網絡地址和端口號也修改成從構造參數中獲取,到這裏AndServer的東西基本完了,實際上我們就搭建一個調so的接口,並無過多的業務邏輯,因此代碼就是使用的最簡單的

Service代碼編寫

我們這裏用按鈕的點擊事件啓動Service,故在activity_main.xml中添加一個button並指定點擊事件
安全

接下來編寫自定義Service代碼

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基礎,這裏就不贅述了)
注意一下,這裏從0.0.0.0獲取inetAddress,可不要寫錯了,localhost和0.0.0.0的區別請移步搜索引擎
而後就是向ServerManager的構造函數傳遞context,inetAddress,port用來new對象,隨後開啓服務 最後注意檢查下manifest文件中Service的聲明
bash

開啓Service,並獲取本機ip

回到咱們的MainActivity.java的operate(button的點擊事件)編寫啓動Service代碼服務器

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上,這樣咱們就不用去設置再查看了
我在網上找到了一個獲取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中編碼

獲取了一下本機地址和Android SDK版本(Android 8以後啓動Service方式不同)

申請權限,啓動App

最後一步就是爲app申請網絡權限了

隨後鏈接咱們的手機,運行項目,測試一下,點擊開啓服務
看下AndroidStudio日誌
好像一切正常,在瀏覽器訪問下試試(ip就是app中顯示的ip地址)
如圖正常訪問到了咱們想要的內容 回過頭來講下Service,打開咱們手機的設置,找到應用程序管理-運行中的服務(手機不一樣,方式不一樣)
能夠看到咱們的程序,運行了一個服務,這個服務就是我們編碼的MyService
接下來殺掉該App進程,再次查看運行中的服務
我這裏在權限管理設置了自動運行,能夠保持服務的運行。(這個地方仍是根據系統有大小差別) 至此使用App起http服務調so就完成了


文章做者:「夜幕團隊 NightTeam 」- 妄爲

夜幕團隊成立於 2019 年,團隊成員包括崔慶才、周子淇、陳祥安、唐軼飛、馮威、蔡晉、戴煌金、張冶青和韋世東。

涉獵的主要編程語言爲 Python、Rust、C++、Go,領域涵蓋爬蟲、深度學習、服務研發和對象存儲等。團隊非正亦非邪,只作認爲對的事情,請你們當心。

本篇文章由一文多發平臺ArtiPub自動發佈

相關文章
相關標籤/搜索