diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 321ef5b3b..9df32d6d6 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 9bcda4ff2..1c1254989 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -37,7 +37,7 @@ using namespace DFHack; // a full memory barrier! better be safe than sorry. #define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); -class SHMProcess::Private +class Process::Private { public: Private() @@ -68,7 +68,7 @@ class SHMProcess::Private bool DF_GetPID(pid_t & ret); }; -bool SHMProcess::Private::waitWhile (DF_PINGPONG state) +bool Process::Private::waitWhile (DF_PINGPONG state) { uint32_t cnt = 0; struct shmid_ds descriptor; @@ -104,7 +104,7 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) return true; } -bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) +bool Process::Private::DF_TestBridgeVersion(bool & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_VERSION; gcc_barrier @@ -116,7 +116,7 @@ bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) return true; } -bool SHMProcess::Private::DF_GetPID(pid_t & ret) +bool Process::Private::DF_GetPID(pid_t & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_PID; gcc_barrier @@ -128,7 +128,7 @@ bool SHMProcess::Private::DF_GetPID(pid_t & ret) return true; } -SHMProcess::SHMProcess(vector & known_versions) +Process::Process(vector & known_versions) : d(new Private()) { char exe_link_name [256]; @@ -204,21 +204,21 @@ SHMProcess::SHMProcess(vector & known_versions) shmdt(d->my_shm); // detach so we don't attach twice when attach() is called } -bool SHMProcess::isSuspended() +bool Process::isSuspended() { return d->suspended; } -bool SHMProcess::isAttached() +bool Process::isAttached() { return d->attached; } -bool SHMProcess::isIdentified() +bool Process::isIdentified() { return d->identified; } -bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector & known_versions) +bool Process::Private::validate(char * exe_file, uint32_t pid, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -247,7 +247,7 @@ bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector attached) { @@ -261,29 +261,29 @@ SHMProcess::~SHMProcess() delete d; } -memory_info * SHMProcess::getDescriptor() +memory_info * Process::getDescriptor() { return d->my_descriptor; } -DFWindow * SHMProcess::getWindow() +DFWindow * Process::getWindow() { return d->my_window; } -int SHMProcess::getPID() +int Process::getPID() { return d->my_pid; } //FIXME: implement -bool SHMProcess::getThreadIDs(vector & threads ) +bool Process::getThreadIDs(vector & threads ) { return false; } //FIXME: cross-reference with ELF segment entries? -void SHMProcess::getMemRanges( vector & ranges ) +void Process::getMemRanges( vector & ranges ) { char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 @@ -309,7 +309,7 @@ void SHMProcess::getMemRanges( vector & ranges ) } } -bool SHMProcess::suspend() +bool Process::suspend() { if(!d->attached) { @@ -328,7 +328,7 @@ bool SHMProcess::suspend() return true; } -bool SHMProcess::asyncSuspend() +bool Process::asyncSuspend() { if(!d->attached) { @@ -350,12 +350,12 @@ bool SHMProcess::asyncSuspend() } } -bool SHMProcess::forceresume() +bool Process::forceresume() { return resume(); } -bool SHMProcess::resume() +bool Process::resume() { if(!d->attached) return false; @@ -367,7 +367,7 @@ bool SHMProcess::resume() } -bool SHMProcess::attach() +bool Process::attach() { int status; if(g_pProcess != 0) @@ -396,7 +396,7 @@ bool SHMProcess::attach() return false; } -bool SHMProcess::detach() +bool Process::detach() { if(!d->attached) { @@ -420,7 +420,7 @@ bool SHMProcess::detach() return false; } -void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) +void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { // normal read under 1MB if(size <= SHM_BODY) @@ -457,7 +457,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff } } -uint8_t SHMProcess::readByte (const uint32_t offset) +uint8_t Process::readByte (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -466,7 +466,7 @@ uint8_t SHMProcess::readByte (const uint32_t offset) return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) +void Process::readByte (const uint32_t offset, uint8_t &val ) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -475,7 +475,7 @@ void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) val = ((shm_retval *)d->my_shm)->value; } -uint16_t SHMProcess::readWord (const uint32_t offset) +uint16_t Process::readWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -484,7 +484,7 @@ uint16_t SHMProcess::readWord (const uint32_t offset) return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readWord (const uint32_t offset, uint16_t &val) +void Process::readWord (const uint32_t offset, uint16_t &val) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -493,7 +493,7 @@ void SHMProcess::readWord (const uint32_t offset, uint16_t &val) val = ((shm_retval *)d->my_shm)->value; } -uint32_t SHMProcess::readDWord (const uint32_t offset) +uint32_t Process::readDWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -501,7 +501,7 @@ uint32_t SHMProcess::readDWord (const uint32_t offset) d->waitWhile(DFPP_READ_DWORD); return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) +void Process::readDWord (const uint32_t offset, uint32_t &val) { ((shm_read_small *)d->my_shm)->address = offset; gcc_barrier @@ -514,7 +514,7 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) * WRITING */ -void SHMProcess::writeDWord (uint32_t offset, uint32_t data) +void Process::writeDWord (uint32_t offset, uint32_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -524,7 +524,7 @@ void SHMProcess::writeDWord (uint32_t offset, uint32_t data) } // using these is expensive. -void SHMProcess::writeWord (uint32_t offset, uint16_t data) +void Process::writeWord (uint32_t offset, uint16_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -533,7 +533,7 @@ void SHMProcess::writeWord (uint32_t offset, uint16_t data) d->waitWhile(DFPP_WRITE_WORD); } -void SHMProcess::writeByte (uint32_t offset, uint8_t data) +void Process::writeByte (uint32_t offset, uint8_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -542,7 +542,7 @@ void SHMProcess::writeByte (uint32_t offset, uint8_t data) d->waitWhile(DFPP_WRITE_BYTE); } -void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) +void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { // normal write under 1MB if(size <= SHM_BODY) @@ -580,7 +580,7 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf } // FIXME: butt-fugly -const std::string SHMProcess::readCString (uint32_t offset) +const std::string Process::readCString (uint32_t offset) { std::string temp; char temp_c[256]; @@ -597,7 +597,7 @@ const std::string SHMProcess::readCString (uint32_t offset) return temp; } -DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) +DfVector Process::readVector (uint32_t offset, uint32_t item_size) { /* GNU libstdc++ vector is three pointers long @@ -613,7 +613,7 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) return DfVector(start,size,item_size); } -const std::string SHMProcess::readSTLString(uint32_t offset) +const std::string Process::readSTLString(uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -623,7 +623,7 @@ const std::string SHMProcess::readSTLString(uint32_t offset) return(string( (char *)d->my_shm+SHM_HEADER)); } -size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -636,7 +636,7 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa return fit; } -void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) +void Process::writeSTLString(const uint32_t address, const std::string writeString) { ((shm_write_small *)d->my_shm)->address = address; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator @@ -645,7 +645,7 @@ void SHMProcess::writeSTLString(const uint32_t address, const std::string writeS d->waitWhile(DFPP_WRITE_STL_STRING); } -string SHMProcess::readClassName (uint32_t vptr) +string Process::readClassName (uint32_t vptr) { int typeinfo = readDWord(vptr - 0x4); int typestring = readDWord(typeinfo + 0x4); diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp deleted file mode 100644 index 880a9a06a..000000000 --- a/library/DFProcess-linux-wine.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/* -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 deleted file mode 100644 index 3294cbaf2..000000000 --- a/library/DFProcess-linux.cpp +++ /dev/null @@ -1,546 +0,0 @@ -/* -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 fcdee360b..e31ff1185 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -26,7 +26,7 @@ distribution. using namespace DFHack; // a full memory barrier! better be safe than sorry. -class SHMProcess::Private +class Process::Private { public: Private() @@ -60,7 +60,7 @@ class SHMProcess::Private }; // is the other side still there? -bool SHMProcess::Private::isValidSV() +bool Process::Private::isValidSV() { // try if CL mutex is free uint32_t result = WaitForSingleObject(DFSVMutex,0); @@ -87,7 +87,7 @@ bool SHMProcess::Private::isValidSV() } } -bool SHMProcess::Private::waitWhile (DF_PINGPONG state) +bool Process::Private::waitWhile (DF_PINGPONG state) { uint32_t cnt = 0; SCHED_YIELD // yield the CPU, valid only on single-core CPUs @@ -121,7 +121,7 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) return true; } -bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) +bool Process::Private::DF_TestBridgeVersion(bool & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_VERSION; full_barrier @@ -133,7 +133,7 @@ bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) return true; } -bool SHMProcess::Private::DF_GetPID(uint32_t & ret) +bool Process::Private::DF_GetPID(uint32_t & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_PID; full_barrier @@ -145,7 +145,7 @@ bool SHMProcess::Private::DF_GetPID(uint32_t & ret) return true; } -SHMProcess::SHMProcess(vector & known_versions) +Process::Process(vector & known_versions) : d(new Private()) { // get server and client mutex @@ -274,21 +274,21 @@ SHMProcess::SHMProcess(vector & known_versions) detach(); } -bool SHMProcess::isSuspended() +bool Process::isSuspended() { return d->suspended; } -bool SHMProcess::isAttached() +bool Process::isAttached() { return d->attached; } -bool SHMProcess::isIdentified() +bool Process::isIdentified() { return d->identified; } -SHMProcess::~SHMProcess() +Process::~Process() { if(d->attached) { @@ -315,29 +315,29 @@ SHMProcess::~SHMProcess() delete d; } -memory_info * SHMProcess::getDescriptor() +memory_info * Process::getDescriptor() { return d->my_descriptor; } -DFWindow * SHMProcess::getWindow() +DFWindow * Process::getWindow() { return d->my_window; } -int SHMProcess::getPID() +int Process::getPID() { return d->my_pid; } //FIXME: implement -bool SHMProcess::getThreadIDs(vector & threads ) +bool Process::getThreadIDs(vector & threads ) { return false; } //FIXME: cross-reference with ELF segment entries? -void SHMProcess::getMemRanges( vector & ranges ) +void Process::getMemRanges( vector & ranges ) { char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 @@ -363,7 +363,7 @@ void SHMProcess::getMemRanges( vector & ranges ) } } -bool SHMProcess::suspend() +bool Process::suspend() { if(!d->attached) { @@ -385,7 +385,7 @@ bool SHMProcess::suspend() return true; } -bool SHMProcess::asyncSuspend() +bool Process::asyncSuspend() { if(!d->attached) { @@ -407,12 +407,12 @@ bool SHMProcess::asyncSuspend() } } -bool SHMProcess::forceresume() +bool Process::forceresume() { return resume(); } -bool SHMProcess::resume() +bool Process::resume() { if(!d->attached) { @@ -430,7 +430,7 @@ bool SHMProcess::resume() } -bool SHMProcess::attach() +bool Process::attach() { if(g_pProcess != 0) { @@ -478,7 +478,7 @@ bool SHMProcess::attach() return true; } -bool SHMProcess::detach() +bool Process::detach() { if(!d->attached) { @@ -494,7 +494,7 @@ bool SHMProcess::detach() return true; } -void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) +void Process::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { // normal read under 1MB if(size <= SHM_BODY) @@ -531,7 +531,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff } } -uint8_t SHMProcess::readByte (const uint32_t offset) +uint8_t Process::readByte (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -540,7 +540,7 @@ uint8_t SHMProcess::readByte (const uint32_t offset) return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) +void Process::readByte (const uint32_t offset, uint8_t &val ) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -549,7 +549,7 @@ void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) val = ((shm_retval *)d->my_shm)->value; } -uint16_t SHMProcess::readWord (const uint32_t offset) +uint16_t Process::readWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -558,7 +558,7 @@ uint16_t SHMProcess::readWord (const uint32_t offset) return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readWord (const uint32_t offset, uint16_t &val) +void Process::readWord (const uint32_t offset, uint16_t &val) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -567,7 +567,7 @@ void SHMProcess::readWord (const uint32_t offset, uint16_t &val) val = ((shm_retval *)d->my_shm)->value; } -uint32_t SHMProcess::readDWord (const uint32_t offset) +uint32_t Process::readDWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -575,7 +575,7 @@ uint32_t SHMProcess::readDWord (const uint32_t offset) d->waitWhile(DFPP_READ_DWORD); return ((shm_retval *)d->my_shm)->value; } -void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) +void Process::readDWord (const uint32_t offset, uint32_t &val) { ((shm_read_small *)d->my_shm)->address = offset; full_barrier @@ -588,7 +588,7 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) * WRITING */ -void SHMProcess::writeDWord (uint32_t offset, uint32_t data) +void Process::writeDWord (uint32_t offset, uint32_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -598,7 +598,7 @@ void SHMProcess::writeDWord (uint32_t offset, uint32_t data) } // using these is expensive. -void SHMProcess::writeWord (uint32_t offset, uint16_t data) +void Process::writeWord (uint32_t offset, uint16_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -607,7 +607,7 @@ void SHMProcess::writeWord (uint32_t offset, uint16_t data) d->waitWhile(DFPP_WRITE_WORD); } -void SHMProcess::writeByte (uint32_t offset, uint8_t data) +void Process::writeByte (uint32_t offset, uint8_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; @@ -616,7 +616,7 @@ void SHMProcess::writeByte (uint32_t offset, uint8_t data) d->waitWhile(DFPP_WRITE_BYTE); } -void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) +void Process::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { // normal write under 1MB if(size <= SHM_BODY) @@ -654,7 +654,7 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf } // FIXME: butt-fugly -const std::string SHMProcess::readCString (uint32_t offset) +const std::string Process::readCString (uint32_t offset) { std::string temp; char temp_c[256]; @@ -671,7 +671,7 @@ const std::string SHMProcess::readCString (uint32_t offset) return temp; } -DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) +DfVector Process::readVector (uint32_t offset, uint32_t item_size) { /* MSVC++ vector is four pointers long @@ -689,7 +689,7 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) return DfVector(start,size,item_size); } -const std::string SHMProcess::readSTLString(uint32_t offset) +const std::string Process::readSTLString(uint32_t offset) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 ((shm_read_small *)d->my_shm)->address = offset; @@ -702,7 +702,7 @@ const std::string SHMProcess::readSTLString(uint32_t offset) return(string(d->my_shm+SHM_HEADER)); } -size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +size_t Process::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 ((shm_read_small *)d->my_shm)->address = offset; @@ -716,7 +716,7 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa return real; } -void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) +void Process::writeSTLString(const uint32_t address, const std::string writeString) { ((shm_write_small *)d->my_shm)->address = address/*-4*/; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator @@ -725,7 +725,7 @@ void SHMProcess::writeSTLString(const uint32_t address, const std::string writeS d->waitWhile(DFPP_WRITE_STL_STRING); } -string SHMProcess::readClassName (uint32_t vptr) +string Process::readClassName (uint32_t vptr) { int rtti = readDWord(vptr - 0x4); int typeinfo = readDWord(rtti + 0xC); diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp deleted file mode 100644 index eaba63abd..000000000 --- a/library/DFProcess-windows.cpp +++ /dev/null @@ -1,482 +0,0 @@ -/* -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 7eac5fe18..28525daec 100644 --- a/library/DFProcess.h +++ b/library/DFProcess.h @@ -61,135 +61,28 @@ namespace DFHack class DFHACK_EXPORT Process { - public: - // this is the single most important destructor ever. ~px - 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 - virtual bool detach() = 0; - - // synchronous suspend - // waits for DF to be actually suspended, - // this might take a while depending on implementation - virtual bool suspend() = 0; - // asynchronous suspend to use together with polling and timers - 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; - }; - - class DFHACK_EXPORT NormalProcess : virtual public Process - { - friend class ProcessEnumerator; - class Private; private: - Private * const d; - + class Private; + Private *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); + // this is the single most important destructor ever. ~px + Process(vector & known_versions); + ~Process(); - 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(); - }; - - class DFHACK_EXPORT SHMProcess : virtual public Process - { - friend class ProcessEnumerator; - class Private; - private: - Private * const d; - - public: - SHMProcess(vector & known_versions); - ~SHMProcess(); // Set up stuff so we can read memory bool attach(); + // detach from DF, resume its execution if it's suspended bool detach(); + // synchronous suspend + // waits for DF to be actually suspended, + // this might take a while depending on implementation bool suspend(); + // asynchronous suspend to use together with polling and timers bool asyncSuspend(); + // resume DF execution bool resume(); + // force-resume DF execution - maybe nonsense in this branch? :P bool forceresume(); uint32_t readDWord(const uint32_t address); @@ -212,72 +105,22 @@ 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(); }; - -#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(); - }; -#endif } #endif diff --git a/library/DFProcessEnumerator-linux.cpp b/library/DFProcessEnumerator-linux.cpp index f4d02e2a1..783bff27c 100644 --- a/library/DFProcessEnumerator-linux.cpp +++ b/library/DFProcessEnumerator-linux.cpp @@ -48,7 +48,7 @@ bool ProcessEnumerator::findProcessess() struct dirent *dir_entry_p; Process *p = 0; - p = new SHMProcess(d->meminfo->meminfo); + p = new Process(d->meminfo->meminfo); if(p->isIdentified()) { d->processes.push_back(p); @@ -58,7 +58,7 @@ bool ProcessEnumerator::findProcessess() delete p; p = 0; } - + /* // Open /proc/ directory dir_p = opendir("/proc/"); // Reading /proc/ entries @@ -91,6 +91,7 @@ bool ProcessEnumerator::findProcessess() } } 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 f77aabb2d..f263e6380 100644 --- a/library/DFProcessEnumerator-windows.cpp +++ b/library/DFProcessEnumerator-windows.cpp @@ -36,39 +36,13 @@ 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() { // Get the list of process identifiers. DWORD ProcArray[2048], memoryNeeded, numProccesses; { - Process * p = new SHMProcess(d->meminfo->meminfo); + Process * p = new Process(d->meminfo->meminfo); if(p->isIdentified()) { d->processes.push_back(p); @@ -80,7 +54,7 @@ bool ProcessEnumerator::findProcessess() p = 0; } } - + /* EnableDebugPriv(); if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) { @@ -104,7 +78,7 @@ bool ProcessEnumerator::findProcessess() delete q; q = 0; } - } + }*/ if(d->processes.size()) return true; return false;