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

develop
Petr Mrázek 2010-03-02 01:18:34 +01:00
parent 236a28b606
commit b3424418e6
9 changed files with 102 additions and 1907 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

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

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

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

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

@ -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 <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,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<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;
};
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();
};
class DFHACK_EXPORT SHMProcess : virtual public Process
{
friend class ProcessEnumerator;
class Private;
private:
Private * const d;
Private *d;
public:
SHMProcess(vector <memory_info *> & known_versions);
~SHMProcess();
// this is the single most important destructor ever. ~px
Process(vector <memory_info *> & known_versions);
~Process();
// 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<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();
};
#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();
};
#endif
}
#endif

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

@ -36,39 +36,13 @@ 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()
{
// 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;