/** * NetNuke - Erases all storage media deteced by the system * Copyright (C) 2009 Joseph Hunkeler * * This file is part of NetNuke. * * NetNuke is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NetNuke is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NetNuke. If not, see . **/ #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #else #include "human_readable.h" #include #include #endif #include "netnuke.h" /* Global variables */ static int64_t error; static bool udef_verbose = false; static int8_t udef_wmode = 0; /* O_SYNC is default */ static int16_t udef_nukelevel = 1; /* Static patterns is default */ static int16_t udef_passes = 1; static bool udef_testmode = true; /* Test mode should always be enabled by default. */ static int32_t udef_blocksize = 512; /* 1 block = 512 bytes*/ /* List of media types that we can nuke */ #ifdef __FreeBSD__ const char* mediaList[17] = { "ad", //ATAPI "da", //SCSI "adv", //AdvanSys Narrow "adw", //AdvanSys Wide "amd", //AMD 53C974 (Tekram DC390(T)) "ncr", //NCR PCI "bt", //BusLogic "aha", //Adaptec 154x/1535 "ahb", //Adaptec 174x "ahc", //Adaptec 274x/284x/294x "aic", //Adaptec 152x/AIC-6360/AIC-6260 "isp", //QLogic "dpt", //DPT RAID "amr", //AMI MegaRAID "mlx", //Mylex DAC960 RAID "wt", //Wangtek and Archive QIC-02/QIC-36 }; #else const char* mediaList[3] = { "hd", //ATAPI "sd", //SCSI }; #endif /* Prototypes */ void fillRandom(int32_t buffer[], uint64_t length); int32_t nuke(const char* media, uint64_t size); uint64_t getSize(const char* media); void echoList(void); int humanize_number(char *buf, size_t len, int64_t bytes, const char *suffix, int scale, int flags); void fillRandom(int32_t buffer[], uint64_t length) { uint32_t random, random_count; /* Initialize random seed */ srand(time(NULL) * time(NULL) / 3 + 6201985 * 3.14159); /* Fills the write buffer with random garbage */ int32_t linebreak = 0; for(random_count = 0; random_count < length; random_count++) { random = rand() % RAND_MAX; buffer[random_count] = random; /* printf("0x%0.8X ", (char)random); if(linebreak == 5) { printf("\n"); linebreak = -1; } */ linebreak++; } //printf("\n"); } int nuke(const char* media, uint64_t size) { /* test with 1G worth of data */ size = (1024 * 1024) * 1000; char mediaSize[BUFSIZ]; char writeSize[BUFSIZ]; char writePerSecond[BUFSIZ]; uint64_t byteSize = 1024; uint64_t times, block; int32_t wTable[byteSize]; uint32_t startTime, currentTime, endTime; int32_t retainer = 0; int fd = open("/dev/null", O_WRONLY | O_ASYNC); if(!fd) { perror("nuke"); exit(1); } /* Generate a size string based on the media size. example: 256M */ humanize_number(mediaSize, 5, (uint64_t)size, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf("Wiping %s: %ju (%s)\n", media, (intmax_t)size, mediaSize); /* Determine how many writes and at what byte size */ times = size / byteSize; /* Dump random garbage to the write table */ fillRandom(&wTable, byteSize); startTime = time(NULL); for( block = 0 ; block <= times ; block++) { currentTime = time(NULL); long double bytes = (float)(size / times * block); error = write(fd, (char*)wTable, sizeof(wTable)); switch(error) { case EIO: { int64_t blockPosition = (int64_t)lseek(fd, -1, SEEK_CUR); printf("I/O Error: Unable to write to block \"%jd\"\n", blockPosition); } break; default: break; }; /* Generate a size string based on bytes written. example: 256M */ humanize_number(writeSize, 5, bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); /* Generate a size string based on writes per second. example: 256M */ humanize_number(writePerSecond, 5, (intmax_t)((long double)bytes / ((long double)currentTime - (long double)startTime)), "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); /* Save I/O by printing our progress every X iterations with a retainer*/ if(retainer > 128 || block == times) { /* Output our progress */ printf("\t%jd of %jd blocks [ %s / %3.1Lf%% / %s/s ]\r", block, times, writeSize, (bytes / (long double)size) * 100L, writePerSecond ); retainer = -1; /* Recycle the write table with random garbage */ fillRandom(&wTable, byteSize); } ++retainer; } endTime = time(NULL); putchar('\n'); close(fd); return 0; } void echoList() { int i = 0; int mt = 0; int mediaFound = 0; do { /* Media size */ uint64_t size; /* Holds the media type, and number. example: ad0 */ char media[BUFSIZ]; char mediaShort[BUFSIZ]; /* Generate a device string based on the current interation*/ #ifdef __FreeBSD__ sprintf(media, "/dev/%s%d", mediaList[mt], i); #else sprintf(media, "/dev/%s%c", mediaList[mt], 'a' + i); #endif /* Set media size */ size = getSize(media); /* We MUST use the int64_t cast. Unsigned integers cannot have a negative value. * Otherwise this loop would never end. */ if((int64_t)size > 0L) { strncpy(mediaShort, &media[5], strlen(&media[5])); printf("%s: %jd\n", mediaShort , (intmax_t)size); mediaFound++; } else { /* To prevent overrunning */ if(mediaList[mt] == NULL || mediaList[mt] == '\0') break; /* mediaList iteration */ mt++; /* Reset iteratior so that the next increment is zero */ i = -1; } i++; } while( 1 ); printf("%d device%c detected\n", mediaFound, mediaFound < 1 || mediaFound > 1 ? 's' : 0 ); putchar('\n'); } /* Function: getSize * Argument 1: Fully qualified path to device * Return: Media size, or -1 on failure */ uint64_t getSize(const char* media) { /* Media size */ uint64_t size; /* Attempt to open the media read-only */ int fd = open(media, O_RDONLY); if(fd) { /* Set media size */ #ifdef __FreeBSD__ error = ioctl(fd, DIOCGMEDIASIZE, &size); #else error = ioctl(fd, BLKGETSIZE, &size); /* For Linux we must adjust the size returned, because we want bytes, not blocks */ size *= 512; #endif } else exit(1); if(error != 0) { /* Errors will be completely ambiguous due to the nature of this * program. */ return -1; } /* Close the media file descriptor */ close(fd); /* Return the size of the media */ return size; } void usage(const char* cmd) { putchar('\n'); printf("usage: %s\n", cmd); printf("--help -h This message\n"); printf("--write-mode s -w s Valid values:\n\ 0: Synchronous (default)\n\ 1: Asynchronous\n"); printf("--nuke-level n -nl n Varying levels of destruction:\n\ 0: Zero out (quick wipe)\n\ 1: Static patterns (0xA, 0xB, ...)\n\ 2: Fast random (single random buffer across media)\n\ 3: Slow random (generate random buffer every 128 blocks)\n\ 4: Ultra-slow re-writing method\n"); printf("--passes n -p n Number of wipes to perform on a single device\n"); printf("--disable-test Disables test-mode, and allows write operations\n"); printf("--block-size n -b n Blocks at once\n"); printf("--verbose -v\n"); printf("--version -V\n"); putchar('\n'); } void version(const char* cmd) { printf("NetNuke v%d.%d-%s\n \ Copyright (C) 2009 %s <%s>\n \ This software is licensed under %sv%d\n\n%s\n\n", NETNUKE_VERSION_MAJOR, NETNUKE_VERSION_MINOR, NETNUKE_VERSION_REVISION, NETNUKE_AUTHOR, NETNUKE_AUTHOR_EMAIL, NETNUKE_LICENSE_TYPE, NETNUKE_LICENSE_VERSION, NETNUKE_LICENSE_PREAMBLE ); printf("NOTE: humanize_number was ported from NetBSD to function on Linux.\n"); printf("That source code (from libutil) is licensed under the BSD software license.\n\n"); } #define ARGMATCH(arg) strncmp(argv[tok], arg, strlen(arg)+1) == 0 #define ARGFILTER() \ if(argv[tok] == NULL) fprintf(stderr, "%s was null.\n", __LINE__) #define ARGVALINT(ref) tok++; ref = atoi(argv[tok]) int main(int argc, char* argv[]) { int tok = 0; for(tok = 0; tok < argc; tok++) { if(ARGMATCH("--help") || ARGMATCH("-h")) { usage(argv[0]); exit(0); } if(ARGMATCH("--version") || ARGMATCH("-V")) { version(argv[0]); exit(0); } if(ARGMATCH("--verbose") || ARGMATCH("-v")) { udef_verbose = true; } if(ARGMATCH("--write-mode") || ARGMATCH("-w")) { ARGVALINT(udef_wmode); if(udef_verbose) printf("write mode is %d\n", udef_wmode); } if(ARGMATCH("--nuke-level") || ARGMATCH("-n")) { ARGFILTER(); ARGVALINT(udef_nukelevel); if(udef_verbose) printf("nuke level is %d\n", udef_nukelevel); } if(ARGMATCH("--passes") || ARGMATCH("-p")) { } if(ARGMATCH("--disable-test")) { } if(ARGMATCH("--block-size") || ARGMATCH("-b")) { } } int i = 0; int mt = 0; echoList(); do { /* Media size */ uint64_t size; /* Holds the media type, and number. example: ad0 */ char media[BUFSIZ]; /* Generate a device string based on the current interation*/ #ifdef __FreeBSD__ sprintf(media, "/dev/%s%d", mediaList[mt], i); #else sprintf(media, "/dev/%s%c", mediaList[mt], 'a' + i); #endif /* Set media size */ size = getSize(media); /* We MUST use the int64_t cast. Unsigned integers cannot have a negative value. * Otherwise this loop would never end. */ if((int64_t)size > 0L) { //printf("%s: %jd (%s)\n", media, (intmax_t)size, buf); nuke(media, size); } else { /* To prevent overrunning */ if(mediaList[mt] == NULL || mediaList[mt] == '\0') break; /* mediaList iteration */ mt++; /* Reset iteratior so that the next increment is zero */ i = -1; } i++; } while( 1 ); /* End */ return 0; }