进程信息分析检测原理详解
概述
processInfo函数是LinuxGun应急响应工具中的核心进程分析模块,通过多维度的进程检测技术来发现系统中的异常进程、恶意进程和潜在的安全威胁。该函数采用了从基础进程信息收集到高级隐藏进程检测的分层检测策略。(函数在 LinuxGun 中是第三个模块,故大段落从 3 开始方便与函数对应)
项目地址:https://github.com/sun977/Linuxgun
一、检测项详细说明
3.1 基础进程信息收集
检测命令: ps -auxww
检测原理:
- 获取系统中所有运行进程的详细信息
-a
: 显示所有用户的进程-u
: 显示进程的用户信息-x
: 显示没有控制终端的进程-ww
: 不限制输出宽度,显示完整的命令行
安全意义:
- 建立进程基线,为后续分析提供数据基础
- 统计进程总数,异常的进程数量可能表明系统被感染
- 同时方便后期复查时发现遗漏的异常进程信息
3.2 内存占用TOP5进程检测
检测命令: ps -aux | sort -nr -k 4 | head -5
检测原理:
- 按内存占用率(%MEM)降序排列
-nr
: 数值逆序排序-k 4
: 按第4列(内存占用率)排序head -5
: 取前5个结果
安全意义:
- 识别内存消耗异常的进程
- 某些恶意软件(如挖矿程序)会大量消耗内存资源
- 内存泄漏或DoS攻击的早期发现
3.3 高内存占用进程检测
检测阈值: 内存占用率 ≥ 20%
检测原理:
- 使用awk过滤内存占用超过20%的进程
awk '{if($4>=20) print $0}'
: 判断第4列(内存占用率)是否≥20%
安全意义:
- 发现异常的高内存消耗进程
- 可能的恶意挖矿程序、内存炸弹或系统异常
- 帮助识别资源滥用行为
3.4 CPU占用TOP5进程检测
检测命令: ps -aux | sort -nr -k 3 | head -5
检测原理:
- 按CPU占用率(%CPU)降序排列
-k 3
: 按第3列(CPU占用率)排序
安全意义:
- 识别CPU密集型进程
- 发现可能的挖矿程序、暴力破解工具
- 系统性能异常的根因分析
3.5 高CPU占用进程检测
检测阈值: CPU占用率 ≥ 20%
检测原理:
- 使用awk过滤CPU占用超过20%的进程
awk '{if($3>=20) print $0}'
: 判断第3列(CPU占用率)是否≥20%
安全意义:
- 发现异常的高CPU消耗进程
- 挖矿程序、DDoS工具的典型特征
- 系统资源滥用检测
3.6 敏感进程匹配检测
检测依据: checkrules/dangerspslist.txt
规则文件
检测原理:
- 读取预定义的危险进程名称列表
- 使用awk模式匹配:
$11 ~ proc
(匹配命令字段) - 统计匹配到的敏感进程数量
安全意义:
- 基于威胁情报的已知恶意进程检测(危险进程列表可自定义)
- 包含常见的黑客工具、后门程序、恶意软件
- 提供快速的威胁识别能力
规则文件内容示例:
- 网络扫描工具: nmap, masscan
- 后门工具: nc, netcat
- 提权工具: sudo, su相关异常
- 挖矿程序: xmrig, cpuminer
3.7 异常进程检测(/proc vs ps)
检测原理:
- 获取ps命令显示的所有PID:
ps -eo pid --no-headers
- 获取/proc目录中的所有数字目录(进程PID):
ls /proc/ | grep '^[0-9]\+$'
- 比较两个列表,找出存在于/proc但不在ps中的进程
技术细节:
- 读取进程信息:
/proc/PID/comm
,/proc/PID/cmdline
,/proc/PID/stat
- 获取进程状态和启动时间
- 检测进程隐藏技术
安全意义:
- 发现使用进程隐藏技术的rootkit
- 检测内核级恶意模块
- 识别高级持久性威胁(APT)的隐蔽技术
3.8 高级进程隐藏检测技术
3.8.1 进程树完整性检查(孤儿进程检测)
检测原理:
- 获取所有进程的PID和PPID关系:
ps -eo pid,ppid
- 检查每个进程的父进程是否存在
- 排除init进程(PID=1)和内核线程(PID=0,2)
安全意义:
- 发现可疑的孤儿进程
- 某些恶意程序会故意创建孤儿进程来逃避检测
- 进程注入攻击的痕迹发现
3.8.2 网络连接与进程对应关系检查
检测原理:
- macOS系统: 使用
lsof -i -n -P
命令 - Linux系统: 使用
netstat -tulnp
或ss -tulnp
命令 - 提取网络连接对应的PID,检查进程是否真实存在
技术细节:
- lsof输出格式: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
- netstat输出格式: 最后一列包含PID/进程名
- ss输出格式: 包含pid=数字的模式
安全意义:
- 发现网络连接对应的进程已被隐藏
- 检测网络型恶意软件的隐蔽技术
- 识别僵尸网络(botnet)的通信进程
3.8.3 进程内存映射异常检查
检测原理:
- 遍历
/proc/[0-9]*/maps
文件 - 搜索可疑的内存映射模式:
rwxp.*[heap]
: 堆区域具有可执行权限rwxp.*[stack]
: 栈区域具有可执行权限rwxp.*deleted
: 指向已删除文件的可执行映射
技术原理:
- 正常情况: 堆(heap)和栈(stack)只应有读写权限(rw-)
- 异常情况: 具有执行权限(rwx)可能表明:
- 栈溢出攻击
- Shellcode注入
- 内存马驻留
- 无文件攻击
安全意义:
- 检测内存注入攻击
- 发现无文件恶意软件
- 识别高级的代码注入技术
- rootkit内存隐藏技术检测
3.8.4 进程文件描述符异常检查
检测原理:
- 遍历
/proc/[0-9]*/fd/
目录 - 统计指向已删除文件的文件描述符数量
- 使用
ls -l
查看符号链接,搜索"(deleted)"标记
技术原理:
- 当进程打开文件后,即使文件被删除,文件描述符仍然有效
- 恶意软件常用技术:检测恶意进程删除自身文件但保持在内存中运行
安全意义:
- 检测恶意进程的自删除行为
- 发现内存驻留型恶意软件
- 识别反取证技术
- 无文件攻击的检测
3.8.5 系统调用表完整性检查
检测原理:
- 检查
/proc/kallsyms
文件中的sys_call_table
符号 - 搜索可疑的内核符号名称
- 需要root权限才能执行
技术背景:
- 系统调用表(sys_call_table): Linux内核中存储所有系统调用函数指针的数组
- 正常情况: sys_call_table符号在/proc/kallsyms中可见
- 被攻击: rootkit可能隐藏或修改这个符号
检测内容:
- sys_call_table符号检查: 验证系统调用表是否被隐藏
- 可疑内核符号搜索: 查找包含以下关键词的符号(这些特征比较明显不会遇到,实战时可以结合已知情报做关键字匹配):
- “hide”: 隐藏功能
- “rootkit”: rootkit相关
- “stealth”: 隐蔽功能
- “hook”: 钩子函数
安全意义:
- rootkit检测的核心技术
- 内核级恶意模块发现
- 系统调用劫持检测
- 高级持久性威胁(APT)的内核级检测
二、检测技术总结
检测层次
- 基础层: 进程信息收集和资源占用分析
- 规则层: 基于威胁情报的已知恶意进程检测
- 行为层: 异常进程行为和隐藏技术检测
- 内核层: 系统调用表和内核符号完整性检查
检测覆盖的威胁类型
- 恶意软件: 病毒、木马、后门
- 挖矿程序: 加密货币挖矿恶意软件
- Rootkit: 内核级和用户级rootkit
- APT工具: 高级持久性威胁工具
- 网络攻击工具: 扫描器、DDoS工具
- 提权工具: 本地提权恶意程序
- 内存马: 无文件恶意软件
- 进程注入: 代码注入和进程hollowing
技术优势
- 多维度检测: 从进程、内存、网络、内核多个维度进行检测
- 深度分析: 不仅检测进程存在,还分析进程行为和内存特征
- 隐藏技术对抗: 专门针对各种进程隐藏技术设计检测逻辑
- 实时性: 基于当前系统状态进行实时检测
检测局限性
- 权限依赖: 某些检测需要root权限
- 系统兼容: 部分检测依赖特定的系统文件和命令
- 误报可能: 某些正常程序可能触发异常检测
- 性能影响: 大量的文件系统访问可能影响系统性能
三、待补充
- 动态进程: 函数未包含动态进程 ID或进程组的查杀类型,此类型更多的需要安全人员实际判断。(后续有时间再补充该类型的检查手段)
- 线程检查:函数未包含线程级别恶意文件的查杀,杀死线程的方法和杀死进程一样,因为在Linux中线程的概念就是轻量级进程(后续有时间再补充)
- 潜伏期进程:函数在应对具有潜伏期的恶意进程时具有天然的劣势,因为谁也不能保证你运行了检查函数时恶意进程就正好在活跃期。遇到有耐心的对手只能比他更有耐心才有致胜的希望(祝你好运)。
四、应急响应建议
当检测到异常进程时,建议采取以下应急响应措施:
- 立即隔离: 断开网络连接,防止数据泄露
- 进程分析: 详细分析可疑进程的行为和来源
- 内存取证: 使用内存取证工具进一步分析
- 文件分析: 分析相关文件的完整性和恶意性
- 网络分析: 检查网络连接和通信行为
- 系统加固: 修复漏洞,加强系统安全配置
- 持续监控: 建立长期的安全监控机制
五、processInfo函数原文
processInfo(){
local start_time=$(date +%s)
log_operation "MOUDLE - PROCESSINFO" "开始进程信息分析和安全检测" "START"
echo -e "${GREEN}==========${YELLOW}3. Process Info Analysis${GREEN}==========${NC}"
echo -e "${YELLOW}[3.1] 输出所有系统进程[ps -auxww]:${NC}"
log_message "INFO" "开始获取所有系统进程信息"
if ps -auxww >/dev/null 2>&1; then
ps -auxww
local process_count=$(ps -auxww | wc -l)
log_message "INFO" "成功获取系统进程信息,共${process_count}个进程"
else
handle_error 1 "获取系统进程信息失败" "processInfo"
fi
echo -e "${YELLOW}[3.2] 检查内存占用top5的进程[ps -aux | sort -nr -k 4 | head -5]:${NC}"
log_message "INFO" "开始检查内存占用top5进程"
if ps -aux | sort -nr -k 4 | head -5 >/dev/null 2>&1; then
ps -aux | sort -nr -k 4 | head -5
log_message "INFO" "内存占用top5进程检查完成"
else
handle_error 1 "内存占用进程检查失败" "processInfo"
fi
echo -e "${YELLOW}[3.3] 检查内存占用超过20%的进程:${NC}"
log_message "INFO" "开始检查内存占用超过20%的进程"
high_mem_processes=$(ps -aux | sort -nr -k 4 | awk '{if($4>=20) print $0}' | head -5)
if [ -n "$high_mem_processes" ]; then
echo "$high_mem_processes"
local high_mem_count=$(echo "$high_mem_processes" | wc -l)
log_message "WARN" "发现${high_mem_count}个内存占用超过20%的进程"
else
echo -e "${GREEN}[SUCC] 未发现内存占用超过20%的进程${NC}"
log_message "INFO" "未发现内存占用超过20%的进程"
fi
echo -e "${YELLOW}[3.4] 检查CPU占用top5的进程[ps -aux | sort -nr -k 3 | head -5]:${NC}"
log_message "INFO" "开始检查CPU占用top5进程"
if ps -aux | sort -nr -k 3 | head -5 >/dev/null 2>&1; then
ps -aux | sort -nr -k 3 | head -5
log_message "INFO" "CPU占用top5进程检查完成"
else
handle_error 1 "CPU占用进程检查失败" "processInfo"
fi
echo -e "${YELLOW}[3.5] 检查CPU占用超过20%的进程:${NC}"
log_message "INFO" "开始检查CPU占用超过20%的进程"
high_cpu_processes=$(ps -aux | sort -nr -k 3 | awk '{if($3>=20) print $0}' | head -5)
if [ -n "$high_cpu_processes" ]; then
echo "$high_cpu_processes"
local high_cpu_count=$(echo "$high_cpu_processes" | wc -l)
log_message "WARN" "发现${high_cpu_count}个CPU占用超过20%的进程"
else
echo -e "${GREEN}[SUCC] 未发现CPU占用超过20%的进程${NC}"
log_message "INFO" "未发现CPU占用超过20%的进程"
fi
# 敏感进程匹配[匹配规则]
echo -e "${YELLOW}[3.6] 根据规则列表 dangerspslist.txt 匹配检查敏感进程:${NC}"
log_message "INFO" "开始敏感进程检测"
if [ ! -f "${current_dir}/checkrules/dangerspslist.txt" ]; then
echo -e "${RED}[WARN] 敏感进程规则文件不存在: ${current_dir}/checkrules/dangerspslist.txt${NC}"
log_message "WARN" "敏感进程规则文件不存在: ${current_dir}/checkrules/dangerspslist.txt"
else
danger_ps_list=$(cat ${current_dir}/checkrules/dangerspslist.txt) || handle_error 1 "读取敏感进程规则文件失败" "processInfo"
ps_output=$(ps -auxww) || handle_error 1 "获取进程列表失败" "processInfo"
local total_dangerous_processes=0
for psname in $danger_ps_list; do
filtered_output=$(echo "$ps_output" | awk -v proc="$psname" '
BEGIN { found = 0 }
{
if ($11 ~ proc) {
print;
found++;
}
}
END {
if (found > 0) {
printf($0)
printf("\n'${RED}'[WARN] 发现敏感进程: %s, 进程数量: %d'${NC}'\n", proc, found);
}
}'
)
if [ -n "$filtered_output" ]; then
echo -e "${RED}$filtered_output${NC}"
local process_count=$(echo "$filtered_output" | grep -c "$psname" || echo "0")
total_dangerous_processes=$((total_dangerous_processes + process_count))
log_message "WARN" "发现敏感进程数量: $process_count"
fi
done
if [ $total_dangerous_processes -eq 0 ]; then
echo -e "${GREEN}[SUCC] 未发现敏感进程${NC}"
log_message "INFO" "未发现敏感进程"
else
log_message "WARN" "共发现 ${total_dangerous_processes} 个敏感进程"
fi
fi
printf "\n"
# 异常进程检测:如果存在 /proc 目录中有进程文件夹,但是在 ps -aux 命令里没有显示的,就认为可能是异常进程
echo -e "${YELLOW}[3.7] 正在检查异常进程(存在于/proc但不在ps命令中显示):${NC}"
log_message "INFO" "开始异常进程检测"
# 获取所有ps命令显示的PID
ps_pids=$(ps -eo pid --no-headers | tr -d ' ') || handle_error 1 "获取ps进程列表失败" "processInfo"
# 获取/proc目录中的所有数字目录(进程PID)
proc_pids=$(ls /proc/ 2>/dev/null | grep '^[0-9]\+$') || handle_error 1 "获取/proc目录进程列表失败" "processInfo"
# 检查异常进程
anomalous_processes=() # 用于存储异常进程的数组
for proc_pid in $proc_pids; do
# 检查该PID是否在ps命令输出中
if ! echo "$ps_pids" | grep -q "^${proc_pid}$"; then
# 验证/proc/PID目录确实存在且可访问
if [ -d "/proc/$proc_pid" ] && [ -r "/proc/$proc_pid/stat" ]; then
# 尝试读取进程信息
if [ -r "/proc/$proc_pid/comm" ]; then
proc_name=$(cat "/proc/$proc_pid/comm" 2>/dev/null || echo "unknown")
else
proc_name="unknown"
fi
if [ -r "/proc/$proc_pid/cmdline" ]; then
proc_cmdline=$(cat "/proc/$proc_pid/cmdline" 2>/dev/null | tr '\0' ' ' || echo "unknown")
else
proc_cmdline="unknown"
fi
# 获取进程状态
if [ -r "/proc/$proc_pid/stat" ]; then
proc_stat=$(cat "/proc/$proc_pid/stat" 2>/dev/null | awk '{print $3}' || echo "unknown")
else
proc_stat="unknown"
fi
# 获取进程启动时间
if [ -r "/proc/$proc_pid" ]; then
proc_start_time=$(stat -c %Y "/proc/$proc_pid" 2>/dev/null || echo "unknown")
if [ "$proc_start_time" != "unknown" ]; then
proc_start_time=$(date -d @$proc_start_time 2>/dev/null || echo "unknown")
fi
else
proc_start_time="unknown"
fi
anomalous_processes+=("PID:$proc_pid | Name:$proc_name | State:$proc_stat | StartTime:$proc_start_time | Cmdline:$proc_cmdline")
fi
fi
done
# 输出异常进程结果
if [ ${#anomalous_processes[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#anomalous_processes[@]} 个异常进程(存在于/proc但不在ps中显示):${NC}"
for anomalous in "${anomalous_processes[@]}"; do
echo -e "${RED}[WARN] $anomalous${NC}"
done
echo -e "${RED}[WARN] 建议进一步调查这些进程,可能存在进程隐藏${NC}"
log_message "WARN" "发现${#anomalous_processes[@]}个异常进程"
else
echo -e "${GREEN}[SUCC] 未发现异常进程,所有/proc中的进程都能在ps命令中找到${NC}"
log_message "INFO" "未发现异常进程,所有/proc中的进程都能在ps命令中找到"
fi
printf "\n"
# 高级进程隐藏检测技术
echo -e "${YELLOW}[3.8] 执行高级进程隐藏检测:${NC}"
log_message "INFO" "开始高级进程隐藏检测"
# 1. 检查进程树完整性
echo -e "${YELLOW}[3.8.1] 检查进程树完整性(孤儿进程检测):${NC}"
log_message "INFO" "开始进程树完整性检查"
orphan_processes=()
ps_tree_output=$(ps -eo pid,ppid 2>/dev/null | tail -n +2) || handle_error 1 "获取进程树信息失败" "processInfo"
while IFS= read -r line; do
# 使用更精确的字段提取,处理不同系统的ps输出格式
pid=$(echo "$line" | awk '{print $1}')
ppid=$(echo "$line" | awk '{print $2}')
# 验证PID和PPID都是数字
if [[ "$pid" =~ ^[0-9]+$ ]] && [[ "$ppid" =~ ^[0-9]+$ ]]; then
# 检查父进程是否存在(除了init进程和内核线程)
if [ "$ppid" != "0" ] && [ "$ppid" != "1" ] && [ "$ppid" != "2" ]; then
if ! ps -p "$ppid" > /dev/null 2>&1; then
orphan_processes+=("PID:$pid PPID:$ppid (父进程不存在)")
fi
fi
fi
done <<< "$ps_tree_output"
if [ ${#orphan_processes[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#orphan_processes[@]} 个可疑孤儿进程:${NC}"
for orphan in "${orphan_processes[@]}"; do
echo -e "${RED}[WARN] $orphan${NC}"
done
log_message "WARN" "发现${#orphan_processes[@]}个可疑孤儿进程"
else
echo -e "${GREEN}[SUCC] 进程树完整性检查通过${NC}"
log_message "INFO" "进程树完整性检查通过"
fi
printf "\n"
# 2. 检查网络连接与进程对应关系
echo -e "${YELLOW}[3.8.2] 检查网络连接与进程对应关系:${NC}"
log_message "INFO" "开始检查网络连接与进程对应关系"
unknown_connections=()
# 检测操作系统类型并使用相应的命令
if [[ "$(uname)" == "Darwin" ]]; then # macOS
# macOS系统使用lsof命令
if command -v lsof > /dev/null 2>&1; then
lsof_output=$(lsof -i -n -P 2>/dev/null | tail -n +2) || handle_error 1 "lsof命令执行失败" "processInfo"
while IFS= read -r line; do
# lsof输出格式: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
if echo "$line" | grep -E "(TCP|UDP)" > /dev/null; then
pid=$(echo "$line" | awk '{print $2}')
# 验证PID是数字且检查进程是否存在
if [[ "$pid" =~ ^[0-9]+$ ]] && ! ps -p "$pid" > /dev/null 2>&1; then
proc_name=$(echo "$line" | awk '{print $1}')
unknown_connections+=("连接: $line (进程PID:$pid Name:$proc_name 不存在)")
fi
fi
done <<< "$lsof_output"
else
echo -e "${YELLOW}[INFO] macOS系统未找到lsof命令,跳过网络连接检查${NC}"
log_message "WARN" "macOS系统未找到lsof命令,跳过网络连接检查"
fi
else
# Linux系统使用netstat或ss命令
if command -v netstat > /dev/null 2>&1; then
netstat_output=$(netstat -tulnp 2>/dev/null | grep -v '^Active') || handle_error 1 "netstat命令执行失败" "processInfo"
while IFS= read -r line; do
if echo "$line" | grep -q "/"; then
pid_info=$(echo "$line" | awk '{print $NF}')
pid=$(echo "$pid_info" | cut -d'/' -f1)
if [ "$pid" != "-" ] && [[ "$pid" =~ ^[0-9]+$ ]] && ! ps -p "$pid" > /dev/null 2>&1; then
unknown_connections+=("连接: $line (进程PID:$pid 不存在)")
fi
fi
done <<< "$netstat_output"
else
# 使用ss命令作为备选
if command -v ss > /dev/null 2>&1; then
ss_output=$(ss -tulnp 2>/dev/null) || handle_error 1 "ss命令执行失败" "processInfo"
while IFS= read -r line; do
if echo "$line" | grep -q "pid="; then
pid=$(echo "$line" | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
if [ -n "$pid" ] && [[ "$pid" =~ ^[0-9]+$ ]] && ! ps -p "$pid" > /dev/null 2>&1; then
unknown_connections+=("连接: $line (进程PID:$pid 不存在)")
fi
fi
done <<< "$ss_output"
else
echo -e "${RED}[WARN] Linux系统未找到netstat或ss命令,跳过网络连接检查${NC}"
log_message "WARN" "Linux系统未找到netstat或ss命令,跳过网络连接检查"
fi
fi
fi
if [ ${#unknown_connections[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#unknown_connections[@]} 个可疑网络连接:${NC}"
for conn in "${unknown_connections[@]}"; do
echo -e "${RED}[WARN] $conn${NC}"
done
log_message "WARN" "发现${#unknown_connections[@]}个可疑网络连接"
else
echo -e "${GREEN}[SUCC] 网络连接与进程对应关系检查通过${NC}"
log_message "INFO" "网络连接与进程对应关系检查通过"
fi
printf "\n"
# 3. 检查进程内存映射异常
echo -e "${YELLOW}[3.8.3] 检查进程内存映射异常:${NC}"
log_message "INFO" "开始进程内存映射异常检查"
suspicious_maps=() # 存储可疑内存映射
for proc_dir in /proc/[0-9]*; do
if [ -d "$proc_dir" ] && [ -r "$proc_dir/maps" ]; then # 检查进程目录是否存在和maps文件是否可读
pid=$(basename "$proc_dir")
# 检查是否有可疑的内存映射(如可执行的匿名映射)
## 原理: 通过grep命令匹配maps文件中的rwxp权限的行,并判断是否包含[heap]或[stack]或deleted
## rwxp.*\[heap\]: 堆区域具有读写执行权限(异常|正常堆不应该具有可执行权限,只有 rw-)
## rwxp.*\[stack\]: 栈区域具有读写执行权限(异常|正常栈栈不应该具有可执行权限,只有 rw- 可能是栈溢出攻击,或者 shellcode 直接执行机器码)
## rwxp.*deleted: 指向已经删除的文件的可执行内存映射(异常|内存马或者恶意代码)
## 恶意软件删除自身文件但保持在内存中运行
## 无文件攻击的检测 和 rootkit隐藏技术发现
# 判断进程是否仍然存在
if ! ps -p "$pid" >/dev/null; then
handle_error 0 "进程 ${pid} 已不存在" "processInfo"
continue # 跳过当前循环
fi
# suspicious_map=$(grep -E "(rwxp.*\[heap\]|rwxp.*\[stack\]|rwxp.*deleted)" "$proc_dir/maps" 2>/dev/null) || handle_error 0 "读取进程 ${pid} 内存映射失败" "processInfo"
# 读取内存映射文件内容(防止多次读取)
map_content=$(cat "$proc_dir/maps" 2>/dev/null)
# 判断是否读取失败(如文件不存在、权限不足)
if [ $? -ne 0 ]; then
handle_error 0 "读取进程 ${pid} 内存映射失败" "processInfo"
continue # 跳过当前进程
fi
# 执行 grep 匹配可疑映射(即使没有匹配内容也不报错)
suspicious_map=$(echo "$map_content" | grep -E "(rwxp.*$$heap$$|rwxp.*$$stack$$|rwxp.*deleted)")
# 判断是否有匹配内
if [ -n "$suspicious_map" ]; then
proc_name=$(cat "$proc_dir/comm" 2>/dev/null || echo "unknown")
suspicious_maps+=("PID:$pid Name:$proc_name 可疑内存映射")
fi
fi
done
if [ ${#suspicious_maps[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#suspicious_maps[@]} 个进程存在可疑内存映射:${NC}"
for map in "${suspicious_maps[@]}"; do
echo -e "${RED}[WARN] $map${NC}"
done
log_message "WARN" "发现${#suspicious_maps[@]}个进程存在可疑内存映射"
else
echo -e "${GREEN}[SUCC] 进程内存映射检查通过${NC}"
log_message "INFO" "进程内存映射检查通过"
fi
printf "\n"
# 4. 检查进程文件描述符异常[(deleted)]
echo -e "${YELLOW}[3.8.4] 检查进程文件描述符异常[(deleted)]:${NC}"
log_message "INFO" "开始进程文件描述符异常检查"
suspicious_fds=() # 用于存储异常文件描述符的数组
for proc_dir in /proc/[0-9]*; do
if [ -d "$proc_dir/fd" ] && [ -r "$proc_dir/fd" ]; then
pid=$(basename "$proc_dir")
# 检查是否有指向已删除文件的文件描述符
deleted_files=$(ls -l "$proc_dir/fd/" 2>/dev/null | grep "(deleted)" | wc -l) || handle_error 0 "读取进程 ${pid} 文件描述符失败" "processInfo"
if [ "$deleted_files" -gt 0 ]; then
proc_name=$(cat "$proc_dir/comm" 2>/dev/null || echo "unknown")
suspicious_fds+=("PID:$pid Name:$proc_name 有${deleted_files}个已删除文件的文件描述符")
# 输出fd是(deleted)的进程pid和进程名
# 检测恶意进程删除自身进程然后在内存里驻留
fi
fi
done
if [ ${#suspicious_fds[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#suspicious_fds[@]} 个进程存在可疑文件描述符[(deleted)]:${NC}"
for fd in "${suspicious_fds[@]}"; do
echo -e "${RED}[WARN] $fd${NC}"
done
log_message "WARN" "发现${#suspicious_fds[@]}个进程存在可疑文件描述符"
else
echo -e "${GREEN}[SUCC] 进程文件描述符检查通过${NC}"
log_message "INFO" "进程文件描述符检查通过"
fi
printf "\n"
# 5. 检查系统调用表完整性(需要root权限)
echo -e "${YELLOW}[3.8.5] 检查系统调用表完整性[sys_call_table(/proc/kallsyms)]:${NC}"
log_message "INFO" "开始系统调用表完整性检查"
## 原理: 通过查看系统调用表,判断系统调用表是否被修改[rootkit检测和内核级模块检测常用的技术]
# 什么是系统调用表(sys_call_table)
# - 定义 :Linux内核中存储所有系统调用函数指针的数组
# - 作用 :当用户程序调用系统调用时,内核通过这个表找到对应的处理函数
# - 位置 :位于内核内存空间,通过 /proc/kallsyms 可以查看其地址
# 检测系统表的意义:
# 1. Rootkit检测
# 系统调用表劫持 是rootkit的常用技术:
# - 正常情况 : sys_call_table 符号在 /proc/kallsyms 中可见
# - 被攻击 :rootkit可能隐藏或修改这个符号来逃避检测
# 2. 内核级恶意模块检测
# 通过搜索可疑符号名称,可以发现:
# - 恶意内核模块 :包含 "rootkit"、"hide" 等字样的符号
# - Hook技术 :用于拦截和修改系统调用的钩子函数
# - 隐蔽功能 :用于隐藏进程、文件、网络连接的功能
if [ "$(id -u)" -eq 0 ]; then
if [ -r "/proc/kallsyms" ]; then
# 检查sys_call_table符号是否存在
sys_call_table=$(grep "sys_call_table" /proc/kallsyms 2>/dev/null) || handle_error 0 "读取/proc/kallsyms失败" "processInfo"
if [ -n "$sys_call_table" ]; then
echo -e "${YELLOW}[INFO] 系统调用表符号存在: $sys_call_table ${NC}"
log_message "INFO" "系统调用表符号存在"
else
echo -e "${RED}[WARN] 警告: 无法找到sys_call_table符号,可能被隐藏${NC}"
log_message "WARN" "无法找到sys_call_table符号,可能被隐藏"
fi
# 检查可疑的内核符号[过滤可能恶意的符号(自定义)]
suspicious_symbols=$(grep -E "(hide|rootkit|stealth|hook)" /proc/kallsyms 2>/dev/null) || handle_error 0 "搜索可疑内核符号失败" "processInfo"
if [ -n "$suspicious_symbols" ]; then
echo -e "${RED}[WARN] 发现可疑内核符号:${NC}"
echo "$suspicious_symbols"
log_message "WARN" "发现可疑内核符号"
else
echo -e "${GREEN}[SUCC] 未发现可疑内核符号${NC}"
log_message "INFO" "未发现可疑内核符号"
fi
else
echo -e "${YELLOW}[INFO] /proc/kallsyms不可读,跳过系统调用表检查${NC}"
log_message "WARN" "/proc/kallsyms不可读,跳过系统调用表检查"
fi
else
echo -e "${RED}[WARN] 需要root权限进行系统调用表检查${NC}"
log_message "WARN" "需要root权限进行系统调用表检查"
fi
printf "\n"
# 6. 检查进程启动时间异常
echo -e "${YELLOW}[3.8.6] 检查进程启动时间异常:${NC}"
log_message "INFO" "开始进程启动时间异常检查"
time_anomalies=()
current_time=$(date +%s) || handle_error 1 "获取当前时间失败" "processInfo"
ps_output=$(ps -eo pid,lstart --no-headers 2>/dev/null) || handle_error 1 "获取进程启动时间失败" "processInfo"
while IFS= read -r line; do
pid=$(echo "$line" | awk '{print $1}')
start_time=$(echo "$line" | awk '{print $2}')
# 检查启动时间是否在未来(可能的时间篡改)
if [ "$start_time" -gt "$current_time" ]; then
proc_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
time_anomalies+=("PID:$pid Name:$proc_name 启动时间异常(未来时间)")
fi
done <<< "$(echo "$ps_output" | while read -r pid lstart_str; do echo "$pid $(date -d \"$lstart_str\" +%s 2>/dev/null || echo 0)"; done)"
if [ ${#time_anomalies[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#time_anomalies[@]} 个进程启动时间异常:${NC}"
for anomaly in "${time_anomalies[@]}"; do
echo -e "${RED}[WARN] $anomaly${NC}"
done
log_message "WARN" "发现${#time_anomalies[@]}个进程启动时间异常"
else
echo -e "${GREEN}[SUCC] 进程启动时间检查通过${NC}"
log_message "INFO" "进程启动时间检查通过"
fi
printf "\n"
# 7. 检查进程环境变量异常
# 这段代码通过以下机制检测潜在威胁:
# 1. LD_PRELOAD检测 :这是最常见的rootkit技术,通过预加载恶意库来劫持系统调用
# 2. 动态库路径检测 :异常的LD_LIBRARY_PATH设置可能指向恶意库
# 3. 明显恶意标识 :直接搜索ROOTKIT、HIDE等明显的恶意软件标识
echo -e "${YELLOW}[3.8.7] 检查进程环境变量异常:${NC}"
log_message "INFO" "开始进程环境变量异常检查"
env_anomalies=()
for proc_dir in /proc/[0-9]*; do
if [ -r "$proc_dir/environ" ]; then
pid=$(basename "$proc_dir")
# 判断进程是否仍然存在
if ! ps -p "$pid" >/dev/null; then
handle_error 0 "进程 ${pid} 已不存在" "processInfo"
continue # 跳过当前循环
fi
# 检查可疑的环境变量 读取 environ 文件
# suspicious_env=$(tr '\0' '\n' < "$proc_dir/environ" 2>/dev/null | grep -E "(LD_PRELOAD|LD_LIBRARY_PATH.*\.so|ROOTKIT|HIDE)" 2>/dev/null) || handle_error 0 "读取进程 ${pid} 环境变量失败" "processInfo"
# 先读取环境变量内容并转换为换行分隔
env_content=$(tr '\0' '\n' < "$proc_dir/environ" 2>/dev/null)
# 判断 tr 是否失败(比如文件不存在、权限问题)
if [ $? -ne 0 ]; then
handle_error 0 "读取进程 ${pid} 环境变量失败" "processInfo"
continue # 跳过当前循环
fi
# 然后执行 grep,不管有没有匹配内容,都不触发错误
suspicious_env=$(echo "$env_content" | grep -E "(LD_PRELOAD|LD_LIBRARY_PATH.*\.so|ROOTKIT|HIDE)")
# 判断是否有匹配内容
if [ -n "$suspicious_env" ]; then
proc_name=$(cat "$proc_dir/comm" 2>/dev/null || echo "unknown")
env_anomalies+=("PID:$pid Name:$proc_name 可疑环境变量: $(echo \"$suspicious_env\" | head -1)")
fi
fi
done
if [ ${#env_anomalies[@]} -gt 0 ]; then
echo -e "${RED}[WARN] 发现 ${#env_anomalies[@]} 个进程存在可疑环境变量:${NC}"
for env in "${env_anomalies[@]}"; do
echo -e "${RED}[WARN] $env${NC}"
done
log_message "WARN" "发现${#env_anomalies[@]}个进程存在可疑环境变量"
else
echo -e "${GREEN}[SUCC] 进程环境变量检查通过${NC}"
log_message "INFO" "进程环境变量检查通过"
fi
printf "\n"
# 记录性能和操作日志
local end_time=$(date +%s)
log_performance "processInfo" "$start_time" "$end_time"
log_operation "MOUDLE - PROCESSINFO" "进程信息分析和安全检测完成" "END"
}
本文档基于项目 LinuxGun 中 linuxGun[Pro].sh中的 processInfo 函数分析编写,详细说明了Linux应急响应中进程检测的技术原理和实现方法。
by 久违(Sun977)