pidinfo根据进程ID获取进程信息

admin 2026-05-14 13:18:09 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍了一个名为pidinfo的跨平台进程信息获取工具,提供了Shell和Python两个版本。该工具能够获取进程的基本信息(如PID、PPID、用户、CPU/内存使用率、状态、启动时间、命令行等)、工作目录、可执行文件路径以及线程数。Shell版本存在依赖bc命令的兼容性问题,Python版本通过重写解决了该问题,并增加了在Linux下显示进程网络连接信息的功能。 综合评分: 65 文章分类: 安全工具,其他


cover_image

pidinfo 根据进程 ID 获取进程信息

原创

hyang0 hyang0

生有可恋

2026年5月13日 05:17 湖北

在小说阅读器读本章

去阅读

之前写了一个 Shell 版本的,效果如下:

Shell 版脚本兼容性不好,有些系统中没有 bc 命令,在计算内存用量时会报错。脚本如下:

cat bin/pidinfo.sh#!/bin/bash
# pidinfo - Get process information (works on macOS and Linux)# Usage: pidinfo <pid># - On Linux: uses /proc filesystem# - On macOS: uses ps command
pidinfo() {&nbsp; &nbsp;&nbsp;if&nbsp;[&nbsp;$#&nbsp;-ne 1 ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Usage: pidinfo <pid>"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Get detailed process information"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;local&nbsp;pid=$1
&nbsp; &nbsp;&nbsp;# Check if process exists&nbsp; &nbsp;&nbsp;if&nbsp;! ps -p&nbsp;"$pid"&nbsp;> /dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Error: Process&nbsp;$pid&nbsp;does not exist"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;echo&nbsp;"========================================"&nbsp; &nbsp;&nbsp;echo&nbsp;"Process Information for PID:&nbsp;$pid"&nbsp; &nbsp;&nbsp;echo&nbsp;"========================================"
&nbsp; &nbsp;&nbsp;# Get basic info with ps&nbsp; &nbsp;&nbsp;local&nbsp;user=$(ps -o user= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;local&nbsp;ppid=$(ps -o ppid= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;local&nbsp;command=$(ps -o&nbsp;command= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;# macOS uses lstart, Linux uses started&nbsp; &nbsp;&nbsp;if&nbsp;ps -o started= -p $$ >/dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;started=$(ps -o started= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;else&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;started=$(ps -o lstart= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;local&nbsp;mem=$(ps -o rss= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;local&nbsp;pcpu=$(ps -o %cpu= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')&nbsp; &nbsp;&nbsp;local&nbsp;state=$(ps -o state= -p&nbsp;"$pid"&nbsp;| sed&nbsp;'s/^ *//; s/ *$//')
&nbsp; &nbsp;&nbsp;echo&nbsp;"PID: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$pid"&nbsp; &nbsp;&nbsp;echo&nbsp;"Parent PID: &nbsp; &nbsp; &nbsp; &nbsp;$ppid"&nbsp; &nbsp;&nbsp;echo&nbsp;"Executing User: &nbsp; &nbsp;$user"&nbsp; &nbsp;&nbsp;echo&nbsp;"Process State: &nbsp; &nbsp;&nbsp;$state"&nbsp; &nbsp;&nbsp;echo&nbsp;"Started: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$started"
&nbsp; &nbsp;&nbsp;# Convert RSS KB to human readable&nbsp; &nbsp;&nbsp;if&nbsp;[ -n&nbsp;"$mem"&nbsp;] && [&nbsp;"$mem"&nbsp;-gt 0 ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;[&nbsp;"$mem"&nbsp;-gt 1048576 ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;mem_human=$(echo&nbsp;"scale=2;&nbsp;$mem&nbsp;/ 1048576"&nbsp;| bc) GB&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;elif&nbsp;[&nbsp;"$mem"&nbsp;-gt 1024 ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;mem_human=$(echo&nbsp;"scale=2;&nbsp;$mem&nbsp;/ 1024"&nbsp;| bc) MB&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;mem_human="$mem&nbsp;KB"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"RSS Memory: &nbsp; &nbsp; &nbsp; &nbsp;$mem_human"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"CPU Usage: &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$pcpu%"&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;# Get working directory (different methods)&nbsp; &nbsp;&nbsp;if&nbsp;[ -d /proc/$pid/cwd ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# Linux /proc&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;cwd=$(readlink&nbsp;/proc/$pid/cwd 2>/dev/null)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Working Directory:&nbsp;$cwd"&nbsp; &nbsp;&nbsp;elif&nbsp;command&nbsp;-v lsof >/dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# macOS with lsof&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;cwd=$(lsof -p&nbsp;"$pid"&nbsp;-a -d cwd 2>/dev/null | grep&nbsp;'cwd'&nbsp;| awk&nbsp;'NR==1{print $9}')&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;[ -n&nbsp;"$cwd"&nbsp;];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Working Directory:&nbsp;$cwd"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;# Get executable path&nbsp; &nbsp;&nbsp;if&nbsp;[ -L /proc/$pid/exe ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# Linux /proc&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;exe=$(readlink&nbsp;/proc/$pid/exe 2>/dev/null)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Executable Path: &nbsp;&nbsp;$exe"&nbsp; &nbsp;&nbsp;elif&nbsp;command&nbsp;-v lsof >/dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# macOS with lsof&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;exe=$(lsof -p&nbsp;"$pid"&nbsp;2>/dev/null | grep&nbsp;'txt'&nbsp;| awk&nbsp;'NR==1{print $9}')&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;[ -n&nbsp;"$exe"&nbsp;];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Executable Path: &nbsp;&nbsp;$exe"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;echo&nbsp;"Command Line: &nbsp; &nbsp; &nbsp;$command"
&nbsp; &nbsp;&nbsp;# Get thread count&nbsp; &nbsp;&nbsp;if&nbsp;[ -f /proc/$pid/status ];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# Linux&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;threads=$(grep -E&nbsp;"^Threads:"&nbsp;/proc/$pid/status | awk&nbsp;'{print $2}')&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Thread Count: &nbsp; &nbsp; &nbsp;$threads"&nbsp; &nbsp;&nbsp;elif&nbsp;command&nbsp;-v ps >/dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# macOS: mach factor can get threads via sysctl&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;command&nbsp;-v sysctl >/dev/null 2>&1;&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;local&nbsp;threads=$(sysctl -n machdep.task_info.pid&nbsp;"$pid".num_threads 2>/dev/null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;[ -n&nbsp;"$threads"&nbsp;] && [&nbsp;"$threads"&nbsp;!=&nbsp;"0"&nbsp;];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;echo&nbsp;"Thread Count: &nbsp; &nbsp; &nbsp;$threads"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;fi
&nbsp; &nbsp;&nbsp;echo&nbsp;"========================================"}
# If this script is executed directly instead of sourced, run the functionif&nbsp;[[&nbsp;"${BASH_SOURCE[0]}"&nbsp;==&nbsp;"${0}"&nbsp;]];&nbsp;then&nbsp; &nbsp; pidinfo&nbsp;"$@"fi

将脚本丢给 AI 让它重写一个 Python 版的,并加入进程打开的网络连接信息。

Python 版效果如下:

重写后的 Python 代码为:

#!/usr/bin/env python3"""pidinfo.py - 获取进程详细信息(支持 Linux / macOS)用法: python pidinfo.py <pid>
Linux 下额外显示该进程自身打开的 TCP 连接(通过 /proc 文件系统)macOS 下仅显示基础信息(不显示 TCP 连接,因为缺少 /proc)"""
import&nbsp;osimport&nbsp;sysimport&nbsp;globimport&nbsp;subprocessimport&nbsp;timeimport&nbsp;socketimport&nbsp;structfrom&nbsp;ipaddress&nbsp;import&nbsp;ip_addressfrom&nbsp;datetime&nbsp;import&nbsp;datetime
# ================== 通用工具函数 ==================
def&nbsp;get_process_info(pid):&nbsp; &nbsp;&nbsp;"""返回进程基本信息字典,失败返回 None"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 使用 ps 命令获取通用信息(跨平台)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 格式: pid, ppid, user, %cpu, rss, state, lstart, command&nbsp; &nbsp; &nbsp; &nbsp; cmd = ['ps',&nbsp;'-p',&nbsp;str(pid),&nbsp;'-o',&nbsp;'pid=',&nbsp;'-o',&nbsp;'ppid=',&nbsp;'-o',&nbsp;'user=',&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-o',&nbsp;'%cpu=',&nbsp;'-o',&nbsp;'rss=',&nbsp;'-o',&nbsp;'state=',&nbsp;'-o',&nbsp;'lstart=',&nbsp;'-o',&nbsp;'command=']&nbsp; &nbsp; &nbsp; &nbsp; output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True).strip()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;output:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp; parts = output.split(None,&nbsp;8) &nbsp;# 最多9部分,command 可能包含空格&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;len(parts) <&nbsp;9:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 解析字段&nbsp; &nbsp; &nbsp; &nbsp; info = {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'pid':&nbsp;int(parts[0]),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'ppid':&nbsp;int(parts[1]),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'user': parts[2],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'cpu':&nbsp;float(parts[3]),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'rss_kb':&nbsp;int(parts[4]),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'state': parts[5],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'started':&nbsp;' '.join(parts[6:8]), &nbsp;# lstart 通常是两部分日期时间&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'command': parts[8]&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;info&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_thread_count_linux(pid):&nbsp; &nbsp;&nbsp;"""Linux 下读取 /proc/pid/status 中的线程数"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;with&nbsp;open(f'/proc/{pid}/status',&nbsp;'r')&nbsp;as&nbsp;f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;line.startswith('Threads:'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;int(line.split()[1])&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass&nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_thread_count_macos(pid):&nbsp; &nbsp;&nbsp;"""macOS 下通过 sysctl 获取线程数(需要 root 或合适权限)"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; out = subprocess.check_output(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ['sysctl',&nbsp;'-n',&nbsp;f'machdep.task_info.pid.{pid}.num_threads'],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stderr=subprocess.DEVNULL, text=True&nbsp; &nbsp; &nbsp; &nbsp; ).strip()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;int(out)&nbsp;if&nbsp;out&nbsp;else&nbsp;None&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_cwd_linux(pid):&nbsp; &nbsp;&nbsp;"""Linux 下读取 /proc/pid/cwd 符号链接"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;os.readlink(f'/proc/{pid}/cwd')&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_cwd_macos(pid):&nbsp; &nbsp;&nbsp;"""macOS 下使用 lsof 获取工作目录(需要安装 lsof)"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; out = subprocess.check_output(['lsof',&nbsp;'-p',&nbsp;str(pid),&nbsp;'-a',&nbsp;'-d',&nbsp;'cwd',&nbsp;'-Fn'],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stderr=subprocess.DEVNULL, text=True)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;out.split('\n'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;line.startswith('n/'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;line[1:] &nbsp;# 去掉 'n' 前缀&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass&nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_exe_linux(pid):&nbsp; &nbsp;&nbsp;"""Linux 下读取 /proc/pid/exe 符号链接"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;os.readlink(f'/proc/{pid}/exe')&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;get_exe_macos(pid):&nbsp; &nbsp;&nbsp;"""macOS 下使用 lsof 获取可执行文件路径"""&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; out = subprocess.check_output(['lsof',&nbsp;'-p',&nbsp;str(pid),&nbsp;'-Fn'],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stderr=subprocess.DEVNULL, text=True)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;out.split('\n'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 可执行文件通常标记为 'n' 且路径不含 'txt' 区分,简单取第一个非 fd 路径&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;line.startswith('n/')&nbsp;and&nbsp;' (deleted)'&nbsp;not&nbsp;in&nbsp;line:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;line[1:]&nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass&nbsp; &nbsp;&nbsp;return&nbsp;None
def&nbsp;human_readable_size(kb):&nbsp; &nbsp;&nbsp;"""将 KB 转换为人类可读格式"""&nbsp; &nbsp;&nbsp;if&nbsp;kb >=&nbsp;1024&nbsp;*&nbsp;1024:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;f"{kb / (1024&nbsp;*&nbsp;1024):.2f}&nbsp;GB"&nbsp; &nbsp;&nbsp;elif&nbsp;kb >=&nbsp;1024:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;f"{kb /&nbsp;1024:.2f}&nbsp;MB"&nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;f"{kb}&nbsp;KB"
# ================== TCP 连接相关函数(仅 Linux) ==================
TCP_STATES = {&nbsp; &nbsp;&nbsp;'01':&nbsp;'ESTABLISHED',&nbsp;'02':&nbsp;'SYN_SENT',&nbsp;'03':&nbsp;'SYN_RECV',&nbsp; &nbsp;&nbsp;'04':&nbsp;'FIN_WAIT1',&nbsp;'05':&nbsp;'FIN_WAIT2',&nbsp;'06':&nbsp;'TIME_WAIT',&nbsp; &nbsp;&nbsp;'07':&nbsp;'CLOSE',&nbsp;'08':&nbsp;'CLOSE_WAIT',&nbsp;'09':&nbsp;'LAST_ACK',&nbsp; &nbsp;&nbsp;'0A':&nbsp;'LISTEN',&nbsp;'0B':&nbsp;'CLOSING',&nbsp;'0C':&nbsp;'NEW_SYN_RECV'}
def&nbsp;parse_ipv4_hex(hex_str):&nbsp; &nbsp;&nbsp;"""小端序 IPv4 十六进制 -> 点分十进制"""&nbsp; &nbsp; octets = [hex_str[i:i+2]&nbsp;for&nbsp;i&nbsp;in&nbsp;range(0,&nbsp;len(hex_str),&nbsp;2)]&nbsp; &nbsp; octets.reverse()&nbsp; &nbsp; ip_int =&nbsp;int(''.join(octets),&nbsp;16)&nbsp; &nbsp;&nbsp;return&nbsp;socket.inet_ntoa(struct.pack('!I', ip_int))
def&nbsp;parse_ipv6_hex(hex_str):&nbsp; &nbsp;&nbsp;"""小端序 IPv6 十六进制 -> 标准 IPv6 地址"""&nbsp; &nbsp;&nbsp;# 每4个字符一组(两个字节),反转顺序&nbsp; &nbsp; groups = [hex_str[i:i+4]&nbsp;for&nbsp;i&nbsp;in&nbsp;range(0,&nbsp;len(hex_str),&nbsp;4)]&nbsp; &nbsp; groups.reverse()&nbsp; &nbsp; hex_str_nbo =&nbsp;''.join(groups)&nbsp; &nbsp; addr_bytes =&nbsp;bytes.fromhex(hex_str_nbo)&nbsp; &nbsp;&nbsp;return&nbsp;str(ip_address(addr_bytes))
def&nbsp;parse_address_port(addr_port_str):&nbsp; &nbsp;&nbsp;"""解析 'IP:PORT' 十六进制字符串 -> (ip_str, port_int)"""&nbsp; &nbsp; addr_hex, port_hex = addr_port_str.split(':')&nbsp; &nbsp; port =&nbsp;int(port_hex,&nbsp;16)&nbsp; &nbsp;&nbsp;if&nbsp;len(addr_hex) ==&nbsp;8: &nbsp; &nbsp; &nbsp;# IPv4&nbsp; &nbsp; &nbsp; &nbsp; ip = parse_ipv4_hex(addr_hex)&nbsp; &nbsp;&nbsp;elif&nbsp;len(addr_hex) ==&nbsp;32: &nbsp;&nbsp;# IPv6&nbsp; &nbsp; &nbsp; &nbsp; ip = parse_ipv6_hex(addr_hex)&nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;ValueError(f"未知地址格式:&nbsp;{addr_hex}")&nbsp; &nbsp;&nbsp;return&nbsp;ip, port
def&nbsp;get_process_socket_inodes(pid):&nbsp; &nbsp;&nbsp;"""返回该进程持有的所有 socket inode 集合"""&nbsp; &nbsp; fd_dir =&nbsp;f'/proc/{pid}/fd'&nbsp; &nbsp; inodes =&nbsp;set()&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;os.path.isdir(fd_dir):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;inodes&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;fd_name&nbsp;in&nbsp;os.listdir(fd_dir):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fd_path = os.path.join(fd_dir, fd_name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; link = os.readlink(fd_path)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;(OSError, PermissionError):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 链接格式: socket:[12345678]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;link.startswith('socket:[')&nbsp;and&nbsp;link.endswith(']'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inode_str = link[8:-1]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;inode_str.isdigit():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inodes.add(int(inode_str))&nbsp; &nbsp;&nbsp;except&nbsp;PermissionError:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"警告: 没有权限读取&nbsp;{fd_dir},请使用 root 运行以获得精确 TCP 连接信息", file=sys.stderr)&nbsp; &nbsp;&nbsp;return&nbsp;inodes
def&nbsp;get_tcp_connections_linux(pid, valid_inodes):&nbsp; &nbsp;&nbsp;"""从 /proc/pid/net/tcp 和 tcp6 中过滤出属于该进程的 TCP 连接"""&nbsp; &nbsp; connections = []&nbsp; &nbsp;&nbsp;for&nbsp;proto&nbsp;in&nbsp;['tcp',&nbsp;'tcp6']:&nbsp; &nbsp; &nbsp; &nbsp; net_path =&nbsp;f'/proc/{pid}/net/{proto}'&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;os.path.exists(net_path):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;with&nbsp;open(net_path,&nbsp;'r')&nbsp;as&nbsp;f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lines = f.readlines()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;PermissionError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"警告: 无法读取&nbsp;{net_path},请使用 root 运行", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"警告: 读取&nbsp;{net_path}&nbsp;时出错:&nbsp;{e}", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;lines[1:]: &nbsp;# 跳过标题行&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts = line.split()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;len(parts) <&nbsp;10:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 字段索引: sl, local_addr, rem_addr, st, tx_queue:rx_queue, tr:tm->when, retrnsmt, uid, timeout, inode, ...&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; local_addr = parts[1]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rem_addr = parts[2]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; st_hex = parts[3]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uid = parts[7]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inode_str = parts[9]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inode_int =&nbsp;int(inode_str)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;ValueError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;inode_int&nbsp;not&nbsp;in&nbsp;valid_inodes:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; local_ip, local_port = parse_address_port(local_addr)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; remote_ip, remote_port = parse_address_port(rem_addr)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; local_ip, local_port = local_addr,&nbsp;'解析失败'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; remote_ip, remote_port = rem_addr,&nbsp;'解析失败'
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state = TCP_STATES.get(st_hex.upper(), st_hex)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; connections.append({&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'protocol': proto.upper(),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'local':&nbsp;f"{local_ip}:{local_port}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'remote':&nbsp;f"{remote_ip}:{remote_port}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'state': state,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'uid': uid,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'inode': inode_int&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp;&nbsp;return&nbsp;connections
def&nbsp;print_tcp_connections(pid):&nbsp; &nbsp;&nbsp;"""打印进程自身打开的 TCP 连接(仅 Linux)"""&nbsp; &nbsp;&nbsp;if&nbsp;sys.platform !=&nbsp;'linux':&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp; &nbsp; socket_inodes = get_process_socket_inodes(pid)&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;socket_inodes:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("\n无法获取该进程的 TCP 连接信息(进程没有 socket 或权限不足)")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp; &nbsp; conns = get_tcp_connections_linux(pid, socket_inodes)&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;conns:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("\n该进程没有打开任何 TCP 连接")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp; &nbsp;&nbsp;print("\n--- TCP 连接(该进程自身打开) ---")&nbsp; &nbsp; header =&nbsp;f"{'Proto':<6}&nbsp;{'Local Address':<30}&nbsp;{'Remote Address':<30}&nbsp;{'State':<12}&nbsp;{'UID':<6}&nbsp;{'Inode'}"&nbsp; &nbsp;&nbsp;print(header)&nbsp; &nbsp;&nbsp;print("-"&nbsp;*&nbsp;len(header))&nbsp; &nbsp;&nbsp;for&nbsp;c&nbsp;in&nbsp;conns:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"{c['protocol']:<6}&nbsp;{c['local']:<30}&nbsp;{c['remote']:<30}&nbsp;{c['state']:<12}&nbsp;{c['uid']:<6}&nbsp;{c['inode']}")
# ================== 主函数 ==================
def&nbsp;main():&nbsp; &nbsp;&nbsp;if&nbsp;len(sys.argv) !=&nbsp;2:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"用法:&nbsp;{sys.argv[0]}&nbsp;<pid>", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; pid =&nbsp;int(sys.argv[1])&nbsp; &nbsp;&nbsp;except&nbsp;ValueError:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("错误: PID 必须是整数", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp;&nbsp;# 检查进程是否存在&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; os.kill(pid,&nbsp;0)&nbsp; &nbsp;&nbsp;except&nbsp;ProcessLookupError:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"错误: 进程&nbsp;{pid}&nbsp;不存在", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp; info = get_process_info(pid)&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;info:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"错误: 无法获取进程&nbsp;{pid}&nbsp;的信息", file=sys.stderr)&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp;&nbsp;# 打印基本信息&nbsp; &nbsp;&nbsp;print("="&nbsp;*&nbsp;40)&nbsp; &nbsp;&nbsp;print(f"进程信息 PID:&nbsp;{pid}")&nbsp; &nbsp;&nbsp;print("="&nbsp;*&nbsp;40)&nbsp; &nbsp;&nbsp;print(f"PID: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;{info['pid']}")&nbsp; &nbsp;&nbsp;print(f"父进程 PID: &nbsp; &nbsp; &nbsp; &nbsp;{info['ppid']}")&nbsp; &nbsp;&nbsp;print(f"执行用户: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{info['user']}")&nbsp; &nbsp;&nbsp;print(f"进程状态: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{info['state']}")&nbsp; &nbsp;&nbsp;print(f"启动时间: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{info['started']}")&nbsp; &nbsp;&nbsp;if&nbsp;info['rss_kb'] >&nbsp;0:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"RSS 内存: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{human_readable_size(info['rss_kb'])}")&nbsp; &nbsp;&nbsp;print(f"CPU 使用率: &nbsp; &nbsp; &nbsp; &nbsp;{info['cpu']}%")&nbsp; &nbsp;&nbsp;print(f"命令行: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{info['command']}")
&nbsp; &nbsp;&nbsp;# 工作目录(Linux / macOS 不同)&nbsp; &nbsp;&nbsp;if&nbsp;sys.platform ==&nbsp;'linux':&nbsp; &nbsp; &nbsp; &nbsp; cwd = get_cwd_linux(pid)&nbsp; &nbsp; &nbsp; &nbsp; exe = get_exe_linux(pid)&nbsp; &nbsp; &nbsp; &nbsp; threads = get_thread_count_linux(pid)&nbsp; &nbsp;&nbsp;elif&nbsp;sys.platform ==&nbsp;'darwin':&nbsp; &nbsp; &nbsp; &nbsp; cwd = get_cwd_macos(pid)&nbsp; &nbsp; &nbsp; &nbsp; exe = get_exe_macos(pid)&nbsp; &nbsp; &nbsp; &nbsp; threads = get_thread_count_macos(pid)&nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; cwd = exe = threads =&nbsp;None&nbsp; &nbsp;&nbsp;if&nbsp;cwd:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"工作目录: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{cwd}")&nbsp; &nbsp;&nbsp;if&nbsp;exe:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"可执行文件路径: &nbsp; &nbsp;{exe}")&nbsp; &nbsp;&nbsp;if&nbsp;threads&nbsp;is&nbsp;not&nbsp;None:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"线程数量: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{threads}")
&nbsp; &nbsp;&nbsp;# 显示 TCP 连接(仅 Linux)&nbsp; &nbsp;&nbsp;if&nbsp;sys.platform ==&nbsp;'linux':&nbsp; &nbsp; &nbsp; &nbsp; print_tcp_connections(pid)&nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("\n注:非 Linux 系统不支持通过 /proc 获取 TCP 连接信息")
&nbsp; &nbsp;&nbsp;print("="&nbsp;*&nbsp;40)
if&nbsp;__name__ ==&nbsp;'__main__':&nbsp; &nbsp; main()

全文完。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:生有可恋 hyang0 hyang0《pidinfo 根据进程 ID 获取进程信息》

评论:0   参与:  0