예제 #1
0
void MFNWorkunitWordlistSession::handle_read(const boost::system::error_code& error,
            size_t bytes_transferred) {
    //printf("In session::handle_read()\n");
    //printf("Buffer (%d): %c\n", bytes_transferred, data_[0]);

    if (!error) {
        
        // Sleep if the list is too big already.
        while (this->networkPlainQueue->GetTotalWordsQueued() > MAX_PENDING_WORD_COUNT) {
            //printf("Sleeping for wordcount...\n");
            CHSleep(1);
        }
        
        if (bytes_transferred > 0) {
            std::copy((uint8_t*)&data_[0], ((uint8_t*) &data_[0]) + bytes_transferred, std::back_inserter(this->charBuffer));
        }
        if (this->charBuffer.size() > 10000000) {
            this->addWordsToQueue();
        }

        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            boost::bind(&MFNWorkunitWordlistSession::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

    } else {
        // Report the disconnect
        //printf("\n\nDSC: %s\n", this->hostIpAddress);
        fflush(stdout);
        this->addWordsToQueue();
        delete this;
    }
}
예제 #2
0
void MFNHashTypePlain::RunGPUWorkunit(CHWorkunitRobustElement* WU) {
    trace_printf("MFNHashTypePlain::RunGPUWorkunit()\n");

    /**
     * High-res timer - this should work properly on both Windows & Posix.
     */
    CHHiresTimer Timer, WorkunitTimer;

    uint64_t perThread, start_point = 0;
    uint64_t step_count = 0;
    uint64_t tempPerStep = 0;

    WorkunitTimer.start();
    
    // Kernel run time: seconds
    float ref_time = 0.0f;
    // Kernel run time: Milliseconds
    float ref_time_ms = 0.0f;
    
    float ref_time_total = 0.0f;

    // Default number per steps.  As this is updated quickly, this just needs to be in the ballpark.
    if (this->perStep == 0) {
        this->perStep = 50;
    }

    klaunch_printf("Thread %d total kernel width: %d\n", this->threadId, this->TotalKernelWidth);
    klaunch_printf("Thread %d blocks/threads/vec: %d/%d/%d\n", this->threadId, this->GPUBlocks, this->GPUThreads, this->VectorWidth);
    
    // Calculate how many iterations per thread - divide total by the number of
    // total threads, then add one to deal with truncation.
    perThread = WU->EndPoint - WU->StartPoint;
    perThread /= (this->TotalKernelWidth);
    perThread++;
    
    klaunch_printf("Total kernel width: %d\n", this->TotalKernelWidth);
    klaunch_printf("perThread: %d\n", perThread);

    // Set up the password start points for loading as blocks.
    this->setStartPasswords32(perThread, start_point + WU->StartPoint);
    // Copy them to the GPU.
    this->copyStartPointsToDevice();

    // Start the timer.
    Timer.start();

    while (start_point <= perThread) {
        step_count++;

        //this->setStartPoints(perThread, start_point + WU->StartPoint);

        if ((start_point + this->perStep) > perThread) {
            klaunch_printf("start_point: %lu\n", start_point);
            klaunch_printf("per_thread: %lu\n", perThread);
            klaunch_printf("Will overrun by %lu\n", (start_point + this->perStep) - perThread);
            tempPerStep = this->perStep;
            this->perStep = (perThread - start_point) + 1;
            klaunch_printf("Final per_step: %lu\n", this->perStep);
        }
        
        // We sync here and wait for the GPU to finish.
        this->synchronizeThreads();

        ref_time = Timer.getElapsedTime();
        ref_time_ms = Timer.getElapsedTimeInMilliSec();
        klaunch_printf("ref_time: %f s\n", ref_time);
        ref_time_total += ref_time;
        
        // Run this roughly every second, or every step if target_ms is >500
        if ((step_count < 5) || (this->kernelTimeMs > 500) || (step_count % (1000 / this->kernelTimeMs) == 0)) {

            this->copyDeviceFoundPasswordsToHost();
            this->outputFoundHashes();
            
            // Only set the crack speed if we have set one...
            if (step_count > 5) {
                this->Display->setThreadCrackSpeed(this->threadId,
                        (float) (this->TotalKernelWidth *
                        this->perStep) / (ref_time));
            }
            // If the current execution time is not correct, adjust.
            if ((ref_time_ms > 0) && (step_count > 2) &&
                    ((ref_time_ms < (this->kernelTimeMs * 0.9)) ||
                    (ref_time_ms > (this->kernelTimeMs * 1.1)))) {
                this->perStep = (uint64_t) ((float) this->perStep *
                        ((float) this->kernelTimeMs / ref_time_ms));
                if (0) {
                    printf("\nThread %d Adjusting passwords per step to %d\n",
                            this->gpuDeviceId, (unsigned int) this->perStep);
                }
            }
        }
        
        
        // If we are to pause, hang here.
        if (global_interface.pause) {
            while (global_interface.pause) {
                // Just hang out until the pause is broken...
                CHSleep(1);
            }
        }
        // Exit option
        if (global_interface.exit) {
            return;
        }


        //this->copyStartPointsToDevice();
        //this->synchronizeThreads();
        Timer.start();

        klaunch_printf("Launching kernel: \n");
        klaunch_printf("  start_point: %lu\n", start_point);
        klaunch_printf("  perStep: %lu\n", this->perStep);

        this->launchKernel();

        // Increment start point by however many we did
        start_point += this->perStep;
        

    }
    this->synchronizeThreads();
    
    // Perform a final rate calculation.
    // In some cases, the device is too fast for the normal speed reporting
    // to get triggered.
    Timer.stop();
    ref_time = Timer.getElapsedTime();
    this->Display->setThreadCrackSpeed(this->threadId,
        (float) (this->TotalKernelWidth *
        this->perStep) / (ref_time));

    this->copyDeviceFoundPasswordsToHost();
    this->outputFoundHashes();
    
    WorkunitTimer.stop();
    
    klaunch_printf("Workunit rate: %f\n", (WU->EndPoint - WU->StartPoint) / WorkunitTimer.getElapsedTime());
    klaunch_printf("Workunit timer: %f\n", WorkunitTimer.getElapsedTime());
    klaunch_printf("ref_time_total: %f\n", ref_time_total);
    
    if (tempPerStep) {
        klaunch_printf("Correcting perStep from current %lu to perm %lu\n", this->perStep, tempPerStep);
        this->perStep = tempPerStep;
    }
    
    return;
}
예제 #3
0
void MFNHashTypePlain::crackPasswordLength(int passwordLength) {
    trace_printf("MFNHashTypePlain::crackPasswordLength(%d)\n", passwordLength);

    uint64_t i;
    char statusBuffer[1000];
    struct CHWorkunitRobustElement WU;


    // New cracking - do NOT need to rendezvous threads.
    this->threadRendezvous = 0;

    // Acquire a setup mutex if we're the first thread.
    this->MFNHashTypePlainMutex.lock();

    // If static data is not set up, do so.
    // This data is shared across all instances.
    if (!this->staticDataInitialized) {
        this->threadRendezvous = 0;

        mt_printf("Thread %d doing MFNHashTypePlain setup.\n", this->threadId);

        this->Display->setPasswordLen(passwordLength);
        this->passwordLength = passwordLength;

        // Determine the password length in words.  If not a multiple of 4, round up.
        // Include the end padding bit in this calculation.
        this->passwordLengthWords = (this->passwordLength + 1);
        if (passwordLengthWords % 4) {
            passwordLengthWords = (passwordLengthWords + 4) & 0xfffc;
        }

        
        // Get the raw list of hashes.
        this->activeHashesRaw = this->HashFile->ExportUncrackedHashList();
        // Get the active charset.
        this->currentCharset = this->Charset->getCharset();
        
        mt_printf("Thread %d Charset length: %d\n", this->threadId, this->currentCharset.size());

        // If the charset length is 1, it is a single charset.  Tag as such.
        if (this->currentCharset.size() == 1) {
            this->isSingleCharset = 1;
        } else {
            this->isSingleCharset = 0;
        }
        
        this->setupCharsetArrays();

        this->Display->Refresh();

        // Preprocess all the hashes for the current password length.
        for (i = 0; i < this->activeHashesRaw.size(); i++) {
            this->activeHashesProcessed.push_back(this->preProcessHash(this->activeHashesRaw[i]));
        }

        // Sort and unique the hashes.
        this->sortHashes();

        // Set up the device-format hash list
        this->copyHashesIntoDeviceFormat();

        // If this is *not* a server-only instance, create bitmaps
        if (!this->CommandLineData->GetIsServerOnly()) {
            this->createLookupBitmaps();
        }
        this->staticDataInitialized = 1;
    }
    
    this->MFNHashTypePlainMutex.unlock();

    // Retrieve our client ID.
    this->ClientId = this->Workunit->GetClientId();
    sprintf(statusBuffer, "Td %d: CID %d.", this->threadId, this->ClientId);
    this->Display->addStatusLine(statusBuffer);

    // If the device ID is 0, the device is the fastest in the system.
    // This is true for CUDA, and possibly OpenCL.  If this thread is for
    // a non-zero device, wait a second.  This allows the fastest GPU to
    // take the work.  Otherwise, we don't care what order they enter.
    if (this->gpuDeviceId) {
        CHSleep(1);
    }

    // Do all the device-specific setup.
    this->setupDevice();

    // Reset the per-step data for the new password length.
    this->perStep = 0;

    // Allocate the thread and GPU memory.
    this->allocateThreadAndDeviceMemory();

    // Copy all the run data to the device.
    this->copyDataToDevice();

    // Copy the kernel-specific constant data to the device.
    this->copyConstantDataToDevice();

    // I... *think* we're ready to rock!
    // As long as we aren't supposed to exit, keep running.
    while(!global_interface.exit && !this->threadRendezvous) {
        WU = this->Workunit->GetNextWorkunit(ClientId);
        if (!WU.IsValid) {
            // If a null workunit came in, rendezvous the threads.
            this->threadRendezvous = 1;
            // Workunit came back null -
            sprintf(statusBuffer, "Td %d: out of WU.", this->threadId);
            this->Display->addStatusLine(statusBuffer);
            break;
        }
        if (this->CommandLineData->GetDevDebug()) {
            printf("Thread %d has workunit ID %d\n", this->threadId, WU.WorkUnitID);
        }
        this->RunGPUWorkunit(&WU);

        // If we are NOT aborting, submit the unit.
        // If we are force-exiting, do not submit the workunit!
        if (!global_interface.exit) {
            this->Workunit->SubmitWorkunit(WU);
        }
        this->Display->Refresh();
        //sprintf(this->statusBuffer, "WU rate: %0.1f", this->Workunit->GetAverageRate());
        //this->Display->addStatusLine(this->statusBuffer);
    }

    // Done with cracking - out of workunits.  Clean up & wait.

    // Free memory.
    this->freeThreadAndDeviceMemory();
    // Do final device teardown.
    this->teardownDevice();
    // Report speed of 0.
    this->Display->setThreadCrackSpeed(this->threadId, 0);


    // Wait until all workunits are back from remote systems.
    if (this->Workunit->GetNumberOfCompletedWorkunits() < this->Workunit->GetNumberOfWorkunits()) {
        sprintf(statusBuffer, "Waiting for workunits...");
        this->Display->addStatusLine(statusBuffer);
    }

    while (this->Workunit->GetNumberOfCompletedWorkunits() < this->Workunit->GetNumberOfWorkunits()) {
        CHSleep(1);
        //printf("Completed WU: %d\n", this->Workunit->GetNumberOfCompletedWorkunits());
        //printf("Total WU: %d\n", this->Workunit->GetNumberOfWorkunits());
        this->Display->Refresh();
        // Make termination work properly for the server
        if (global_interface.exit) {
            break;
        }
    }
    this->staticDataInitialized = 0;
}
예제 #4
0
// Runs the multiforcer in standalone or network server mode.
void runStandaloneOrServerMode() {
    int i;

    CHCharsetNew *Charset;
    MFNWorkunitBase *Workunit;
    CHHashFileV *HashFile;
    MFNDisplay *Display;
    char printBuffer[1000];
    MFNHashClassLauncher HashClassLauncher;
    MFNHashIdentifiers *HashIdentifiers;
    MFNNetworkServer *NetworkServer = NULL;
    MFNGeneralInformation *GeneralInformation;
    MFNCommandLineData *CommandLineData;
    
    
    uint32_t hashId;

    int maxPasswordLength = 0;
    
    CommandLineData = MultiforcerGlobalClassFactory.getCommandlinedataClass();

    // Default size.  May be overridden.
    int WorkunitSize = 32;
    std::vector<uint8_t> RestoreData;
    std::string ResumeFilename;

    {
        char ResumeTimestampBuffer[1024];
        struct timeval resume_time;
        time_t resume_time_t;
        // Get the resume filename with timestamp.
        gettimeofday(&resume_time, NULL);
        resume_time_t=resume_time.tv_sec;
        memset(ResumeTimestampBuffer, 0, sizeof(ResumeTimestampBuffer));
        strftime(ResumeTimestampBuffer, 128, "%Y-%m-%d-%H-%M-%S", localtime(&resume_time_t));
        ResumeFilename = "CM-Resume-";
        ResumeFilename += ResumeTimestampBuffer;
        ResumeFilename += ".mfr";
    }

    // Determine the hash type
    HashIdentifiers = MultiforcerGlobalClassFactory.getHashIdentifiersClass();
    if (!HashIdentifiers) {
        printf("Cannot get hash identifiers class!\n");
        exit(1);
    }
    hashId = HashIdentifiers->GetHashIdFromString(CommandLineData->GetHashTypeString());
    if (hashId == MFN_HASHTYPE_UNDEFINED) {
        printf("Invalid hash type %s!\n", CommandLineData->GetHashTypeString().c_str());
        HashIdentifiers->PrintAllHashTypes();
        exit(1);
    }

    GeneralInformation = MultiforcerGlobalClassFactory.getGeneralInformationClass();
    GeneralInformation->setCharsetClassId(CH_CHARSET_NEW_CLASS_ID);
    GeneralInformation->setHashId(hashId);
    GeneralInformation->setPasswordLength(CommandLineData->GetMinPasswordLength());

    
    // Get our classes
    MultiforcerGlobalClassFactory.setCharsetClassType(CH_CHARSET_NEW_CLASS_ID);
    if (CommandLineData->GetDebug()) {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_DEBUG);
    } else if (CommandLineData->GetDaemon()) {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_DAEMON);
    } else {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_CURSES);
    }
    
    // If this is a wordlist class, use a wordlist workunit.
    if (HashIdentifiers->GetHasWordlist()) {
        MultiforcerGlobalClassFactory.setWorkunitClassType(MFN_WORKUNIT_WORDLIST_CLASS_ID);
    } else {
        MultiforcerGlobalClassFactory.setWorkunitClassType(MFN_WORKUNIT_ROBUST_CLASS_ID);
    }

    // Set up the hash specific stuff.
    MultiforcerGlobalClassFactory.setHashfileClassType(HashIdentifiers->GetHashData().HashFileIdentifier);
    HashClassLauncher.setHashType(HashIdentifiers->GetHashData().HashTypeIdentifier);

    
    Charset = MultiforcerGlobalClassFactory.getCharsetClass();
    if (!Charset) {
        printf("Cannot get charset class!\n");
        exit(1);
    }

    Workunit = MultiforcerGlobalClassFactory.getWorkunitClass();
    if (!Workunit) {
        printf("Cannot get workunit class!\n");
        exit(1);
    }
    HashFile = MultiforcerGlobalClassFactory.getHashfileClass();
    if (!HashFile) {
        printf("Cannot get hashfile class!\n");
        exit(1);
    }

    Display = MultiforcerGlobalClassFactory.getDisplayClass();
    if (!Display) {
        printf("Cannot get display class!\n");
        exit(1);
    }
    

    if (CommandLineData->GetDevDebug()) {
        Workunit->EnableDebugOutput();
    }
    
    

/*
    if (CommandLineData->GetUseRestoreFile()) {
        if (!RobustWorkunit->LoadStateFromFile(CommandLineData->GetRestoreFileName())) {
            printf("Loading state from file failed.\n");
            exit(1);
        }
        RestoreData = RobustWorkunit->GetResumeMetadata();
        CommandLineData->SetDataFromRestore(RestoreData);
        // Overwrite the existing one as we progress.
        RobustWorkunit->SetResumeFile(ResumeFilename);
    } else {
        RobustWorkunit->SetResumeFile(ResumeFilename);
    }
*/

    // Set the hash type being used.
    //HashTypes.SetHashId(CommandLineData->GetHashType());

    // Get the HashType class and HashFile class

    /*


*/

    // If an output file is to be used, set it here.
    if (CommandLineData->GetOutputFileName().length()) {
        HashFile->SetFoundHashesOutputFilename(CommandLineData->GetOutputFileName());
    }
    // If the workunit size was set on the command line, use it here.
    if (CommandLineData->GetWorkunitBits()) {
        WorkunitSize = CommandLineData->GetWorkunitBits();
    } else {
        WorkunitSize = HashIdentifiers->GetDefaultWorkunitSizeBits();
    }

    if (!Charset->readCharsetFromFile(CommandLineData->GetCharsetFileName())) {
        printf("Cannot open charset!\n");
        exit(1);
    }
    //printf("Charset opened properly.\n");
    
    if (!HashFile->OpenHashFile(CommandLineData->GetHashListFileName())) {
        printf("Cannot open hash file!\n");
        exit(1);
    }
    //printf("Hashfile opened properly.\n");
    

    // Add hex output option if desired.
    HashFile->SetAddHexOutput(CommandLineData->GetAddHexOutput());
    HashFile->setPrintAlgorithm(CommandLineData->GetPrintAlgorithms());

    // If requested bring the network online and assign types to it
    if (CommandLineData->GetIsNetworkServer()) {
        MultiforcerGlobalClassFactory.setNetworkServerPort(CommandLineData->GetNetworkPort());
        NetworkServer = MultiforcerGlobalClassFactory.getNetworkServerClass();

        NetworkServer->startNetwork();

        // Update the display with the server info.
        char portBuffer[16];
        sprintf(portBuffer, "%d", CommandLineData->GetNetworkPort());
        Display->setSystemMode(SYSTEM_MODE_SERVER, std::string(portBuffer));
    }

    Display->setHashName(HashIdentifiers->GetHashData().HashDescriptor);
    
    if (!CommandLineData->GetIsServerOnly()) {
        if (!HashClassLauncher.addAllDevices(CommandLineData->GetDevicesToUse())) {
            printf("Cannot add devices!\n");
            exit(1);
        }
    }


    // Pick the desired max length.
    if (CommandLineData->GetMaxPasswordLength()) {
        maxPasswordLength = CommandLineData->GetMaxPasswordLength();
    } else {
        maxPasswordLength = HashIdentifiers->GetMaxSupportedLength();
    }
    
    for (i = CommandLineData->GetMinPasswordLength(); i <= maxPasswordLength; i++) {
        uint64_t NumberOfPasswords;
        
        GeneralInformation->setPasswordLength(i);
        Display->setPasswordLen(i);
        // Set the status line to indicate where we are.
        sprintf(printBuffer, "Starting pw len %d", i);
        Display->addStatusLine(printBuffer);

        // If no hashes are left, exit.
        if (HashFile->GetUncrackedHashCount() == 0) {
            global_interface.exit = 1;
            strcpy(global_interface.exit_message, "All hashes found!  Exiting!\n");
            break;
        }
        
        NumberOfPasswords = Charset->getPasswordSpaceSize(i);
        // Check for errors from charset and break.
        if (global_interface.exit) {
            break;
        }
/*
        // Provide the correct metadata to the workunit class
        RestoreData = CommandLineData->GetRestoreData(i);
        RobustWorkunit->SetResumeMetadata(RestoreData);

        // If we are NOT restoring, create new workunits.
        if (!CommandLineData->GetUseRestoreFile()) {
            RobustWorkunit->CreateWorkunits(NumberOfPasswords, WorkunitSize, i);
        }

        if (global_interface.exit) {
            break;
        }
*/
        Workunit->CreateWorkunits(NumberOfPasswords, WorkunitSize, i);
        
        // If there are threads running locally, launch them.
        if (!CommandLineData->GetIsServerOnly()) {
            HashClassLauncher.launchThreads(i);
        }
        // Wait until all the workunits are completed.  This is useful in
        // server-only mode.
        while (Workunit->GetNumberOfCompletedWorkunits() < Workunit->GetNumberOfWorkunits()) {
            CHSleep(1);
            Display->Refresh();
            // Make termination work properly for the server
            if (global_interface.exit) {
                // Break from the while loop.
                break;
            }
        }

        // Break from the password length loop.
        if (global_interface.exit) {
            break;
        }
    }

    delete Workunit;
    delete Display;

    HashFile->PrintAllFoundHashes();
    
    // If we are outputting unfound hashes, do it now.
    if (CommandLineData->GetUnfoundOutputFileName().length()) {
        HashFile->OutputUnfoundHashesToFile(CommandLineData->GetUnfoundOutputFileName());
    }
}
예제 #5
0
/**
 * This function will wait for a server to arrive, do all the setup for the
 * instance, and will continue until the server disconnects, at which point this
 * function will return.
 */
void runNetworkOneConnect() {
    MFNCommandLineData *CommandLineData;
    MFNNetworkClient *NetworkClient;
    MFNHashIdentifiers *HashIdentifiers;
    MFNGeneralInformation *GeneralInformation;
    MFNDisplay *Display;
    MFNHashClassLauncher HashClassLauncher;

    CommandLineData = MultiforcerGlobalClassFactory.getCommandlinedataClass();
    GeneralInformation = MultiforcerGlobalClassFactory.getGeneralInformationClass();
    HashIdentifiers = MultiforcerGlobalClassFactory.getHashIdentifiersClass();
    
    // Set up the network client class.  This will wait until a connection is
    // created, or until the user exits.
    MultiforcerGlobalClassFactory.setNetworkClientPort(CommandLineData->GetNetworkPort());
    MultiforcerGlobalClassFactory.setNetworkClientRemoteHost(CommandLineData->GetNetworkRemoteHostname());
    MultiforcerGlobalClassFactory.setNetworkClientOneshot(0);
    NetworkClient = MultiforcerGlobalClassFactory.getNetworkClientClass();
   
    // If the user has requested an exit, we can just terminate now.
    if (global_interface.user_exit) {
        exit(0);
    }
    
    // Update the general information structure to continue setup.
    NetworkClient->updateGeneralInfo();
    
    // Set the hash ID from the server, and continue setting up the system.
    HashIdentifiers->SetHashId(GeneralInformation->getHashId());
    
    MultiforcerGlobalClassFactory.setCharsetClassType(CH_CHARSET_NEW_CLASS_ID);
    
    if (CommandLineData->GetDebug()) {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_DEBUG);
    } else if (CommandLineData->GetDaemon()) {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_DAEMON);
    } else {
        MultiforcerGlobalClassFactory.setDisplayClassType(MFN_DISPLAY_CLASS_CURSES);
    }
    
    MultiforcerGlobalClassFactory.setWorkunitClassType(MFN_WORKUNIT_NETWORK_CLASS_ID);

    // Set up the hash specific stuff based on the general information.
    MultiforcerGlobalClassFactory.setHashfileClassType(HashIdentifiers->GetHashData().HashFileIdentifier);
    
    HashClassLauncher.setHashType(HashIdentifiers->GetHashData().HashTypeIdentifier);
    
    NetworkClient->updateCharset();

    // TODO: This should handle non-existent devices cleanly.
    if (!HashClassLauncher.addAllDevices(CommandLineData->GetDevicesToUse())) {
        printf("Cannot add devices!\n");
        exit(1);
    }

    // Display online - no more output!
    Display = MultiforcerGlobalClassFactory.getDisplayClass();
    Display->setHashName(HashIdentifiers->GetHashData().HashDescriptor);


    // Loop until the server has gone away
    while (!global_interface.exit) {
        // Get the password length/etc.
        NetworkClient->updateGeneralInfo();
        
        MultiforcerGlobalClassFactory.getWorkunitClass()->
                setPasswordLength(GeneralInformation->getPasswordLength());
        
        Display->setPasswordLen(GeneralInformation->getPasswordLength());

        NetworkClient->updateUncrackedHashes();
        
        // Only run if there are hashes to crack.
        if (MultiforcerGlobalClassFactory.getHashfileClass()->GetUncrackedHashCount()) {
            HashClassLauncher.launchThreads(GeneralInformation->getPasswordLength());
        } else {
            // No hashes left - why should we continue to try and run?
            CHSleep(5);
            break;
        }
    }
    // Reset for future runs.
    global_interface.exit = 0;
    
    // Out of the main loop - clean up.
    MultiforcerGlobalClassFactory.destroyCharsetClass();
    MultiforcerGlobalClassFactory.destroyHashfileClass();
    MultiforcerGlobalClassFactory.destroyNetworkClientClass();
    MultiforcerGlobalClassFactory.destroyWorkunitClass();
    MultiforcerGlobalClassFactory.destroyDisplayClass();
}