程式如何分辨誰在kill()自己

freerock發表於2007-09-28
至少對於Linux、FreeBSD、Solaris、AIX這四種作業系統,有一種辦法。不要安裝傳

統sa_handler訊號控制程式碼,而是安裝sa_sigaction訊號控制程式碼。細節請man sigaction並

參照標頭檔案加強理解。下面是一個可移植演示程式。



--------------------------------------------------------------------------

/*

* For x86/Linux RedHat_8 2.4.18-14

* For x86/FreeBSD 4.5-RELEASE

* For SPARC/Solaris 8

* For AIX 4.3.3.0

*

* gcc -Wall -pipe -O3 -s -o siginfo_test siginfo_test.c

*/



/************************************************************************

* *

* Head File *

* *

************************************************************************/



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <strings.h>

#include <signal.h>

#include <unistd.h>

#include <setjmp.h>

#include <sys/time.h>



/************************************************************************

* *

* Macro *

* *

************************************************************************/



/*

* for signal handlers

*/

typedef void Sigfunc ( int, siginfo_t *, void * );



#define PRIVATE_SIG_ERR ((Sigfunc *)-1)



/************************************************************************

* *

* Function Prototype *

* *

************************************************************************/



static void Atexit ( void ( * func ) ( void ) );

static void init_signal ( void );

static void init_timer ( unsigned int s );

static void on_alarm ( int signo, siginfo_t *si, void *unused );

static void on_segvbus ( int signo, siginfo_t *si, void *unused );

static void on_terminate ( int signo, siginfo_t *si, void *unused );

static Sigfunc * PrivateSignal ( int signo, Sigfunc *func );

static int Setitimer ( int which,

struct itimerval *value,

struct itimerval *ovalue );

static Sigfunc * Signal ( int signo, Sigfunc *func );

static void terminate ( void );



/************************************************************************

* *

* Static Global Var *

* *

************************************************************************/



static sigjmp_buf jmpbuf;

static volatile sig_atomic_t canjump = 0;



/************************************************************************/



static void Atexit ( void ( * func ) ( void ) )

{

if ( atexit( func ) != 0 )

{

exit( EXIT_FAILURE );

}

return;

} /* end of Atexit */



/*

* 初始化訊號控制程式碼

*/

static void init_signal ( void )

{

unsigned int i;



Atexit( terminate );

for ( i = 1; i < 9; i++ )

{

Signal( i, on_terminate );

}

Signal( SIGTERM, on_terminate );

Signal( SIGALRM, on_alarm );

Signal( SIGSEGV, on_segvbus );

Signal( SIGBUS , on_segvbus );

return;

} /* end of init_signal */



/*

* 我們的定時器精度只支援到秒

*/

static void init_timer ( unsigned int s )

{

struct itimerval value;



value.it_value.tv_sec = s;

value.it_value.tv_usec = 0;

/*

* 只生效一次

*/

value.it_interval.tv_sec = 0;

value.it_interval.tv_usec = 0;

Setitimer( ITIMER_REAL, &value, NULL );

return;

} /* end of init_timer */



static void on_alarm ( int signo, siginfo_t *si, void *unused )

{

fprintf

(

stderr,

"/n"

"signo = %d/n"

"si = 0x%08X/n"

"unused = 0x%08X/n",

signo,

( unsigned int )si,

( unsigned int )unused

);

if ( NULL != si )

{

fprintf

(

stderr,

"si->si_signo = %d/n"

"si->si_errno = %d/n"

"si->si_code = %d/n"

"si->si_pid = %u/n"

"si->si_uid = %u/n"

"si->si_status = %d/n"

"si->si_addr = 0x%08X/n",

si->si_signo,

si->si_errno,

si->si_code,

( unsigned int )si->si_pid,

( unsigned int )si->si_uid,

( int )si->si_status,

( unsigned int )si->si_addr

);

}

return;

} /* end of on_alarm */



static void on_segvbus ( int signo, siginfo_t *si, void *unused )

{

fprintf

(

stderr,

"/n"

"signo = %d/n"

"si = 0x%08X/n"

"unused = 0x%08X/n",

signo,

( unsigned int )si,

( unsigned int )unused

);

if ( NULL != si )

{

fprintf

(

stderr,

"si->si_signo = %d/n"

"si->si_errno = %d/n"

"si->si_code = %d/n"

"si->si_pid = %u/n"

"si->si_uid = %u/n"

"si->si_status = %d/n"

"si->si_addr = 0x%08X/n",

si->si_signo,

si->si_errno,

si->si_code,

( unsigned int )si->si_pid,

( unsigned int )si->si_uid,

( int )si->si_status,

( unsigned int )si->si_addr

);

}

if ( 0 == canjump )

{

/*

* unexpected signal, ignore

*/

return;

}

canjump = 0;

/*

* jump back to main, don't return

*/

siglongjmp( jmpbuf, signo );

} /* end of on_segvbus */



/*

* 參如下標頭檔案瞭解siginfo_t定義

*

* Linux /usr/include/bits/siginfo.h

* FreeBSD /usr/include/sys/signal.h

* Solaris /usr/include/sys/siginfo.h

* AIX /usr/include/sys/signal.h

*

* 第三形參不推薦使用,context相關。

*/

static void on_terminate ( int signo, siginfo_t *si, void *unused )

{

/*

* 就上四種OS而言,測試Solaris,執行siginfo_test,按Ctrl-C進入該流程時

* si為NULL,其他三種系統則不同。

*/

if ( NULL != si )

{

/*

* 演示用,不推薦在訊號控制程式碼中使用fprintf()

*/

fprintf

(

stderr,

"/n"

"signo = %d/n"

"si = 0x%08X/n"

"unused = 0x%08X/n"

"si->si_signo = %d/n"

"si->si_errno = %d/n"

"si->si_code = %d/n",

signo,

( unsigned int )si,

( unsigned int )unused,

si->si_signo,

si->si_errno,

si->si_code

);

/*

* si_code為SI_USER時意味著"signal sent by another process with kill()"

*

* 就上四種OS而言,我所測試的FreeBSD反應與其他三種不同,kill程式時

* si_code始終為0,而FreeBSD有如下定義:

*

* #define SI_USER 0x10001

*

* 如果不判斷si_code,強行顯示si_pid、si_uid,對於FreeBSD而言總是0。

* 下面出於方便演示目的,沒有判斷si_code。正確作法應該判斷si_code,

* 然後顯示聯合的不同成員。

*/

fprintf

(

stderr,

"si->si_pid = %u/n"

"si->si_uid = %u/n"

"si->si_status = %d/n"

"si->si_addr = 0x%08X/n",

( unsigned int )si->si_pid,

( unsigned int )si->si_uid,

( int )si->si_status,

( unsigned int )si->si_addr

);

}

else

{

fprintf

(

stderr,

"/n"

"signo = %d/n"

"si = 0x%08X/n"

"unused = 0x%08X/n",

signo,

( unsigned int )si,

( unsigned int )unused

);

}

/*

* 這次我們使用atexit()函式

*/

exit( EXIT_SUCCESS );

} /* end of on_terminate */



/*

* 與以前版本不同之處在於使用sa_sigaction,而不是sa_handler。

*/

static Sigfunc * PrivateSignal ( int signo, Sigfunc *func )

{



#if 0



from Linux /usr/include/bits/sigaction.h



struct sigaction

{

union

{

/*

* Used if SA_SIGINFO is not set.

*/

__sighandler_t sa_handler;

/*

* Used if SA_SIGINFO is set.

*/

void ( *sa_sigaction ) ( int, siginfo_t *, void * );

} __sigaction_handler;



#define sa_handler __sigaction_handler.sa_handler

#define sa_sigaction __sigaction_handler.sa_sigaction



/*

* Additional set of signals to be blocked.

*/

__sigset_t sa_mask;

/*

* Special flags.

*/

int sa_flags;

/*

* The sa_restorer element is obsolete and should not be used.

* POSIX does not specify a sa_restorer element.

*

* Restore handler.

*/

void ( *sa_restorer ) ( void );

};



#endif



struct sigaction act, oact;



memset( &act, 0, sizeof( act ) );

sigemptyset( &act.sa_mask );

/*

* Invoke signal-catching function with three arguments instead of one.

*/

act.sa_flags = SA_SIGINFO;

act.sa_sigaction = func;

if ( SIGALRM == signo )

{

#ifdef SA_INTERRUPT

/*

* SunOS 4.x

*/

act.sa_flags |= SA_INTERRUPT;

#endif

}

else

{

#ifdef SA_RESTART

/*

* SVR4, 4.4BSD

*/

act.sa_flags |= SA_RESTART;

#endif

}

if ( sigaction( signo, &act, &oact ) < 0 )

{

return( PRIVATE_SIG_ERR );

}

return( oact.sa_sigaction );

} /* end of PrivateSignal */



static int Setitimer ( int which, struct itimerval *value, struct itimerval *ovalue )

{

int ret;



if ( ( ret = setitimer( which, value, ovalue ) ) < 0 )

{

perror( "setitimer error" );

exit( EXIT_FAILURE );

}

return( ret );

} /* end of Setitimer */



static Sigfunc * Signal ( int signo, Sigfunc *func )

{

Sigfunc *sigfunc;



if ( PRIVATE_SIG_ERR == ( sigfunc = PrivateSignal( signo, func ) ) )

{

perror( "signal error" );

exit( EXIT_FAILURE );

}

return( sigfunc );

} /* end of Signal */



static void terminate ( void )

{

/*

* _exit( EXIT_SUCCESS );

*/

return;

} /* end of terminate */



int main ( int argc, char * argv[] )

{

/*

* for autovar, must be volatile

*/

volatile unsigned char *p;



init_signal();

p = ( unsigned char * )&p;

if ( 0 != sigsetjmp( jmpbuf, 1 ) )

{

printf

(

"p = 0x%08X/n",

( unsigned int )p

);

goto main_continue;

}

/*

* now sigsetjump() is OK

*/

canjump = 1;

while ( 1 )

{

/*

* 誘發SIGSEGV、SIGBUS

*/

*p = *p;

p++;

}



main_continue:



/*

* 啟動定時器

*/

init_timer( 1 );

while ( 1 )

{

/*

* 形成阻塞,降低CPU佔用率

*/

getchar();

}

return( EXIT_SUCCESS );

} /* end of main */



/************************************************************************/



--------------------------------------------------------------------------



這種技術是作業系統實現相關的。FreeBSD的si_code與標頭檔案不相符。除了FreeBSD,

其他三種OS的si_addr如願反映了棧底地址、si_pid/si_uid也能正確反映kill()訊號

源。而Solaris會出現si為NULL的情形。下面是Linux上執行示例:



[scz@ /home/scz/src]> ./siginfo_test



signo = 11

si = 0xBFFFF6B0

unused = 0xBFFFF730

si->si_signo = 11

si->si_errno = 0

si->si_code = 1

si->si_pid = 3221225472

si->si_uid = 1869479936

si->si_status = 2712942

si->si_addr = 0xC0000000 <= 棧底地址

p = 0xC0000000



signo = 14

si = 0xBFFFF5A8

unused = 0xBFFFF628

si->si_signo = 14

si->si_errno = 0

si->si_code = 128 <= SI_KERNEL 0x80 Send by kernel.

si->si_pid = 0

si->si_uid = 0

si->si_status = 896820224

si->si_addr = 0x00000000

^Z

[scz@ /home/scz/src]> bg %1

[scz@ /home/scz/src]> kill %1



signo = 15

si = 0xBFFFF5A8

unused = 0xBFFFF628

si->si_signo = 15

si->si_errno = 0

si->si_code = 0 <= SI_USER 0x00 Sent by kill, sigsend, raise.

si->si_pid = 27712 <= kill()訊號源

si->si_uid = 1000 <= kill()訊號源

si->si_status = 896820224

si->si_addr = 0x00006C40

[scz@ /home/scz/src]> echo $

27712

[scz@ /home/scz/src]> id

uid=1000(scz) gid=0(root) groups=0(root)

[scz@ /home/scz/src]>



最後結論,對於x86/FreeBSD 4.5-RELEASE,無法利用該技術分辨kill()訊號源。其

他三種作業系統可以利用該技術。



一個有趣的想法,程式分辨出kill()訊號源,反向kill訊號源。
 

相關文章