diff --git a/Makefile b/Makefile index 355b1e7..def1cbe 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,18 @@ # Standard flags -CPPFLAGS = -std=gnu++11 -O3 -pthread -g3 -Wall -c -fmessage-length=0 -MMD -fPIC +CPPFLAGS = -std=gnu++11 -O3 -g3 -Wall -c -fmessage-length=0 -MMD -fPIC PYTHON = /usr/include/python2.7 BOOST_INC = /usr/include +OBJS = ./prngs/LSBState.o ./prngs/GlibcRand.o ./prngs/Mt19937.o ./prngs/Ruby.o ./PRNGFactory.o ./Untwister.o +all: GlibcRand Mt19937 RubyRand LSBState PRNGFactory Untwister + # Make the cli binary + g++ $(CPPFLAGS) -pthread -MF"main.d" -MT"main.d" -o "main.o" "./main.cpp" + g++ -std=gnu++11 -O3 -pthread $(OBJS) main.o -o untwister -all: GlibcRand Mt19937 RubyRand LSBState PRNGFactory - # Make the binary - g++ $(CPPFLAGS) -MF"untwister.d" -MT"untwister.d" -o "untwister.o" "./untwister.cpp" - g++ -std=gnu++11 -O3 -pthread -o "untwister" ./prngs/LSBState.o ./prngs/GlibcRand.o ./prngs/Mt19937.o ./prngs/Ruby.o ./PRNGFactory.o ./untwister.o +python: GlibcRand Mt19937 RubyRand LSBState PRNGFactory Untwister + # Make the shared object + g++ $(CPPFLAGS) -I$(PYTHON) -I$(BOOST_INC) py-untwister.cpp -o py-untwister.o + g++ -std=c++11 -shared -fPIC -O3 $(OBJS) py-untwister.o -lboost_python -lpython2.7 -o untwister.so GlibcRand: g++ $(CPPFLAGS) -MF"prngs/GlibcRand.d" -MT"prngs/GlibcRand.d" -o "prngs/GlibcRand.o" "./prngs/GlibcRand.cpp" @@ -23,14 +28,9 @@ LSBState: PRNGFactory: g++ $(CPPFLAGS) -MF"PRNGFactory.d" -MT"PRNGFactory.d" -o "PRNGFactory.o" "./PRNGFactory.cpp" - g++ $(CPPFLAGS) -MF"untwister.d" -MT"untwister.d" -o "untwister.o" "./untwister.cpp" - g++ -std=gnu++11 -O3 -pthread -o "untwister" ./prngs/LSBState.o ./prngs/GlibcRand.o ./prngs/Mt19937.o ./prngs/Ruby.o ./PRNGFactory.o ./untwister.o -python: GlibcRand Mt19937 RubyRand LSBState PRNGFactory - # Make the shared object - g++ $(CPPFLAGS) -I$(PYTHON) -I$(BOOST_INC) py-untwister.cpp -o py-untwister.o - g++ -std=c++11 -shared -fPIC -O3 ./prngs/LSBState.o ./prngs/GlibcRand.o ./prngs/Mt19937.o ./prngs/Ruby.o ./PRNGFactory.o py-untwister.o \ - -lboost_python -lpython2.7 -o untwister.so +Untwister: + g++ $(CPPFLAGS) -pthread -MF"Untwister.d" -MT"Untwister.d" -o "Untwister.o" "./Untwister.cpp" clean: diff --git a/README.md b/README.md index 84717fd..a268089 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ Untwister ========= -Multi-threaded seed recovery tool for common PRNGs +Multi-threaded seed recovery tool for common PRNGs. + Supported PRNGs ================= -* Glibc rand() -* Mersenne Twister (mt19937) -* Ruby's MT-variant rand() +* Glibc's rand() +* Mersenne Twister (MT19937) +* Ruby's MT-variant DEFAULT::rand() Usage ======== @@ -53,9 +54,19 @@ Examples: Python Bindings ================= * Python 2.7 -* Requires Boost C++ Python library (use apt-get or brew) +* Requires Boost C++ Python library + +### Ubuntu/Debian Linux ``` +sudo apt-get install python-dev libboost-python-dev +make python +``` + +### OSX + +``` +brew install boost --with-python make python ``` diff --git a/untwister.h b/Untwister.cpp similarity index 50% rename from untwister.h rename to Untwister.cpp index 698b36a..4ec6f99 100644 --- a/untwister.h +++ b/Untwister.cpp @@ -15,62 +15,106 @@ along with this program. If not, see . */ -#ifndef UNTWISTER_H_ -#define UNTWISTER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ConsoleColors.h" // TODO: Sepearte logic from stdout calls -#include "PRNGFactory.h" -#include "prngs/PRNG.h" - -using std::chrono::seconds; -using std::chrono::milliseconds; -using std::chrono::duration_cast; -using std::chrono::steady_clock; - -// Pair of -typedef std::pair Seed; - -static std::vector observedOutputs; -static const unsigned int ONE_YEAR = 31536000; +#include "Untwister.h" -/* Yeah lots of parameters, but such is the life of a thread */ -void BruteForce(unsigned int id, std::atomic *isCompleted, std::vector* > *answers, - std::vector *status, double minimumConfidence, uint32_t startingSeed, uint32_t endingSeed, - uint32_t depth, std::string rng) +Untwister::Untwister() { + isCompleted = new std::atomic(false); + isRunning = new std::atomic(false); + observedOutputs = new std::vector; + depth = DEFAULT_DEPTH; + minConfidence = DEFAULT_MIN_CONFIDENCE; + threads = std::thread::hardware_concurrency(); + prng = "glibc-rand"; + answers = new std::vector* >(threads); + status = new std::vector(threads); +} + +Untwister::Untwister(unsigned int observationSize) +{ + isCompleted = new std::atomic(false); + isRunning = new std::atomic(false); + observedOutputs = new std::vector(observationSize); + depth = DEFAULT_DEPTH; + minConfidence = DEFAULT_MIN_CONFIDENCE; + threads = std::thread::hardware_concurrency(); + prng = "glibc-rand"; + answers = new std::vector* >(threads); + status = new std::vector(threads); +} + +Untwister::~Untwister() +{ + delete isCompleted; + delete isRunning; + delete observedOutputs; + delete answers; + delete status; +} + +std::vector Untwister::bruteforce(uint32_t lowerBoundSeed, uint32_t upperBoundSeed) +{ + + std::vector labor = divisionOfLabor(upperBoundSeed - lowerBoundSeed, threads); + uint32_t startAt = lowerBoundSeed; + + std::vector pool = std::vector(threads); + for(unsigned int id = 0; id < threads; ++id) + { + uint32_t endAt = startAt + labor.at(id); + pool[id] = std::thread(&Untwister::worker, this, id, startAt, endAt); + startAt += labor.at(id); + } + + isRunning->store(true, std::memory_order_relaxed); + + for(unsigned int id = 0; id < pool.size(); ++id) + { + pool[id].join(); + } + + std::vector results = std::vector(); + for (unsigned int id = 0; id < answers->size(); ++id) + { + for (unsigned int index = 0; index < answers->at(id)->size(); ++index) + { + results.push_back(answers->at(id)->at(index)); + } + delete answers->at(id); + } + + return results; +} + +/* This method is execute as a seperate thread */ +void Untwister::worker(unsigned int id, uint32_t startingSeed, uint32_t endingSeed) +{ + /* Each thread must have a local factory unless you like mutexes and/or segfaults */ PRNGFactory factory; - PRNG *generator = factory.getInstance(rng); - answers->at(id) = new std::vector; + PRNG *generator = factory.getInstance(prng); + answers->at(id) = new std::vector(); for(uint32_t seedIndex = startingSeed; seedIndex <= endingSeed; ++seedIndex) { if(isCompleted->load(std::memory_order_relaxed)) + { break; // Some other thread found the seed + } generator->seed(seedIndex); uint32_t matchesFound = 0; for(uint32_t index = 0; index < depth; index++) { uint32_t nextRand = generator->random(); - uint32_t observed = observedOutputs[matchesFound]; + uint32_t observed = observedOutputs->at(matchesFound); if(observed == nextRand) { matchesFound++; - if(matchesFound == observedOutputs.size()) + if(matchesFound == observedOutputs->size()) { break; // This seed is a winner if we get to the end } @@ -78,13 +122,13 @@ void BruteForce(unsigned int id, std::atomic *isCompleted, std::vectorat(id) = seedIndex - startingSeed; - double confidence = ((double) matchesFound / (double) observedOutputs.size()) * 100.0; - if(minimumConfidence <= confidence) + double confidence = ((double) matchesFound / (double) observedOutputs->size()) * 100.0; + if(minConfidence <= confidence) { Seed seed = Seed(seedIndex, confidence); answers->at(id)->push_back(seed); } - if(matchesFound == observedOutputs.size()) + if(matchesFound == observedOutputs->size()) { isCompleted->store(true, std::memory_order_relaxed); } @@ -92,51 +136,6 @@ void BruteForce(unsigned int id, std::atomic *isCompleted, std::vector DivisionOfLabor(uint32_t sizeOfWork, uint32_t numberOfWorkers) -{ - uint32_t work = sizeOfWork / numberOfWorkers; - uint32_t leftover = sizeOfWork % numberOfWorkers; - std::vector labor(numberOfWorkers); - for(uint32_t index = 0; index < numberOfWorkers; ++index) - { - if(0 < leftover) - { - labor[index] = work + 1; - --leftover; - } - else - { - labor[index] = work; - } - } - return labor; -} - -/* Generic Threading */ -void StartBruteForce(unsigned int threads, std::vector* > *answers, double minimumConfidence, - uint32_t lowerBoundSeed, uint32_t upperBoundSeed, uint32_t depth, std::string rng) -{ - std::atomic *isCompleted = new std::atomic(false); - std::vector pool(threads); - std::vector *status = new std::vector(threads); - std::vector labor = DivisionOfLabor(upperBoundSeed - lowerBoundSeed, threads); - uint32_t startAt = lowerBoundSeed; - - for(unsigned int id = 0; id < threads; ++id) - { - uint32_t endAt = startAt + labor.at(id); - pool[id] = std::thread(BruteForce, id, isCompleted, answers, status, minimumConfidence, startAt, endAt, depth, rng); - startAt += labor.at(id); - } - - for(unsigned int id = 0; id < pool.size(); ++id) - { - pool[id].join(); - } - delete status; -} - /* This is the "smarter" method of breaking RNGs. We use consecutive integers @@ -144,18 +143,17 @@ void StartBruteForce(unsigned int threads, std::vector* > *ans method, however, we won't typically recover an actual seed value. But the effect is the same. */ -bool InferState(const std::string& rng) +bool Untwister::inferState() { - std::cout << INFO << "Trying state inference" << std::endl; PRNGFactory factory; - PRNG *generator = factory.getInstance(rng); + PRNG *generator = factory.getInstance(prng); uint32_t stateSize = generator->getStateSize(); - if(observedOutputs.size() <= stateSize) + if(observedOutputs->size() <= stateSize) { - std::cout << WARN << "Not enough observed values to perform state inference." << std::endl; - std::cout << WARN << "Try again with more than " << stateSize << " values" << std::endl; + std::cout << WARN << "Not enough observed values to perform state inference," + << " try again with more than " << stateSize << " values." << std::endl; return false; } @@ -164,36 +162,36 @@ bool InferState(const std::string& rng) /* Guaranteed from the above to loop at least one time */ std::vector scores; std::vector best_state; - for(uint32_t i = 0; i < (observedOutputs.size() - stateSize); i++) + for(uint32_t index = 0; index < (observedOutputs->size() - stateSize); ++index) { - std::vector::const_iterator first = observedOutputs.begin() + i; - std::vector::const_iterator last = observedOutputs.begin() + i + stateSize; + std::vector::const_iterator first = observedOutputs->begin() + index; + std::vector::const_iterator last = observedOutputs->begin() + index + stateSize; std::vector state(first, last); /* Make predictions based on the state */ std::vector evidenceForward - ((std::vector::const_iterator)observedOutputs.begin(), first); + ((std::vector::const_iterator)observedOutputs->begin(), first); std::vector evidenceBackward - (last+1, (std::vector::const_iterator)observedOutputs.end()); + (last+1, (std::vector::const_iterator)observedOutputs->end()); generator->setState(state); /* Provide additional evidence for tuning on PRNGs that require it */ - generator->setEvidence(observedOutputs); + generator->setEvidence((*observedOutputs)); generator->tune(evidenceForward, evidenceBackward); std::vector predictions_forward = - generator->predictForward(((observedOutputs.size() - stateSize) - i)); - std::vector predictions_backward = - generator->predictBackward(i); + generator->predictForward(((observedOutputs->size() - stateSize) - index)); + + std::vector predictions_backward = generator->predictBackward(index); /* Test the prediction against the rest of the observed data */ /* Forward */ uint32_t matchesFound = 0; uint32_t index_pred = 0; - uint32_t index_obs = i + stateSize; - while(index_obs < observedOutputs.size() && index_pred < predictions_forward.size()) + uint32_t index_obs = index + stateSize; + while(index_obs < observedOutputs->size() && index_pred < predictions_forward.size()) { - if(observedOutputs[index_obs] == predictions_forward[index_pred]) + if(observedOutputs->at(index_obs) == predictions_forward[index_pred]) { matchesFound++; index_obs++; @@ -203,10 +201,10 @@ bool InferState(const std::string& rng) /* Backward */ index_pred = 0; - index_obs = i; + index_obs = index; while(index_obs > 0 && index_pred < predictions_backward.size()) { - if(observedOutputs[index_obs] == predictions_backward[index_pred]) + if(observedOutputs->at(index_obs) == predictions_backward[index_pred]) { matchesFound++; index_obs--; @@ -215,7 +213,7 @@ bool InferState(const std::string& rng) } /* If we get a perfect guess, then try reversing out the seed, and exit */ - if(matchesFound == (observedOutputs.size() - stateSize)) + if(matchesFound == (observedOutputs->size() - stateSize)) { uint32_t outSeed = 0; if(generator->reverseToSeed(&outSeed, 10000)) @@ -235,7 +233,7 @@ bool InferState(const std::string& rng) return true; } - double score = (double)(matchesFound*100) / (double)(observedOutputs.size() - stateSize); + double score = (double)(matchesFound*100) / (double)(observedOutputs->size() - stateSize); scores.push_back(score); if(score > highscore) { @@ -259,33 +257,42 @@ bool InferState(const std::string& rng) std::cout << INFO << "State Inference failed" << std::endl; } + delete generator; return false; } -/* For easier testing, will generate a series of random numbers at a given seed and depth*/ -std::vector GenerateSample(uint32_t seed, uint32_t depth, std::string rng) +std::vector Untwister::generateSampleFromSeed(uint32_t seed) { PRNGFactory factory; - PRNG *generator = factory.getInstance(rng); + PRNG *generator = factory.getInstance(prng); generator->seed(seed); + PRNG *distance_gen = factory.getInstance(prng); + distance_gen->seed(time(NULL)); + uint32_t distance = distance_gen->random() % (depth - 10); - std::vector results; - for (unsigned int index = 0; index < depth; ++index) + // Burn a bunch of random numbers + for (uint32_t index = 0; index < distance; ++index) + { + generator->random(); + } + + std::vector results(10); + for (unsigned int index = 0; index < 10; ++index) { results.push_back(generator->random()); } delete generator; + delete distance_gen; return results; } -/* For easier testing, will generate a series of random numbers at a given state and depth */ -std::vector GenerateSample(std::vector state, uint32_t depth, std::string rng) +std::vector Untwister::generateSampleFromState() { PRNGFactory factory; - PRNG *generator = factory.getInstance(rng); - generator->setState(state); + PRNG *generator = factory.getInstance(prng); + generator->setState((*observedOutputs)); - std::vector results; + std::vector results(depth); for (unsigned int index = 0; index < depth; ++index) { results.push_back(generator->random()); @@ -294,4 +301,121 @@ std::vector GenerateSample(std::vector state, uint32_t depth return results; } -#endif /* UNTWISTER_H_ */ +/* Divide X work among Y number of threads, and evenly distribute remainders */ +std::vector Untwister::divisionOfLabor(uint32_t sizeOfWork, uint32_t numberOfWorkers) +{ + uint32_t work = sizeOfWork / numberOfWorkers; + uint32_t leftover = sizeOfWork % numberOfWorkers; + std::vector labor(numberOfWorkers); + for(uint32_t index = 0; index < numberOfWorkers; ++index) + { + if (0 < leftover) + { + labor[index] = work + 1; + --leftover; + } + else + { + labor[index] = work; + } + } + return labor; +} + +void Untwister::addObservedOutput(uint32_t observedOutput) +{ + observedOutputs->push_back(observedOutput); +} + +std::vector* Untwister::getObservedOutputs() +{ + return observedOutputs; +} + +std::vector* Untwister::getStatus() +{ + return status; +} + +std::vector Untwister::getPRNGNames() +{ + PRNGFactory factory; + return factory.getNames(); +} + + +void Untwister::setPRNG(std::string prng) +{ + if (isSupportedPRNG(prng)) + { + this->prng = prng; + } +} + +void Untwister::setPRNG(char *prng) +{ + setPRNG(std::string(prng)); +} + +std::string Untwister::getPRNG() +{ + return prng; +} + +bool Untwister::isSupportedPRNG(char *prng) +{ + return isSupportedPRNG(std::string(prng)); +} + +bool Untwister::isSupportedPRNG(std::string prng) +{ + std::vector names = getPRNGNames(); + return std::find(names.begin(), names.end(), prng) == names.end() ? false:true; +} + +void Untwister::setThreads(unsigned int threads) +{ + if (!isRunning->load(std::memory_order_relaxed)) + { + this->threads = threads; + delete answers; + delete status; + answers = new std::vector* >(threads); + status = new std::vector(threads); + } +} + +unsigned int Untwister::getThreads() +{ + return threads; +} + +void Untwister::setDepth(unsigned int depth) +{ + this->depth = depth; +} + +unsigned int Untwister::getDepth() +{ + return depth; +} + +void Untwister::setMinConfidence(double minConfidence) +{ + this->minConfidence = minConfidence; +} + +double Untwister::getMinConfidence() +{ + return minConfidence; +} + +std::atomic* Untwister::getIsCompleted() +{ + return isCompleted; +} + +std::atomic* Untwister::getIsRunning() +{ + return isRunning; +} diff --git a/Untwister.h b/Untwister.h new file mode 100644 index 0000000..f82aea9 --- /dev/null +++ b/Untwister.h @@ -0,0 +1,91 @@ +/* + Copyright Dan Petro, 2014 + + This program 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. + + This program 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 this program. If not, see . +*/ + +#ifndef UNTWISTER_H_ +#define UNTWISTER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ConsoleColors.h" +#include "PRNGFactory.h" +#include "prngs/PRNG.h" + +// Pair of +typedef std::pair Seed; + +static const uint32_t DEFAULT_DEPTH = 1000; +static const double DEFAULT_MIN_CONFIDENCE = 100.0; + +class Untwister +{ + +public: + Untwister(); + Untwister(unsigned int threads); + Untwister(unsigned int threads, unsigned int observationSize); + virtual ~Untwister(); + + std::vector bruteforce(uint32_t lowerBoundSeed, uint32_t upperBoundSeed); + bool inferState(); + + std::vector getPRNGNames(); + void setPRNG(std::string prng); + void setPRNG(char *prng); + std::string getPRNG(); + bool isSupportedPRNG(std::string prng); + bool isSupportedPRNG(char* prng); + + void setMinConfidence(double minConfidence); + double getMinConfidence(); + void setDepth(uint32_t depth); + uint32_t getDepth(); + void setThreads(unsigned int threads); + unsigned int getThreads(); + void addObservedOutput(uint32_t observedOutput); + std::vector* getObservedOutputs(); + std::vector* getStatus(); + std::atomic* getIsCompleted(); + std::atomic* getIsRunning(); + + std::vector generateSampleFromSeed(uint32_t seed); + std::vector generateSampleFromState(); + +private: + unsigned int threads; + double minConfidence; + uint32_t depth; + std::string prng; + std::atomic *isCompleted; + std::atomic *isRunning; + std::vector *status; + std::vector* > *answers; + std::vector *observedOutputs; + + void worker(unsigned int id, uint32_t startingSeed, uint32_t endingSeed); + std::vector divisionOfLabor(uint32_t sizeOfWork, uint32_t numberOfWorkers); + +}; + +#endif /* UNTWISTER_H_ */ diff --git a/untwister.cpp b/main.cpp similarity index 56% rename from untwister.cpp rename to main.cpp index fb6f27f..68d9f87 100644 --- a/untwister.cpp +++ b/main.cpp @@ -18,12 +18,34 @@ #include #include +#include +#include +#include +#include +#include -#include "untwister.h" #include "ConsoleColors.h" +#include "Untwister.h" +using std::chrono::seconds; +using std::chrono::milliseconds; +using std::chrono::duration; +using std::chrono::duration_cast; +using std::chrono::steady_clock; -void Usage(PRNGFactory factory, unsigned int threads) +static const unsigned int ONE_YEAR = 31536000; +static const unsigned int TRACE_SIZE = 10; + +/* Segfault handler - for debugging only */ +void handler(int sig) { + void *trace[TRACE_SIZE]; + size_t size = backtrace(trace, TRACE_SIZE); + std::cerr << "[!] SIGSEGV: " << sig << std::endl; + backtrace_symbols_fd(trace, size, 2); + exit(1); +} + +void Usage(Untwister *untwister) { std::cout << BOLD << "Untwister" << RESET << " - Recover PRNG seeds from observed values." << std::endl; std::cout << "\t-i [-d ] [-r ] [-g ] [-t ] [-c ]\n" << std::endl; @@ -34,85 +56,115 @@ void Usage(PRNGFactory factory, unsigned int threads) std::cout << "\t\tChoosing a higher depth value will make brute forcing take longer (linearly), but is" << std::endl; std::cout << "\t\trequired for cases where the generator has been used many times already." << std::endl; std::cout << "\t-r \n\t\tThe PRNG algorithm to use. Supported PRNG algorithms:" << std::endl; - std::vector names = factory.getNames(); + std::vector names = untwister->getPRNGNames(); for (unsigned int index = 0; index < names.size(); ++index) { std::cout << "\t\t" << BOLD << " * " << RESET << names[index]; if (index == 0) + { std::cout << " (default)"; + } std::cout << std::endl; } std::cout << "\t-u\n\t\tUse bruteforce, but only for unix timestamp values within a range of +/- 1 " << std::endl; std::cout << "\t\tyear from the current time." << std::endl; std::cout << "\t-g \n\t\tGenerate a test set of random numbers from the given seed (at a random depth)" << std::endl; std::cout << "\t-c \n\t\tSet the minimum confidence percentage to report" << std::endl; - std::cout << "\t-t \n\t\tSpawn this many threads (default is " << threads << ")" << std::endl; + std::cout << "\t-t \n\t\tSpawn this many threads (default is " << untwister->getThreads() << ")" << std::endl; std::cout << std::endl; } -void StatusThread(std::vector& pool, bool& isCompleted, uint32_t totalWork, std::vector *status) + +void DisplayProgress(Untwister *untwister, uint32_t totalWork) { - double percent = 0; - steady_clock::time_point start = steady_clock::now(); - while (!isCompleted) + std::atomic* isRunning = untwister->getIsRunning(); + while (!isRunning->load(std::memory_order_relaxed)) + { + std::this_thread::sleep_for(milliseconds(100)); + } + + double percent = 0.0; + double seedsPerSec = 0.0; + double timeLeft = 0.0; + steady_clock::time_point started = steady_clock::now(); + std::vector *status = untwister->getStatus(); + std::atomic *isCompleted = untwister->getIsCompleted(); + char spinner[] = {'|', '/', '-', '\\'}; + unsigned int count = 0; + while (!isCompleted->load(std::memory_order_relaxed)) { unsigned int sum = 0; + duration time_span = duration_cast>(steady_clock::now() - started); for (unsigned int index = 0; index < status->size(); ++index) { sum += status->at(index); } percent = ((double) sum / (double) totalWork) * 100.0; - isCompleted = (100.0 <= percent); - std::cout << CLEAR << DEBUG << "Progress: " << percent << '%'; - std::cout << " (" << (int) duration_cast(steady_clock::now() - start).count() << " seconds)"; + if (0 < time_span.count()) + { + seedsPerSec = (double) sum / (double) time_span.count(); + if (0 == count % 20) + { + timeLeft = ((double) (totalWork - sum) / seedsPerSec) / 60.0; + } + } + + std::cout << CLEAR << BOLD << PURPLE << "[" << spinner[count % 4] << "]" << RESET + << " Progress: " << percent << '%' + << " [" << sum << " / " << totalWork << "]" + << " ~" << seedsPerSec << "/sec" + << " " << timeLeft << " minute(s)"; std::cout.flush(); - std::this_thread::sleep_for(milliseconds(150)); + ++count; + std::this_thread::sleep_for(milliseconds(100)); } std::cout << CLEAR; } -void FindSeed(const std::string& rng, unsigned int threads, double minimumConfidence, uint32_t lowerBoundSeed, - uint32_t upperBoundSeed, uint32_t depth) +void FindSeed(Untwister *untwister, uint32_t lowerBoundSeed, uint32_t upperBoundSeed) { - std::cout << INFO << "Looking for seed using " << rng << std::endl; - std::cout << INFO << "Spawning " << threads << " worker thread(s) ..." << std::endl; + std::cout << INFO << "Looking for seed using " << BOLD << untwister->getPRNG() << RESET << std::endl; + std::cout << INFO << "Spawning " << untwister->getThreads() << " worker thread(s) ..." << std::endl; - - /* Each thread needs their own set of answers to avoid locking */ - std::vector* > *answers = new std::vector* >(threads); steady_clock::time_point elapsed = steady_clock::now(); - StartBruteForce(threads, answers, minimumConfidence, lowerBoundSeed, upperBoundSeed, depth, rng); + std::thread progressThread(DisplayProgress, untwister, upperBoundSeed - lowerBoundSeed); - std::cout << INFO << "Completed in " << duration_cast(steady_clock::now() - elapsed).count() + auto results = untwister->bruteforce(lowerBoundSeed, upperBoundSeed); + auto isCompleted = untwister->getIsCompleted(); + if (!isCompleted->load(std::memory_order_relaxed)) + { + isCompleted->store(true, std::memory_order_relaxed); + } + progressThread.join(); + + /* Total time elapsed */ + std::cout << INFO << "Completed in " + << duration_cast(steady_clock::now() - elapsed).count() << " second(s)" << std::endl; /* Display results */ - for (unsigned int id = 0; id < answers->size(); ++id) + for (unsigned int index = 0; index < results.size(); ++index) { - /* Look for answers from each thread */ - for (unsigned int index = 0; index < answers->at(id)->size(); ++index) - { - std::cout << SUCCESS << "Found seed " << answers->at(id)->at(index).first - << " with a confidence of " << answers->at(id)->at(index).second - << '%' << std::endl; - } - delete answers->at(id); + std::cout << SUCCESS << "Found seed " << results[index].first + << " with a confidence of " << results[index].second + << '%' << std::endl; } - delete answers; } int main(int argc, char *argv[]) { + /* Signal Handlers */ + signal(SIGSEGV, handler); + signal(SIGILL, handler); + signal(SIGABRT, handler); + int c; - unsigned int threads = std::thread::hardware_concurrency(); + uint32_t lowerBoundSeed = 0; uint32_t upperBoundSeed = UINT_MAX; - uint32_t depth = 1000; uint32_t seed = 0; bool generateFlag = false; - double minimumConfidence = 100.0; - PRNGFactory factory; - std::string rng = factory.getNames()[0]; + Untwister *untwister = new Untwister(); while ((c = getopt(argc, argv, "d:i:g:t:r:c:uh")) != -1) { @@ -132,23 +184,30 @@ int main(int argc, char *argv[]) } case 'r': { - rng = optarg; - std::vector names = factory.getNames(); - if (std::find(names.begin(), names.end(), rng) == names.end()) + if (!untwister->isSupportedPRNG(optarg)) { std::cerr << WARN << "ERROR: The PRNG \"" << optarg << "\" is not supported, see -h" << std::endl; return EXIT_FAILURE; } + else + { + untwister->setPRNG(optarg); + } break; } case 'd': { - depth = strtoul(optarg, NULL, 10); + unsigned int depth = strtoul(optarg, NULL, 10); if (depth == 0) { std::cerr << WARN << "ERROR: Please enter a valid depth > 1" << std::endl; return EXIT_FAILURE; } + else + { + std::cout << INFO << "Depth set to: " << depth << std::endl; + untwister->setDepth(depth); + } break; } case 'i': @@ -161,33 +220,43 @@ int main(int argc, char *argv[]) std::string line; while (std::getline(infile, line)) { - observedOutputs.push_back(strtoul(line.c_str(), NULL, 0)); + uint32_t value = strtoul(line.c_str(), NULL, 0); + untwister->addObservedOutput(value); } break; } case 't': { - threads = strtoul(optarg, NULL, 10); + unsigned int threads = strtoul(optarg, NULL, 10); if (threads == 0) { std::cerr << WARN << "ERROR: Please enter a valid number of threads > 1" << std::endl; return EXIT_FAILURE; } + else + { + untwister->setThreads(threads); + } break; } case 'c': { - minimumConfidence = ::atof(optarg); + double minimumConfidence = ::atof(optarg); if (minimumConfidence <= 0 || 100.0 < minimumConfidence) { std::cerr << WARN << "ERROR: Invalid confidence percentage " << std::endl; return EXIT_FAILURE; } + else + { + std::cout << INFO << "Minimum confidence set to: " << minimumConfidence << std::endl; + untwister->setMinConfidence(minimumConfidence); + } break; } case 'h': { - Usage(factory, threads); + Usage(untwister); return EXIT_SUCCESS; } case '?': @@ -198,46 +267,51 @@ int main(int argc, char *argv[]) std::cerr << "Unknown option `-" << optopt << "'." << std::endl; else std::cerr << "Unknown option character `" << optopt << "'." << std::endl; - Usage(factory, threads); + Usage(untwister); return EXIT_FAILURE; } default: { - Usage(factory, threads); + Usage(untwister); return EXIT_FAILURE; } } } - if(generateFlag) + if (generateFlag) { std::vector results; - if(observedOutputs.empty()) + if(untwister->getObservedOutputs()->empty()) { - results = GenerateSample(seed, depth, rng); + results = untwister->generateSampleFromSeed(seed); } else { - results = GenerateSample(observedOutputs, depth, rng); + results = untwister->generateSampleFromState(); } for (unsigned int index = 0; index < results.size(); ++index) + { std::cout << results.at(index) << std::endl; + } return EXIT_SUCCESS; } - if(observedOutputs.empty()) + if (untwister->getObservedOutputs()->empty()) { - Usage(factory, threads); + Usage(untwister); std::cerr << WARN << "ERROR: No input numbers provided. Use -i to provide a file" << std::endl; return EXIT_FAILURE; } - if(InferState(rng)) + if (untwister->inferState()) { return EXIT_SUCCESS; } - FindSeed(rng, threads, minimumConfidence, lowerBoundSeed, upperBoundSeed, depth); + + FindSeed(untwister, lowerBoundSeed, upperBoundSeed); + delete untwister; + return EXIT_SUCCESS; } diff --git a/py-untwister.cpp b/py-untwister.cpp index 6f1982d..1842d68 100644 --- a/py-untwister.cpp +++ b/py-untwister.cpp @@ -10,15 +10,17 @@ #include #include #include +#include -#include "untwister.h" +#include "Untwister.h" using namespace boost::python; static const unsigned int TRACE_SIZE = 10; /* Segfault handler - for debugging only */ -void handler(int sig) { +void handler(int sig) +{ void *trace[TRACE_SIZE]; size_t size = backtrace(trace, TRACE_SIZE); std::cerr << "[!] SIGSEGV: " << sig << std::endl; @@ -30,6 +32,7 @@ void handler(int sig) { void PythonInit() { signal(SIGSEGV, handler); + signal(SIGABRT, handler); if(!Py_IsInitialized()) { Py_Initialize(); @@ -37,82 +40,64 @@ void PythonInit() } } -/* Raises a Python exception if the string is not a supported prng */ -void CheckPRNG(std::string prng) +/* Find a seed */ +list FindSeed(std::string prng, list inputs, unsigned int threads, float minimumConfidence, + uint32_t lowerBoundSeed, uint32_t upperBoundSeed, uint32_t depth) { - PRNGFactory factory; - std::vector names = factory.getNames(); - if(std::find(names.begin(), names.end(), prng) == names.end()) + Untwister *untwister = new Untwister(len(inputs)); + if(!untwister->isSupportedPRNG(prng)) { /* Raise Python Exception */ PyErr_SetString(PyExc_ValueError, "Unsupported PRNG"); throw error_already_set(); } -} - -/* Find a seed */ -list FindSeed(const std::string& rng, list inputs, unsigned int threads, float minimumConfidence, - uint32_t lowerBoundSeed, uint32_t upperBoundSeed, uint32_t depth) -{ - CheckPRNG(rng); + untwister->setPRNG(prng); + untwister->setThreads(threads); + untwister->setDepth(depth); + untwister->setMinConfidence(minimumConfidence); /* Convert Python list object to observedOutputs's std::vector */ - for(unsigned int index = 0; index < len(inputs); ++index) { - unsigned int data = boost::python::extract(inputs[index]); - observedOutputs.push_back(data); + for(unsigned int index = 0; index < len(inputs); ++index) + { + uint32_t data = boost::python::extract(inputs[index]); + untwister->getObservedOutputs()->at(index) = data; } - std::vector* > *answers = new std::vector* >(threads); - /* Suspend Python's thread, so we can use native C++ threads */ - PyThreadState* pyThreadState = PyEval_SaveThread(); - StartBruteForce(threads, answers, (double) minimumConfidence, lowerBoundSeed, upperBoundSeed, depth, rng); + PyThreadState *pyThreadState = PyEval_SaveThread(); + + std::vector > results = untwister->bruteforce(lowerBoundSeed, upperBoundSeed); + /* Clean up and restore Python thread state */ PyEval_RestoreThread(pyThreadState); - pyThreadState = NULL; /* Covert answers to python list of tuples */ - list results; - for(unsigned int id = 0; id < answers->size(); ++id) - { - for(unsigned int index = 0; index < answers->at(id)->size(); ++index) - { - tuple result = make_tuple(answers->at(id)->at(index).first, answers->at(id)->at(index).second); - results.append(result); - } - delete answers->at(id); - } - delete answers; - return results; -} - -/* Generate a sample sequence */ -list Sample(std::string prng, uint32_t seed, uint32_t depth) -{ - CheckPRNG(prng); - - std::vector results = GenerateSample(seed, depth, prng); - /* Convert results to a Python object */ - list sample; + list pyResults; for(unsigned int index = 0; index < results.size(); ++index) { - sample.append(results.at(index)); + tuple pySeed = make_tuple(results[index].first, results[index].second); + pyResults.append(pySeed); } - return sample; + delete untwister; + return pyResults; } /* List all supported PRNGs */ list Prngs() { - PRNGFactory factory; - std::vector names = factory.getNames(); - list prngs; + Untwister *untwister = new Untwister(); + std::vector names = untwister->getPRNGNames(); + + list pyPRNGs; for(unsigned int index = 0; index < names.size(); ++index) { - prngs.append(names.at(index)); + pyPRNGs.append(names[index]); } - return prngs; + + delete untwister; + + return pyPRNGs; } /* Python interface */ @@ -128,7 +113,7 @@ BOOST_PYTHON_MODULE(untwister) { unsigned int threads = std::thread::hardware_concurrency(); current.attr("THREADS") = threads; - def("prngs", Prngs, "\n List of supported PRNGs"); + def("get_prngs", Prngs, "\n List of supported PRNGs"); def( "find_seed", @@ -138,11 +123,4 @@ BOOST_PYTHON_MODULE(untwister) { "\nThis is the cracking module for a generic mersenne twister 19932" ); - def( - "generate_sample", - Sample, - (arg("prng"), arg("seed"), arg("depth") = 1000), - "\n Generate a sample using a given PRNG" - ); - } \ No newline at end of file