mirror of
https://github.com/BishopFox/untwister.git
synced 2026-11-07 10:35:58 -10:00
Add support for Ruby's bounded integer random PRNG
- As if you did: rand(int..int) - Added general bounding support, too - Uses two new cmd flags: -m min and -M max
This commit is contained in:
1 parent
d64102f7eb
commit
40190438a3
14 files changed
+202
-13
No files matched your search
@@ -45,6 +45,10 @@ Untwister - Recover PRNG seeds from observed values.
|
||||
Set the minimum confidence percentage to report
|
||||
-t <threads>
|
||||
Spawn this many threads (default is your number of CPUs)
|
||||
-m <min bound>
|
||||
Set the minimum bound, for a bounded PRNG function
|
||||
-M <max bound>
|
||||
Set the maximum bound, for a bounded PRNG function
|
||||
|
||||
Examples:
|
||||
Cracking a list of random numbers in test_ints.txt
|
||||
|
||||
+48
-2
@@ -24,6 +24,7 @@ Untwister::Untwister()
|
||||
m_isCompleted = new std::atomic<bool>(false);
|
||||
m_isRunning = new std::atomic<bool>(false);
|
||||
m_isStarting = new std::atomic<bool>(false);
|
||||
m_isBounded = new std::atomic<bool>(false);
|
||||
m_observedOutputs = new std::vector<uint32_t>;
|
||||
m_depth = DEFAULT_DEPTH;
|
||||
m_minConfidence = DEFAULT_MIN_CONFIDENCE;
|
||||
@@ -38,6 +39,7 @@ Untwister::Untwister(unsigned int observationSize)
|
||||
m_isCompleted = new std::atomic<bool>(false);
|
||||
m_isRunning = new std::atomic<bool>(false);
|
||||
m_isStarting = new std::atomic<bool>(false);
|
||||
m_isBounded = new std::atomic<bool>(false);
|
||||
m_observedOutputs = new std::vector<uint32_t>(observationSize);
|
||||
m_depth = DEFAULT_DEPTH;
|
||||
m_minConfidence = DEFAULT_MIN_CONFIDENCE;
|
||||
@@ -55,12 +57,13 @@ Untwister::~Untwister()
|
||||
delete m_answers;
|
||||
delete m_status;
|
||||
delete m_isStarting;
|
||||
delete m_isBounded;
|
||||
}
|
||||
|
||||
/*
|
||||
isStarting: The call to bruteforce() has occured but all workers threads have not started yet
|
||||
isRunning: All threads have started and it is safe to call getStatus() externally
|
||||
isCompleted: The operation has completed and all worker threads have joined
|
||||
isRunning: All threads have started and it is safe to call getStatus() externally
|
||||
isCompleted: The operation has completed and all worker threads have joined
|
||||
*/
|
||||
std::vector<Seed> Untwister::bruteforce(uint32_t lowerBoundSeed, uint32_t upperBoundSeed)
|
||||
{
|
||||
@@ -118,6 +121,10 @@ void Untwister::m_worker(unsigned int id, uint32_t startingSeed, uint32_t ending
|
||||
/* Each thread must have a local factory unless you like mutexes and/or segfaults */
|
||||
PRNGFactory factory;
|
||||
PRNG *generator = factory.getInstance(m_prng);
|
||||
if(isBounded())
|
||||
{
|
||||
generator->setBounds(m_minBound, m_maxBound);
|
||||
}
|
||||
m_answers->at(id) = new std::vector<Seed>();
|
||||
m_status->at(id) = 0;
|
||||
|
||||
@@ -276,6 +283,11 @@ void Untwister::generateSampleFromSeed(uint32_t depth, uint32_t seed)
|
||||
{
|
||||
PRNGFactory factory;
|
||||
PRNG *generator = factory.getInstance(m_prng);
|
||||
/* If we're bounded, then apply those bounds now */
|
||||
if(isBounded())
|
||||
{
|
||||
generator->setBounds(m_minBound, m_maxBound);
|
||||
}
|
||||
generator->seed(seed);
|
||||
|
||||
for (unsigned int index = 0; index < depth; ++index)
|
||||
@@ -420,3 +432,37 @@ std::atomic<bool>* Untwister::getIsRunning()
|
||||
{
|
||||
return m_isRunning;
|
||||
}
|
||||
|
||||
void Untwister::setBounds(uint32_t min, uint32_t max)
|
||||
{
|
||||
if(m_prng == GLIBC_RAND)
|
||||
{
|
||||
std::string err = "Glibc does not have a bounded rand() function. If your application produces ";
|
||||
err += "bounded random numbers, consider making a Python mangling script. ";
|
||||
throw err;
|
||||
}
|
||||
else if(m_prng == PHP_MT_RAND)
|
||||
{
|
||||
std::string err = "PHP's bounded rand call is not yet supported in untwister. TODO. Sorry!";
|
||||
throw err;
|
||||
}
|
||||
else if(m_prng == MT19937)
|
||||
{
|
||||
std::string err = "C++ does not have a bounded random function. If your application produces ";
|
||||
err += "bounded random numbers, consider making a Python mangling script. ";
|
||||
throw err;
|
||||
}
|
||||
else if(m_prng == RUBY_RAND)
|
||||
{
|
||||
//Everything is fine
|
||||
}
|
||||
|
||||
m_minBound = min;
|
||||
m_maxBound = max;
|
||||
m_isBounded->store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool Untwister::isBounded()
|
||||
{
|
||||
return m_isBounded->load(std::memory_order_relaxed);
|
||||
}
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
std::vector<uint32_t>* getStatus();
|
||||
std::atomic<bool>* getIsCompleted();
|
||||
std::atomic<bool>* getIsRunning();
|
||||
void setBounds(uint32_t, uint32_t);
|
||||
bool isBounded();
|
||||
|
||||
void generateSampleFromSeed(uint32_t depth, uint32_t seed);
|
||||
|
||||
@@ -84,6 +86,10 @@ private:
|
||||
std::vector<std::vector<Seed>* > *m_answers;
|
||||
std::vector<uint32_t> *m_observedOutputs;
|
||||
|
||||
std::atomic<bool> *m_isBounded;
|
||||
uint32_t m_minBound;
|
||||
uint32_t m_maxBound;
|
||||
|
||||
void m_worker(unsigned int id, uint32_t startingSeed, uint32_t endingSeed);
|
||||
std::vector<uint32_t> m_divisionOfLabor(uint32_t sizeOfWork);
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ void Usage(Untwister *untwister)
|
||||
std::cout << "\t-D <depth>\n\t\tThe quantity of random numbers to generate when using the -g flag (default 20)" << std::endl;
|
||||
std::cout << "\t-c <confidence>\n\t\tSet the minimum confidence percentage to report" << std::endl;
|
||||
std::cout << "\t-t <threads>\n\t\tSpawn this many threads (default is " << untwister->getThreads() << ")" << std::endl;
|
||||
std::cout << "\t-m <min bound>\n\t\tSet the minimum bound, for a bounded PRNG function" << std::endl;
|
||||
std::cout << "\t-M <max bound>\n\t\tSet the maximum bound, for a bounded PRNG function" << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
@@ -200,14 +202,30 @@ int main(int argc, char *argv[])
|
||||
uint32_t upperBoundSeed = UINT_MAX;
|
||||
uint32_t seed = 0;
|
||||
uint32_t generationDepth = 20;
|
||||
uint32_t min_bound = 0;
|
||||
uint32_t max_bound = -1;
|
||||
bool boundedMinFlag = false;
|
||||
bool boundedMaxFlag = false;
|
||||
bool generateFlag = false;
|
||||
bool bruteforce = false;
|
||||
Untwister *untwister = new Untwister();
|
||||
|
||||
while ((c = getopt(argc, argv, "D:d:i:g:t:r:c:ubh")) != -1)
|
||||
while ((c = getopt(argc, argv, "m:M:D:d:i:g:t:r:c:ubh")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
{
|
||||
min_bound = strtoul(optarg, NULL, 10);
|
||||
boundedMinFlag = true;
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
{
|
||||
max_bound = strtoul(optarg, NULL, 10);
|
||||
boundedMaxFlag = true;
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
{
|
||||
if(optarg != NULL)
|
||||
@@ -331,21 +349,61 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* You can't just set one bound flag */
|
||||
if (boundedMaxFlag ^ boundedMinFlag)
|
||||
{
|
||||
Usage(untwister);
|
||||
std::cerr << WARN << "ERROR: If you want to have a bounded range, provide both -m min and -M max" << std::endl;
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
/* Set the bounds, if there any any */
|
||||
if (boundedMaxFlag && boundedMinFlag)
|
||||
{
|
||||
if(max_bound <= min_bound)
|
||||
{
|
||||
Usage(untwister);
|
||||
std::cerr << WARN << "ERROR: Min bound (-m) must be less than max bound (-M)" << std::endl;
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
try
|
||||
{
|
||||
untwister->setBounds(min_bound, max_bound);
|
||||
}
|
||||
catch(const std::string& ex)
|
||||
{
|
||||
std::cerr << WARN << "ERROR: " << ex << std::endl;
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
/* If we're generating numbers instead of cracking a seed, then let's do that. */
|
||||
if (generateFlag)
|
||||
{
|
||||
std::vector<uint32_t> results;
|
||||
untwister->generateSampleFromSeed(generationDepth, seed);
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (untwister->getObservedOutputs()->empty())
|
||||
/* Check input file for contents */
|
||||
if (untwister->getObservedOutputs()->empty())
|
||||
{
|
||||
Usage(untwister);
|
||||
std::cerr << WARN << "ERROR: No input numbers provided. Use -i <file> to provide a file" << std::endl;
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (!inferenceAttack(untwister) || bruteforce)
|
||||
/* Perform inference attack. Skip it if we're bounded or it was manually skipped */
|
||||
if ((boundedMaxFlag && boundedMinFlag) || bruteforce)
|
||||
{
|
||||
bruteforceAttack(untwister, lowerBoundSeed, upperBoundSeed);
|
||||
std::cout << INFO << "Skipping inference attack..." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
inferenceAttack(untwister);
|
||||
}
|
||||
bruteforceAttack(untwister, lowerBoundSeed, upperBoundSeed);
|
||||
delete untwister;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
+6
-1
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "GlibcRand.h"
|
||||
|
||||
#include "../ConsoleColors.h"
|
||||
|
||||
GlibcRand::GlibcRand()
|
||||
{
|
||||
@@ -439,3 +439,8 @@ void GlibcRand::tune(std::vector<uint32_t> evidenceForward, std::vector<uint32_t
|
||||
tune_chainChecking();
|
||||
tune_repeatedIncrements();
|
||||
}
|
||||
|
||||
void GlibcRand::setBounds(uint32_t min, uint32_t max)
|
||||
{
|
||||
//Setting bounds is unsupported in gblibc, so do nothing here. In fact, this should not get called.
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
void seed(uint32_t value);
|
||||
uint32_t getSeed(void);
|
||||
uint32_t random(void);
|
||||
void setBounds(uint32_t, uint32_t);
|
||||
|
||||
private:
|
||||
uint32_t seedValue;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "Mt19937.h"
|
||||
#include "../ConsoleColors.h"
|
||||
|
||||
Mt19937::Mt19937()
|
||||
{
|
||||
@@ -81,3 +82,8 @@ void Mt19937::setEvidence(std::vector<uint32_t>)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Mt19937::setBounds(uint32_t min, uint32_t max)
|
||||
{
|
||||
//Setting bounds is unsupported in C++ rand, so do nothing here. In fact, this should not get called.
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
void tune(std::vector<uint32_t>, std::vector<uint32_t>);
|
||||
|
||||
bool reverseToSeed(uint32_t *, uint32_t);
|
||||
void setBounds(uint32_t, uint32_t);
|
||||
|
||||
private:
|
||||
uint32_t seedValue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PHP_mt19937.h"
|
||||
#include "../ConsoleColors.h"
|
||||
|
||||
PHP_mt19937::PHP_mt19937()
|
||||
{
|
||||
@@ -127,3 +128,8 @@ void PHP_mt19937::setEvidence(std::vector<uint32_t>)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PHP_mt19937::setBounds(uint32_t min, uint32_t max)
|
||||
{
|
||||
//TODO add support for this!
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
void tune(std::vector<uint32_t>, std::vector<uint32_t>);
|
||||
|
||||
bool reverseToSeed(uint32_t *, uint32_t);
|
||||
void setBounds(uint32_t, uint32_t);
|
||||
|
||||
private:
|
||||
void php_mt_initialize(uint32_t s);
|
||||
|
||||
+4
-1
@@ -50,13 +50,16 @@ public:
|
||||
virtual std::vector<uint32_t> predictBackward(uint32_t) = 0;
|
||||
virtual void tune(std::vector<uint32_t>, std::vector<uint32_t>) = 0;
|
||||
virtual bool reverseToSeed(uint32_t *, uint32_t) = 0;
|
||||
virtual void setBounds(uint32_t, uint32_t) = 0;
|
||||
|
||||
virtual ~PRNG(){};
|
||||
|
||||
protected:
|
||||
std::vector<uint32_t> m_state;
|
||||
std::vector<uint32_t> m_evidence;
|
||||
|
||||
uint32_t m_minBound;
|
||||
uint32_t m_maxBound;
|
||||
bool m_isBounded;
|
||||
};
|
||||
|
||||
#endif /* PRNG_H_ */
|
||||
+41
-3
@@ -6,15 +6,18 @@
|
||||
*/
|
||||
|
||||
#include "Ruby.h"
|
||||
#include "../ConsoleColors.h"
|
||||
#include <iostream>
|
||||
|
||||
Ruby::Ruby()
|
||||
{
|
||||
m_isBounded = false;
|
||||
seedValue = 0;
|
||||
m_mt = new MT;
|
||||
init_genrand(m_mt, seedValue);
|
||||
}
|
||||
|
||||
Ruby::~Ruby()
|
||||
Ruby::~Ruby()
|
||||
{
|
||||
delete m_mt;
|
||||
m_mt = NULL;
|
||||
@@ -40,9 +43,28 @@ uint32_t Ruby::getSeed()
|
||||
|
||||
uint32_t Ruby::random()
|
||||
{
|
||||
return genrand_int32(m_mt);
|
||||
}
|
||||
if(m_isBounded)
|
||||
{
|
||||
/* generate a number between 0 and (max-min), then scale it back up */
|
||||
uint32_t limit = m_maxBound - m_minBound;
|
||||
|
||||
/* Ruby does an algorithm of retries within a power of two bound */
|
||||
uint32_t mask = make_mask(limit);
|
||||
while(true)
|
||||
{
|
||||
uint32_t val = genrand_int32(m_mt);
|
||||
val &= mask;
|
||||
if(val < limit)
|
||||
{
|
||||
return val + m_minBound;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return genrand_int32(m_mt);
|
||||
}
|
||||
}
|
||||
|
||||
void Ruby::init_genrand(struct MT* mt, unsigned int s)
|
||||
{
|
||||
@@ -139,3 +161,19 @@ void Ruby::setEvidence(std::vector<uint32_t>)
|
||||
|
||||
}
|
||||
|
||||
uint32_t Ruby::make_mask(uint32_t x)
|
||||
{
|
||||
x = x | x >> 1;
|
||||
x = x | x >> 2;
|
||||
x = x | x >> 4;
|
||||
x = x | x >> 8;
|
||||
x = x | x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
void Ruby::setBounds(uint32_t min, uint32_t max)
|
||||
{
|
||||
m_minBound = min;
|
||||
m_maxBound = max;
|
||||
m_isBounded = true;
|
||||
}
|
||||
@@ -36,11 +36,13 @@ public:
|
||||
void tune(std::vector<uint32_t>, std::vector<uint32_t>);
|
||||
|
||||
bool reverseToSeed(uint32_t *, uint32_t);
|
||||
void setBounds(uint32_t, uint32_t);
|
||||
|
||||
private:
|
||||
void init_genrand(struct MT *mt, unsigned int s);
|
||||
void next_state(struct MT *mt);
|
||||
uint32_t genrand_int32(struct MT *mt);
|
||||
uint32_t make_mask(uint32_t x);
|
||||
|
||||
MT *m_mt;
|
||||
uint32_t seedValue;
|
||||
|
||||
+13
-1
@@ -42,5 +42,17 @@ void TestRuby::randomTest()
|
||||
CPPUNIT_ASSERT(ruby->random() == 3100331191);
|
||||
CPPUNIT_ASSERT(ruby->random() == 3480951327);
|
||||
CPPUNIT_ASSERT(ruby->random() == 4150831638);
|
||||
|
||||
/* Bounded Ruby rand(int, int) calls */
|
||||
ruby->setBounds(1234, 9876);
|
||||
ruby->seed(6789);
|
||||
CPPUNIT_ASSERT(ruby->random() == 4081);
|
||||
CPPUNIT_ASSERT(ruby->random() == 2200);
|
||||
CPPUNIT_ASSERT(ruby->random() == 8047);
|
||||
ruby->setBounds(1234, 9876);
|
||||
ruby->seed(6789);
|
||||
CPPUNIT_ASSERT(ruby->random() == 4081);
|
||||
CPPUNIT_ASSERT(ruby->random() == 2200);
|
||||
CPPUNIT_ASSERT(ruby->random() == 8047);
|
||||
delete ruby;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user