文档章节

AndServer+Service打造Android服务器实现so文件调用

夜幕NightTeam
 夜幕NightTeam
发布于 09/18 14:59
字数 2129
阅读 16
收藏 0

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"
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 函数

package com.fw.myapplication.ndktest;

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

    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

 

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.java 的代码


接下来把官方文档-服务器的代码 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 的声明

开启 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 就完成了

 


好了,上面就是利用 AndServer 打造 Android 服务器调 so 文件的整体思路和流程,如果你懒得看的话,直接用我写好的 App 修修补补也是可以的,只需要发送消息【AndServer搭建Web服务调so】到公众号【NightTeam】即可。


文章作者:「夜幕团队 NightTeam 」- 妄为

夜幕团队成立于 2019 年,团队成员包括崔庆才、周子淇、陈祥安、唐轶飞、冯威、蔡晋、戴煌金、张冶青和韦世东。

涉猎的主要编程语言为 Python、Rust、C++、Go,领域涵盖爬虫、深度学习、服务研发和对象存储等。团队非正亦非邪,只做认为对的事情,请大家小心。

© 著作权归作者所有

夜幕NightTeam
粉丝 0
博文 8
码字总数 23776
作品 0
深圳
私信 提问
AndServer+Service 打造 Android 服务器实现 so 文件调用

扫码或搜索:进击的Coder 发送 即可立即永久解锁本站全部文章 so 文件调用 随着 Android 移动安全的高速发展,不管是为了执行效率还是程序的安全性等,关键代码下沉 native 层已成为基本操作...

NightTeam
09/20
0
0
Android开发:使用EasyPay打造全能移动支付框架

前言 在这之前,笔者发布了两篇移动app支付相关博文,得到一些关注,但是由于博文中代码零碎,有些读者私信博主,以及加笔者qq咨询相关问题。考虑到这些,笔者把之前项目中的支付相关代码从业...

MichaelX
2018/10/30
0
0
Android四款系统架构工具

Spring for Android   Spring for Android是Spring框架在Android平台上的扩展,旨在简化Android原生应用的开发流程,提高开发者的工作效率。Spring for Android可以帮助开发者简化应用与服...

zhwj407906450
2014/10/21
241
0
基于Android平台的Web服务技术研究

基于Android平台的Web服务技术研究 Android 平台是Google 于2007 年11 月推出的一种智能手机平台,由操作系统、中间件、用户界面、应用软件组成,全面整合的移动"软件栈".诚然,提到Android...

李禾根
2012/10/09
601
2
Android的Camera架构介绍

Thanks hanchao3c for sharing the article! 第一部分 Camera概述 Android的Camera包含取景器(viewfinder)和拍摄照片的功能。目前Android发布版的Camera程序虽然功能比较简单,但是其程序的...

Jerikc
2012/04/13
4.4K
1

没有更多内容

加载失败,请刷新页面

加载更多

Android OkHttp + Retrofit 取消请求的方法

本文链接 前言 在某一个界面,用户发起了一个网络请求,因为某种原因用户在网络请求完成前离开了当前界面,比较好的做法是取消这个网络请求。对于OkHttp来说,具体是调用Call的cancel方法。 ...

shzwork
今天
6
0
并发编程之Callable异步,Future模式

Callable 在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或...

codeobj
今天
6
0
Ubuntu环境下安装PaddlePaddle

开篇 深度学习技术是目前非常热门的技术,笔者在闲暇之余决定学习一下这门技术,入门选择了百度开源的PaddlePaddle框架。 paddlepaddle介绍 飞桨(PaddlePaddle) 是国际领先的端到端开源深度学...

豫华商
今天
6
0
LeetCode 第 287 号问题:寻找重复数,一道非常简单的数组遍历题,加上四个条件后感觉无从下手

今天分享的题目来源于 LeetCode 第 287 号问题:寻找重复数。 题目描述 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只...

五分钟学算法
今天
6
0
vuex mapActions

本文转载于:专业的前端网站➧vuex mapActions 在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在...

前端老手
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部