// 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 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" template class holder { public: vector values; SegmentedFinder & sf; holder(SegmentedFinder& sff):sf(sff){}; bool isValid(size_t idx) { }; }; class address { public: uint64_t addr_; unsigned int valid : 1; virtual void print(SegmentedFinder& sff) { cout << hex << "0x" << addr_ << endl; }; address(const uint64_t addr) { addr_ = addr; valid = false; } virtual address & operator=(const uint64_t in) { addr_ = in; valid = false; return *this; } virtual bool isValid(SegmentedFinder& sff) { if(valid) return true; if(sff.getSegmentForAddress(addr_)) { valid = 1; } } virtual bool equals (SegmentedFinder & sf, address & rhs) { return rhs.addr_ == addr_; } }; // pointer to a null-terminated byte string class Cstr: public address { void print(SegmentedFinder & sf) { cout << hex << "0x" << addr_ << ": \"" << sf.Translate(addr_) << "\"" << endl; } bool equals(SegmentedFinder & sf,const char * rhs) { uint32_t addr2 = *(sf.Translate(addr_)); return strcmp(sf.Translate(addr2), rhs) == 0; } template bool equalsP(SegmentedFinder & sf,inType rhs) { return Predicate(addr_, sf, rhs); } bool isValid(SegmentedFinder& sf) { if (address::isValid(sf)) { // read the pointer uint32_t addr2 = *(sf.Translate(addr_)); // is it a real pointer? a pretty weak test, but whatever. if(sf.getSegmentForAddress(addr2)) return true; } return false; } }; // STL STRING #ifdef LINUX_BUILD class STLstr: public address { }; #endif #ifndef LINUX_BUILD class STLstr: public address { }; #endif // STL VECTOR #ifdef LINUX_BUILD class Vector: public address { }; #endif #ifndef LINUX_BUILD class Vector: public address { }; #endif class Int64: public address{}; class Int32: public address{}; class Int16: public address{}; class Int8: public address{}; 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; } string rdWinString( char * offset, SegmentedFinder & sf ) { char * start_offset = offset + 4; uint32_t length = *(uint32_t *)(offset + 20); uint32_t capacity = *(uint32_t *)(offset + 24); 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; } 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(int 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::VersionInfo::OS_WINDOWS) { start = min(11, (int)ranges.size()); end = min(14, (int)ranges.size()); } else if(p->getDescriptor()->getOS() == DFHack::VersionInfo::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++; } } 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; } } template bool Incremental ( vector &found, const char * what, T& output, const char *singular = "address", const char *plural = "addresses" ) { string select; if(found.empty()) { cout << "search ready - insert " << what << ", 'p' for results, 'p #' to limit number of results" << endl; } else if( found.size() == 1) { cout << "Found single "<< singular <<"!" << endl; cout << hex << "0x" << found[0] << endl; } else { cout << "Found " << dec << found.size() << " " << plural <<"." << endl; } incremental_more: cout << ">>"; std::getline(cin, select); size_t num = 0; if( sscanf(select.c_str(),"p %d", &num) && num > 0) { cout << "Found "<< plural <<":" << endl; for(int i = 0; i < min(found.size(), num);i++) { cout << hex << "0x" << found[i] << endl; } goto incremental_more; } else if(select == "p") { cout << "Found "<< plural <<":" << endl; for(int i = 0; i < found.size();i++) { cout << hex << "0x" << found[i] << endl; } goto incremental_more; } else if(select.empty()) { return false; } else { if( sscanf(select.c_str(),"0x%x", &output) == 1 ) { //cout << dec << output << endl; return true; } if( sscanf(select.c_str(),"%d", &output) == 1 ) { //cout << dec << output << endl; return true; } stringstream ss (stringstream::in | stringstream::out); ss << select; ss >> output; if(!ss.fail()) { return true; } cout << "not a valid value for type: " << what << endl; goto incremental_more; } } void FindIntegers(DFHack::ContextManager & DFMgr, vector & ranges) { // input / validation of variable size int size; do { getNumber("Select variable size (1,2,4 bytes)",size, 4); } while (size != 1 && size != 2 && size != 4); // input / validation of variable alignment (default is to use the same alignment as size) int alignment; do { getNumber("Select variable alignment (1,2,4 bytes)",alignment, size); } while (alignment != 1 && alignment != 2 && alignment != 4); uint32_t test1; vector found; found.reserve(100); while(Incremental(found, "integer",test1)) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); switch(size) { case 1: sf.Incremental(test1,alignment,found, equalityP); break; case 2: sf.Incremental(test1,alignment,found, equalityP); break; case 4: sf.Incremental(test1,alignment,found, equalityP); break; } DF->Detach(); } } void FindVectorByLength(DFHack::ContextManager & DFMgr, vector & ranges ) { int element_size; do { getNumber("Select searched vector item size in bytes",element_size, 4); } while (element_size < 1); uint32_t length; vector found; found.reserve(100); while (Incremental(found, "vector length",length,"vector","vectors")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); //sf.Incremental(0,4,found,vectorAll); //sf.Filter(length * element_size,found,vectorLength); sf.Incremental(length * element_size, 4 , found, vectorLength); DF->Detach(); } } void FindVectorByObjectRawname(DFHack::ContextManager & DFMgr, vector & ranges) { vector found; string select; while (Incremental(found, "raw name",select,"vector","vectors")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); sf.Find(0,4,found, vectorAll); sf.Filter(select.c_str(),found, vectorString); DF->Detach(); } } void FindVectorByFirstObjectRawname(DFHack::ContextManager & DFMgr, vector & ranges) { vector found; string select; while (Incremental(found, "raw name",select,"vector","vectors")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); sf.Find(0,4,found, vectorAll); sf.Filter(select.c_str(),found, vectorStringFirst); DF->Detach(); } } struct VectorSizeFunctor : public binary_function { VectorSizeFunctor(SegmentedFinder & sf):sf_(sf){} bool operator()( uint64_t lhs, uint64_t rhs) { vecTriplet* left = sf_.Translate(lhs); vecTriplet* right = sf_.Translate(rhs); return ((left->finish - left->start) < (right->finish - right->start)); } SegmentedFinder & sf_; }; void FindVectorByBounds(DFHack::ContextManager & DFMgr, vector & ranges) { vector found; uint32_t select; while (Incremental(found, "address between vector.start and vector.end",select,"vector","vectors")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); sf.Find(0,4,found, vectorAll); sf.Filter(select,found, vectorAddrWithin); // sort by size of vector std::sort(found.begin(), found.end(), VectorSizeFunctor(sf)); DF->Detach(); } } void FindPtrVectorsByObjectAddress(DFHack::ContextManager & DFMgr, vector & ranges) { vector found; uint32_t select; while (Incremental(found, "object address",select,"vector","vectors")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); sf.Find(0,4,found, vectorAll); sf.Filter(select,found, vectorOfPtrWithin); DF->Detach(); } } void FindStrings(DFHack::ContextManager & DFMgr, vector & ranges) { vector found; string select; while (Incremental(found,"string",select,"string","strings")) { DFMgr.Refresh(); DFHack::Context * DF = DFMgr.getSingleContext(); DF->Attach(); SegmentedFinder sf(ranges,DF); sf.Incremental< const char * ,uint32_t>(select.c_str(),1,found, findString); DF->Detach(); } } void printFound(vector &found, const char * what) { cout << what << ":" << endl; for(int 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(int 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; } } // meh #pragma pack(1) struct tilecolors { uint16_t fore; uint16_t back; uint16_t bright; }; #pragma pack() void automatedLangtables(DFHack::Context * DF, vector & ranges) { 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); /* // trim vectors. anything with > 10000 entries is not interesting for(uint64_t i = 0; i < allVectors.size();i++) { vecTriplet* vtrip = sf.Translate(allVectors[i]); if(vtrip) { uint64_t length = (vtrip->finish - vtrip->start) / 4; if(length < 10000 ) { filtVectors.push_back(allVectors[i]); } } } */ 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; Eoffset = sf.FindInRange ('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 = {sizeof(tilecolors), &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 = {sizeof(tilecolors), &toadtc}; Eoffset = sf.FindInRange (bs_toadc, findBytestream, toad, 0x300 ); if(Eoffset) { cout << "Toad: colors = 0x" << hex << Eoffset - toad << endl; } } if(to_use) { } } 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(); DFHack::VersionInfo::OSType os = minfo->getOS(); string prompt = "Select search type: 1=number(default), 2=vector by length, 3=vector>object>string,\n" " 4=string, 5=automated offset search, 6=vector by address in its array,\n" " 7=pointer vector by address of an object, 8=vector>first object>string\n"; int mode; do { getNumber(prompt,mode, 1, false); } while (mode < 1 || mode > 8 ); switch (mode) { case 1: DF->Detach(); FindIntegers(DFMgr, selected_ranges); break; case 2: DF->Detach(); FindVectorByLength(DFMgr, selected_ranges); break; case 3: DF->Detach(); FindVectorByObjectRawname(DFMgr, selected_ranges); break; case 4: DF->Detach(); FindStrings(DFMgr, selected_ranges); break; case 5: automatedLangtables(DF,selected_ranges); break; case 6: DF->Detach(); FindVectorByBounds(DFMgr,selected_ranges); break; case 7: DF->Detach(); FindPtrVectorsByObjectAddress(DFMgr,selected_ranges); break; case 8: DF->Detach(); FindVectorByFirstObjectRawname(DFMgr, selected_ranges); break; default: cout << "not implemented :(" << endl; } #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore(); #endif return 0; }