diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 9df32d6d6..321ef5b3b 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -47,15 +47,15 @@ stdint_win.h ) SET(PROJECT_SRCS_LINUX -#DFProcess-linux.cpp +DFProcess-linux.cpp DFProcess-linux-SHM.cpp -#DFProcess-linux-wine.cpp +DFProcess-linux-wine.cpp DFWindow-linux.cpp DFProcessEnumerator-linux.cpp ) SET(PROJECT_SRCS_WINDOWS -#DFProcess-windows.cpp +DFProcess-windows.cpp DFProcess-windows-SHM.cpp DFWindow-windows.cpp DFProcessEnumerator-windows.cpp diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 581635e5e..1ae294355 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -38,7 +38,7 @@ using namespace DFHack; // a full memory barrier! better be safe than sorry. #define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); -class Process::Private +class SHMProcess::Private { public: Private() @@ -86,7 +86,7 @@ class Process::Private Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) we end up with this silly thing */ -bool Process::Private::waitWhile (uint32_t state) +bool SHMProcess::Private::waitWhile (uint32_t state) { uint32_t cnt = 0; struct shmid_ds descriptor; @@ -134,7 +134,7 @@ bool Process::Private::waitWhile (uint32_t state) Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) we end up with this silly thing */ -bool Process::waitWhile (uint32_t state) +bool SHMProcess::waitWhile (uint32_t state) { return d->waitWhile(state); } @@ -149,7 +149,7 @@ uint32_t OS_getAffinity() } -bool Process::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) { SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); gcc_barrier @@ -166,7 +166,7 @@ bool Process::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) return true; } -Process::Process(uint32_t PID, vector< memory_info* >& known_versions) +SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) : d(new Private()) { char exe_link_name [256]; @@ -242,21 +242,21 @@ Process::Process(uint32_t PID, vector< memory_info* >& known_versions) shmdt(d->my_shm); // detach so we don't attach twice when attach() is called } -bool Process::isSuspended() +bool SHMProcess::isSuspended() { return d->suspended; } -bool Process::isAttached() +bool SHMProcess::isAttached() { return d->attached; } -bool Process::isIdentified() +bool SHMProcess::isIdentified() { return d->identified; } -bool Process::Private::validate(char * exe_file, uint32_t pid, vector & known_versions) +bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -285,7 +285,7 @@ bool Process::Private::validate(char * exe_file, uint32_t pid, vector attached) { @@ -299,29 +299,29 @@ Process::~Process() delete d; } -memory_info * Process::getDescriptor() +memory_info * SHMProcess::getDescriptor() { return d->my_descriptor; } -DFWindow * Process::getWindow() +DFWindow * SHMProcess::getWindow() { return d->my_window; } -int Process::getPID() +int SHMProcess::getPID() { return d->my_pid; } //FIXME: implement -bool Process::getThreadIDs(vector & threads ) +bool SHMProcess::getThreadIDs(vector & threads ) { return false; } //FIXME: cross-reference with ELF segment entries? -void Process::getMemRanges( vector & ranges ) +void SHMProcess::getMemRanges( vector & ranges ) { char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 @@ -347,7 +347,7 @@ void Process::getMemRanges( vector & ranges ) } } -bool Process::suspend() +bool SHMProcess::suspend() { if(!d->attached) { @@ -366,7 +366,7 @@ bool Process::suspend() return true; } -bool Process::asyncSuspend() +bool SHMProcess::asyncSuspend() { if(!d->attached) { @@ -388,12 +388,12 @@ bool Process::asyncSuspend() } } -bool Process::forceresume() +bool SHMProcess::forceresume() { return resume(); } -bool Process::resume() +bool SHMProcess::resume() { if(!d->attached) return false; @@ -405,7 +405,7 @@ bool Process::resume() } -bool Process::attach() +bool SHMProcess::attach() { int status; if(g_pProcess != 0) @@ -434,7 +434,7 @@ bool Process::attach() return false; } -bool Process::detach() +bool SHMProcess::detach() { if(!d->attached) { @@ -458,7 +458,7 @@ bool Process::detach() return false; } -void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) +void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { // normal read under 1MB if(size <= SHM_BODY) @@ -495,7 +495,7 @@ void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) } } -uint8_t Process::readByte (const uint32_t offset) +uint8_t SHMProcess::readByte (const uint32_t offset) { D_SHMHDR->address = offset; gcc_barrier @@ -504,7 +504,7 @@ uint8_t Process::readByte (const uint32_t offset) return D_SHMHDR->value; } -void Process::readByte (const uint32_t offset, uint8_t &val ) +void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { D_SHMHDR->address = offset; gcc_barrier @@ -513,7 +513,7 @@ void Process::readByte (const uint32_t offset, uint8_t &val ) val = D_SHMHDR->value; } -uint16_t Process::readWord (const uint32_t offset) +uint16_t SHMProcess::readWord (const uint32_t offset) { D_SHMHDR->address = offset; gcc_barrier @@ -522,7 +522,7 @@ uint16_t Process::readWord (const uint32_t offset) return D_SHMHDR->value; } -void Process::readWord (const uint32_t offset, uint16_t &val) +void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { D_SHMHDR->address = offset; gcc_barrier @@ -531,7 +531,7 @@ void Process::readWord (const uint32_t offset, uint16_t &val) val = D_SHMHDR->value; } -uint32_t Process::readDWord (const uint32_t offset) +uint32_t SHMProcess::readDWord (const uint32_t offset) { D_SHMHDR->address = offset; gcc_barrier @@ -539,7 +539,7 @@ uint32_t Process::readDWord (const uint32_t offset) waitWhile(CORE_READ_DWORD); return D_SHMHDR->value; } -void Process::readDWord (const uint32_t offset, uint32_t &val) +void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { D_SHMHDR->address = offset; gcc_barrier @@ -552,7 +552,7 @@ void Process::readDWord (const uint32_t offset, uint32_t &val) * WRITING */ -void Process::writeDWord (uint32_t offset, uint32_t data) +void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -562,7 +562,7 @@ void Process::writeDWord (uint32_t offset, uint32_t data) } // using these is expensive. -void Process::writeWord (uint32_t offset, uint16_t data) +void SHMProcess::writeWord (uint32_t offset, uint16_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -571,7 +571,7 @@ void Process::writeWord (uint32_t offset, uint16_t data) waitWhile(CORE_WRITE_WORD); } -void Process::writeByte (uint32_t offset, uint8_t data) +void SHMProcess::writeByte (uint32_t offset, uint8_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -580,7 +580,7 @@ void Process::writeByte (uint32_t offset, uint8_t data) waitWhile(CORE_WRITE_BYTE); } -void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) +void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { // normal write under 1MB if(size <= SHM_BODY) @@ -618,7 +618,7 @@ void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer } // FIXME: butt-fugly -const std::string Process::readCString (uint32_t offset) +const std::string SHMProcess::readCString (uint32_t offset) { std::string temp; char temp_c[256]; @@ -635,7 +635,7 @@ const std::string Process::readCString (uint32_t offset) return temp; } -DfVector Process::readVector (uint32_t offset, uint32_t item_size) +DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) { /* GNU libstdc++ vector is three pointers long @@ -651,7 +651,7 @@ DfVector Process::readVector (uint32_t offset, uint32_t item_size) return DfVector(start,size,item_size); } -const std::string Process::readSTLString(uint32_t offset) +const std::string SHMProcess::readSTLString(uint32_t offset) { D_SHMHDR->address = offset; full_barrier @@ -661,7 +661,7 @@ const std::string Process::readSTLString(uint32_t offset) return(string( (char *)d->my_shm+SHM_HEADER)); } -size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { D_SHMHDR->address = offset; full_barrier @@ -674,7 +674,7 @@ size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacit return fit; } -void Process::writeSTLString(const uint32_t address, const std::string writeString) +void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { D_SHMHDR->address = address; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator @@ -683,7 +683,7 @@ void Process::writeSTLString(const uint32_t address, const std::string writeStri waitWhile(CORE_WRITE_STL_STRING); } -string Process::readClassName (uint32_t vptr) +string SHMProcess::readClassName (uint32_t vptr) { int typeinfo = readDWord(vptr - 0x4); int typestring = readDWord(typeinfo + 0x4); @@ -697,7 +697,7 @@ string Process::readClassName (uint32_t vptr) // *!!DON'T BE AN UNSUSPECTING FOOL!!* // the whole SHM thing works only because copying DWORDS is an atomic operation on i386 and x86_64 archs // get module index by name and version. bool 1 = error -bool Process::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); payload->version = version; @@ -721,7 +721,7 @@ bool Process::getModuleIndex (const char * name, const uint32_t version, uint32_ return true; } -char * Process::getSHMStart (void) +char * SHMProcess::getSHMStart (void) { return d->my_shm; } \ No newline at end of file diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp new file mode 100644 index 000000000..880a9a06a --- /dev/null +++ b/library/DFProcess-linux-wine.cpp @@ -0,0 +1,595 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "DFCommonInternal.h" +#include +#include +#include +using namespace DFHack; + +class WineProcess::Private +{ + public: + Private() + { + my_descriptor = NULL; + my_handle = NULL; + my_window = NULL; + my_pid = 0; + attached = false; + suspended = false; + memFileHandle = 0; + }; + ~Private(){}; + DFWindow* my_window; + memory_info * my_descriptor; + ProcessHandle my_handle; + uint32_t my_pid; + string memFile; + int memFileHandle; + bool attached; + bool suspended; + bool identified; + bool validate(char * exe_file, uint32_t pid, char * mem_file, vector & known_versions); +}; + +WineProcess::WineProcess(uint32_t pid, vector & known_versions) +: d(new Private()) +{ + char dir_name [256]; + char exe_link_name [256]; + char mem_name [256]; + char cwd_name [256]; + char cmdline_name [256]; + char target_name[1024]; + int target_result; + + d->identified = false; + + sprintf(dir_name,"/proc/%d/", pid); + sprintf(exe_link_name,"/proc/%d/exe", pid); + sprintf(mem_name,"/proc/%d/mem", pid); + sprintf(cwd_name,"/proc/%d/cwd", pid); + sprintf(cmdline_name,"/proc/%d/cmdline", pid); + + // resolve /proc/PID/exe link + target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); + if (target_result == -1) + { + return; + } + // make sure we have a null terminated string... + target_name[target_result] = 0; + + // FIXME: this fails when the wine process isn't started from the 'current working directory'. strip path data from cmdline + // is this windows version of Df running in wine? + if(strstr(target_name, "wine-preloader")!= NULL) + { + // get working directory + target_result = readlink(cwd_name, target_name, sizeof(target_name)-1); + target_name[target_result] = 0; + + // got path to executable, do the same for its name + ifstream ifs ( cmdline_name , ifstream::in ); + string cmdline; + getline(ifs,cmdline); + if (cmdline.find("dwarfort-w.exe") != string::npos || cmdline.find("dwarfort.exe") != string::npos || cmdline.find("Dwarf Fortress.exe") != string::npos) + { + char exe_link[1024]; + // put executable name and path together + sprintf(exe_link,"%s/%s",target_name,cmdline.c_str()); + + // create wine process, add it to the vector + d->identified = d->validate(exe_link,pid,mem_name,known_versions); + d->my_window = new DFWindow(this); + return; + } + } +} + +bool WineProcess::isSuspended() +{ + return d->suspended; +} +bool WineProcess::isAttached() +{ + return d->attached; +} + +bool WineProcess::isIdentified() +{ + return d->identified; +} + +bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file, std::vector< memory_info* >& known_versions) +{ + md5wrapper md5; + // get hash of the running DF process + string hash = md5.getHashFromFile(exe_file); + vector::iterator it; + + // iterate over the list of memory locations + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) + { + string thishash; + try + { + thishash = (*it)->getString("md5"); + } + catch (Error::MissingMemoryDefinition& e) + { + continue; + } + // are the md5 hashes the same? + if(memory_info::OS_WINDOWS == (*it)->getOS() && hash == thishash) + { + memory_info * m = *it; + my_descriptor = m; + my_handle = my_pid = pid; + // tell WineProcess about the /proc/PID/mem file + memFile = mem_file; + identified = true; + return true; + } + } + return false; +} + +WineProcess::~WineProcess() +{ + if(d->attached) + { + detach(); + } + if(d->my_window) + delete d->my_window; + delete d; +} + +memory_info * WineProcess::getDescriptor() +{ + return d->my_descriptor; +} + +DFWindow * WineProcess::getWindow() +{ + return d->my_window; +} + +int WineProcess::getPID() +{ + return d->my_pid; +} + +//FIXME: implement +bool WineProcess::getThreadIDs(vector & threads ) +{ + return false; +} + +//FIXME: cross-reference with ELF segment entries? +void WineProcess::getMemRanges( vector & ranges ) +{ + char buffer[1024]; + char permissions[5]; // r/-, w/-, x/-, p/s, 0 + + sprintf(buffer, "/proc/%lu/maps", d->my_pid); + FILE *mapFile = ::fopen(buffer, "r"); + uint64_t offset, device1, device2, node; + + while (fgets(buffer, 1024, mapFile)) + { + t_memrange temp; + temp.name[0] = 0; + sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s", + &temp.start, + &temp.end, + (char*)&permissions, + &offset, &device1, &device2, &node, + (char*)&temp.name); + temp.read = permissions[0] == 'r'; + temp.write = permissions[1] == 'w'; + temp.execute = permissions[2] == 'x'; + ranges.push_back(temp); + } +} + +bool WineProcess::asyncSuspend() +{ + return suspend(); +} + +bool WineProcess::suspend() +{ + int status; + if(!d->attached) + return false; + if(d->suspended) + return true; + if (kill(d->my_handle, SIGSTOP) == -1) + { + // no, we got an error + perror("kill SIGSTOP error"); + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("DF exited during suspend call"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; + return true; +} + +bool WineProcess::forceresume() +{ + return resume(); +} + +bool WineProcess::resume() +{ + if(!d->attached) + return false; + if(!d->suspended) + return true; + if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace resume error"); + return false; + } + d->suspended = false; + return true; +} + + +bool WineProcess::attach() +{ + int status; + if(g_pProcess != NULL) + { + return false; + } + // can we attach? + if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace attach error"); + cerr << "attach failed on pid " << d->my_handle << endl; + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("wait inside attach()"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; + + int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY); + if(proc_pid_mem == -1) + { + ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + cerr << d->memFile << endl; + cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl; + perror("open(memFile.c_str(),O_RDONLY)"); + return false; + } + else + { + d->attached = true; + g_pProcess = this; + + d->memFileHandle = proc_pid_mem; + return true; // we are attached + } +} + +bool WineProcess::detach() +{ + if(!d->attached) return false; + if(!d->suspended) suspend(); + int result = 0; + // close /proc/PID/mem + result = close(d->memFileHandle); + if(result == -1) + { + cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl; + perror("mem file close"); + return false; + } + else + { + // detach + result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + if(result == -1) + { + cerr << "couldn't detach from process pid" << d->my_handle << endl; + perror("ptrace detach"); + return false; + } + else + { + d->attached = false; + g_pProcess = NULL; + return true; + } + } +} + + +// danger: uses recursion! +void WineProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target) +{ + if(size == 0) return; + + ssize_t result; + result = pread(d->memFileHandle, target,size,offset); + if(result != size) + { + if(result == -1) + { + cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl; + cerr << "errno: " << errno << endl; + errno = 0; + } + else + { + read(offset + result, size - result, target + result); + } + } +} + +uint8_t WineProcess::readByte (const uint32_t offset) +{ + uint8_t val; + read(offset, 1, &val); + return val; +} + +void WineProcess::readByte (const uint32_t offset, uint8_t &val ) +{ + read(offset, 1, &val); +} + +uint16_t WineProcess::readWord (const uint32_t offset) +{ + uint16_t val; + read(offset, 2, (uint8_t *) &val); + return val; +} + +void WineProcess::readWord (const uint32_t offset, uint16_t &val) +{ + read(offset, 2, (uint8_t *) &val); +} + +uint32_t WineProcess::readDWord (const uint32_t offset) +{ + uint32_t val; + read(offset, 4, (uint8_t *) &val); + return val; +} +void WineProcess::readDWord (const uint32_t offset, uint32_t &val) +{ + read(offset, 4, (uint8_t *) &val); +} + +/* + * WRITING + */ + +void WineProcess::writeDWord (uint32_t offset, uint32_t data) +{ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, data); +} + +// using these is expensive. +void WineProcess::writeWord (uint32_t offset, uint16_t data) +{ + uint32_t orig = readDWord(offset); + orig &= 0xFFFF0000; + orig |= data; + /* + orig |= 0x0000FFFF; + orig &= data; + */ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig); +} + +void WineProcess::writeByte (uint32_t offset, uint8_t data) +{ + uint32_t orig = readDWord(offset); + orig &= 0xFFFFFF00; + orig |= data; + /* + orig |= 0x000000FF; + orig &= data; + */ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig); +} + +// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS +void WineProcess::write (uint32_t offset, uint32_t size, uint8_t *source) +{ + uint32_t indexptr = 0; + while (size > 0) + { + // default: we push 4 bytes + if(size >= 4) + { + writeDWord(offset, *(uint32_t *) (source + indexptr)); + offset +=4; + indexptr +=4; + size -=4; + } + // last is either three or 2 bytes + else if(size >= 2) + { + writeWord(offset, *(uint16_t *) (source + indexptr)); + offset +=2; + indexptr +=2; + size -=2; + } + // finishing move + else if(size == 1) + { + writeByte(offset, *(uint8_t *) (source + indexptr)); + return; + } + } +} + +const std::string WineProcess::readCString (uint32_t offset) +{ + std::string temp; + char temp_c[256]; + int counter = 0; + char r; + do + { + r = readByte(offset+counter); + temp_c[counter] = r; + counter++; + } while (r && counter < 255); + temp_c[counter] = 0; + temp = temp_c; + return temp; +} + +DfVector WineProcess::readVector (uint32_t offset, uint32_t item_size) +{ + /* + MSVC++ vector is four pointers long + ptr allocator + ptr start + ptr end + ptr alloc_end + + we don't care about alloc_end because we don't try to add stuff + we also don't care about the allocator thing in front + */ + uint32_t start = g_pProcess->readDWord(offset+4); + uint32_t end = g_pProcess->readDWord(offset+8); + uint32_t size = (end - start) /4; + return DfVector(start,size,item_size); +} + +size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +{ + /* + MSVC++ string + ptr allocator + union + { + char[16] start; + char * start_ptr + } + Uint32 length + Uint32 capacity + */ + uint32_t start_offset = offset + 4; + size_t length = g_pProcess->readDWord(offset + 20); + + size_t capacity = g_pProcess->readDWord(offset + 24); + size_t read_real = min(length, bufcapacity-1);// keep space for null termination + + // read data from inside the string structure + if(capacity < 16) + { + g_pProcess->read(start_offset, read_real , (uint8_t *)buffer); + } + else // read data from what the offset + 4 dword points to + { + start_offset = g_pProcess->readDWord(start_offset);// dereference the start offset + g_pProcess->read(start_offset, read_real, (uint8_t *)buffer); + } + + buffer[read_real] = 0; + return read_real; +} + +const string WineProcess::readSTLString (uint32_t offset) +{ + /* + MSVC++ string + ptr allocator + union + { + char[16] start; + char * start_ptr + } + Uint32 length + Uint32 capacity + */ + uint32_t start_offset = offset + 4; + uint32_t length = g_pProcess->readDWord(offset + 20); + uint32_t capacity = g_pProcess->readDWord(offset + 24); + char * temp = new char[capacity+1]; + + // read data from inside the string structure + if(capacity < 16) + { + g_pProcess->read(start_offset, capacity, (uint8_t *)temp); + } + else // read data from what the offset + 4 dword points to + { + start_offset = g_pProcess->readDWord(start_offset);// dereference the start offset + g_pProcess->read(start_offset, capacity, (uint8_t *)temp); + } + + temp[length] = 0; + string ret = temp; + delete temp; + return ret; +} + +string WineProcess::readClassName (uint32_t vptr) +{ + int rtti = readDWord(vptr - 0x4); + int typeinfo = readDWord(rtti + 0xC); + string raw = readCString(typeinfo + 0xC); // skips the .?AV + raw.resize(raw.length() - 4);// trim st@@ from end + return raw; +} \ No newline at end of file diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp new file mode 100644 index 000000000..3294cbaf2 --- /dev/null +++ b/library/DFProcess-linux.cpp @@ -0,0 +1,546 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "DFCommonInternal.h" +#include +#include +using namespace DFHack; + +class NormalProcess::Private +{ + public: + Private() + { + my_descriptor = NULL; + my_handle = NULL; + my_window = NULL; + my_pid = 0; + attached = false; + suspended = false; + memFileHandle = 0; + }; + ~Private(){}; + DFWindow* my_window; + memory_info * my_descriptor; + ProcessHandle my_handle; + uint32_t my_pid; + string memFile; + int memFileHandle; + bool attached; + bool suspended; + bool identified; + bool validate(char * exe_file, uint32_t pid, char * mem_file, vector & known_versions); +}; + +NormalProcess::NormalProcess(uint32_t pid, vector< memory_info* >& known_versions) +: d(new Private()) +{ + char dir_name [256]; + char exe_link_name [256]; + char mem_name [256]; + char cwd_name [256]; + char cmdline_name [256]; + char target_name[1024]; + int target_result; + + d->identified = false; + + sprintf(dir_name,"/proc/%d/", pid); + sprintf(exe_link_name,"/proc/%d/exe", pid); + sprintf(mem_name,"/proc/%d/mem", pid); + sprintf(cwd_name,"/proc/%d/cwd", pid); + sprintf(cmdline_name,"/proc/%d/cmdline", pid); + + // resolve /proc/PID/exe link + target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); + if (target_result == -1) + { + return; + } + // make sure we have a null terminated string... + target_name[target_result] = 0; + + // is this the regular linux DF? + if (strstr(target_name, "dwarfort.exe") != NULL) + { + // create linux process, add it to the vector + d->identified = d->validate(target_name,pid,mem_name,known_versions ); + d->my_window = new DFWindow(this); + return; + } +} + +bool NormalProcess::isSuspended() +{ + return d->suspended; +} +bool NormalProcess::isAttached() +{ + return d->attached; +} + +bool NormalProcess::isIdentified() +{ + return d->identified; +} + +bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) +{ + md5wrapper md5; + // get hash of the running DF process + string hash = md5.getHashFromFile(exe_file); + vector::iterator it; + + // iterate over the list of memory locations + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) + { + try + { + if(hash == (*it)->getString("md5")) // are the md5 hashes the same? + { + memory_info * m = *it; + if (memory_info::OS_LINUX == m->getOS()) + { + my_descriptor = m; + my_handle = my_pid = pid; + } + else + { + // some error happened, continue with next process + continue; + } + // tell NormalProcess about the /proc/PID/mem file + this->memFile = memFile; + identified = true; + return true; + } + } + catch (Error::MissingMemoryDefinition&) + { + continue; + } + } + return false; +} + +NormalProcess::~NormalProcess() +{ + if(d->attached) + { + detach(); + } + // destroy data model. this is assigned by processmanager + if(d->my_window) + delete d->my_window; + delete d; +} + +memory_info * NormalProcess::getDescriptor() +{ + return d->my_descriptor; +} + +DFWindow * NormalProcess::getWindow() +{ + return d->my_window; +} + +int NormalProcess::getPID() +{ + return d->my_pid; +} + +//FIXME: implement +bool NormalProcess::getThreadIDs(vector & threads ) +{ + return false; +} + +//FIXME: cross-reference with ELF segment entries? +void NormalProcess::getMemRanges( vector & ranges ) +{ + char buffer[1024]; + char permissions[5]; // r/-, w/-, x/-, p/s, 0 + + sprintf(buffer, "/proc/%lu/maps", d->my_pid); + FILE *mapFile = ::fopen(buffer, "r"); + uint64_t offset, device1, device2, node; + + while (fgets(buffer, 1024, mapFile)) + { + t_memrange temp; + temp.name[0] = 0; + sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s", + &temp.start, + &temp.end, + (char*)&permissions, + &offset, &device1, &device2, &node, + (char*)&temp.name); + temp.read = permissions[0] == 'r'; + temp.write = permissions[1] == 'w'; + temp.execute = permissions[2] == 'x'; + ranges.push_back(temp); + } +} + +bool NormalProcess::asyncSuspend() +{ + return suspend(); +} + +bool NormalProcess::suspend() +{ + int status; + if(!d->attached) + return false; + if(d->suspended) + return true; + if (kill(d->my_handle, SIGSTOP) == -1) + { + // no, we got an error + perror("kill SIGSTOP error"); + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("DF exited during suspend call"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; + return true; +} + +bool NormalProcess::forceresume() +{ + return resume(); +} + +bool NormalProcess::resume() +{ + if(!d->attached) + return false; + if(!d->suspended) + return true; + if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace resume error"); + return false; + } + d->suspended = false; + return true; +} + + +bool NormalProcess::attach() +{ + int status; + if(g_pProcess != NULL) + { + return false; + } + // can we attach? + if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace attach error"); + cerr << "attach failed on pid " << d->my_handle << endl; + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("wait inside attach()"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; + + int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY); + if(proc_pid_mem == -1) + { + ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl; + perror("open(memFile.c_str(),O_RDONLY)"); + return false; + } + else + { + d->attached = true; + g_pProcess = this; + + d->memFileHandle = proc_pid_mem; + return true; // we are attached + } +} + +bool NormalProcess::detach() +{ + if(!d->attached) return false; + if(!d->suspended) suspend(); + int result = 0; + // close /proc/PID/mem + result = close(d->memFileHandle); + if(result == -1) + { + cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl; + perror("mem file close"); + return false; + } + else + { + // detach + result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + if(result == -1) + { + cerr << "couldn't detach from process pid" << d->my_handle << endl; + perror("ptrace detach"); + return false; + } + else + { + d->attached = false; + g_pProcess = NULL; + return true; + } + } +} + + +// danger: uses recursion! +void NormalProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target) +{ + if(size == 0) return; + + ssize_t result; + result = pread(d->memFileHandle, target,size,offset); + if(result != size) + { + if(result == -1) + { + cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl; + cerr << "errno: " << errno << endl; + errno = 0; + } + else + { + read(offset + result, size - result, target + result); + } + } +} + +uint8_t NormalProcess::readByte (const uint32_t offset) +{ + uint8_t val; + read(offset, 1, &val); + return val; +} + +void NormalProcess::readByte (const uint32_t offset, uint8_t &val ) +{ + read(offset, 1, &val); +} + +uint16_t NormalProcess::readWord (const uint32_t offset) +{ + uint16_t val; + read(offset, 2, (uint8_t *) &val); + return val; +} + +void NormalProcess::readWord (const uint32_t offset, uint16_t &val) +{ + read(offset, 2, (uint8_t *) &val); +} + +uint32_t NormalProcess::readDWord (const uint32_t offset) +{ + uint32_t val; + read(offset, 4, (uint8_t *) &val); + return val; +} +void NormalProcess::readDWord (const uint32_t offset, uint32_t &val) +{ + read(offset, 4, (uint8_t *) &val); +} + +/* + * WRITING + */ + +void NormalProcess::writeDWord (uint32_t offset, uint32_t data) +{ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, data); +} + +// using these is expensive. +void NormalProcess::writeWord (uint32_t offset, uint16_t data) +{ + uint32_t orig = readDWord(offset); + orig &= 0xFFFF0000; + orig |= data; + /* + orig |= 0x0000FFFF; + orig &= data; + */ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig); +} + +void NormalProcess::writeByte (uint32_t offset, uint8_t data) +{ + uint32_t orig = readDWord(offset); + orig &= 0xFFFFFF00; + orig |= data; + /* + orig |= 0x000000FF; + orig &= data; + */ + ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig); +} + +// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS +void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source) +{ + uint32_t indexptr = 0; + while (size > 0) + { + // default: we push 4 bytes + if(size >= 4) + { + writeDWord(offset, *(uint32_t *) (source + indexptr)); + offset +=4; + indexptr +=4; + size -=4; + } + // last is either three or 2 bytes + else if(size >= 2) + { + writeWord(offset, *(uint16_t *) (source + indexptr)); + offset +=2; + indexptr +=2; + size -=2; + } + // finishing move + else if(size == 1) + { + writeByte(offset, *(uint8_t *) (source + indexptr)); + return; + } + } +} + +const std::string NormalProcess::readCString (uint32_t offset) +{ + std::string temp; + char temp_c[256]; + int counter = 0; + char r; + do + { + r = readByte(offset+counter); + temp_c[counter] = r; + counter++; + } while (r && counter < 255); + temp_c[counter] = 0; + temp = temp_c; + return temp; +} + +DfVector NormalProcess::readVector (uint32_t offset, uint32_t item_size) +{ + /* + GNU libstdc++ vector is three pointers long + ptr start + ptr end + ptr alloc_end + + we don't care about alloc_end because we don't try to add stuff + */ + uint32_t start = g_pProcess->readDWord(offset); + uint32_t end = g_pProcess->readDWord(offset+4); + uint32_t size = (end - start) /4; + return DfVector(start,size,item_size); +} + +struct _Rep_base +{ + uint32_t _M_length; + uint32_t _M_capacity; + uint32_t _M_refcount; +}; + +size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +{ + _Rep_base header; + offset = g_pProcess->readDWord(offset); + g_pProcess->read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header); + size_t read_real = min((size_t)header._M_length, bufcapacity-1);// keep space for null termination + g_pProcess->read(offset,read_real,(uint8_t * )buffer); + buffer[read_real] = 0; + return read_real; +} + +const string NormalProcess::readSTLString (uint32_t offset) +{ + _Rep_base header; + + offset = g_pProcess->readDWord(offset); + g_pProcess->read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header); + + // FIXME: use char* everywhere, avoid string + char * temp = new char[header._M_length+1]; + g_pProcess->read(offset,header._M_length+1,(uint8_t * )temp); + string ret(temp); + delete temp; + return ret; +} + +string NormalProcess::readClassName (uint32_t vptr) +{ + int typeinfo = readDWord(vptr - 0x4); + int typestring = readDWord(typeinfo + 0x4); + string raw = readCString(typestring); + size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers + size_t end = raw.length(); + return raw.substr(start,end-start - 2); // trim the 'st' from the end +} \ No newline at end of file diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 59fe25bd1..8886c0e7c 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -27,7 +27,7 @@ distribution. using namespace DFHack; // a full memory barrier! better be safe than sorry. -class Process::Private +class SHMProcess::Private { public: Private() @@ -72,7 +72,7 @@ class Process::Private #define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER)) // is the other side still there? -bool Process::Private::isValidSV() +bool SHMProcess::Private::isValidSV() { // try if CL mutex is free uint32_t result = WaitForSingleObject(DFSVMutex,0); @@ -99,12 +99,12 @@ bool Process::Private::isValidSV() } } -bool Process::waitWhile (uint32_t state) +bool SHMProcess::waitWhile (uint32_t state) { return d->waitWhile(state); } -bool Process::Private::waitWhile (uint32_t state) +bool SHMProcess::Private::waitWhile (uint32_t state) { uint32_t cnt = 0; while (SHMCMD == state) @@ -149,7 +149,7 @@ uint32_t OS_getAffinity() return dwProcessAffinityMask; } -bool Process::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) { SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); full_barrier @@ -166,7 +166,7 @@ bool Process::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) return true; } -Process::Process(uint32_t PID, vector & known_versions) +SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) : d(new Private()) { char svmutexname [256]; @@ -237,7 +237,7 @@ Process::Process(uint32_t PID, vector & known_versions) if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) { CloseHandle(hProcess); - cout << "EnumProcessModules fail'd" << endl; + // cout << "EnumProcessModules fail'd" << endl; break; } // got base ;) @@ -295,21 +295,21 @@ Process::Process(uint32_t PID, vector & known_versions) detach(); } -bool Process::isSuspended() +bool SHMProcess::isSuspended() { return d->suspended; } -bool Process::isAttached() +bool SHMProcess::isAttached() { return d->attached; } -bool Process::isIdentified() +bool SHMProcess::isIdentified() { return d->identified; } -Process::~Process() +SHMProcess::~SHMProcess() { if(d->attached) { @@ -336,29 +336,29 @@ Process::~Process() delete d; } -memory_info * Process::getDescriptor() +memory_info * SHMProcess::getDescriptor() { return d->my_descriptor; } -DFWindow * Process::getWindow() +DFWindow * SHMProcess::getWindow() { return d->my_window; } -int Process::getPID() +int SHMProcess::getPID() { return d->my_pid; } //FIXME: implement -bool Process::getThreadIDs(vector & threads ) +bool SHMProcess::getThreadIDs(vector & threads ) { return false; } //FIXME: cross-reference with ELF segment entries? -void Process::getMemRanges( vector & ranges ) +void SHMProcess::getMemRanges( vector & ranges ) { char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 @@ -384,7 +384,7 @@ void Process::getMemRanges( vector & ranges ) } } -bool Process::suspend() +bool SHMProcess::suspend() { if(!d->attached) { @@ -406,7 +406,7 @@ bool Process::suspend() return true; } -bool Process::asyncSuspend() +bool SHMProcess::asyncSuspend() { if(!d->attached) { @@ -428,12 +428,12 @@ bool Process::asyncSuspend() } } -bool Process::forceresume() +bool SHMProcess::forceresume() { return resume(); } -bool Process::resume() +bool SHMProcess::resume() { if(!d->attached) { @@ -451,7 +451,7 @@ bool Process::resume() } -bool Process::attach() +bool SHMProcess::attach() { if(g_pProcess != 0) { @@ -502,7 +502,7 @@ bool Process::attach() return true; } -bool Process::detach() +bool SHMProcess::detach() { if(!d->attached) { @@ -518,7 +518,7 @@ bool Process::detach() return true; } -void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) +void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { // normal read under 1MB if(size <= SHM_BODY) @@ -555,7 +555,7 @@ void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) } } -uint8_t Process::readByte (const uint32_t offset) +uint8_t SHMProcess::readByte (const uint32_t offset) { D_SHMHDR->address = offset; full_barrier @@ -564,7 +564,7 @@ uint8_t Process::readByte (const uint32_t offset) return D_SHMHDR->value; } -void Process::readByte (const uint32_t offset, uint8_t &val ) +void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { D_SHMHDR->address = offset; full_barrier @@ -573,7 +573,7 @@ void Process::readByte (const uint32_t offset, uint8_t &val ) val = D_SHMHDR->value; } -uint16_t Process::readWord (const uint32_t offset) +uint16_t SHMProcess::readWord (const uint32_t offset) { D_SHMHDR->address = offset; full_barrier @@ -582,7 +582,7 @@ uint16_t Process::readWord (const uint32_t offset) return D_SHMHDR->value; } -void Process::readWord (const uint32_t offset, uint16_t &val) +void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { D_SHMHDR->address = offset; full_barrier @@ -591,7 +591,7 @@ void Process::readWord (const uint32_t offset, uint16_t &val) val = D_SHMHDR->value; } -uint32_t Process::readDWord (const uint32_t offset) +uint32_t SHMProcess::readDWord (const uint32_t offset) { D_SHMHDR->address = offset; full_barrier @@ -599,7 +599,7 @@ uint32_t Process::readDWord (const uint32_t offset) d->waitWhile(CORE_READ_DWORD); return D_SHMHDR->value; } -void Process::readDWord (const uint32_t offset, uint32_t &val) +void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { D_SHMHDR->address = offset; full_barrier @@ -612,7 +612,7 @@ void Process::readDWord (const uint32_t offset, uint32_t &val) * WRITING */ -void Process::writeDWord (uint32_t offset, uint32_t data) +void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -622,7 +622,7 @@ void Process::writeDWord (uint32_t offset, uint32_t data) } // using these is expensive. -void Process::writeWord (uint32_t offset, uint16_t data) +void SHMProcess::writeWord (uint32_t offset, uint16_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -631,7 +631,7 @@ void Process::writeWord (uint32_t offset, uint16_t data) d->waitWhile(CORE_WRITE_WORD); } -void Process::writeByte (uint32_t offset, uint8_t data) +void SHMProcess::writeByte (uint32_t offset, uint8_t data) { D_SHMHDR->address = offset; D_SHMHDR->value = data; @@ -640,7 +640,7 @@ void Process::writeByte (uint32_t offset, uint8_t data) d->waitWhile(CORE_WRITE_BYTE); } -void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) +void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { // normal write under 1MB if(size <= SHM_BODY) @@ -678,7 +678,7 @@ void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer } // FIXME: butt-fugly -const std::string Process::readCString (uint32_t offset) +const std::string SHMProcess::readCString (uint32_t offset) { std::string temp; char temp_c[256]; @@ -695,7 +695,7 @@ const std::string Process::readCString (uint32_t offset) return temp; } -DfVector Process::readVector (uint32_t offset, uint32_t item_size) +DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) { /* MSVC++ vector is four pointers long @@ -713,7 +713,7 @@ DfVector Process::readVector (uint32_t offset, uint32_t item_size) return DfVector(start,size,item_size); } -const std::string Process::readSTLString(uint32_t offset) +const std::string SHMProcess::readSTLString(uint32_t offset) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 D_SHMHDR->address = offset; @@ -726,7 +726,7 @@ const std::string Process::readSTLString(uint32_t offset) return(string(d->my_shm+SHM_HEADER)); } -size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 D_SHMHDR->address = offset; @@ -740,7 +740,7 @@ size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacit return real; } -void Process::writeSTLString(const uint32_t address, const std::string writeString) +void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { D_SHMHDR->address = address/*-4*/; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator @@ -749,7 +749,7 @@ void Process::writeSTLString(const uint32_t address, const std::string writeStri d->waitWhile(CORE_WRITE_STL_STRING); } -string Process::readClassName (uint32_t vptr) +string SHMProcess::readClassName (uint32_t vptr) { int rtti = readDWord(vptr - 0x4); int typeinfo = readDWord(rtti + 0xC); @@ -759,7 +759,7 @@ string Process::readClassName (uint32_t vptr) } // get module index by name and version. bool 1 = error -bool Process::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); payload->version = version; @@ -772,7 +772,7 @@ bool Process::getModuleIndex (const char * name, const uint32_t version, uint32_ return true; } -char * Process::getSHMStart (void) +char * SHMProcess::getSHMStart (void) { return d->my_shm; } \ No newline at end of file diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp new file mode 100644 index 000000000..72ab8e06a --- /dev/null +++ b/library/DFProcess-windows.cpp @@ -0,0 +1,482 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "DFCommonInternal.h" +using namespace DFHack; + +class NormalProcess::Private +{ + public: + Private() + { + my_descriptor = NULL; + my_handle = NULL; + my_main_thread = NULL; + my_window = NULL; + my_pid = 0; + attached = false; + suspended = false; + }; + ~Private(){}; + memory_info * my_descriptor; + DFWindow * my_window; + ProcessHandle my_handle; + HANDLE my_main_thread; + uint32_t my_pid; + string memFile; + bool attached; + bool suspended; + bool identified; +}; + +NormalProcess::NormalProcess(uint32_t pid, vector & known_versions) +: d(new Private()) +{ + HMODULE hmod = NULL; + DWORD junk; + HANDLE hProcess; + bool found = false; + + IMAGE_NT_HEADERS32 pe_header; + IMAGE_SECTION_HEADER sections[16]; + d->identified = false; + // open process + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); + if (NULL == hProcess) + return; + + // try getting the first module of the process + if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) + { + CloseHandle(hProcess); + // cout << "EnumProcessModules fail'd" << endl; + return; //if enumprocessModules fails, give up + } + + // got base ;) + uint32_t base = (uint32_t)hmod; + + // temporarily assign this to allow some checks + d->my_handle = hProcess; + // read from this process + uint32_t pe_offset = readDWord(base+0x3C); + read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); + read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); + d->my_handle = 0; + + // see if there's a version entry that matches this process + vector::iterator it; + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) + { + // filter by OS + if(memory_info::OS_WINDOWS != (*it)->getOS()) + continue; + uint32_t pe_timestamp; + // filter by timestamp, skip entries without a timestamp + try + { + pe_timestamp = (*it)->getHexValue("pe_timestamp"); + } + catch(Error::MissingMemoryDefinition& e) + { + continue; + } + if (pe_timestamp != pe_header.FileHeader.TimeDateStamp) + continue; + + // all went well + { + printf("Match found! Using version %s.\n", (*it)->getVersion().c_str()); + d->identified = true; + // give the process a data model and memory layout fixed for the base of first module + memory_info *m = new memory_info(**it); + m->RebaseAll(base); + // keep track of created memory_info object so we can destroy it later + d->my_descriptor = m; + // process is responsible for destroying its data model + d->my_pid = pid; + d->my_handle = hProcess; + d->identified = true; + + // TODO: detect errors in thread enumeration + vector threads; + getThreadIDs( threads ); + d->my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]); + + found = true; + break; // break the iterator loop + } + } + // close handle of processes that aren't DF + if(!found) + { + CloseHandle(hProcess); + } + else + { + d->my_window = new DFWindow(this); + } +} +/* +*/ + +NormalProcess::~NormalProcess() +{ + if(d->attached) + { + detach(); + } + // destroy our rebased copy of the memory descriptor + delete d->my_descriptor; + if(d->my_handle != NULL) + { + CloseHandle(d->my_handle); + } + if(d->my_main_thread != NULL) + { + CloseHandle(d->my_main_thread); + } + if(d->my_window) + { + delete d->my_window; + } + delete d; +} + +memory_info * NormalProcess::getDescriptor() +{ + return d->my_descriptor; +} + +DFWindow * NormalProcess::getWindow() +{ + return d->my_window; +} + +int NormalProcess::getPID() +{ + return d->my_pid; +} + +bool NormalProcess::isSuspended() +{ + return d->suspended; +} +bool NormalProcess::isAttached() +{ + return d->attached; +} + +bool NormalProcess::isIdentified() +{ + return d->identified; +} + +bool NormalProcess::asyncSuspend() +{ + return suspend(); +} + +bool NormalProcess::suspend() +{ + if(!d->attached) + return false; + if(d->suspended) + { + return true; + } + SuspendThread(d->my_main_thread); + d->suspended = true; + return true; +} + +bool NormalProcess::forceresume() +{ + if(!d->attached) + return false; + while (ResumeThread(d->my_main_thread) > 1); + d->suspended = false; + return true; +} + + +bool NormalProcess::resume() +{ + if(!d->attached) + return false; + if(!d->suspended) + { + return true; + } + ResumeThread(d->my_main_thread); + d->suspended = false; + return true; +} + +bool NormalProcess::attach() +{ + if(g_pProcess != NULL) + { + return false; + } + d->attached = true; + g_pProcess = this; + suspend(); + + return true; +} + + +bool NormalProcess::detach() +{ + if(!d->attached) + { + return false; + } + resume(); + d->attached = false; + g_pProcess = NULL; + return true; +} + +bool NormalProcess::getThreadIDs(vector & threads ) +{ + HANDLE AllThreads = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( AllThreads == INVALID_HANDLE_VALUE ) + { + return false; + } + te32.dwSize = sizeof(THREADENTRY32 ); + + if( !Thread32First( AllThreads, &te32 ) ) + { + CloseHandle( AllThreads ); + return false; + } + + do + { + if( te32.th32OwnerProcessID == d->my_pid ) + { + threads.push_back(te32.th32ThreadID); + } + } while( Thread32Next(AllThreads, &te32 ) ); + + CloseHandle( AllThreads ); + return true; +} + +//FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries +void NormalProcess::getMemRanges( vector & ranges ) +{ + // code here is taken from hexsearch by Silas Dunmore. + // As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here + + // I'm faking this, because there's no way I'm using VirtualQuery + + t_memrange temp; + uint32_t base = d->my_descriptor->getBase(); + temp.start = base + 0x1000; // more fakery. + temp.end = base + readDWord(base+readDWord(base+0x3C)+0x50)-1; // yay for magic. + temp.read = 1; + temp.write = 1; + temp.execute = 0; // fake + strcpy(temp.name,"pants");// that's right. I'm calling it pants. Windows can go to HELL + ranges.push_back(temp); +} + +uint8_t NormalProcess::readByte (const uint32_t offset) +{ + uint8_t result; + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint8_t), NULL); + return result; +} + +void NormalProcess::readByte (const uint32_t offset,uint8_t &result) +{ + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint8_t), NULL); +} + +uint16_t NormalProcess::readWord (const uint32_t offset) +{ + uint16_t result; + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint16_t), NULL); + return result; +} + +void NormalProcess::readWord (const uint32_t offset, uint16_t &result) +{ + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint16_t), NULL); +} + +uint32_t NormalProcess::readDWord (const uint32_t offset) +{ + uint32_t result; + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint32_t), NULL); + return result; +} + +void NormalProcess::readDWord (const uint32_t offset, uint32_t &result) +{ + ReadProcessMemory(d->my_handle, (int*) offset, &result, sizeof(uint32_t), NULL); +} + +void NormalProcess::read (const uint32_t offset, uint32_t size, uint8_t *target) +{ + ReadProcessMemory(d->my_handle, (int*) offset, target, size, NULL); +} + +// WRITING +void NormalProcess::writeDWord (const uint32_t offset, uint32_t data) +{ + WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(uint32_t), NULL); +} + +// using these is expensive. +void NormalProcess::writeWord (uint32_t offset, uint16_t data) +{ + WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(uint16_t), NULL); +} + +void NormalProcess::writeByte (uint32_t offset, uint8_t data) +{ + WriteProcessMemory(d->my_handle, (int*) offset, &data, sizeof(uint8_t), NULL); +} + +void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source) +{ + WriteProcessMemory(d->my_handle, (int*) offset, source, size, NULL); +} + + + +///FIXME: reduce use of temporary objects +const string NormalProcess::readCString (const uint32_t offset) +{ + string temp; + char temp_c[256]; + DWORD read; + ReadProcessMemory(d->my_handle, (int *) offset, temp_c, 255, &read); + temp_c[read+1] = 0; + temp = temp_c; + return temp; +} + +DfVector NormalProcess::readVector (uint32_t offset, uint32_t item_size) +{ + /* + MSVC++ vector is four pointers long + ptr allocator + ptr start + ptr end + ptr alloc_end + + we don't care about alloc_end because we don't try to add stuff + we also don't care about the allocator thing in front + */ + uint32_t start = g_pProcess->readDWord(offset+4); + uint32_t end = g_pProcess->readDWord(offset+8); + uint32_t size = (end - start) /4; + return DfVector(start,size,item_size); +} + +size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +{ + /* + MSVC++ string + ptr allocator + union + { + char[16] start; + char * start_ptr +} +Uint32 length +Uint32 capacity +*/ + uint32_t start_offset = offset + 4; + size_t length = g_pProcess->readDWord(offset + 20); + + size_t capacity = g_pProcess->readDWord(offset + 24); + size_t read_real = min(length, bufcapacity-1);// keep space for null termination + + // read data from inside the string structure + if(capacity < 16) + { + g_pProcess->read(start_offset, read_real , (uint8_t *)buffer); + } + else // read data from what the offset + 4 dword points to + { + start_offset = g_pProcess->readDWord(start_offset);// dereference the start offset + g_pProcess->read(start_offset, read_real, (uint8_t *)buffer); + } + + buffer[read_real] = 0; + return read_real; +} + +const string NormalProcess::readSTLString (uint32_t offset) +{ + /* + MSVC++ string + ptr allocator + union + { + char[16] start; + char * start_ptr + } + Uint32 length + Uint32 capacity + */ + uint32_t start_offset = offset + 4; + uint32_t length = g_pProcess->readDWord(offset + 20); + uint32_t capacity = g_pProcess->readDWord(offset + 24); + char * temp = new char[capacity+1]; + + // read data from inside the string structure + if(capacity < 16) + { + g_pProcess->read(start_offset, capacity, (uint8_t *)temp); + } + else // read data from what the offset + 4 dword points to + { + start_offset = g_pProcess->readDWord(start_offset);// dereference the start offset + g_pProcess->read(start_offset, capacity, (uint8_t *)temp); + } + + temp[length] = 0; + string ret = temp; + delete temp; + return ret; +} + +string NormalProcess::readClassName (uint32_t vptr) +{ + int rtti = readDWord(vptr - 0x4); + int typeinfo = readDWord(rtti + 0xC); + string raw = readCString(typeinfo + 0xC); // skips the .?AV + raw.resize(raw.length() - 4);// trim st@@ from end + return raw; +} \ No newline at end of file diff --git a/library/DFProcess.h b/library/DFProcess.h index 91d1d773e..878e68d37 100644 --- a/library/DFProcess.h +++ b/library/DFProcess.h @@ -61,28 +61,147 @@ namespace DFHack class DFHACK_EXPORT Process { - private: - class Private; - Private *d; public: // this is the single most important destructor ever. ~px - Process(uint32_t PID ,vector & known_versions); - ~Process(); - - // Set up stuff so we can read memory - bool attach(); + virtual ~Process(){}; + // Set up stuff so we can read memory, suspends synchronously + virtual bool attach() = 0; // detach from DF, resume its execution if it's suspended - bool detach(); + virtual bool detach() = 0; // synchronous suspend // waits for DF to be actually suspended, // this might take a while depending on implementation - bool suspend(); + virtual bool suspend() = 0; // asynchronous suspend to use together with polling and timers - bool asyncSuspend(); + virtual bool asyncSuspend() = 0; // resume DF execution + virtual bool resume() = 0; + // force-resume DF execution + virtual bool forceresume() = 0; + + virtual uint32_t readDWord(const uint32_t address) = 0; + virtual void readDWord(const uint32_t address, uint32_t & value) = 0; + virtual uint16_t readWord(const uint32_t address) = 0; + virtual void readWord(const uint32_t address, uint16_t & value) = 0; + virtual uint8_t readByte(const uint32_t address) = 0; + virtual void readByte(const uint32_t address, uint8_t & value) = 0; + virtual void read( uint32_t address, uint32_t length, uint8_t* buffer) = 0; + + virtual void writeDWord(const uint32_t address, const uint32_t value) = 0; + virtual void writeWord(const uint32_t address, const uint16_t value) = 0; + virtual void writeByte(const uint32_t address, const uint8_t value) = 0; + virtual void write(uint32_t address, uint32_t length, uint8_t* buffer) = 0; + + // read a string + virtual const string readSTLString (uint32_t offset) = 0; + virtual size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) = 0; + virtual void writeSTLString(const uint32_t address, const std::string writeString) = 0; + // read a vector from memory + virtual DfVector readVector (uint32_t offset, uint32_t item_size) = 0; + // get class name of an object with rtti/type info + virtual string readClassName(uint32_t vptr) = 0; + + virtual const std::string readCString (uint32_t offset) = 0; + + virtual bool isSuspended() = 0; + virtual bool isAttached() = 0; + virtual bool isIdentified() = 0; + + // find the thread IDs of the process + virtual bool getThreadIDs(vector & threads ) = 0; + // get virtual memory ranges of the process (what is mapped where) + virtual void getMemRanges( vector & ranges ) = 0; + + // get the flattened Memory.xml entry of this process + virtual memory_info *getDescriptor() = 0; + // get the DF's window (first that can be found ~_~) + virtual DFWindow * getWindow() = 0; + // get the DF Process ID + virtual int getPID() = 0; + // get module index by name and version. bool 1 = error + virtual bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) = 0; + // get the SHM start if available + virtual char * getSHMStart (void) = 0; + // wait for a SHM state. returns 0 without the SHM + virtual bool waitWhile (uint32_t state) = 0; + }; + + class DFHACK_EXPORT NormalProcess : virtual public Process + { + friend class ProcessEnumerator; + class Private; + private: + Private * const d; + + public: + NormalProcess(uint32_t pid, vector & known_versions); + ~NormalProcess(); + bool attach(); + bool detach(); + + bool suspend(); + bool asyncSuspend(); + bool resume(); + bool forceresume(); + + uint32_t readDWord(const uint32_t address); + void readDWord(const uint32_t address, uint32_t & value); + uint16_t readWord(const uint32_t address); + void readWord(const uint32_t address, uint16_t & value); + uint8_t readByte(const uint32_t address); + void readByte(const uint32_t address, uint8_t & value); + void read( uint32_t address, uint32_t length, uint8_t* buffer); + + void writeDWord(const uint32_t address, const uint32_t value); + void writeWord(const uint32_t address, const uint16_t value); + void writeByte(const uint32_t address, const uint8_t value); + void write(uint32_t address, uint32_t length, uint8_t* buffer); + + const string readSTLString (uint32_t offset); + size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); + void writeSTLString(const uint32_t address, const std::string writeString){}; + // read a vector from memory + DfVector readVector (uint32_t offset, uint32_t item_size); + // get class name of an object with rtti/type info + string readClassName(uint32_t vptr); + + const std::string readCString (uint32_t offset); + + bool isSuspended(); + bool isAttached(); + bool isIdentified(); + + bool getThreadIDs(vector & threads ); + void getMemRanges( vector & ranges ); + memory_info *getDescriptor(); + DFWindow * getWindow(); + int getPID(); + // get module index by name and version. bool 1 = error + bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; + // get the SHM start if available + char * getSHMStart (void){return 0;}; + // wait for a SHM state. returns 0 without the SHM + bool waitWhile (uint32_t state){return false;}; + }; + + class DFHACK_EXPORT SHMProcess : virtual public Process + { + friend class ProcessEnumerator; + class Private; + private: + Private * const d; + + public: + SHMProcess(uint32_t PID, vector & known_versions); + ~SHMProcess(); + // Set up stuff so we can read memory + bool attach(); + bool detach(); + + bool suspend(); + bool asyncSuspend(); bool resume(); - // force-resume DF execution - maybe nonsense in this branch? :P bool forceresume(); uint32_t readDWord(const uint32_t address); @@ -105,21 +224,17 @@ namespace DFHack DfVector readVector (uint32_t offset, uint32_t item_size); // get class name of an object with rtti/type info string readClassName(uint32_t vptr); + const std::string readCString (uint32_t offset); bool isSuspended(); bool isAttached(); bool isIdentified(); - // find the thread IDs of the process bool getThreadIDs(vector & threads ); - // get virtual memory ranges of the process (what is mapped where) void getMemRanges( vector & ranges ); - // get the flattened Memory.xml entry of this process memory_info *getDescriptor(); - // get the DF's window (first that can be found ~_~) DFWindow * getWindow(); - // get the DF Process ID int getPID(); // get module index by name and version. bool 1 = error bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT); @@ -128,5 +243,65 @@ namespace DFHack // wait for a SHM state. returns 0 without the SHM bool waitWhile (uint32_t state); }; + +#ifdef LINUX_BUILD + class DFHACK_EXPORT WineProcess : virtual public Process + { + friend class ProcessEnumerator; + class Private; + private: + Private * const d; + + public: + WineProcess(uint32_t pid, vector & known_versions); + ~WineProcess(); + bool attach(); + bool detach(); + + bool suspend(); + bool asyncSuspend(); + bool resume(); + bool forceresume(); + + uint32_t readDWord(const uint32_t address); + void readDWord(const uint32_t address, uint32_t & value); + uint16_t readWord(const uint32_t address); + void readWord(const uint32_t address, uint16_t & value); + uint8_t readByte(const uint32_t address); + void readByte(const uint32_t address, uint8_t & value); + void read( uint32_t address, uint32_t length, uint8_t* buffer); + + void writeDWord(const uint32_t address, const uint32_t value); + void writeWord(const uint32_t address, const uint16_t value); + void writeByte(const uint32_t address, const uint8_t value); + void write(uint32_t address, uint32_t length, uint8_t* buffer); + + const string readSTLString (uint32_t offset); + size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); + void writeSTLString(const uint32_t address, const std::string writeString){}; + // read a vector from memory + DfVector readVector (uint32_t offset, uint32_t item_size); + // get class name of an object with rtti/type info + string readClassName(uint32_t vptr); + + const std::string readCString (uint32_t offset); + + bool isSuspended(); + bool isAttached(); + bool isIdentified(); + + bool getThreadIDs(vector & threads ); + void getMemRanges( vector & ranges ); + memory_info *getDescriptor(); + DFWindow * getWindow(); + int getPID(); + // get module index by name and version. bool 1 = error + bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; + // get the SHM start if available + char * getSHMStart (void){return 0;}; + // wait for a SHM state. returns 0 without the SHM + bool waitWhile (uint32_t state){return false;}; + }; +#endif } #endif diff --git a/library/DFProcessEnumerator-linux.cpp b/library/DFProcessEnumerator-linux.cpp index b67e83b3a..c4eddd127 100644 --- a/library/DFProcessEnumerator-linux.cpp +++ b/library/DFProcessEnumerator-linux.cpp @@ -46,19 +46,6 @@ bool ProcessEnumerator::findProcessess() { DIR *dir_p; struct dirent *dir_entry_p; - /* - Process *p = 0; - p = new Process(d->meminfo->meminfo); - if(p->isIdentified()) - { - d->processes.push_back(p); - } - else - { - delete p; - p = 0; - } - */ // Open /proc/ directory dir_p = opendir("/proc/"); // Reading /proc/ entries @@ -69,7 +56,17 @@ bool ProcessEnumerator::findProcessess() { continue; } - Process *p2 = new Process(atoi(dir_entry_p->d_name),d->meminfo->meminfo); + Process *p1 = new SHMProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); + if(p1->isIdentified()) + { + d->processes.push_back(p1); + continue; + } + else + { + delete p1; + } + Process *p2 = new NormalProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); if(p2->isIdentified()) { d->processes.push_back(p2); @@ -79,7 +76,6 @@ bool ProcessEnumerator::findProcessess() { delete p2; } - /* Process *p3 = new WineProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); if(p3->isIdentified()) { @@ -90,10 +86,9 @@ bool ProcessEnumerator::findProcessess() { delete p3; } - */ + } closedir(dir_p); - // return value depends on if we found some DF processes if(d->processes.size()) { diff --git a/library/DFProcessEnumerator-windows.cpp b/library/DFProcessEnumerator-windows.cpp index 1c906cb9d..a922b5b9a 100644 --- a/library/DFProcessEnumerator-windows.cpp +++ b/library/DFProcessEnumerator-windows.cpp @@ -36,6 +36,32 @@ class DFHack::ProcessEnumerator::Private std::vector processes; }; +// some magic - will come in handy when we start doing debugger stuff on Windows +bool EnableDebugPriv() +{ + bool bRET = FALSE; + TOKEN_PRIVILEGES tp; + HANDLE hToken; + + if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) + { + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + if (hToken != INVALID_HANDLE_VALUE) + { + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tp.PrivilegeCount = 1; + if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, 0, 0)) + { + bRET = TRUE; + } + CloseHandle(hToken); + } + } + } + return bRET; +} + // WINDOWS version of the process finder bool ProcessEnumerator::findProcessess() { @@ -50,11 +76,12 @@ bool ProcessEnumerator::findProcessess() // Calculate how many process identifiers were returned. numProccesses = memoryNeeded / sizeof(DWORD); - + EnableDebugPriv(); + // iterate through processes for ( int i = 0; i < (int)numProccesses; i++ ) { - Process *p = new Process(ProcArray[i],d->meminfo->meminfo); + Process *p = new SHMProcess(ProcArray[i],d->meminfo->meminfo); if(p->isIdentified()) { d->processes.push_back(p); @@ -64,14 +91,10 @@ bool ProcessEnumerator::findProcessess() delete p; p = 0; } - } - /* - { - Process * p = new Process(d->meminfo->meminfo); + p = new NormalProcess(ProcArray[i],d->meminfo->meminfo); if(p->isIdentified()) { d->processes.push_back(p); - return true; } else { @@ -79,7 +102,6 @@ bool ProcessEnumerator::findProcessess() p = 0; } } - */ if(d->processes.size()) return true; return false;