544 lines
14 KiB
C
544 lines
14 KiB
C
|
#ifndef SEGMENTED_FINDER_H
|
||
|
#define SEGMENTED_FINDER_H
|
||
|
#include <malloc.h>
|
||
|
#include <iosfwd>
|
||
|
#include <iterator>
|
||
|
|
||
|
class SegmentedFinder;
|
||
|
class SegmentFinder
|
||
|
{
|
||
|
public:
|
||
|
SegmentFinder(DFHack::t_memrange & mr, DFHack::Context * DF, SegmentedFinder * SF)
|
||
|
{
|
||
|
_DF = DF;
|
||
|
mr_ = mr;
|
||
|
valid=false;
|
||
|
if(mr.valid)
|
||
|
{
|
||
|
mr_.buffer = (uint8_t *)malloc (mr_.end - mr_.start);
|
||
|
_SF = SF;
|
||
|
try
|
||
|
{
|
||
|
DF->ReadRaw(mr_.start,(mr_.end - mr_.start),mr_.buffer);
|
||
|
valid = true;
|
||
|
}
|
||
|
catch (DFHack::Error::MemoryAccessDenied &)
|
||
|
{
|
||
|
free(mr_.buffer);
|
||
|
valid = false;
|
||
|
mr.valid = false; // mark the range passed in as bad
|
||
|
cout << "Range 0x" << hex << mr_.start << " - 0x" << mr_.end;
|
||
|
|
||
|
if (strlen(mr_.name) != 0)
|
||
|
cout << " (" << mr_.name << ")";
|
||
|
|
||
|
cout << dec << " not readable." << endl;
|
||
|
cout << "Skipping this range on future scans." << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
~SegmentFinder()
|
||
|
{
|
||
|
if(valid)
|
||
|
free(mr_.buffer);
|
||
|
}
|
||
|
bool isValid()
|
||
|
{
|
||
|
return valid;
|
||
|
}
|
||
|
template <class needleType, class hayType, typename comparator >
|
||
|
bool Find (needleType needle, const uint8_t increment , vector <uint64_t> &newfound, comparator oper)
|
||
|
{
|
||
|
if(!valid) return !newfound.empty();
|
||
|
//loop
|
||
|
for(uint64_t offset = 0; offset < (mr_.end - mr_.start) - sizeof(hayType); offset += increment)
|
||
|
{
|
||
|
if( oper(_SF,(hayType *)(mr_.buffer + offset), needle) )
|
||
|
newfound.push_back(mr_.start + offset);
|
||
|
}
|
||
|
return !newfound.empty();
|
||
|
}
|
||
|
|
||
|
template < class needleType, class hayType, typename comparator >
|
||
|
uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length)
|
||
|
{
|
||
|
if(!valid) return 0;
|
||
|
uint64_t stopper = min((mr_.end - mr_.start) - sizeof(hayType), (start - mr_.start) - sizeof(hayType) + length);
|
||
|
//loop
|
||
|
for(uint64_t offset = start - mr_.start; offset < stopper; offset +=1)
|
||
|
{
|
||
|
if( oper(_SF,(hayType *)(mr_.buffer + offset), needle) )
|
||
|
return mr_.start + offset;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
template <class needleType, class hayType, typename comparator >
|
||
|
bool Filter (needleType needle, vector <uint64_t> &found, vector <uint64_t> &newfound, comparator oper)
|
||
|
{
|
||
|
if(!valid) return !newfound.empty();
|
||
|
for( uint64_t i = 0; i < found.size(); i++)
|
||
|
{
|
||
|
if(mr_.isInRange(found[i]))
|
||
|
{
|
||
|
uint64_t corrected = found[i] - mr_.start;
|
||
|
if( oper(_SF,(hayType *)(mr_.buffer + corrected), needle) )
|
||
|
newfound.push_back(found[i]);
|
||
|
}
|
||
|
}
|
||
|
return !newfound.empty();
|
||
|
}
|
||
|
private:
|
||
|
friend class SegmentedFinder;
|
||
|
SegmentedFinder * _SF;
|
||
|
DFHack::Context * _DF;
|
||
|
DFHack::t_memrange mr_;
|
||
|
bool valid;
|
||
|
};
|
||
|
|
||
|
class SegmentedFinder
|
||
|
{
|
||
|
public:
|
||
|
SegmentedFinder(vector <DFHack::t_memrange>& ranges, DFHack::Context * DF)
|
||
|
{
|
||
|
_DF = DF;
|
||
|
for(size_t i = 0; i < ranges.size(); i++)
|
||
|
{
|
||
|
segments.push_back(new SegmentFinder(ranges[i], DF, this));
|
||
|
}
|
||
|
}
|
||
|
~SegmentedFinder()
|
||
|
{
|
||
|
for(size_t i = 0; i < segments.size(); i++)
|
||
|
{
|
||
|
delete segments[i];
|
||
|
}
|
||
|
}
|
||
|
SegmentFinder * getSegmentForAddress (uint64_t addr)
|
||
|
{
|
||
|
for(size_t i = 0; i < segments.size(); i++)
|
||
|
{
|
||
|
if(segments[i]->mr_.isInRange(addr))
|
||
|
{
|
||
|
return segments[i];
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
template <class needleType, class hayType, typename comparator >
|
||
|
bool Find (const needleType needle, const uint8_t increment, vector <uint64_t> &found, comparator oper)
|
||
|
{
|
||
|
found.clear();
|
||
|
for(size_t i = 0; i < segments.size(); i++)
|
||
|
{
|
||
|
segments[i]->Find<needleType,hayType,comparator>(needle, increment, found, oper);
|
||
|
}
|
||
|
return !(found.empty());
|
||
|
}
|
||
|
|
||
|
template < class needleType, class hayType, typename comparator >
|
||
|
uint64_t FindInRange (needleType needle, comparator oper, uint64_t start, uint64_t length)
|
||
|
{
|
||
|
SegmentFinder * sf = getSegmentForAddress(start);
|
||
|
if(sf)
|
||
|
{
|
||
|
return sf->FindInRange<needleType,hayType,comparator>(needle, oper, start, length);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
template <class needleType, class hayType, typename comparator >
|
||
|
bool Filter (const needleType needle, vector <uint64_t> &found, comparator oper)
|
||
|
{
|
||
|
vector <uint64_t> newfound;
|
||
|
for(size_t i = 0; i < segments.size(); i++)
|
||
|
{
|
||
|
segments[i]->Filter<needleType,hayType,comparator>(needle, found, newfound, oper);
|
||
|
}
|
||
|
found.clear();
|
||
|
found = newfound;
|
||
|
return !(found.empty());
|
||
|
}
|
||
|
|
||
|
template <class needleType, class hayType, typename comparator >
|
||
|
bool Incremental (needleType needle, const uint8_t increment ,vector <uint64_t> &found, comparator oper)
|
||
|
{
|
||
|
if(found.empty())
|
||
|
{
|
||
|
return Find <needleType, hayType, comparator>(needle,increment,found,oper);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return Filter <needleType, hayType, comparator>(needle,found,oper);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
T * Translate(uint64_t address)
|
||
|
{
|
||
|
for(size_t i = 0; i < segments.size(); i++)
|
||
|
{
|
||
|
if(segments[i]->mr_.isInRange(address))
|
||
|
{
|
||
|
return (T *) (segments[i]->mr_.buffer + address - segments[i]->mr_.start);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
T Read(uint64_t address)
|
||
|
{
|
||
|
return *Translate<T>(address);
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
bool Read(uint64_t address, T& target)
|
||
|
{
|
||
|
T * test = Translate<T>(address);
|
||
|
if(test)
|
||
|
{
|
||
|
target = *test;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
private:
|
||
|
DFHack::Context * _DF;
|
||
|
vector <SegmentFinder *> segments;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
bool equalityP (SegmentedFinder* s, T *x, T y)
|
||
|
{
|
||
|
return (*x) == y;
|
||
|
}
|
||
|
|
||
|
struct vecTriplet
|
||
|
{
|
||
|
uint32_t start;
|
||
|
uint32_t finish;
|
||
|
uint32_t alloc_finish;
|
||
|
};
|
||
|
|
||
|
template <typename Needle>
|
||
|
bool vectorLength (SegmentedFinder* s, vecTriplet *x, Needle &y)
|
||
|
{
|
||
|
if(x->start <= x->finish && x->finish <= x->alloc_finish)
|
||
|
if((x->finish - x->start) == y)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// find a vector of 32bit pointers, where an object pointed to has a string 'y' as the first member
|
||
|
bool vectorString (SegmentedFinder* s, vecTriplet *x, const char *y)
|
||
|
{
|
||
|
uint32_t object_ptr;
|
||
|
// iterate over vector of pointers
|
||
|
for(uint32_t idx = x->start; idx < x->finish; idx += sizeof(uint32_t))
|
||
|
{
|
||
|
// deref ptr idx, get ptr to object
|
||
|
if(!s->Read(idx,object_ptr))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
// deref ptr to first object, get ptr to string
|
||
|
uint32_t string_ptr;
|
||
|
if(!s->Read(object_ptr,string_ptr))
|
||
|
return false;
|
||
|
// get string location in our local cache
|
||
|
char * str = s->Translate<char>(string_ptr);
|
||
|
if(!str)
|
||
|
return false;
|
||
|
if(strcmp(y, str) == 0)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// find a vector of 32bit pointers, where the first object pointed to has a string 'y' as the first member
|
||
|
bool vectorStringFirst (SegmentedFinder* s, vecTriplet *x, const char *y)
|
||
|
{
|
||
|
uint32_t object_ptr;
|
||
|
uint32_t idx = x->start;
|
||
|
// deref ptr idx, get ptr to object
|
||
|
if(!s->Read(idx,object_ptr))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
// deref ptr to first object, get ptr to string
|
||
|
uint32_t string_ptr;
|
||
|
if(!s->Read(object_ptr,string_ptr))
|
||
|
return false;
|
||
|
// get string location in our local cache
|
||
|
char * str = s->Translate<char>(string_ptr);
|
||
|
if(!str)
|
||
|
return false;
|
||
|
if(strcmp(y, str) == 0)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// test if the address is between vector.start and vector.finish
|
||
|
// not very useful alone, but could be a good step to filter some things
|
||
|
bool vectorAddrWithin (SegmentedFinder* s, vecTriplet *x, uint32_t address)
|
||
|
{
|
||
|
if(address < x->finish && address >= x->start)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// test if an object address is within the vector of pointers
|
||
|
//
|
||
|
bool vectorOfPtrWithin (SegmentedFinder* s, vecTriplet *x, uint32_t address)
|
||
|
{
|
||
|
uint32_t object_ptr;
|
||
|
for(uint32_t idx = x->start; idx < x->finish; idx += sizeof(uint32_t))
|
||
|
{
|
||
|
if(!s->Read(idx,object_ptr))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if(object_ptr == address)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool vectorAll (SegmentedFinder* s, vecTriplet *x, int )
|
||
|
{
|
||
|
if(x->start <= x->finish && x->finish <= x->alloc_finish)
|
||
|
{
|
||
|
if(s->getSegmentForAddress(x->start) == s->getSegmentForAddress(x->finish)
|
||
|
&& s->getSegmentForAddress(x->finish) == s->getSegmentForAddress(x->alloc_finish))
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
class Bytestreamdata
|
||
|
{
|
||
|
public:
|
||
|
void * object;
|
||
|
uint32_t length;
|
||
|
uint32_t allocated;
|
||
|
uint32_t n_used;
|
||
|
};
|
||
|
|
||
|
class Bytestream
|
||
|
{
|
||
|
public:
|
||
|
Bytestream(void * obj, uint32_t len, bool alloc = false)
|
||
|
{
|
||
|
d = new Bytestreamdata();
|
||
|
d->allocated = alloc;
|
||
|
d->object = obj;
|
||
|
d->length = len;
|
||
|
d->n_used = 1;
|
||
|
constant = false;
|
||
|
}
|
||
|
Bytestream()
|
||
|
{
|
||
|
d = new Bytestreamdata();
|
||
|
d->allocated = false;
|
||
|
d->object = 0;
|
||
|
d->length = 0;
|
||
|
d->n_used = 1;
|
||
|
constant = false;
|
||
|
}
|
||
|
Bytestream( Bytestream & bs)
|
||
|
{
|
||
|
d =bs.d;
|
||
|
d->n_used++;
|
||
|
constant = false;
|
||
|
}
|
||
|
Bytestream( const Bytestream & bs)
|
||
|
{
|
||
|
d =bs.d;
|
||
|
d->n_used++;
|
||
|
constant = true;
|
||
|
}
|
||
|
~Bytestream()
|
||
|
{
|
||
|
d->n_used --;
|
||
|
if(d->allocated && d->object && d->n_used == 0)
|
||
|
{
|
||
|
free (d->object);
|
||
|
free (d);
|
||
|
}
|
||
|
}
|
||
|
bool Allocate(size_t bytes)
|
||
|
{
|
||
|
if(constant)
|
||
|
return false;
|
||
|
if(d->allocated)
|
||
|
{
|
||
|
d->object = realloc(d->object, bytes);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->object = malloc( bytes );
|
||
|
}
|
||
|
|
||
|
if(d->object)
|
||
|
{
|
||
|
d->allocated = bytes;
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->allocated = 0;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
template < class T >
|
||
|
bool insert( T what )
|
||
|
{
|
||
|
if(constant)
|
||
|
return false;
|
||
|
if(d->length+sizeof(T) >= d->allocated)
|
||
|
Allocate((d->length+sizeof(T)) * 2);
|
||
|
(*(T *)( (uint64_t)d->object + d->length)) = what;
|
||
|
d->length += sizeof(T);
|
||
|
return true;
|
||
|
}
|
||
|
Bytestreamdata * d;
|
||
|
bool constant;
|
||
|
};
|
||
|
std::ostream& operator<< ( std::ostream& out, Bytestream& bs )
|
||
|
{
|
||
|
if(bs.d->object)
|
||
|
{
|
||
|
out << "bytestream " << dec << bs.d->length << "/" << bs.d->allocated << " bytes" << endl;
|
||
|
for(size_t i = 0; i < bs.d->length; i++)
|
||
|
{
|
||
|
out << hex << (int) ((uint8_t *) bs.d->object)[i] << " ";
|
||
|
}
|
||
|
out << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
out << "empty bytestresm" << endl;
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
std::istream& operator>> ( std::istream& out, Bytestream& bs )
|
||
|
{
|
||
|
string read;
|
||
|
while(!out.eof())
|
||
|
{
|
||
|
string tmp;
|
||
|
out >> tmp;
|
||
|
read.append(tmp);
|
||
|
}
|
||
|
cout << read << endl;
|
||
|
bs.d->length = 0;
|
||
|
size_t first = read.find_first_of("\"");
|
||
|
size_t last = read.find_last_of("\"");
|
||
|
size_t start = first + 1;
|
||
|
if(first == read.npos)
|
||
|
{
|
||
|
std::transform(read.begin(), read.end(), read.begin(), (int(*)(int)) tolower);
|
||
|
bs.Allocate(read.size()); // overkill. size / 2 should be good, but this is safe
|
||
|
int state = 0;
|
||
|
char big = 0;
|
||
|
char small = 0;
|
||
|
string::iterator it = read.begin();
|
||
|
// iterate through string, construct a bytestream out of 00-FF bytes
|
||
|
while(it != read.end())
|
||
|
{
|
||
|
char reads = *it;
|
||
|
if((reads >='0' && reads <= '9'))
|
||
|
{
|
||
|
if(state == 0)
|
||
|
{
|
||
|
big = reads - '0';
|
||
|
state = 1;
|
||
|
}
|
||
|
else if(state == 1)
|
||
|
{
|
||
|
small = reads - '0';
|
||
|
state = 0;
|
||
|
bs.insert<char>(big*16 + small);
|
||
|
}
|
||
|
}
|
||
|
if((reads >= 'a' && reads <= 'f'))
|
||
|
{
|
||
|
if(state == 0)
|
||
|
{
|
||
|
big = reads - 'a' + 10;
|
||
|
state = 1;
|
||
|
}
|
||
|
else if(state == 1)
|
||
|
{
|
||
|
small = reads - 'a' + 10;
|
||
|
state = 0;
|
||
|
bs.insert<char>(big*16 + small);
|
||
|
}
|
||
|
}
|
||
|
it++;
|
||
|
}
|
||
|
// we end in state= 1. should we add or should we trim... or throw errors?
|
||
|
// I decided on adding
|
||
|
if (state == 1)
|
||
|
{
|
||
|
small = 0;
|
||
|
bs.insert<char>(big*16 + small);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(last == first)
|
||
|
{
|
||
|
// only one "
|
||
|
last = read.size();
|
||
|
}
|
||
|
size_t length = last - start;
|
||
|
// construct bytestream out of stuff between ""
|
||
|
bs.d->length = length;
|
||
|
if(length)
|
||
|
{
|
||
|
// todo: Bytestream should be able to handle this without external code
|
||
|
bs.Allocate(length);
|
||
|
bs.d->length = length;
|
||
|
const char* strstart = read.c_str();
|
||
|
memcpy(bs.d->object, strstart + start, length);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bs.d->object = 0;
|
||
|
}
|
||
|
}
|
||
|
cout << bs;
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
bool findBytestream (SegmentedFinder* s, void *addr, Bytestream compare )
|
||
|
{
|
||
|
if(memcmp(addr, compare.d->object, compare.d->length) == 0)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool findString (SegmentedFinder* s, uint32_t *addr, const char * compare )
|
||
|
{
|
||
|
// read string pointer, translate to local scheme
|
||
|
char *str = s->Translate<char>(*addr);
|
||
|
// verify
|
||
|
if(!str)
|
||
|
return false;
|
||
|
if(strcmp(str, compare) == 0)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool findStrBuffer (SegmentedFinder* s, uint32_t *addr, const char * compare )
|
||
|
{
|
||
|
if(memcmp((const char *)addr, compare, strlen(compare)) == 0)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#endif // SEGMENTED_FINDER_H
|