Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用。Rootkit一词更多地是指被作为驱动程序,加载到操作系统内核中的恶意软件。
chkrootkit简介chkrootkit是一个linux下检RootKit的脚本,在某些检测中会调用当前目录的检测程序
官网:http://www.chkrootkit.org/
下载源码:ftp://ftp.pangeia.com.br/pub/seg/pac/chkrootkit.tar.gz
解压后执行 make
命令,就可以使用了
一般直接运行,一旦有INFECTED,说明可能被植入了RootKit
./chkrootkit | grep INFECTED
总体流程
首先删除别名,确保接下来的一些操作不会用了被RootKit改变了的别名
### workaround for some Bourne shell implementations
unalias login > /dev/null 2>&1
unalias ls > /dev/null 2>&1
unalias netstat > /dev/null 2>&1
unalias ps > /dev/null 2>&1
unalias dirname > /dev/null 2>&1
一开始会检测一些必要的命令是否可用,可执行,因为检测基于这些命令
cmdlist="
awk
cut
echo
egrep
find
head
id
ls
netstat
ps
sed
ssh
strings
uname
"
接下来就是检测ps的参数ax好不好使,好使就使用ax,不好使就用-fe
# Check if ps command is ok
if ${ps} ax >/dev/null 2>&1 ; then
ps_cmd="ax"
else
ps_cmd="-fe"
fi
当然还需检测你是否是root,就根据你的id号是否为0
if [ `${id} | ${cut} -d= -f2 | ${cut} -d\( -f1` -ne 0 ]; then
echo "$0 need root privileges"
exit 1
fi
接下来就是默认执行所有测试,当然你也可以指定测试的命令
if [ $# -gt 0 ]
then
### perform only tests supplied as arguments
for arg in $*
do
### check if is a valid test name
if echo "${TROJAN} ${TOOLS}"| \
${egrep} -v "${L_REGEXP}$arg${R_REGEXP}" > /dev/null 2>&1
then
echo >&2 "$0: \`$arg': not a known test"
exit 1
fi
done
LIST=$*
else
### this is the default: perform all tests
LIST="${TROJAN} ${TOOLS}"
fi
接下来只是对是否开启调试模式,用户是否指定了要检测的根目录进行处理
if [ "${DEBUG}" = "t" ]; then
set -x
fi
if [ "${ROOTDIR}" != "/" ]; then
### remove trailing `/'
ROOTDIR=`echo ${ROOTDIR} | ${sed} -e 's/\/*$//g'`
for dir in ${pth}
do
if echo ${dir} | ${egrep} '^/' > /dev/null 2>&1
then
newpth="${newpth} ${ROOTDIR}${dir}"
else
newpth="${newpth} ${ROOTDIR}/${dir}"
fi
done
pth=${newpth}
ROOTDIR="${ROOTDIR}/"
fi
最后便是循环调用各个check函数进行处理了
for cmd in ${LIST}
do
if echo "${TROJAN}" | \
${egrep} "${L_REGEXP}$cmd${R_REGEXP}" > /dev/null 2>&1
then
if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
printn "Checking \`${cmd}'... "
fi
chk_${cmd}
STATUS=$?
### quiet mode
if [ "${QUIET}" = "t" ]; then
### show only INFECTED status
if [ ${STATUS} -eq 0 ]; then
echo "Checking \`${cmd}'... INFECTED"
fi
continue
fi
case $STATUS in
0) echo "INFECTED";;
1) echo "not infected";;
2) echo "not tested";;
3) echo "not found";;
4) echo "infected but disabled";;
5) ;; ### expert mode
esac
else
### external tool
if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
printn "Checking \`$cmd'... "
fi
${cmd}
fi
done
那么接下来每个check方法到底是怎么检测的呢?接下来
检测方法通过分析脚本,总结出检测方法如下:
- 搜索通用的ROOTKIT特征的字符串
- 对某种特定的rootkits,或者命令的特殊的感染特征进行检测
- 对某种特定的rootkits的生成的特定文件的检测
- 对程序的SUID位的设置进行检测
- 对ldsopreload的检测
- 查找可疑的log文件
- 查找可疑的php文件
- 检测.history文件
- 检测有无程序监听了一些可疑的端口
- 检测Linux可加载内核模块
- 检测有无隐藏进程
- 检测目录的软链接异常
- 检测网络接口的异常
- 检测用户的登录日志
- 检测上一次登录
- 检测可疑的没有tty记录的进程
下面对上面这些方法结合脚本代码进行简单说明
搜索通用的ROOTKIT特征的字符串
搜索的是下面的比较通用的ROOTKIT字符串
# Many trojaned commands have this label
GENERIC_ROOTKIT_LABEL="^/bin/.*sh$|bash|elite$|vejeta|\.ark|iroffer"
可以看到前两个都是shell相关的,相关的示例代码如下:
chk_chfn () {
STATUS=${NOT_INFECTED}
CMD=`loc chfn chfn $pth`
[ ${?} -ne 0 ] && return ${NOT_FOUND}
if [ "${EXPERT}" = "t" ]; then
expertmode_output "${strings} -a ${CMD}"
return 5
fi
case "${SYSTEM}" in
Linux)
if ${strings} -a ${CMD} | ${egrep} "${GENERIC_ROOTKIT_LABEL}" \
>/dev/null 2>&1
then
STATUS=${INFECTED}
fi;;
FreeBSD)
[ `echo $V | ${awk} '{ if ( $1 >= 5.0) print 1; else print 0 }'` -eq 1 ] && n=1 || n=2
if [ `${strings} -a ${CMD} | \
${egrep} -c "${GENERIC_ROOTKIT_LABEL}"` -ne $n ]
then
STATUS=${INFECTED}
fi;;
esac
return ${STATUS}
}
程序针对Linux和FreeBSD系统分开处理,都是通过strings获取二进制程序中的字符串,再使用egrep命令去正则匹配,匹配成功就将返回值STATUS设置为INFECTED这个常量(这个在文件开头处定义了)
对某种特定的rootkits,或者命令的特殊的感染特征进行检测
比如这个amd命令的某个感染特征
chk_amd () {
STATUS=${NOT_INFECTED}
AMD_INFECTED_LABEL="blah"
CMD=`loc amd amd $pth`
if [ ! -x "${CMD}" ]; then
return ${NOT_FOUND}
fi
if [ "${EXPERT}" = "t" ]; then
expertmode_output "${strings} -a ${CMD}"
return 5
fi
if ${strings} -a ${CMD} | ${egrep} "${AMD_INFECTED_LABEL}" >/dev/null 2>&1
then
STATUS=${INFECTED}
fi
return ${STATUS}
}
下面这个检测crontab的nobody用户,并且定时任务中有数字的, 可能是Lupper.Worm
当然还是有CRONTAB_I_L这个特殊的检测
chk_crontab () {
STATUS=${NOT_INFECTED}
CRONTAB_I_L="crontab.*666"
CMD=`loc crontab crontab $pth`
if [ ! -r ${CMD} ]
then
return ${NOT_FOUND}
fi
if [ "${EXPERT}" = "t" ]; then
expertmode_output "${CMD} -l -u nobody"
return 5
fi
# slackware's crontab have a bug
if ( ${CMD} -l -u nobody | $egrep [0-9] ) >/dev/null 2>&1 ; then
${echo} "Warning: crontab for nobody found, possible Lupper.Worm... "
if ${CMD} -l -u nobody 2>/dev/null | ${egrep} $CRONTAB_I_L >/dev/null 2>&1
then
STATUS=${INFECTED}
fi
fi
return ${STATUS}
}
对Ramen Worm进行特征匹配
if ${egrep} "^asp" ${ROOTDIR}etc/inetd.conf >/dev/null 2>&1; then
echo "Warning: Possible Ramen Worm installed in inetd.conf"
STATUS=${INFECTED}
fi
对某种特定的rootkits生成的特定文件的检测
如下面的HiDrootkit和t0rn
### HiDrootkit
if [ "${QUIET}" != "t" ]; then printn \
"Searching for HiDrootkit's default dir... "; fi
if [ -d ${ROOTDIR}var/lib/games/.k ]
then
echo "Possible HiDrootkit installed"
else
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
fi
### t0rn
if [ "${QUIET}" != "t" ]; then printn\
"Searching for t0rn's default files and dirs... "; fi
if [ -f ${ROOTDIR}etc/ttyhash -o -f ${ROOTDIR}sbin/xlogin -o \
-d ${ROOTDIR}usr/src/.puta -o -r ${ROOTDIR}lib/ldlib.tk -o \
-d ${ROOTDIR}usr/info/.t0rn ]
then
echo "Possible t0rn rootkit installed"
else
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
fi
对程序的SUID位的设置进行检测
chk_basename () {
STATUS=${NOT_INFECTED}
CMD=`loc basename basename $pth`
if [ "${EXPERT}" = "t" ]; then
expertmode_output "${strings} -a ${CMD}"
expertmode_output "${ls} -l ${CMD}"
return 5
fi
if ${strings} -a ${CMD} | ${egrep} "${GENERIC_ROOTKIT_LABEL}" > /dev/null 2>&1
then
STATUS=${INFECTED}
fi
[ "$SYSTEM" != "OSF1" ] &&
{
if ${ls} -l ${CMD} | ${egrep} "^...s" > /dev/null 2>&1
then
STATUS=${INFECTED}
fi
}
return ${STATUS}
}
这个除了检测有无关键字,还检测SUID位有无设置
对ldsopreload的检测
chk_ldsopreload() {
STATUS=${NOT_INFECTED}
CMD="${ROOTDIR}lib/libshow.so ${ROOTDIR}lib/libproc.a"
if [ "${SYSTEM}" = "Linux" ]
then
if [ ! -x ./strings-static ]; then
printn "can't exec ./strings-static, "
return ${NOT_TESTED}
fi
if [ "${EXPERT}" = "t" ]; then
expertmode_output "./strings-static -a ${CMD}"
return 5
fi
### strings must be a statically linked binary.
if ./strings-static -a ${CMD} > /dev/null 2>&1
then
STATUS=${INFECTED}
fi
else
STATUS=${NOT_TESTED}
fi
return ${STATUS}
}
检测是否有libshow.so,libproc.a,有就说明感染了恶意so文件
可以看到为了保险起见,作者使用的是自己目录下静态编译的strings进行检测
查找可疑的log文件
例子如下:
files=`${find} ${ROOTDIR}dev ${ROOTDIR}tmp ${ROOTDIR}lib ${ROOTDIR}etc ${ROOTDIR}var \
${findargs} \( -name "tcp.log" -o -name ".linux-sniff" -o -name "sniff-l0g" -o -name "core_" \) \
2>/dev/null`
if [ "${files}" = "" ]
then
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
else
echo
echo ${files}
fi
查找可疑的php文件
查找一些可疑的php文件
###
### Suspect PHP files
###
if [ "${QUIET}" != "t" ]; then
printn "Searching for suspect PHP files... "; fi
files="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -name '*.php' 2> /dev/null`"
if [ `echo abc | head -n 1` = "abc" ]; then
fileshead="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -type f -exec head -n 1 {} \; | $egrep '#!.*php' 2> /dev/null`"
else
fileshead="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -type f -exec head -1 {} \; | grep '#!.*php' 2> /dev/null`"
fi
if [ "${files}" = "" -a "${fileshead}" = "" ]; then
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
else
echo
echo "${files}"
echo "${fileshead}"
fi
检测.history文件
看看history有没有被清空了,或者软连接到其他地方了
if [ "${QUIET}" != "t" ]; then \
printn "Searching for anomalies in shell history files... "; fi
files=""
if [ ! -z "${SHELL}" -a ! -z "${HOME}" ]; then
files=`${find} ${ROOTDIR}${HOME} ${findargs} -name '.*history' -size 0`
[ ! -z "${files}" ] && \
echo "Warning: \`${files}' file size is zero"
files1=`${find} ${ROOTDIR}${HOME} ${findargs} -name '.*history' \( -links 2 -o -type l \)`
[ ! -z "${files1}" ] && \
echo "Warning: \`${files1}' is linked to another file"
fi
if [ -z "${files}" -a -z "${files1}" ]; then
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
fi
检测有无程序监听了一些可疑的端口
检测代码如下:
bindshell () {
PORT="114|145|465|511|600|1008|1524|1999|1978|2881|3049|3133|3879|4000|4369|5190|5665|6667|10008|12321|23132|27374|29364|30999|31336|31337|37998|45454|47017|47889|60001|7222"
OPT="-an"
PI=""
if [ "${ROOTDIR}" != "/" ]; then
echo "not tested"
return ${NOT_TESTED}
fi
if [ "${EXPERT}" = "t" ]; then
expertmode_output "${netstat} ${OPT}"
return 5
fi
for P in `echo $PORT | ${sed} 's/|/ /g'`; do
if ${netstat} "${OPT}" | ${egrep} "^tcp.*LIST|^udp" | ${egrep} \
"[.:]${P}[^0-9.:]" >/dev/null 2>&1
then
PI="${PI} ${P}"
fi
done
if [ "${PI}" != "" ]
then
echo "INFECTED PORTS: ($PI)"
else
if [ "${QUIET}" != "t" ]; then echo "not infected"; fi
fi
}
检测Linux可加载内核模块
lkm ()
{
prog=""
if [ \( "${SYSTEM}" = "Linux" -o \( "${SYSTEM}" = "FreeBSD" -a \
`echo ${V} | ${awk} '{ if ($1 > 4.3 || $1 < 6.0) print 1; else print 0 }'` -eq 1 \) \) -a "${ROOTDIR}" = "/" ]; then
[ -x ./chkproc -a "`find /proc | wc -l`" -gt 1 ] && prog="./chkproc"
[ -x ./chkdirs ] && prog="$prog ./chkdirs"
if [ "$prog" = "" ]; then
echo "not tested: can't exec $prog"
return ${NOT_TESTED}
fi
if [ "${EXPERT}" = "t" ]; then
[ -r /proc/$KALLSYMS ] && ${egrep} -i "adore|sebek" < /proc/$KALLSYMS 2>/dev/null
[ -d /proc/knark ] && ${ls} -la /proc/knark 2> /dev/null
PV=`$ps -V 2>/dev/null| $cut -d " " -f 3 |${awk} -F . '{ print $1 "." $2 $3 }' | ${awk} '{ if ($0 > 3.19) print 3; else if ($0 < 2.015) print 1; else print 2 }'`
[ "$PV" = "" ] && PV=2
[ "${SYSTEM}" = "SunOS" ] && PV=0
expertmode_output "./chkproc -v -v -p $PV"
return 5
fi
### adore LKM
[ -r /proc/$KALLSYMS ] && \
if `${egrep} -i adore < /proc/$KALLSYMS >/dev/null 2>&1`; then
echo "Warning: Adore LKM installed"
fi
### sebek LKM (Adore based)
[ -r /proc/$KALLSYMS ] && \
if `${egrep} -i sebek < /proc/$KALLSYMS >/dev/null 2>&1`; then
echo "Warning: Sebek LKM installed"
fi
### knark LKM
if [ -d /proc/knark ]; then
echo "Warning: Knark LKM installed"
fi
PV=`$ps -V 2>/dev/null| $cut -d " " -f 3 |${awk} -F . '{ print $1 "." $2 $3 }' | ${awk} '{ if ($0 > 3.19) print 3; else if ($0 < 2.11) print 1; else print 2 }'`
[ "$PV" = "" ] && PV=2
[ "${SYSTEM}" = "SunOS" ] && PV=0
if [ "${DEBUG}" = "t" ]; then
${echo} "*** PV=$PV ***"
fi
if ./chkproc -p ${PV}; then
if [ "${QUIET}" != "t" ]; then echo "chkproc: nothing detected"; fi
else
echo "chkproc: Warning: Possible LKM Trojan installed"
fi
dirs="/tmp"
for i in /usr/share /usr/bin /usr/sbin /lib; do
[ -d $i ] && dirs="$dirs $i"
done
if ./chkdirs $dirs; then
if [ "${QUIET}" != "t" ]; then echo "chkdirs: nothing detected"; fi
else
echo "chkdirs: Warning: Possible LKM Trojan installed"
fi
else
if [ "${QUIET}" != "t" ]; then echo "chkproc: not tested"; fi
fi
}
loadable kernel module (LKM),这个是检测内核模块的 ,看看有无adore,sebek这些内核模块
之后调用chkproc,chkdirs进行检测,这两个下面检测有无隐藏进程,会说到
检测有无隐藏进程
这个代码在chkproc.c中,它通过暴力递归,看看有没有/proc目录存在,而ps查不出来的进程,那么就说明有进程隐藏了
/* Brute force */
strcpy(buf, "/proc/");
retps = retdir = 0;
for (i = FIRST_PROCESS; i <= MAX_PROCESSES; i++)
{
// snprintf(&buf[6], 6, "%d", i);
snprintf(&buf[6], 8, "%d", i);
if (!chdir(buf))
{
if (!dirproc[i] && !psproc[i])
{
#if defined(__linux__)
if (!isathread[i]) {
#endif
retdir++;
if (verbose)
printf ("PID %5d(%s): not in readdir output\n", i, buf);
#if defined(__linux__)
}
#endif
}
if (!psproc[i] ) /* && !kill(i, 0)) */
{
#if defined(__linux__)
if(!isathread[i]) {
#endif
retps++;
if (verbose)
printf ("PID %5d: not in ps output\n", i);
#if defined(__linux__)
}
#endif
}
检测目录的软链接异常
chkdirs比较的是父目录的软链接数和子目录的个数
正常情况下,父目录的软链接数 = 子目录的个数 + 2
if (!linkcount) {
if (lstat(".", &statinfo)) {
fprintf(stderr, "lstat(%s): %s\n", fullpath, strerror(errno));
goto abort;
}
linkcount = statinfo.st_nlink; //获取符号链接数
}
if (!(dirhandle = opendir("."))) {
fprintf(stderr, "opendir(%s): %s\n", fullpath, strerror(errno));
goto abort;
}
numdirs = 0;
dl = (struct dirinfolist *)NULL;
while ((finfo = readdir(dirhandle))) {
if (!strcmp(finfo->d_name, ".") || !strcmp(finfo->d_name, ".."))
continue;
if (lstat(finfo->d_name, &statinfo)) {
fprintf(stderr, "lstat(%s/%s): %s\n",
fullpath, finfo->d_name, strerror(errno));
closedir(dirhandle);
goto abort;
}
if (S_ISDIR(statinfo.st_mode)) { //判断是否是目录
numdirs++;
if (norecurse) continue; /* just count subdirs if "-n" */
/* Otherwise, keep a list of all directories found that have link
count > 2 (indicating directory contains subdirectories). We'll
call check_dir() on each of these subdirectories in a moment...
*/
if (statinfo.st_nlink > 2) {
dptr = dl;
if (!(dl = (struct dirinfolist *)malloc(sizeof(struct dirinfolist)))) {
fprintf(stderr, "malloc() failed: %s\n", strerror(errno));
norecurse = 1;
while (dptr) {
dl = dptr->dil_next;
free((void *)dptr);
dptr = dl;
}
continue;
}
strncpy(dl->dil_name, finfo->d_name, sizeof(dl->dil_name));
dl->dil_lc = statinfo.st_nlink;
dl->dil_next = dptr;
}
}
}
closedir(dirhandle);
/* Parent directory link count had better equal #subdirs+2... */
diff = linkcount - numdirs - 2; //
if (diff) printf("%d\t%s\n", diff, fullpath);
检测网络接口的异常
sniffer () {
if [ "${ROOTDIR}" != "/" ]; then
echo "not tested"
return ${NOT_TESTED}
fi
if [ "$SYSTEM" = "SunOS" ]; then
return ${NOT_TESTED}
fi
if [ "${EXPERT}" = "t" ]; then
expertmode_output "./ifpromisc" -v
return 5
fi
if [ ! -x ./ifpromisc ]; then
echo "not tested: can't exec ./ifpromisc"
return ${NOT_TESTED}
else
[ "${QUIET}" != "t" ] && ./ifpromisc -v || ./ifpromisc -q
fi
}
这个是对网络接口的检测,看看有无开启网卡混杂模式(英语:promiscuous mode)
而PF_PACKET可以操作链路层的数据,可以读取和发送链路层的数据包
./ifpromisc -v
ens3: PF_PACKET(/sbin/dhclient)
virbr0: not promisc and no PF_PACKET sockets
docker0: not promisc and no PF_PACKET sockets
br-47a3d838588a: not promisc and no PF_PACKET sockets
检测用户的登录日志
检测用户的登录相关的log文件
SunOS使用的是check_wtmpx,比较的文件是/var/adm/wtmp,/var/adm/wtmpx,check_wtmpx部分代码,比较这两个文件的一些差异,比如下面的比较uid
if ( memcmp( utmp_entry.ut_id, utmpx_entry.ut_id, 4 ) != 0 )
{
fprintf( stderr, "[ %u ] utmp_entry.ut_id != utmpx_entry.ut_id\n", wtmp_read_counter );
break;
}
其他linux检测的是var/log/wtmp或者var/adm/wtmp
chkwtmp部分代码,查看有无删除了登录时间
gettimeofday(&mytime, &dummy);
act_time=mytime.tv_sec;
wtmpfile[127]='\0';
memcpy(wtmpfile, WTMP_FILENAME, 127);
if ( argc == 3 && !memcmp("-f", argv[1], 2) && *argv[2])
memcpy(wtmpfile, argv[2], 127);
if ((filehandle=open(wtmpfile,O_RDONLY)) < 0) {
fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile);
return(2);
}
while (read (filehandle, (char *) &utmp_ent, sizeof (struct utmp)) > 0) {
if (utmp_ent.ut_time == 0)
del_counter++;
else {
if (del_counter) {
printit(del_counter, start_time,
utmp_ent.ut_time);
t_del++;
del_counter=0;
}
start_time=utmp_ent.ut_time;
}
}
close(filehandle);
if (del_counter)
printit(del_counter, start_time, act_time);
exit((int) t_del+del_counter);
检测上一次登录
使用chklastlog程序检测,下面是部分代码,用户的数据通过getpwent函数获取,其实就是通过/etc/passwd获取,检测基于两点
1、通过比较MAX_ID,与当前的遍历的用户的id,看看id是否大于环境变量MAX_ID
2、看看是否有这样的情况:用户名出现在lastlog,wtmp文件中,而在/etc/passwd中没有的
if ( !nonuser(utmp_ent) && strncmp(utmp_ent.ut_line, "ftp", 3) &&
(uid=localgetpwnam(localpwd,utmp_ent.ut_name)) != NULL )
{
if (*uid > MAX_ID)
{
fprintf(stderr, "MAX_ID is %ld and current uid is %ld, please check\n\r", MAX_ID, *uid );
exit (1);
}
if (!userid[*uid])
{
lseek(fh_lastlog, (long)*uid * sizeof (struct lastlog), 0);
if ((wtmp_bytes_read = read(fh_lastlog, &lastlog_ent, sizeof (struct lastlog))) > 0)
{
if (wtmp_bytes_read < sizeof(struct lastlog))
{
fprintf(stderr, "lastlog entry may be corrupted");
break;
}
if (lastlog_ent.ll_time == 0)
{
if (-1 != (slot = getslot(localpwd, *uid)))
printf("user %s deleted or never logged from lastlog!\n",
NULL != localpwd->uname[slot] ?
(char*)localpwd->uname[slot] : "(null)");
else
printf("deleted user uid(%d) not in passwd\n", *uid);
++status;
}
userid[*uid]=TRUE;
}
}
}
}
检测可疑的没有tty记录的进程
检测的是/var/run/utmp或者/var/adm/utmpx,方法是比较的是ps命令与/var/run/utmp文件之间的差别
y = fetchps(ps_l);
z = fetchutmp(ut_l);
hdr_prntd = 0;
for (h = 0; h < y; h++) { /* loop through 'ps' data */
mtch_fnd = 0;
for (i = 0; i < z; i++) { /* try and match the tty from 'ps' to one in utmp */
if (ut_l[i].ut_type == LOGIN_PROCESS /* ignore getty processes with matching pid from 'ps' */
&& ut_l[i].ut_pid == ps_l[h].ps_pid)
{
mtch_fnd = 1;
break;
}
else if (strncmp(ps_l[h].ps_tty, ut_l[i].ut_tty, /* compare the tty's */
strlen(ps_l[h].ps_tty)) == 0)
{
mtch_fnd = 1;
break;
}
}
if (!mtch_fnd) {
if (!hdr_prntd) {
printf
(" The tty of the following user process(es) were not found\n");
printf(" in %s !\n", UTMP);
printf("! %-9s %7s %-6s %s\n", "RUID", "PID", "TTY",
"CMD");
hdr_prntd = 1;
}
printf("! %-9s %7d %-6s %s", ps_l[h].ps_user,
ps_l[h].ps_pid, ps_l[h].ps_tty, ps_l[h].ps_args);
}
比如下面的检测结果,而我的/var/run/utmp中是没有tty7这个tty的记录的
Checking `chkutmp'... The tty of the following user process(es) were not found
in /var/run/utmp !
! RUID PID TTY CMD
! root 1076 tty7 /usr/lib/xorg/Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch