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:
AltF4 committed 2015-08-14 12:59:23 -07:00
1 parent d64102f7eb
commit 40190438a3
14 files changed
+202 -13

No files matched your search

+4
View File
@@ -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
View File
@@ -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);
}
+6
View File
@@ -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);
+63 -5
View File
@@ -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
View File
@@ -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.
}
+1
View File
@@ -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
View File
@@ -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.
}
+1
View File
@@ -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;
+6
View File
@@ -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!
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -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
View File
@@ -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;
}
}