From eef7025ef8738f96a5ce2adce947cb2ba6bd6fac Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 13 Nov 1999 19:54:54 +0000 Subject: [PATCH] Added support for WriteProcessMemory through the server. --- include/server.h | 13 +++++ scheduler/process.c | 75 ++++++++++++++++++++++--- server/process.c | 131 +++++++++++++++++++++++++++++++++++++++----- server/request.h | 2 + server/trace.c | 20 +++++++ 5 files changed, 219 insertions(+), 22 deletions(-) diff --git a/include/server.h b/include/server.h index 708fe742b8b..d492b888802 100644 --- a/include/server.h +++ b/include/server.h @@ -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 }; diff --git a/scheduler/process.c b/scheduler/process.c index 9c4d38a9f6c..d55c29e8a25 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -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; - 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 65500ba8bb7..034e90d28cb 100644 --- a/server/process.c +++ b/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) + 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 @@ 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 ); + } +} diff --git a/server/request.h b/server/request.h index f46edae5770..1d05bd90467 100644 --- a/server/request.h +++ b/server/request.h @@ -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 */ diff --git a/server/trace.c b/server/trace.c index ff49ed972cc..df2a4feed4f 100644 --- a/server/trace.c +++ b/server/trace.c @@ -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 ### */