Added support for WriteProcessMemory through the server.
diff --git a/include/server.h b/include/server.h
index 708fe74..d492b88 100644
--- a/include/server.h
+++ b/include/server.h
@@ -817,6 +817,18 @@
};
+/* Write data to a process address space */
+struct write_process_memory_request
+{
+ IN int handle; /* process handle */
+ IN void* addr; /* addr to write to (must be int-aligned) */
+ IN int len; /* number of ints to write */
+ IN unsigned int first_mask; /* mask for first word */
+ IN unsigned int last_mask; /* mask for last word */
+ IN unsigned int data[1]; /* data to write */
+};
+
+
/* Everything below this line is generated automatically by tools/make_requests */
/* ### make_requests begin ### */
@@ -894,6 +906,7 @@
REQ_CONTINUE_DEBUG_EVENT,
REQ_DEBUG_PROCESS,
REQ_READ_PROCESS_MEMORY,
+ REQ_WRITE_PROCESS_MEMORY,
REQ_NB_REQUESTS
};
diff --git a/scheduler/process.c b/scheduler/process.c
index 9c4d38a..d55c29e 100644
--- a/scheduler/process.c
+++ b/scheduler/process.c
@@ -1116,18 +1116,77 @@
/***********************************************************************
* WriteProcessMemory (KERNEL32)
- * FIXME: check this, if we ever run win32 binaries in different addressspaces
- * ... and add a sizecheck
*/
-BOOL WINAPI WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress,
- LPVOID lpBuffer, DWORD nSize,
- LPDWORD lpNumberOfBytesWritten )
+BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPVOID buffer, DWORD size,
+ LPDWORD bytes_written )
{
- memcpy(lpBaseAddress,lpBuffer,nSize);
- if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
- return TRUE;
+ unsigned int first_offset, last_offset;
+ struct write_process_memory_request *req = get_req_buffer();
+ unsigned int max = server_remaining( req->data ); /* max length in one request */
+ unsigned int pos, last_mask;
+
+ if (!size)
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+ if (bytes_written) *bytes_written = size;
+
+ /* compute the mask for the first int */
+ req->first_mask = ~0;
+ first_offset = (unsigned int)addr % sizeof(int);
+ memset( &req->first_mask, 0, first_offset );
+
+ /* compute the mask for the last int */
+ last_offset = (size + first_offset) % sizeof(int);
+ last_mask = 0;
+ memset( &last_mask, 0xff, last_offset ? last_offset : sizeof(int) );
+
+ req->handle = process;
+ req->addr = (char *)addr - first_offset;
+ /* for the first request, use the total length */
+ req->len = (size + first_offset + sizeof(int) - 1) / sizeof(int);
+
+ if (size + first_offset < max) /* we can do it in one round */
+ {
+ memcpy( (char *)req->data + first_offset, buffer, size );
+ req->last_mask = last_mask;
+ if (server_call( REQ_WRITE_PROCESS_MEMORY )) goto error;
+ return TRUE;
+ }
+
+ /* needs multiple server calls */
+
+ memcpy( (char *)req->data + first_offset, buffer, max - first_offset );
+ req->last_mask = ~0;
+ if (server_call( REQ_WRITE_PROCESS_MEMORY )) goto error;
+ pos = max - first_offset;
+ size -= pos;
+ while (size)
+ {
+ if (size <= max) /* last one */
+ {
+ req->last_mask = last_mask;
+ max = size;
+ }
+ req->handle = process;
+ req->addr = (char *)addr + pos;
+ req->len = (max + sizeof(int) - 1) / sizeof(int);
+ req->first_mask = ~0;
+ memcpy( req->data, buffer + pos, max );
+ if (server_call( REQ_WRITE_PROCESS_MEMORY )) goto error;
+ pos += max;
+ size -= max;
+ }
+ return TRUE;
+
+ error:
+ if (bytes_written) *bytes_written = 0;
+ return FALSE;
+
}
+
/***********************************************************************
* RegisterServiceProcess (KERNEL, KERNEL32)
*
diff --git a/server/process.c b/server/process.c
index 65500ba..034e90d 100644
--- a/server/process.c
+++ b/server/process.c
@@ -316,35 +316,125 @@
}
}
+/* wrapper for reading an int with ptrace */
+static inline int read_word( int pid, const int *addr, int *data )
+{
+ if (((*data = ptrace( PT_READ_D, pid, addr )) == -1) && errno)
+ {
+ file_set_error();
+ return -1;
+ }
+ return 0;
+}
+
+/* wrapper for writing an int with ptrace */
+static inline int write_word( int pid, int *addr, int data, unsigned int mask )
+{
+ int res;
+ if (mask != ~0)
+ {
+ if (read_word( pid, addr, &res ) == -1) return -1;
+ data = (data & mask) | (res & ~mask);
+ }
+ if ((res = ptrace( PT_WRITE_D, pid, addr, data )) == -1) file_set_error();
+ return res;
+}
+
/* read data from a process memory space */
-/* len is the total size (in ints), max is the size we can actually store in the input buffer */
+/* len is the total size (in ints), max is the size we can actually store in the output buffer */
/* we read the total size in all cases to check for permissions */
-static void read_process_memory( struct process *process, const int *addr, int len,
- int max, int *dest )
+static void read_process_memory( struct process *process, const int *addr,
+ size_t len, size_t max, int *dest )
{
struct thread *thread = process->thread_list;
int pid = thread->unix_pid;
+ if ((unsigned int)addr % sizeof(int)) /* address must be aligned */
+ {
+ set_error( ERROR_INVALID_PARAMETER );
+ return;
+ }
suspend_thread( thread, 0 );
if (thread->attached)
{
- while (len-- > 0)
+ while (len > 0 && max)
{
- int data = ptrace( PT_READ_D, pid, addr );
- if ((data == -1) && errno)
+ if (read_word( pid, addr++, dest++ ) == -1) goto done;
+ max--;
+ len--;
+ }
+ /* check the rest for read permission */
+ if (len > 0)
+ {
+ int dummy, page = get_page_size() / sizeof(int);
+ while (len >= page)
{
- file_set_error();
- break;
+ addr += page;
+ len -= page;
+ if (read_word( pid, addr - 1, &dummy ) == -1) goto done;
}
- if (max)
- {
- *dest++ = data;
- max--;
- }
- addr++;
+ if (len && (read_word( pid, addr + len - 1, &dummy ) == -1)) goto done;
}
}
else set_error( ERROR_ACCESS_DENIED );
+ done:
+ resume_thread( thread );
+}
+
+/* write data to a process memory space */
+/* len is the total size (in ints), max is the size we can actually read from the input buffer */
+/* we check the total size for write permissions */
+static void write_process_memory( struct process *process, int *addr, size_t len,
+ size_t max, unsigned int first_mask,
+ unsigned int last_mask, const int *src )
+{
+ struct thread *thread = process->thread_list;
+ int pid = thread->unix_pid;
+
+ if (!len || ((unsigned int)addr % sizeof(int))) /* address must be aligned */
+ {
+ set_error( ERROR_INVALID_PARAMETER );
+ return;
+ }
+ suspend_thread( thread, 0 );
+ if (thread->attached)
+ {
+ /* first word is special */
+ if (len > 1)
+ {
+ if (write_word( pid, addr++, *src++, first_mask ) == -1) goto done;
+ len--;
+ max--;
+ }
+ else last_mask &= first_mask;
+
+ while (len > 1 && max)
+ {
+ if (write_word( pid, addr++, *src++, ~0 ) == -1) goto done;
+ max--;
+ len--;
+ }
+
+ if (max)
+ {
+ /* last word is special too */
+ if (write_word( pid, addr, *src, last_mask ) == -1) goto done;
+ }
+ else
+ {
+ /* check the rest for write permission */
+ int page = get_page_size() / sizeof(int);
+ while (len >= page)
+ {
+ addr += page;
+ len -= page;
+ if (write_word( pid, addr - 1, 0, 0 ) == -1) goto done;
+ }
+ if (len && (write_word( pid, addr + len - 1, 0, 0 ) == -1)) goto done;
+ }
+ }
+ else set_error( ERROR_ACCESS_DENIED );
+ done:
resume_thread( thread );
}
@@ -487,3 +577,16 @@
release_object( process );
}
}
+
+/* write data to a process address space */
+DECL_HANDLER(write_process_memory)
+{
+ struct process *process;
+
+ if ((process = get_process_from_handle( req->handle, PROCESS_VM_WRITE )))
+ {
+ write_process_memory( process, req->addr, req->len, get_req_size( req->data, sizeof(int) ),
+ req->first_mask, req->last_mask, req->data );
+ release_object( process );
+ }
+}
diff --git a/server/request.h b/server/request.h
index f46edae..1d05bd9 100644
--- a/server/request.h
+++ b/server/request.h
@@ -133,6 +133,7 @@
DECL_HANDLER(continue_debug_event);
DECL_HANDLER(debug_process);
DECL_HANDLER(read_process_memory);
+DECL_HANDLER(write_process_memory);
#ifdef WANT_REQUEST_HANDLERS
@@ -212,6 +213,7 @@
{ (void(*)())req_continue_debug_event, sizeof(struct continue_debug_event_request) },
{ (void(*)())req_debug_process, sizeof(struct debug_process_request) },
{ (void(*)())req_read_process_memory, sizeof(struct read_process_memory_request) },
+ { (void(*)())req_write_process_memory, sizeof(struct write_process_memory_request) },
};
#endif /* WANT_REQUEST_HANDLERS */
diff --git a/server/trace.c b/server/trace.c
index ff49ed9..df2a4fe 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -61,6 +61,12 @@
dump_bytes( (unsigned char *)req->data, count * sizeof(int) );
}
+static void dump_varargs_write_process_memory( struct write_process_memory_request *req )
+{
+ int count = MIN( req->len, get_req_size( req->data, sizeof(int) ) );
+ dump_bytes( (unsigned char *)req->data, count * sizeof(int) );
+}
+
typedef void (*dump_func)( const void *req );
@@ -800,6 +806,17 @@
dump_varargs_read_process_memory( req );
}
+static void dump_write_process_memory_request( struct write_process_memory_request *req )
+{
+ fprintf( stderr, " handle=%d,", req->handle );
+ fprintf( stderr, " addr=%p,", req->addr );
+ fprintf( stderr, " len=%d,", req->len );
+ fprintf( stderr, " first_mask=%08x,", req->first_mask );
+ fprintf( stderr, " last_mask=%08x,", req->last_mask );
+ fprintf( stderr, " data=" );
+ dump_varargs_write_process_memory( req );
+}
+
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_new_thread_request,
@@ -873,6 +890,7 @@
(dump_func)dump_continue_debug_event_request,
(dump_func)dump_debug_process_request,
(dump_func)dump_read_process_memory_request,
+ (dump_func)dump_write_process_memory_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -948,6 +966,7 @@
(dump_func)0,
(dump_func)0,
(dump_func)dump_read_process_memory_reply,
+ (dump_func)0,
};
static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -1023,6 +1042,7 @@
"continue_debug_event",
"debug_process",
"read_process_memory",
+ "write_process_memory",
};
/* ### make_requests end ### */