当前位置: 首页 > news >正文

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用。

在这里插入图片描述

0x00 背景介绍

Linux上的HIDS需要实时对执行的命令进行监控,分析异常或入侵行为,有助于安全事件的发现和预防。为了获取执行命令,大致有如下方法:

  1. 遍历/proc目录,无法捕获瞬间结束的进程。
  2. Linux kprobes调试技术,并非所有Linux都有此特性,需要编译内核时配置。
  3. 修改glic库中的execve函数,但是可通过int0x80绕过glic库,这个之前360 A-TEAM一篇文章有写到过。
  4. 修改sys_call_table,通过LKM(loadable kernel module)实时安装和卸载监控模块,但是内核模块需要适配内核版本。

确定了Hook点之后,就可以选择Hook方案了,这时有几个选择:
在应用层:

1、在ring3通过/etc/ld.so.preload劫持系统调用
2、二次开发glibc加入监控代码(据说某产品就是这么做监控的)
3、基于调试器思想通过ptrace()主动注入

在应用层做Hook的好处是不受内核版本影响,通用性较好,而且技术难度相对较低,但是缺点更明显,因为ring3层的Hook都是针对glibc库做的监控,只要直接陷入0x80中断,就可以绕过glibc库直接调用系统调用,比如近期分析的某挖矿木马:

在这里插入图片描述

既然应用层的监控行不通,那就看看内核层的监控手段:

1、API Inline Hook
2、sys_call_table Hook
3、IDT Hook
4、利用LSM(Linux Security Module)

API Inline Hook以及IDT Hook操作难度较大,而且兼容性较差,利用LSM监控API虽然性能最好,但是必须编译进内核才能使用,不可以实时安装卸载,而sys_call_table的Hook相对易于操作,作为防御者也可以直接从” /boot/System.map-uname -r ”中直接获取sys_call_table地址,也可以利用LKM(loadable kernel module)技术实现实时安装卸载,所以最后选择在内核层Hook sys_call_table实现监控。

综合上面方案的优缺点,我们选择修改sys_call_table中的execve系统调用,虽然要适配内核版本,但是能100%监控执行的命令。

Linux执行命令监控是一种安全机制,它允许系统管理员跟踪和记录用户在系统上执行的所有命令。这种监控对于确保系统安全、防止滥用和进行事后分析非常重要。以下是Linux执行命令监控的详细实现原理和使用教程。

实现原理

Linux执行命令监控的实现原理主要基于以下几个方面:

  1. 审计系统
    Linux的审计系统(auditd)是一个强大的工具,它可以记录系统调用和关键文件的更改。通过配置审计规则,管理员可以监控特定用户的命令执行情况。

  2. 命令历史记录
    Linux的history命令可以记录用户在终端会话中执行的所有命令。这个功能默认启用,并且可以通过.bash_history文件来查看。

  3. 进程监控
    使用如pstophtop等工具,可以实时监控系统上运行的进程。结合其他工具,如awkgrep,可以过滤出特定用户的进程。

  4. 日志文件
    Linux系统会生成多种日志文件,如/var/log/auth.log记录了用户的登录和注销信息,而/var/log/secure(在某些发行版中)记录了认证相关的事件。

  5. 系统调用跟踪
    通过使用如strace这样的工具,可以监控和记录进程执行的系统调用。这对于理解命令执行的底层行为非常有用。

  6. 文件系统监控
    使用inotify工具可以监控文件系统事件,如文件的创建、修改和删除。这对于跟踪命令对文件系统的影响很有帮助。

使用教程

以下是如何在Linux系统中设置和使用命令监控的教程:

1. 使用auditd监控命令执行
  • 安装auditd

    sudo apt-get install auditd audispd-plugins # Debian/Ubuntu
    sudo yum install auditd audispd-plugins # CentOS/RedHat
    
  • 配置审计规则
    使用auditctl命令添加规则,例如,监控所有用户的sudo命令:

    sudo auditctl -w /usr/bin/sudo -p x -k sudo_rule
    

    这里-w指定要监控的文件或目录,-p x表示监控所有执行权限,-k用于为规则命名。

  • 查看审计日志
    审计日志通常存储在/var/log/audit/audit.log。可以使用ausearch工具来搜索和分析日志:

    sudo ausearch -k sudo_rule
    
2. 查看命令历史记录
  • 查看历史命令

    history
    

    或者查看历史记录文件:

    cat ~/.bash_history
    
  • 配置历史记录
    可以编辑用户的.bashrc.bash_profile文件来更改历史记录的行为,例如,增加历史记录的大小或自动记录命令。

3. 监控进程
  • 实时监控进程

    top
    

    或者使用htop,如果已安装:

    htop
    
  • 过滤特定用户的进程

    ps -u username
    
4. 分析日志文件
  • 查看认证日志
    cat /var/log/auth.log
    
    或者使用lesstail进行分页查看。
5. 使用strace跟踪系统调用
  • 监控特定进程
    strace -p pid
    
    其中pid是要监控的进程ID。
6. 监控文件系统事件
  • 监控特定目录
    inotifywait -m -r /path/to/directory
    
    -m表示监控模式,-r表示递归监控。

Linux执行命令监控是一个涉及多个工具和机制的复杂过程。通过合理配置和使用这些工具,系统管理员可以有效地监控和记录用户的行为,从而提高系统的安全性和可审计性。需要注意的是,监控活动应当遵守相关的隐私和法律规定,确保在合法和道德的框架内进行。

0x01 总体架构

首先sys_execve监控模块,需要替换原有的execve系统调用。在执行命令时,首先会进入监控函数,将日志通过NetLink发送到用户态分析程序(如想在此处进行命令拦截,修改代码后也是可以实现的),然后继续执行系统原生的execve函数。

在这里插入图片描述

0x02 获取sys_call_table地址

获取sys_call_table的数组地址,可以通过/boot目录下的System.map文件中查找。

命令如下:
cat /boot/System.map-uname-r| grep sys_call_table
这种方式比较麻烦,在每次insmod内核模块的时候,需要将获取到的地址通过内核模块传参的方式传入。而且System.map并不是每个系统都有的,删除System.map对于系统运行无影响。

我们通过假设加偏移的方法获取到sys_call_table地址,首先假设sys_call_tale地址为sys_close,然后判断sys_call_table[__NR_close]是否等于sys_close,如果不等于则将刚才的sys_call_table偏移sizeof(void *)这么多字节,直到满足之前的判断条件,则说明找到正确的sys_call_table的地址了。

代码如下:

unsigned long **find_sys_call_table(void) {unsigned long ptr;unsigned long *p;pr_err("Start foundsys_call_table.\n");for (ptr = (unsignedlong)sys_close;ptr < (unsignedlong)&loops_per_jiffy;ptr += sizeof(void*)) {p = (unsigned long*)ptr;if (p[__NR_close] ==(unsigned long)sys_close) {pr_err("Foundthe sys_call_table!!! __NR_close[%d] sys_close[%lx]\n""__NR_execve[%d] sct[__NR_execve][0x%lx]\n",__NR_close,(unsigned long)sys_close,__NR_execve,p[__NR_execve]);return (unsignedlong **)p;}  }  return NULL;
}

0x03 修改__NR_execve地址

即使获取到了sys_call_table也无法修改其中的值,因为sys_call_table是一个const类型,在修改时会报错。因此需要将寄存器cr0中的写保护位关掉,wp写保护的对应的bit位为0x00010000。

代码如下:

unsigned long original_cr0;
original_cr0 = read_cr0();
write_cr0(original_cr0 & ~0x00010000);  #解除写保护
orig_stub_execve = (void *)(sys_call_table_ptr[__NR_execve]);
sys_call_table_ptr[__NR_execve]= (void *)monitor_stub_execve_hook;
write_cr0(original_cr0);        #加上写保护

在修改sys_call_hook[__NR_execve]中的地址时,不只是保存原始的execve的地址,同时把所有原始的系统调用全部保存下载。

void *orig_sys_call_table [NR_syscalls];
for(i = 0; i < NR_syscalls - 1; i ++) {orig_sys_call_table[i] =sys_call_table_ptr[i];
}

0x04 Execve进行栈平衡

除了execve之外的其他系统调用,基本只要自定义函数例如:my_sys_write函数,在此函数中预先执行我们的逻辑,然后再执行orig_sys_write函数,参数原模原样传入即可。但是execve不能模仿上面的写法,用以上的方法可能会导致Kernel Panic。
需要进行一下栈平衡,操作如下:

  1. 定义替换原始execve函数的函数monitor_stub_execve_hook
.text
.global monitor_stub_execve_hook
monitor_stub_execve_hook:
  1. 在执行execve监控函数之前,将原始的寄存器进行入栈操作:
pushq   %rbx
pushq   %rdi
pushq   %rsi
pushq   %rdx
pushq   %rcx
pushq   %rax
pushq   %r8
pushq   %r9
pushq   %r10
pushq   %r11
  1. 执行监控函数并Netlink上报操作:
    call monitor_execve_hook

  2. 入栈的寄存器值进行出栈操作

pop     %r11
pop     %r10
pop     %r9
pop     %r8
pop     %rax
pop     %rcx
pop     %rdx
pop     %rsi
pop     %rdi
pushq   %rbx
  1. 执行系统的execve函数
    jmp *orig_sys_call_table(, %rax, 8)

0x05 执行命令信息获取

监控执行命令,如果用户态使用的是相对路径执行,此模块也需要获取出全路径。通过getname()函数获取执行文件名,通过open_exec()和d_path()获取出执行文件全路径。通过current结构体变量获取进程pid,父进程名,ppid等信息。同时也获取运行时的环境变量中PWD,LOGIN相关的值。
最终将获取到的数据组装成字符串,用ascii码值为0x1作为分隔符,通过netlink_broadcast()发送到到用户态分析程序处理。

0x06 监控效果

在加载内核模块,在用户态执行netlink消息接收程序。然后使用相对路径执行命令./t my name is xxxx,然后查看用户态测试程序获取的数据。

在这里插入图片描述
在这里插入图片描述

0x07 版本支持及代码

支持内核版本:2.6.32, >=3.10.0
源代码路径:https://github.com/ysrc/yulong-hids/tree/master/syscall_hook

在Linux系统中,有多种工具可以用来监控执行命令,这些工具可以帮助系统管理员跟踪用户行为、审计系统操作或进行安全分析。以下是20个用于监控Linux执行命令的工具,以及它们的访问链接:

  1. auditd - Linux审计守护进程

    • 访问链接: https://www.linux-audit.com/
  2. syslog-ng - 灵活的日志管理工具

    • 访问链接: https://www.syslog-ng.com/
  3. rsyslog - 用于日志处理的系统服务

    • 访问链接: http://www.rsyslog.com/
  4. logrotate - 管理日志文件的轮换、压缩和删除

    • 访问链接: https://github.com/logrotate/logrotate
  5. logwatch - 监视系统日志并生成报告

    • 访问链接: https://sourceforge.net/projects/logwatch/
  6. Swatch - 监视系统日志文件的变化

    • 访问链接: http://swatch.sourceforge.net/
  7. Glog - C++日志库,支持日志监控

    • 访问链接: https://github.com/google/glog
  8. Logstash - 收集、解析和丰富日志数据

    • 访问链接: https://www.elastic.co/de/products/logstash
  9. Filebeat - 轻量级日志文件收集器

    • 访问链接: https://www.elastic.co/de/products/beats/filebeat
  10. GoAccess - 实时日志分析器和交云式查看器

    • 访问链接: https://goaccess.io/
  11. Graylog - 强大的日志管理平台

    • 访问链接: https://www.graylog.org/
  12. Fluentd - 开源数据收集器

    • 访问链接: https://www.fluentd.org/
  13. Prometheus - 监控系统和应用程序的时间序列数据库

    • 访问链接: https://prometheus.io/
  14. Netdata - 性能监控和可视化工具

    • 访问链接: https://www.netdata.cloud/
  15. Zabbix - 企业级监控解决方案

    • 访问链接: https://www.zabbix.com/
  16. Nagios - 监控系统、网络和基础设施

    • 访问链接: https://www.nagios.org/
  17. Icinga - 监控系统,Nagios的分支

    • 访问链接: https://www.icinga.com/
  18. Puppet - 配置管理平台,可以用于监控配置变更

    • 访问链接: https://puppet.com/
  19. Chef - 自动化平台,用于管理服务器和应用程序

    • 访问链接: https://www.chef.io/
  20. Ansible - 自动化工具,可以用于监控和执行任务

    • 访问链接: https://www.ansible.com/

请注意,上述工具的使用应遵守法律法规,并在获得授权的情况下进行。这些工具主要用于教育和安全研究目的,以提高系统的安全性。在使用这些工具时,应确保不侵犯他人的隐私和权益。

相关文章:

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程&#xff0c;以及相关工具的使用。 0x00 背景介绍 Linux上的HIDS需要实时对执行的命令进行监控&#xff0c;分析异常或入侵行为&#xff0c;有助于安全事件的发现和预防。为了获取执行命令&#xff0c;大致有如下方法&#xff1a; 遍…...

算法设计与分析实验报告c++实现(生命游戏、带锁的门、三壶谜题、串匹配问题、交替放置的碟子)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、 编…...

【电子通识】热风枪的结构与使用方法

热风枪的结构 热风枪是专门用来拆焊、焊接贴片元器件和贴片集成电路的焊接工具&#xff0c;它主要由主机和热风焊枪两大部分构成。 热风枪主要有电源开关、风速设置、温度设置、热风连接等部件组成。根据不同品牌和价位的热风枪&#xff0c;有一些功能齐全的也集成了烙铁功能。…...

mysql知识点

MySQL 中有哪几种锁 表级锁&#xff1a;开销小&#xff0c;加锁快&#xff1b;不会出现死锁&#xff1b;锁定粒度大&#xff0c;发生锁冲突的概率最高&#xff0c;并发度最低。行级锁&#xff1a;开销大&#xff0c;加锁慢&#xff1b;会出现死锁&#xff1b;锁定粒度最小&…...

css Animation 动画-右进左出

transform: rotate&#xff08;旋转&#xff09; | scale&#xff08;缩放&#xff09; | skew&#xff08;倾斜&#xff09; | translate&#xff08;移动&#xff09; ;<style> .jinggao {width: 60vw;display: inline-block;text-align: center;overflow: hidden;box-…...

第十三届蓝桥杯省赛大学B组填空题(c++)

A.扫雷 暴力模拟AC: #include<iostream> using namespace std; const int N105; int n,m,map[N][N],ans[N][N]; int dx[8]{-1,-1,0,1,1,1,0,-1}; int dy[8]{0,1,1,1,0,-1,-1,-1}; int count(int x,int y){int cnt0;for(int i0;i<8;i){int xxxdx[i];int yyydy[i];if(…...

天星金融(原小米金融)深耕金融知识领域,助力消费者提升金融素养

近年来&#xff0c;依托生活和消费品质不断提升的时代契机&#xff0c;信用卡持卡人的数量以及信用卡消费的频率不断增加&#xff0c;信用卡还款问题也日益凸显。部分不法分子打着“智能还款”、“精养提额”的口号“踏浪”入场&#xff0c;实则行诱导、诈骗之实。天星金融&…...

中国手机频段介绍

中国目前有三大运营商&#xff0c;分别是中国移动、中国联通、中国电信&#xff0c;还有一个潜在的运营商中国广电&#xff0c;各家使用的2/3/4G的制式略有不同 中国移动的GSM包括900M和1800M两个频段。 中国移动的4G的TD-LTE包括B34、B38、B39、B40、B41几个频段&#xff0c;…...

企业如何使用SNP Glue将SAP与Snowflake集成?

SNP Glue是SNP的集成技术&#xff0c;适用于任何云平台。它最初是围绕SAP和Hadoop构建的&#xff0c;现在已经发展为一个集成平台&#xff0c;虽然它仍然非常专注SAP&#xff0c;但可以将几乎任何数据源与任何数据目标集成。 我们客户非常感兴趣的数据目标之一是Snowflake。Sno…...

算法设计与分析实验报告c++实现(最近点对问题、循环赛日程安排问题、排序问题、棋盘覆盖问题)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、最…...

Vue - 你知道Vue中computed和watch的区别吗

难度级别:中高级及以上 提问概率:70% 二者都是用来监听数据变化的,而且在日常工作中大部分时候都只是局限于简单实用,所以到了面试中很难全面说出二者的区别。接下来我们看一下,二者究竟有哪些区别呢? 先说computed,它的主要用途是监听…...

POJ2976 Dropping tests——P4377 [USACO18OPEN] Talent Show G 【分数规划二分法+贪心/背包】

POJ2976 Dropping tests 【分数规划二分法+贪心】 有 n 个物品,每个物品有两个权值 a 和b。你可以放弃 k 个物品,选 n-k 个物品,使得最大。 输入多个样例,第一行输入n 和 k,第二行输入n 个 ai ,第三行输入 n 个 bi,输入 0 0 结束。 输出答案乘100 后四舍五入到整数…...

【生产实习-毕设】pyspark学生成绩分析与预测(上)

注意&#xff1a;数据由实习单位老师提供&#xff08;需要自行搜索下载&#xff09;&#xff0c;页面美化为下载模板。 项目介绍&#xff1a;前端页面输入影响成绩的属性&#xff0c;预测出成绩&#xff0c;并作可视化展示——属性对成绩的影响。使用python pyspark 进行数据预…...

【华为笔试题汇总】2024-04-10-华为春招笔试题(第二套)-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…...

Windows 文件夹被占用无法删除

按下键盘上的“Ctrl Alt Delete”键打开任务管理器...

PHP+MySQL组合开发 易企秀H5场景源码系统 带完整的安装代码包以及搭建教程

在数字化时代&#xff0c;企业对于宣传与推广的需求日益增长&#xff0c;而H5页面作为一种轻量级、跨平台的宣传方式&#xff0c;深受企业青睐。为了满足企业对于H5页面制作的需求&#xff0c;我们基于PHPMySQL组合开发了一套易企秀H5场景源码系统&#xff0c;并提供了完整的安…...

抖音小店入驻有什么条件?资金少,没经验的普通人做得起吗?

大家好&#xff0c;我是电商花花。 在直播电商的推动下&#xff0c;抖音小店独特的电商模式下吸引着众多的商家&#xff0c;吸引着一波又一波的创业者入驻&#xff0c;想要在抖音小店上开垦出属于自己的电商净土。 想要入驻抖音小店还需要一些条件&#xff0c;然后才能入驻成…...

游戏行业科普 (二)游戏是怎么做出来,怎么卖出去的?

游戏行业科普系列文章&#xff0c;大家可以关注起来&#xff0c;等我慢慢芬分享~~ 《蛋仔派对》 一、研运流程--游戏是怎么做出来的 一款游戏的开发和运营大体上可以分为预研立项、设计开发、测试调优、发行上线和成熟运营几个阶段。 1&#xff09;预研立项&#xff1a; 初始研…...

Java研学-RBAC权限控制(二)

三 PageHelper 1 分页所需 // 原分页所需 1. 定义QueryObject类&#xff0c;传递分页参数&#xff0c;currentPage&#xff0c;pageSize&#xff0c;get start();方法 2. selectForCount()方法&#xff0c;总条数小于等于0说明不需要分页&#xff0c;大于0说明可以分页 3. se…...

20. 【Android教程】拖动条 SeekBar

这一节要学的控件是 ProgressBar 的升级版&#xff0c;对于 ProgressBar 而言只能展示进度&#xff0c;而不能与用户互动&#xff0c;也就是没有接收用户输入的能力。而本节要学习的 SeekBar 是一种可以“Seek”的 ProgressBar&#xff0c;用户不但可以通过 SeekBar 观察到进度…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...