在APP中如何使用系统属性执行shell脚本

背景

前不久有一个白名单的需求,之前的实现方式由APP来调用framework中添加的接口,再调用到system/netd/ ,之后在执行iptables的命令来设置防火墙规则,详情可见这篇: Android11 iptables实现网络防火墙

但是用这种方式后发现,当防火墙的需求发生变化,每次在firewallController中修改iptables命令,都需要重新增量编译,浪费调试时间,于是有了该篇

本篇主要内容不是为实现白名单需求,而是介绍如何在APP种通过系统属性调用Shell脚本

方案

本次方案的具体方式为

  1. APP添加白名单时,将该规则按指定格式添加到一个文件中,假设文件保存在/`data/netWhiteList.txt
    # 格式
    IP:192.168.1.1 Netmask:24 Port:8000
  1. 编写Shell脚本,内容是大概为: 根据白名单文件,遍历文件内规则,执行iptables命令,设置防火墙规则
  2. Shell脚本添加到rc文件中,设置为service
  3. 设置Shell脚本的se权限
  4. 通过系统属性来调用ctl.start拉起service,执行特定的脚本,还可以指定参数

属性 ctrl.startctrl.stop 是用来启动和停止服务。这里的服务是指定义在 rc 后缀文件中的服务。当我们向 ctrl.start 属性写入一个值时,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入 init.svc.<服务名> 属性中,可以通过查询这个属性值,以确定服务是否已经启动。

平台

展锐 T618 Android11

实现

  1. APP中增加规则,向/data/netWhiteList.txt文件中按指定格式写入规则,记得换行

    // all white list file
    public static final String WHITE_LIST_FILE_PATH = "/data/networkWhiteList.txt";
    // temp add rule 
    public static final String WHITE_LIST_TEMP_ADD_FILE_PATH = "/data/temp_add.txt";
    
    public void writeRuleToFile(String rule) {
          
          
        if (TextUtils.isEmpty(rule)) {
          
          
            return;
        }
        FileWriter writer;
        FileWriter tempWriter;
        File file = new File(Constants.WHITE_LIST_FILE_PATH);
        File tempFile = new File(Constants.WHITE_LIST_TEMP_ADD_FILE_PATH);
        try {
          
          
            if (!file.exists()) {
          
          
                file.createNewFile();
            }
            if (!tempFile.exists()) {
          
          
                tempFile.createNewFile();
            }
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
        if (!file.exists()) {
          
          
            LogUtil.e(TAG, "can't create new File: " + Constants.WHITE_LIST_FILE_PATH);
        }
        try {
          
          
            writer = new FileWriter(Constants.WHITE_LIST_FILE_PATH);
            tempWriter = new FileWriter(Constants.WHITE_LIST_TEMP_ADD_FILE_PATH);
            writer.append(rule).append("\\n");
            writer.flush();
            tempWriter.write(rule);
            tempWriter.write("\\n");
            LogUtil.d(TAG, "write rule: " + rule + " over");
            doFirewallCtl(ACTION_ADD);
            writer.close();
            tempWriter.close();
            tempFile.delete();
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
    }
    
    
  2. 编写Shell脚本,我对Shell脚本实际写的不多,找ChatGPT写完改改还是很是能用,位置的话根据项目情况放置,我这边放在源码/device/sprd/mpool/xxx/module目录下,只要能拷贝到设备中就行,shell脚本内容如下:

    #!/system/bin/sh
    
    PARSE_FILE=/data/networkWhiteList.txt
    TEMP_ADD_FILE=/data/temp_add.txt
    TEMP_REMOVE_FILE=/data/temp_delete.txt
    LOG_FILE=/sdcard/firewall_log.txt
    
    function load_rules() {
          
          
        while read -r line
        do
            ip=$(echo "$line" | awk -F ' ' '{
          
          print $1}' | awk -F ':' '{
          
          print $2}')
            netmask=$(echo "$line" | awk -F ' ' '{
          
          print $2}' | awk -F ':' '{
          
          print $2}')
            port=$(echo "$line" | awk -F ' ' '{
          
          print $3}' | awk -F ':' '{
          
          print $2}')
            add_drop_rule $ip $netmask
            echo "load rule IP: $ip Netmask: $netmask Port: $port  $(date "+%Y-%m-%d %H:%M:%S")" >> $LOG_FILE
            # fi
        done < $PARSE_FILE
    }
    
    function add_rule() {
          
          
        do
            ip=$(echo "$line" | awk -F ' ' '{
          
          print $1}' | awk -F ':' '{
          
          print $2}')
            netmask=$(echo "$line" | awk -F ' ' '{
          
          print $2}' | awk -F ':' '{
          
          print $2}')
            port=$(echo "$line" | awk -F ' ' '{
          
          print $3}' | awk -F ':' '{
          
          print $2}')
            add_drop_rule $ip $netmask
            echo "add rule IP: $ip Netmask: $netmask Port: $port  $(date "+%Y-%m-%d %H:%M:%S")" >> $LOG_FILE
            # fi
        done < $TEMP_ADD_FILE
    }
    
    function remove_rule() {
          
          
        do
            ip=$(echo "$line" | awk -F ' ' '{
          
          print $1}' | awk -F ':' '{
          
          print $2}')
            netmask=$(echo "$line" | awk -F ' ' '{
          
          print $2}' | awk -F ':' '{
          
          print $2}')
            port=$(echo "$line" | awk -F ' ' '{
          
          print $3}' | awk -F ':' '{
          
          print $2}')
            remove_drop_rule $ip $netmask
            echo "remove rule IP: $ip Netmask: $netmask Port: $port  $(date "+%Y-%m-%d %H:%M:%S")" >> $LOG_FILE
            # fi
        done < $TEMP_REMOVE_FILE
    }
    
    function add_drop_rule() {
          
          
        iptables -I OUTPUT \\! -d $1\\/$2 -j DROP
        iptables -I FORWARD \\! -d $1\\/$2 -j DROP
    }
    
    function remove_drop_rule() {
          
          
        iptables -D OUTPUT \\! -d $1\\/$2 -j DROP
        iptables -D FORWARD \\! -d $1\\/$2 -j DROP
    }
    
    function clear_rules() {
          
          
        iptables -F
    }
    
    echo "firewall_ctl count: $# param: $1" >> $LOG_FILE
    if [ $# -eq 0 ]; then
        load_rules $PARSE_FILE
    elif [ $1 == "add" ]; then
        add_rule
    elif [ $1 == "remove" ]; then
        remove_rule
    elif [ $1 == "clear" ]; then
        clear_rules
    fi
    
    

    脚本后可跟添加,删除,清空等参数,执行不同的操作,需要注意脚本头部的#!/system/bin/sh ,之前使用时遇到过写成#!/system/bin/bash#!/bin/bash ,在设备中手动执行可以,但是使用属性调用时会失败的问题

    写完脚本后记得找个mk文件做文件拷贝

    PRODUCT_COPY_FILES += \\
                      $(LOCAL_PATH)/firewall_ctl.sh:system/bin/firewall_ctl.sh \\
    
    
  3. Shell脚本添加到rc文件中,设置为service ,相关的rc文件内容

    #net white list control
    # load rules from file
    service firewall /system/bin/firewall_ctl.sh
        class main
        group root system shell
        user root
        disabled
        oneshot
    
    # add rule
    service firewall_add /system/bin/firewall_ctl.sh add
        class main
        user root 
        group root system shell
        disabled
        oneshot
    
    # remove rule
    service firewall_remove /system/bin/firewall_ctl.sh remove
        class main
        user root 
        group root system shell
        disabled
        oneshot
    
    # clear rules
    service firewall_clear /system/bin/firewall_ctl.sh clear
        class main
        user root 
        group root system shell
        disabled
        oneshot
    
    

    写了4个service,后面通过系统属性执行不同的服务就会执行带不同参数的sh脚本了

  4. 添加脚本的SE权限,两个文件分别是:

    source_code/system/sepolicy/vendor/file_contexts
    source_code/system/sepolicy/vendor/firewall_ctl.te
    
    

    添加内容,具体根据实际情况修改:

    # /system/sepolicy/vendor/file_contexts
    /system/bin/firewall_ctl.sh                                 u:object_r:firewall_ctl_exec:s0
    
    # system/sepolicy/vendor/firewall_ctl.te
    type firewall_ctl, domain;
    type firewall_ctl_exec, system_file_type, exec_type, file_type;
    
    typeattribute firewall_ctl coredomain;
    init_daemon_domain(firewall_ctl)
    
  1. APP中使用系统属性来启动服务,执行特定的脚本操作

    private void doFirewallCtl(int action) {
          
          
        switch (action) {
          
          
            case ACTION_LOAD:
                SystemProperties.set("ctl.start", "firewall");
                break;
            case ACTION_ADD:
                SystemProperties.set("ctl.start", "firewall_add");
                break;
            case ACTION_REMOVE:
                SystemProperties.set("ctl.start", "firewall_remove");
                break;
            case ACTION_CLEAR:
                SystemProperties.set("ctl.start", "firewall_clear");
                break;
        }
    }
    
    

之后如果只是修改iptables的设置命令,就可以直接修改shell脚本了,测试时pull出来修改完后再push进去会方便很多

总结一下:

  1. 编写shell脚本,push到设备/system/bin目录下,手动执行看是否可生效,脚本文件是否正确
  2. 将服务添加到rc文件中
  3. 将脚本放到特定目录下,添加SE权限,执行make selinux_policy 看权限是否有错误,避免整编失败浪费时间
  4. 整编验证

问题

  • 服务调用了但是Shell脚本没执行

    看脚本有没有执行可以在里面重定向到文件来debug,除了看android log外可以看下kernel log ,比如出现:

    02E70 <14> [  114.843612][11-24 17:02:18.843] init: starting service 'firewall_clear'...
    02E7C <11> [  114.848335][11-24 17:02:18.848] init: cannot execv('/system/bin/firewall_ctl.sh'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    02E7D <14> [  114.849522][11-24 17:02:18.849] init: Control message: Processed ctl.start for 'firewall_clear' from pid: 2315 (com.jathine.netwhitelist)
    02E7E <38> [  114.850086][11-24 17:02:18.850] type=1400 audit(1700816539.075:839): avc: denied { write } for comm="ne.netwhitelist" name="networkWhiteList.txt" dev="dm-5" ino=4709 scontext=u:r:system_app:s0 tcontext=u:object_r:system_data_root_file:s0 tclass=file permissive=1
    02E7F <38> [  114.850344][11-24 17:02:18.850] type=1400 audit(1700816539.075:840): avc: denied { open } for comm="ne.netwhitelist" path="/data/networkWhiteList.txt" dev="dm-5" ino=4709 scontext=u:r:system_app:s0 tcontext=u:object_r:system_data_root_file:s0 tclass=file permissive=1
    02E80 <38> [  114.850540][11-24 17:02:18.850] type=1400 audit(1700816539.075:841): avc: denied { getattr } for comm="ne.netwhitelist" path="/data/networkWhiteList.txt" dev="dm-5" ino=4709 scontext=u:r:system_app:s0 tcontext=u:object_r:system_data_root_file:s0 tclass=file permissive=1
    02E81 <14> [  114.852383][11-24 17:02:18.852] init: Service 'firewall_clear' (pid 3366) exited with status 127 oneshot service took 0.005000 seconds in background
    02E82 <14> [  114.852426][11-24 17:02:18.852] init: Sending signal 9 to service 'firewall_clear' (pid 3366) process group...
    
    

    如果在adb中使用sh执行脚本可以运行,但是属性调用不生效,要看下Shell脚本的顶部格式了

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

猜你喜欢

转载自blog.csdn.net/weixin_43440181/article/details/134861190
今日推荐