int MFNHashTypePlainCUDA::setCUDADeviceID(int newCUDADeviceId) {
    trace_printf("MFNHashTypePlainCUDA::setCUDADeviceID(%d)\n", newCUDADeviceId);
    
    CHCUDAUtils *CudaUtils = MultiforcerGlobalClassFactory.getCudaUtilsClass();
    MFNCommandLineData *CommandLineData = MultiforcerGlobalClassFactory.getCommandlinedataClass();
    
    if (newCUDADeviceId >= CudaUtils->getCudaDeviceCount()) {
        printf("Invalid device ID - greater than number of devices!\n");
        exit(1);
    }
    
    this->gpuDeviceId = newCUDADeviceId;
    
    // If the blocks or threads are set, use them, else use the default.
    if (CommandLineData->GetGpuBlocks()) {
        this->GPUBlocks = CommandLineData->GetGpuBlocks();
        klaunch_printf("Using CLI GPU Blocks %d\n", this->GPUBlocks);
    } else {
        this->GPUBlocks = CudaUtils->getCudaDefaultBlockCount(newCUDADeviceId);
        klaunch_printf("Using Default GPU Blocks %d\n", this->GPUBlocks);
    }
    
    if (CommandLineData->GetGpuThreads()) {
        this->GPUThreads = CommandLineData->GetGpuThreads();
        klaunch_printf("Using CLI GPU Threads %d\n", this->GPUThreads);
    } else {
        this->GPUThreads = CudaUtils->getCudaDefaultThreadCount(newCUDADeviceId);
        klaunch_printf("Using Default GPU Threads %d\n", this->GPUThreads);
    }
    
    // If target time is 0, use defaults.
    if (CommandLineData->GetTargetExecutionTimeMs()) {
        this->kernelTimeMs = CommandLineData->GetTargetExecutionTimeMs();
    } else {
        if (CudaUtils->getCudaHasTimeout(newCUDADeviceId)) {
            this->kernelTimeMs = 50;
        } else {
            this->kernelTimeMs = 500;
        }
    }
    
    // Override thread count if needed for hash type.
    this->GPUThreads = this->getMaxHardwareThreads(this->GPUThreads);
    
    this->VectorWidth = 1;
    this->TotalKernelWidth = this->GPUBlocks * this->GPUThreads * this->VectorWidth;
    
    //printf("Successfully added device %d, thread ID %d\n", newCUDADeviceId, this->threadId);
    //printf("Thread %d blocks/threads/vec: %d/%d/%d\n", this->threadId, this->GPUBlocks, this->GPUThreads, this->VectorWidth);

    return 1;
}
void MFNHashTypePlainCUDA::setupDevice() {
    trace_printf("CHHashTypeVPlainCUDA::setupDevice()\n");

    CHCUDAUtils *CudaUtils = MultiforcerGlobalClassFactory.getCudaUtilsClass();

    // Set the CUDA device
    trace_printf("Thread %d setting device to %d\n",this->threadId, this->gpuDeviceId);
    cudaSetDevice(this->gpuDeviceId);

    // If the user requests zerocopy and the device can handle it, add it.
    if (this->CommandLineData->GetUseZeroCopy() &&
            CudaUtils->getCudaCanMapHostMemory(this->gpuDeviceId)) {
        this->useZeroCopy = 1;
    }

    // If the device is integrated & can map memory, add it - integrated devices
    // are already sharing host memory, so no point in copying the data over.
    if (CudaUtils->getCudaIsIntegrated(this->gpuDeviceId) &&
            CudaUtils->getCudaCanMapHostMemory(this->gpuDeviceId)) {
        this->useZeroCopy = 1;
    }

    // Enable blocking sync.  This dramatically reduces CPU usage.
    // If zero copy is being used, set DeviceMapHost as well
    if (this->useZeroCopy) {
        cudaSetDeviceFlags(cudaDeviceBlockingSync | cudaDeviceMapHost);
    } else {
        cudaSetDeviceFlags(cudaDeviceBlockingSync);
    }

}
void MFNWorkunitNetworkClient::SubmitWorkunitById(uint64_t completedWorkunitId) {
    trace_printf("MFNWorkunitNetworkClient::SubmitWorkunitById(%u)\n", completedWorkunitId);
    
    std::list<MFNWorkunitRobustElement>::iterator inflightWorkunit;

    this->workunitMutexBoost.lock();

    // Look for workunit in the list
    for (inflightWorkunit = this->assignedWorkunits.begin(); inflightWorkunit != this->assignedWorkunits.end(); inflightWorkunit++) {
        // Check for the unique Workunit ID
        if (inflightWorkunit->WorkUnitID == completedWorkunitId) {
            if (this->DebugOutput) {
                printf("Found inflight WU ID: %lu\n", inflightWorkunit->WorkUnitID);
            }
            this->assignedWorkunits.erase(inflightWorkunit);
            if (this->DebugOutput) {
                printf("Inflight left: %d\n", this->assignedWorkunits.size());
            }
            break;
        }
    }
    if (this->DebugOutput) {
        this->PrintInternalState();
    }
    this->workunitMutexBoost.unlock();
    MultiforcerGlobalClassFactory.getNetworkClientClass()->
        submitWorkunit(completedWorkunitId);   
}
Exemple #4
0
// Needed for the Windows side of things.
void MFNRun()
{
    if (MultiforcerGlobalClassFactory.getCommandlinedataClass()->GetIsNetworkClient()) {
        runNetworkClientMode();
    } else {
        runStandaloneOrServerMode();
    }
}
int MFNHashTypePlainOpenCL::setOpenCLDeviceID(int newOpenCLPlatformId, int newOpenCLDeviceId) {
    trace_printf("MFNHashTypePlainOpenCL::setOpenCLDeviceID(%d, %d)\n", newOpenCLPlatformId, newOpenCLDeviceId);
    
    MFNCommandLineData *CommandLineData = MultiforcerGlobalClassFactory.getCommandlinedataClass();
    
    this->OpenCL = new CryptohazeOpenCL();

    if (newOpenCLPlatformId > this->OpenCL->getNumberOfPlatforms()) {
        printf("Error: OpenCL Platform ID %d not valid!\n", newOpenCLPlatformId);
        exit(1);
    }

    this->OpenCL->selectPlatformById(newOpenCLPlatformId);

    if (newOpenCLDeviceId > this->OpenCL->getNumberOfDevices()) {
        printf("Error: OpenCL Device ID %d not valid!\n", newOpenCLDeviceId);
        exit(1);
    }

    this->OpenCL->selectDeviceById(newOpenCLDeviceId);

    this->openCLPlatformId = newOpenCLPlatformId;
    this->gpuDeviceId = newOpenCLDeviceId;
    
 
    // If the blocks or threads are set, use them, else use the default.
    if (CommandLineData->GetGpuBlocks()) {
        this->GPUBlocks = CommandLineData->GetGpuBlocks();
    } else {
        this->GPUBlocks = this->OpenCL->getDefaultBlockCount();
    }

    if (CommandLineData->GetGpuThreads()) {
        this->GPUThreads = CommandLineData->GetGpuThreads();
    } else {
        this->GPUThreads = this->OpenCL->getDefaultThreadCount();
    }

    // If target time is 0, use defaults.
    if (CommandLineData->GetTargetExecutionTimeMs()) {
        this->kernelTimeMs = CommandLineData->GetTargetExecutionTimeMs();
    } else {
        this->kernelTimeMs = 100;
    }

    this->OpenCL->createContext();
    this->OpenCL->createCommandQueue();
 
    // For now - set by CLI later.
    this->VectorWidth = 4;

    this->TotalKernelWidth = this->GPUBlocks * this->GPUThreads * this->VectorWidth;

    trace_printf("Thread %d added OpenCL Device (%d, %d)\n", this->threadId,
            newOpenCLPlatformId, newOpenCLDeviceId);;

    return 1;
}
MFNHashTypePlainOpenCL::MFNHashTypePlainOpenCL(int hashLengthBytes) :  MFNHashTypePlain(hashLengthBytes) {
    trace_printf("MFNHashTypePlainOpenCL::MFNHashTypePlainOpenCL(%d)\n", hashLengthBytes);

    this->MFNHashTypeMutex.lock();
    this->threadId = MultiforcerGlobalClassFactory.getDisplayClass()->getFreeThreadId(GPU_THREAD);
    this->numberThreads++;
    trace_printf("MFNHashType GPU/OpenCL Thread ID %d\n", this->threadId);
    this->MFNHashTypeMutex.unlock();

}
void MFNHashTypePlainOpenCL::setupDevice() {
    trace_printf("CHHashTypeVPlainCUDA::setupDevice()\n");
    char buildOptions[1024];
    cl_int errorCode;

    // Set the OpenCL platform & device
    trace_printf("Thread %d setting OpenCL platform/device to %d, %d\n",
            this->threadId, this->openCLPlatformId, this->gpuDeviceId);
    this->OpenCL->selectPlatformById(this->openCLPlatformId);
    this->OpenCL->selectDeviceById(this->gpuDeviceId);
    
    /**
     * Handle generating the kernels.  This involves building with the specified
     * password length, vector width, and BFI_INT status.
     */

    if (MultiforcerGlobalClassFactory.getCommandlinedataClass()->GetUseBfiInt()) {
        // BFI_INT patching - pass BITALIGN to kernel
        sprintf(buildOptions, "-D PASSWORD_LENGTH=%d -D VECTOR_WIDTH=%d -D BITALIGN=1",
            this->passwordLength, this->VectorWidth);
    } else {
        // No BFI_INT patching.
        sprintf(buildOptions, "-D PASSWORD_LENGTH=%d -D VECTOR_WIDTH=%d",
                this->passwordLength, this->VectorWidth);
    }
    this->OpenCL->buildProgramFromManySourcesConcat(this->getHashFileNames(), buildOptions);

    // If the BFI_INT patching is being used, patch the generated binary.
    if (MultiforcerGlobalClassFactory.getCommandlinedataClass()->GetUseBfiInt()) {
        this->OpenCL->doAMDBFIPatch();
    }

    this->HashProgram = this->OpenCL->getProgram();
    this->HashKernel = clCreateKernel (this->HashProgram, this->getHashKernelName().c_str(), &errorCode);

    if (errorCode != CL_SUCCESS) {
        printf("Error: %s\n", print_cl_errstring(errorCode));
        exit(1);
    }

}
void MFNHashTypePlainCUDA::outputFoundHashes() {
    trace_printf("MFNHashTypePlain::outputFoundHashes()\n");
    uint32_t i, j;

    /**
     * A vector containing the hash, processed back into the raw format.
     */
    std::vector<uint8_t> rawHash;
    std::vector<uint8_t> foundPassword;

    uint8_t *hostSuccessArray = this->HostSuccessAddress;
    uint8_t *hostSuccessReportedArray = this->HostSuccessReportedAddress;
    uint8_t *hostPasswords = this->HostFoundPasswordsAddress;

    for (i = 0; i < this->activeHashesProcessed.size(); i++) {
        if (hostSuccessArray[i] && !hostSuccessReportedArray[i]) {
            rawHash = this->postProcessHash(this->activeHashesProcessed[i]);
            // Resize to the max pass length + 1 - this ensures that strlen
            // will find a null byte at the end and stop measuring.
            foundPassword.resize(this->maxFoundPlainLength + 1, 0);
            for (j = 0; j < this->maxFoundPlainLength; j++) {
                foundPassword[j] = hostPasswords[this->maxFoundPlainLength * i + j];
            }
            // Resize to the length of the found password.
            foundPassword.resize(strlen((char *)&foundPassword[0]));
            this->HashFile->ReportFoundPassword(rawHash, foundPassword, hostSuccessArray[i]);
            // Report the found hash over the network.
            if (this->CommandLineData->GetIsNetworkClient()) {
                MultiforcerGlobalClassFactory.getNetworkClientClass()->
                        submitFoundHash(rawHash, foundPassword, hostSuccessArray[i]);
            }
            this->Display->addCrackedPassword(foundPassword);
            hostSuccessReportedArray[i] = 1;
        }
    }

    // Check to see if we should exit (as all hashes are found).
    if (this->HashFile->GetUncrackedHashCount() == 0) {
      //global_interface.exit = 1;
    }
}
Exemple #9
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());
    }
}
Exemple #10
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();
}
struct MFNWorkunitRobustElement MFNWorkunitNetworkClient::GetNextWorkunit(uint32_t NetworkClientId) {
    trace_printf("MFNWorkunitNetworkClient::GetNextWorkunit(%u)\n", NetworkClientId);

    struct MFNWorkunitRobustElement Workunit;

    this->workunitMutexBoost.lock();
    
    if (this->pendingWorkunits.size() == 0) {
        network_printf("No workunits - trying to fetch 10.\n");
        MultiforcerGlobalClassFactory.getNetworkClientClass()->
                fetchWorkunits(MFN_NETWORK_WORKUNIT_NUMBER_WUS_TO_FETCH, 
                this->CurrentPasswordLength);
        network_printf("pending size: %d\n", this->pendingWorkunits.size());
    }

    // Check to see if there are valid workunits left.
    if (this->pendingWorkunits.size() == 0) {
        // If not, return a unit with the specified flags.
        if (this->DebugOutput) {
            printf("pendingWorkunits.size() == 0; returning.\n");
        }
        memset(&Workunit, 0, sizeof(MFNWorkunitRobustElement));
        // Set the flags specified if needed.
        if (this->returnWaitWorkunit) {
            Workunit.Flags = WORKUNIT_DELAY;
        } else if (this->returnTerminateWorkunit) {
            Workunit.Flags = WORKUNIT_TERMINATE;
        }
        // Otherwise, return a null workunit, which is a term signal too.
        this->workunitMutexBoost.unlock();
        if (this->DebugOutput) {
            PrintRobustWorkunit(Workunit);
        }
        return Workunit;
    }

    // We still have workunits left.

    // Get the next waiting workunit from the main queue.
    Workunit = this->pendingWorkunits.front();
    this->pendingWorkunits.pop_front();

    if (this->DebugOutput) {
        printf("Popped WU ID %lu\n", Workunit.WorkUnitID);
    }

    // Set some variables we can make use of.
    Workunit.IsAssigned = 1;

    // Add the workunit to the in-flight queue.
    this->assignedWorkunits.push_back(Workunit);
    if (this->DebugOutput) {
        printf("In flight WUs: %lu\n", this->assignedWorkunits.size());
    }  

    this->workunitMutexBoost.unlock();
    if (this->DebugOutput) {
        PrintRobustWorkunit(Workunit);
    }
    return Workunit;
}