Revert "Nuked many Process classes. Only SHM remains. We'll see how it goes :)"

This reverts commit b3424418e6.

Conflicts:

	library/DFProcess-linux-SHM.cpp
	library/DFProcess-windows-SHM.cpp
	library/DFProcess.h
	library/DFProcessEnumerator-linux.cpp
	library/DFProcessEnumerator-windows.cpp

Minor manual changes were required.
develop
Petr Mrázek 2010-03-09 15:15:15 +01:00
parent 3e581908c1
commit f0087926c5
9 changed files with 1941 additions and 126 deletions

@ -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

@ -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 <memory_info *> & known_versions)
bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector <memory_info *> & 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 <memory_in
return false;
}
Process::~Process()
SHMProcess::~SHMProcess()
{
if(d->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<uint32_t> & threads )
bool SHMProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void Process::getMemRanges( vector<t_memrange> & ranges )
void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
@ -347,7 +347,7 @@ void Process::getMemRanges( vector<t_memrange> & 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;
}

@ -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 <errno.h>
#include <DFError.h>
#include <sys/ptrace.h>
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 <memory_info *> & known_versions);
};
WineProcess::WineProcess(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;
// 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<memory_info *>::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<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void WineProcess::getMemRanges( vector<t_memrange> & 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;
}

@ -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 <errno.h>
#include <sys/ptrace.h>
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 <memory_info *> & 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 <memory_info *> & known_versions)
{
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_file);
vector<memory_info *>::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<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void NormalProcess::getMemRanges( vector<t_memrange> & 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
}

@ -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 <memory_info *> & known_versions)
SHMProcess::SHMProcess(uint32_t PID, vector <memory_info *> & known_versions)
: d(new Private())
{
char svmutexname [256];
@ -237,7 +237,7 @@ Process::Process(uint32_t PID, vector <memory_info *> & 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 <memory_info *> & 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<uint32_t> & threads )
bool SHMProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void Process::getMemRanges( vector<t_memrange> & ranges )
void SHMProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
@ -384,7 +384,7 @@ void Process::getMemRanges( vector<t_memrange> & 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;
}

@ -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 <memory_info *> & 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 *)&sections );
d->my_handle = 0;
// see if there's a version entry that matches this process
vector<memory_info*>::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<uint32_t> 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<uint32_t> & 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<t_memrange> & 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;
}

@ -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 <memory_info *> & 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<uint32_t> & threads ) = 0;
// get virtual memory ranges of the process (what is mapped where)
virtual void getMemRanges( vector<t_memrange> & 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 <memory_info *> & 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<uint32_t> & threads );
void getMemRanges( vector<t_memrange> & 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 <memory_info *> & 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<uint32_t> & threads );
// get virtual memory ranges of the process (what is mapped where)
void getMemRanges( vector<t_memrange> & 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 <memory_info *> & 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<uint32_t> & threads );
void getMemRanges( vector<t_memrange> & 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

@ -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())
{

@ -36,6 +36,32 @@ class DFHack::ProcessEnumerator::Private
std::vector<Process *> 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;