Added support for WriteProcessMemory through the server.
This commit is contained in:
parent
c76ad35a32
commit
eef7025ef8
|
@ -817,6 +817,18 @@ struct read_process_memory_request
|
|||
};
|
||||
|
||||
|
||||
/* 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 @@ enum request
|
|||
REQ_CONTINUE_DEBUG_EVENT,
|
||||
REQ_DEBUG_PROCESS,
|
||||
REQ_READ_PROCESS_MEMORY,
|
||||
REQ_WRITE_PROCESS_MEMORY,
|
||||
REQ_NB_REQUESTS
|
||||
};
|
||||
|
||||
|
|
|
@ -1116,18 +1116,77 @@ BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, DWOR
|
|||
|
||||
/***********************************************************************
|
||||
* 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;
|
||||
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)
|
||||
*
|
||||
|
|
131
server/process.c
131
server/process.c
|
@ -316,35 +316,125 @@ static void set_process_info( struct process *process,
|
|||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
file_set_error();
|
||||
break;
|
||||
}
|
||||
if (max)
|
||||
{
|
||||
*dest++ = data;
|
||||
if (read_word( pid, addr++, dest++ ) == -1) goto done;
|
||||
max--;
|
||||
len--;
|
||||
}
|
||||
addr++;
|
||||
/* check the rest for read permission */
|
||||
if (len > 0)
|
||||
{
|
||||
int dummy, page = get_page_size() / sizeof(int);
|
||||
while (len >= page)
|
||||
{
|
||||
addr += page;
|
||||
len -= page;
|
||||
if (read_word( pid, addr - 1, &dummy ) == -1) goto done;
|
||||
}
|
||||
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 @@ DECL_HANDLER(read_process_memory)
|
|||
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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ DECL_HANDLER(send_debug_event);
|
|||
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 @@ static const struct handler {
|
|||
{ (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 */
|
||||
|
||||
|
|
|
@ -61,6 +61,12 @@ static void dump_varargs_read_process_memory( struct read_process_memory_request
|
|||
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 @@ static void dump_read_process_memory_reply( struct read_process_memory_request *
|
|||
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 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
|||
(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 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
(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 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
|||
"continue_debug_event",
|
||||
"debug_process",
|
||||
"read_process_memory",
|
||||
"write_process_memory",
|
||||
};
|
||||
|
||||
/* ### make_requests end ### */
|
||||
|
|
Loading…
Reference in New Issue