// this is an incremental search tool. It only works on Linux. // here be dragons... and ugly code :P #include #include #include #include #include #include #include #include #include #include using namespace std; #ifndef LINUX_BUILD #define WINVER 0x0500 // this one prevents windows from infecting the global namespace with filth #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include #endif #include #include "SegmentedFinder.h" class Token { public: Token(uint64_t _offset) { offset = _offset; offset_valid = 1; value_valid = 0; parent = 0; } Token(const std::string & offsetn) { full_offset_name = offsetn; offset_valid = 0; value_valid = 0; parent = 0; } Token() { offset_valid = 0; value_valid = 0; parent = 0; } virtual ~Token(){}; virtual bool LoadData(SegmentedFinder * s) = 0; virtual void EmptyData() { value_valid = false; }; virtual bool Match(SegmentedFinder * s, uint64_t offset) = 0; virtual void EmptyOffset() { offset_valid = false; }; virtual bool AcquireOffset(DFHack::VersionInfo * vinfo) { vinfo->getOffset(full_offset_name); return true; } virtual uint32_t Length() = 0; virtual uint64_t getAbsolute(){if(parent) return parent->getAbsolute() + offset; else return offset;}; void setParent( Token *par ) { par = parent; } protected: uint64_t offset;// offset from the start of the parent token std::string full_offset_name; Token * parent; bool offset_valid :1; bool value_valid :1; }; class Byte: virtual public Token { public: Byte(uint64_t _offset):Token(_offset){}; Byte():Token(){}; ~Byte(); virtual bool LoadData(SegmentedFinder * s) { if(offset_valid) { char * ptr = s->Translate(getAbsolute()); if(ptr) { value = *ptr; value_valid = true; return true; } } return false; }; // is the loaded data same as data at offset? yes -> set our offset to that. virtual bool Match(SegmentedFinder * s, uint64_t offs) { if(value_valid && (*s->Translate(parent->getAbsolute() + offset)) == value ) { if(parent) offset = offs - parent->getAbsolute(); else offset = offs; return true; } return false; }; virtual uint32_t Length() { return 1; }; private: char value; }; class Short: virtual public Token { public: Short(uint64_t _offset):Token(_offset){}; Short():Token(){}; ~Short(); virtual bool LoadData(SegmentedFinder * s) { if(offset_valid) { uint16_t * ptr = s->Translate(getAbsolute()); if(ptr) { value = *ptr; value_valid = true; return true; } } return false; }; // is the loaded data same as data at offset? yes -> set our offset to that. virtual bool Match(SegmentedFinder * s, uint64_t offs) { if(value_valid && (*s->Translate(parent->getAbsolute() + offset)) == value ) { if(parent) offset = offs - parent->getAbsolute(); else offset = offs; return true; } return false; }; virtual uint32_t Length() { return 2; }; private: uint16_t value; }; class Long: virtual public Token { public: Long(uint64_t _offset):Token(_offset){}; Long():Token(){}; ~Long(); virtual bool LoadData(SegmentedFinder * s) { if(offset_valid) { uint32_t * ptr = s->Translate(getAbsolute()); if(ptr) { value = *ptr; value_valid = true; return true; } } return false; }; // is the loaded data same as data at offset? yes -> set our offset to that. virtual bool Match(SegmentedFinder * s, uint64_t offs) { if(value_valid && (*s->Translate(offs)) == value ) { if(parent) offset = offs - parent->getAbsolute(); else offset = offs; return true; } return false; }; virtual uint32_t Length(){return 4;}; private: uint32_t value; }; class PtrVector : virtual public Token { public: PtrVector(uint64_t _offset):Token(_offset){}; PtrVector():Token(){}; ~PtrVector(); virtual uint32_t Length(){return 12;}; private: vector value; }; class Pointer: virtual public Token { public: Pointer(uint64_t _offset):Token(_offset){}; Pointer():Token(){}; ~Pointer(); virtual uint32_t Length(){return 4;}; private: uint64_t value; }; class String: virtual public Token { protected: string value; }; class Struct: virtual public Token { public: Struct(uint64_t _offset):Token(_offset){}; Struct():Token(){}; ~Struct(){}; void Add( Token * t ){members.push_back(t);}; virtual uint32_t Length(){return 0;}; // FIXME: temporary solution, should be the minimal length of all the contents combined virtual bool LoadData(SegmentedFinder* s) { bool OK = true; for(size_t i = 0; i < members.size() && OK; i++) OK &= members[i]->LoadData(s); return OK; }; // TODO: IMPLEMENT! virtual bool Match(SegmentedFinder* s, uint64_t offset) { return false; } private: vector members; }; class LinuxString: virtual public String { public: LinuxString(uint64_t _offset):Token(_offset){}; LinuxString():Token(){}; ~LinuxString(){}; virtual uint32_t Length(){return 4;}; virtual bool LoadData(SegmentedFinder* s) { return false; } virtual bool Match(SegmentedFinder* s, uint64_t offset) { return false; } /* // read string pointer, translate to local scheme char *str = sf->Translate(*offset); // verify if(!str) return false; uint32_t length = *(uint32_t *)(offset - 12); uint32_t capacity = *(uint32_t *)(offset - 8); if(length > capacity) return false; //char * temp = new char[length+1]; // read data from inside the string structure //memcpy(temp, str,length + 1); output = str; return true; */ }; class WindowsString: virtual public String { public: WindowsString(uint64_t _offset):Token(_offset){}; WindowsString():Token(){}; ~WindowsString(){}; virtual uint32_t Length(){return 0x1C;}; // FIXME: pouzivat Memory.xml? virtual bool LoadData(SegmentedFinder* s) { return false; } virtual bool Match(SegmentedFinder* s, uint64_t offset) { return false; } string rdWinString( char * offset, SegmentedFinder & sf ) { char * start_offset = offset + 4; // FIXME: pouzivat Memory.xml? uint32_t length = *(uint32_t *)(offset + 20); // FIXME: pouzivat Memory.xml? uint32_t capacity = *(uint32_t *)(offset + 24); // FIXME: pouzivat Memory.xml? char * temp = new char[capacity+1]; // read data from inside the string structure if(capacity < 16) { memcpy(temp, start_offset,capacity); //read(start_offset, capacity, (uint8_t *)temp); } else // read data from what the offset + 4 dword points to { start_offset = sf.Translate(*(uint32_t*)start_offset); memcpy(temp, start_offset,capacity); } temp[length] = 0; string ret = temp; delete temp; return ret; } }; inline void printRange(DFHack::t_memrange * tpr) { std::cout << std::hex << tpr->start << " - " << tpr->end << "|" << (tpr->read ? "r" : "-") << (tpr->write ? "w" : "-") << (tpr->execute ? "x" : "-") << "|" << tpr->name << std::endl; } bool getRanges(DFHack::Process * p, vector & selected_ranges) { vector ranges; selected_ranges.clear(); p->getMemRanges(ranges); cout << "Which range to search? (default is 1-4)" << endl; for(size_t i = 0; i< ranges.size();i++) { cout << dec << "(" << i << ") "; printRange(&(ranges[i])); } int start, end; while(1) { string select; cout << ">>"; std::getline(cin, select); if(select.empty()) { // empty input, assume default. observe the length of the memory range vector // these are hardcoded values, intended for my convenience only if(p->getDescriptor()->getOS() == DFHack::OS_WINDOWS) { start = min(11, (int)ranges.size()); end = min(14, (int)ranges.size()); } else if(p->getDescriptor()->getOS() == DFHack::OS_LINUX) { start = min(2, (int)ranges.size()); end = min(4, (int)ranges.size()); } else { start = 1; end = 1; } break; } // I like the C variants here. much less object clutter else if(sscanf(select.c_str(), "%d-%d", &start, &end) == 2) { start = min(start, (int)ranges.size()); end = min(end, (int)ranges.size()); break; } else { continue; } break; } end++; cout << "selected ranges:" <::iterator it; it = ranges.begin() + start; while (it != ranges.begin() + end) { // check if readable if((*it).read) { selected_ranges.push_back(*it); printRange(&*it); } it++; } return true; } bool getNumber (string prompt, int & output, int def, bool pdef = true) { cout << prompt; if(pdef) cout << " default=" << def << endl; while (1) { string select; cout << ">>"; std::getline(cin, select); if(select.empty()) { output = def; break; } else if( sscanf(select.c_str(), "%d", &output) == 1 ) { break; } else { continue; } } return true; } bool getString (string prompt, string & output) { cout << prompt; cout << ">>"; string select; std::getline(cin, select); if(select.empty()) { return false; } else { output = select; return true; } } // meh #pragma pack(1) struct tilecolors { uint16_t fore; uint16_t back; uint16_t bright; }; #pragma pack() void printFound(vector &found, const char * what) { cout << what << ":" << endl; for(size_t i = 0; i < found.size();i++) { cout << hex << "0x" << found[i] << endl; } } void printFoundStrVec(vector &found, const char * what, SegmentedFinder & s) { cout << what << ":" << endl; for(size_t i = 0; i < found.size();i++) { cout << hex << "0x" << found[i] << endl; cout << "--------------------------" << endl; vecTriplet * vt = s.Translate(found[i]); if(vt) { int j = 0; for(uint32_t idx = vt->start; idx < vt->finish; idx += sizeof(uint32_t)) { uint32_t object_ptr; // deref ptr idx, get ptr to object if(!s.Read(idx,object_ptr)) { cout << "BAD!" << endl; break; } // deref ptr to first object, get ptr to string uint32_t string_ptr; if(!s.Read(object_ptr,string_ptr)) { cout << "BAD!" << endl; break; } // get string location in our local cache char * str = s.Translate(string_ptr); if(!str) { cout << "BAD!" << endl; break; } cout << dec << j << ":" << hex << "0x" << object_ptr << " : " << str << endl; j++; } } else { cout << "BAD!" << endl; break; } cout << "--------------------------" << endl; } } class TokenFactory { DFHack::OSType platform; public: TokenFactory(DFHack::OSType platform_in) { platform = platform_in; } template T * Build() { return new T; } template T * Build(uint64_t offset) { return new T(offset); } }; template <> String * TokenFactory::Build() { switch(platform) { case DFHack::OS_WINDOWS: return new WindowsString(); case DFHack::OS_LINUX: case DFHack::OS_APPLE: return new LinuxString(); case DFHack::OS_BAD: return 0; } }; template <> String * TokenFactory::Build(uint64_t offset) { switch(platform) { case DFHack::OS_WINDOWS: return new WindowsString(offset); case DFHack::OS_LINUX: case DFHack::OS_APPLE: return new LinuxString(offset); case DFHack::OS_BAD: return 0; } }; void autoSearch(DFHack::Context * DF, vector & ranges, DFHack::OSType platform) { cout << "stealing memory..." << endl; SegmentedFinder sf(ranges, DF); TokenFactory tf(platform); cout << "done!" << endl; Struct maps; maps.Add(tf.Build()); maps.Add(tf.Build()); /* vector allVectors; vector filtVectors; vector to_filter; cout << "stealing memory..." << endl; SegmentedFinder sf(ranges, DF); cout << "looking for vectors..." << endl; sf.Find(0,4,allVectors, vectorAll); filtVectors = allVectors; cout << "-------------------" << endl; cout << "!!LANGUAGE TABLES!!" << endl; cout << "-------------------" << endl; uint64_t kulet_vector; uint64_t word_table_offset; uint64_t DWARF_vector; uint64_t DWARF_object; // find lang vector (neutral word table) to_filter = filtVectors; sf.Filter("ABBEY",to_filter, vectorStringFirst); uint64_t lang_addr = to_filter[0]; // find dwarven language word table to_filter = filtVectors; sf.Filter("kulet",to_filter, vectorStringFirst); kulet_vector = to_filter[0]; // find vector of languages to_filter = filtVectors; sf.Filter("DWARF",to_filter, vectorStringFirst); // verify for(int i = 0; i < to_filter.size(); i++) { vecTriplet * vec = sf.Translate(to_filter[i]); if(((vec->finish - vec->start) / 4) == 4) // verified { DWARF_vector = to_filter[i]; DWARF_object = sf.Read(vec->start); // compute word table offset from dwarf word table and dwarf language object addresses word_table_offset = kulet_vector - DWARF_object; break; } } cout << "translation vector: " << hex << "0x" << DWARF_vector << endl; cout << "lang vector: " << hex << "0x" << lang_addr << endl; cout << "word table offset: " << hex << "0x" << word_table_offset << endl; cout << "-------------" << endl; cout << "!!MATERIALS!!" << endl; cout << "-------------" << endl; // inorganics vector to_filter = filtVectors; //sf.Find(257 * 4,4,to_filter,vectorLength); sf.Filter("IRON",to_filter, vectorString); sf.Filter("ONYX",to_filter, vectorString); sf.Filter("RAW_ADAMANTINE",to_filter, vectorString); sf.Filter("BLOODSTONE",to_filter, vectorString); printFound(to_filter,"inorganics"); // organics vector to_filter = filtVectors; sf.Filter(52 * 4,to_filter,vectorLength); sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); printFound(to_filter,"organics"); // tree vector to_filter = filtVectors; sf.Filter(31 * 4,to_filter,vectorLength); sf.Filter("MANGROVE",to_filter, vectorStringFirst); printFound(to_filter,"trees"); // plant vector to_filter = filtVectors; sf.Filter(21 * 4,to_filter,vectorLength); sf.Filter("MUSHROOM_HELMET_PLUMP",to_filter, vectorStringFirst); printFound(to_filter,"plants"); // color descriptors //AMBER, 112 to_filter = filtVectors; sf.Filter(112 * 4,to_filter,vectorLength); sf.Filter("AMBER",to_filter, vectorStringFirst); printFound(to_filter,"color descriptors"); if(!to_filter.empty()) { uint64_t vec = to_filter[0]; vecTriplet *vtColors = sf.Translate(vec); uint32_t colorObj = sf.Read(vtColors->start); cout << "Amber color:" << hex << "0x" << colorObj << endl; // TODO: find string 'amber', the floats } // all descriptors //AMBER, 338 to_filter = filtVectors; sf.Filter(338 * 4,to_filter,vectorLength); sf.Filter("AMBER",to_filter, vectorStringFirst); printFound(to_filter,"all descriptors"); // creature type //ELEPHANT, ?? (demons abound) to_filter = filtVectors; //sf.Find(338 * 4,4,to_filter,vectorLength); sf.Filter("ELEPHANT",to_filter, vectorString); sf.Filter("CAT",to_filter, vectorString); sf.Filter("DWARF",to_filter, vectorString); sf.Filter("WAMBLER_FLUFFY",to_filter, vectorString); sf.Filter("TOAD",to_filter, vectorString); sf.Filter("DEMON_1",to_filter, vectorString); vector toad_first = to_filter; vector elephant_first = to_filter; sf.Filter("TOAD",toad_first, vectorStringFirst); sf.Filter("ELEPHANT",elephant_first, vectorStringFirst); printFoundStrVec(toad_first,"toad-first creature types",sf); printFound(elephant_first,"elephant-first creature types"); printFound(to_filter,"all creature types"); uint64_t to_use = 0; if(!elephant_first.empty()) { to_use = elephant_first[0]; vecTriplet *vtCretypes = sf.Translate(to_use); uint32_t elephant = sf.Read(vtCretypes->start); uint64_t Eoffset; cout << "Elephant: 0x" << hex << elephant << endl; cout << "Elephant: rawname = 0x0" << endl; uint8_t letter_E = 'E'; Eoffset = sf.FindInRange (letter_E,equalityP, elephant, 0x300 ); if(Eoffset) { cout << "Elephant: big E = 0x" << hex << Eoffset - elephant << endl; } Eoffset = sf.FindInRange ("FEMALE",vectorStringFirst, elephant, 0x300 ); if(Eoffset) { cout << "Elephant: caste vector = 0x" << hex << Eoffset - elephant << endl; } Eoffset = sf.FindInRange ("SKIN",vectorStringFirst, elephant, 0x2000 ); if(Eoffset) { cout << "Elephant: extract? vector = 0x" << hex << Eoffset - elephant << endl; } tilecolors eletc = {7,0,0}; Bytestream bs_eletc(&eletc, sizeof(tilecolors)); cout << bs_eletc; Eoffset = sf.FindInRange (bs_eletc, findBytestream, elephant, 0x300 ); if(Eoffset) { cout << "Elephant: colors = 0x" << hex << Eoffset - elephant << endl; } //cout << "Amber color:" << hex << "0x" << colorObj << endl; // TODO: find string 'amber', the floats } if(!toad_first.empty()) { to_use = toad_first[0]; vecTriplet *vtCretypes = sf.Translate(to_use); uint32_t toad = sf.Read(vtCretypes->start); uint64_t Eoffset; cout << "Toad: 0x" << hex << toad << endl; cout << "Toad: rawname = 0x0" << endl; Eoffset = sf.FindInRange (0xF9,equalityP, toad, 0x300 ); if(Eoffset) { cout << "Toad: character (not reliable) = 0x" << hex << Eoffset - toad << endl; } Eoffset = sf.FindInRange ("FEMALE",vectorStringFirst, toad, 0x300 ); if(Eoffset) { cout << "Toad: caste vector = 0x" << hex << Eoffset - toad << endl; } Eoffset = sf.FindInRange ("SKIN",vectorStringFirst, toad, 0x2000 ); if(Eoffset) { cout << "Toad: extract? vector = 0x" << hex << Eoffset - toad << endl; } tilecolors toadtc = {2,0,0}; Bytestream bs_toadc(&toadtc, sizeof(tilecolors)); Eoffset = sf.FindInRange (bs_toadc, findBytestream, toad, 0x300 ); if(Eoffset) { cout << "Toad: colors = 0x" << hex << Eoffset - toad << endl; } }*/ } int main (void) { string select; DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context * DF = DFMgr.getSingleContext(); try { DF->Attach(); } catch (exception& e) { cerr << e.what() << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } DFHack::Process * p = DF->getProcess(); vector selected_ranges; getRanges(p,selected_ranges); DFHack::VersionInfo *minfo = DF->getMemoryInfo(); autoSearch(DF,selected_ranges, minfo->getOS()); #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore(); #endif return 0; }