diff --git a/include/server.h b/include/server.h index 2eada6e8909..708fe742b8b 100644 --- a/include/server.h +++ b/include/server.h @@ -807,6 +807,16 @@ struct debug_process_request }; +/* Read data from a process address space */ +struct read_process_memory_request +{ + IN int handle; /* process handle */ + IN void* addr; /* addr to read from (must be int-aligned) */ + IN int len; /* number of ints to read */ + OUT unsigned int data[1]; /* result data */ +}; + + /* Everything below this line is generated automatically by tools/make_requests */ /* ### make_requests begin ### */ @@ -883,6 +893,7 @@ enum request REQ_SEND_DEBUG_EVENT, REQ_CONTINUE_DEBUG_EVENT, REQ_DEBUG_PROCESS, + REQ_READ_PROCESS_MEMORY, REQ_NB_REQUESTS }; diff --git a/scheduler/process.c b/scheduler/process.c index b115863e142..9c4d38a9f6c 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -1065,20 +1065,55 @@ BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost) return TRUE; } + /*********************************************************************** * ReadProcessMemory (KERNEL32) - * FIXME: check this, if we ever run win32 binaries in different addressspaces - * ... and add a sizecheck */ -BOOL WINAPI ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, - LPVOID lpBuffer, DWORD nSize, - LPDWORD lpNumberOfBytesRead ) +BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, DWORD size, + LPDWORD bytes_read ) { - memcpy(lpBuffer,lpBaseAddress,nSize); - if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize; - return TRUE; + struct read_process_memory_request *req = get_req_buffer(); + unsigned int offset = (unsigned int)addr % sizeof(int); + unsigned int max = server_remaining( req->data ); /* max length in one request */ + unsigned int pos; + + if (bytes_read) *bytes_read = size; + + /* first time, read total length to check for permissions */ + req->handle = process; + req->addr = (char *)addr - offset; + req->len = (size + offset + sizeof(int) - 1) / sizeof(int); + if (server_call( REQ_READ_PROCESS_MEMORY )) goto error; + + if (size <= max - offset) + { + memcpy( buffer, (char *)req->data + offset, size ); + return TRUE; + } + + /* now take care of the remaining data */ + memcpy( buffer, (char *)req->data + offset, max - offset ); + pos = max - offset; + size -= pos; + while (size) + { + if (max > size) max = size; + req->handle = process; + req->addr = (char *)addr + pos; + req->len = (max + sizeof(int) - 1) / sizeof(int); + if (server_call( REQ_READ_PROCESS_MEMORY )) goto error; + memcpy( (char *)buffer + pos, (char *)req->data, max ); + size -= max; + pos += max; + } + return TRUE; + + error: + if (bytes_read) *bytes_read = 0; + return FALSE; } + /*********************************************************************** * WriteProcessMemory (KERNEL32) * FIXME: check this, if we ever run win32 binaries in different addressspaces diff --git a/server/file.c b/server/file.c index 17dd6eee2cf..60f50f4ba51 100644 --- a/server/file.c +++ b/server/file.c @@ -366,6 +366,7 @@ void file_set_error(void) case EINVAL: set_error( ERROR_INVALID_PARAMETER ); break; case ESPIPE: set_error( ERROR_SEEK ); break; case ENOTEMPTY: set_error( ERROR_DIR_NOT_EMPTY ); break; + case EIO: set_error( ERROR_NOACCESS ); break; default: perror("file_set_error"); set_error( ERROR_UNKNOWN ); break; } } diff --git a/server/process.c b/server/process.c index f5ce22039f8..65500ba8bb7 100644 --- a/server/process.c +++ b/server/process.c @@ -5,11 +5,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include @@ -314,6 +316,38 @@ static void set_process_info( struct process *process, } } +/* 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 */ +/* 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 ) +{ + struct thread *thread = process->thread_list; + int pid = thread->unix_pid; + + suspend_thread( thread, 0 ); + if (thread->attached) + { + while (len-- > 0) + { + int data = ptrace( PT_READ_D, pid, addr ); + if ((data == -1) && errno) + { + file_set_error(); + break; + } + if (max) + { + *dest++ = data; + max--; + } + addr++; + } + } + else set_error( ERROR_ACCESS_DENIED ); + resume_thread( thread ); +} + /* take a snapshot of currently running processes */ struct process_snapshot *process_snap( int *count ) { @@ -440,3 +474,16 @@ DECL_HANDLER(set_process_info) release_object( process ); } } + +/* read data from a process address space */ +DECL_HANDLER(read_process_memory) +{ + struct process *process; + + if ((process = get_process_from_handle( req->handle, PROCESS_VM_READ ))) + { + read_process_memory( process, req->addr, req->len, + get_req_size( req->data, sizeof(int) ), req->data ); + release_object( process ); + } +} diff --git a/server/request.h b/server/request.h index 6ee07e67b6b..f46edae5770 100644 --- a/server/request.h +++ b/server/request.h @@ -132,6 +132,7 @@ DECL_HANDLER(wait_debug_event); DECL_HANDLER(send_debug_event); DECL_HANDLER(continue_debug_event); DECL_HANDLER(debug_process); +DECL_HANDLER(read_process_memory); #ifdef WANT_REQUEST_HANDLERS @@ -210,6 +211,7 @@ static const struct handler { { (void(*)())req_send_debug_event, sizeof(struct send_debug_event_request) }, { (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) }, }; #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/thread.c b/server/thread.c index 1861fc9ebc0..d8885462ec8 100644 --- a/server/thread.c +++ b/server/thread.c @@ -337,18 +337,19 @@ void continue_thread( struct thread *thread ) } /* suspend a thread */ -static int suspend_thread( struct thread *thread ) +int suspend_thread( struct thread *thread, int check_limit ) { int old_count = thread->suspend; - if (thread->suspend < MAXIMUM_SUSPEND_COUNT) + if (thread->suspend < MAXIMUM_SUSPEND_COUNT || !check_limit) { if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread ); } + else set_error( ERROR_SIGNAL_REFUSED ); return old_count; } /* resume a thread */ -static int resume_thread( struct thread *thread ) +int resume_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend > 0) @@ -364,7 +365,7 @@ void suspend_all_threads( void ) struct thread *thread; for ( thread = first_thread; thread; thread = thread->next ) if ( thread != current ) - suspend_thread( thread ); + suspend_thread( thread, 0 ); } /* resume all threads but the current */ @@ -709,7 +710,7 @@ DECL_HANDLER(suspend_thread) if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) { - req->count = suspend_thread( thread ); + req->count = suspend_thread( thread, 1 ); release_object( thread ); } } diff --git a/server/thread.h b/server/thread.h index 9d216e6a929..2d5c7a38875 100644 --- a/server/thread.h +++ b/server/thread.h @@ -69,6 +69,8 @@ extern struct thread *get_thread_from_handle( int handle, unsigned int access ); extern void wait4_thread( struct thread *thread, int wait ); extern void stop_thread( struct thread *thread ); extern void continue_thread( struct thread *thread ); +extern int suspend_thread( struct thread *thread, int check_limit ); +extern int resume_thread( struct thread *thread ); extern void suspend_all_threads( void ); extern void resume_all_threads( void ); extern int add_queue( struct object *obj, struct wait_queue_entry *entry ); diff --git a/server/trace.c b/server/trace.c index 1af1367eb32..ff49ed972cc 100644 --- a/server/trace.c +++ b/server/trace.c @@ -11,14 +11,35 @@ #include "request.h" +/* utility functions */ + +static void dump_ints( const int *ptr, int len ) +{ + fputc( '{', stderr ); + while (len) + { + fprintf( stderr, "%d", *ptr++ ); + if (--len) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + +static void dump_bytes( const unsigned char *ptr, int len ) +{ + fputc( '{', stderr ); + while (len) + { + fprintf( stderr, "%02x", *ptr++ ); + if (--len) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + /* dumping for functions for requests that have a variable part */ static void dump_varargs_select( struct select_request *req ) { - int i; - for (i = 0; i < req->count; i++) - fprintf( stderr, "%c%d", i ? ',' : '{', req->handles[i] ); - fprintf( stderr, "}" ); + dump_ints( req->handles, req->count ); } static void dump_varargs_get_apcs( struct get_apcs_request *req ) @@ -31,10 +52,13 @@ static void dump_varargs_get_apcs( struct get_apcs_request *req ) static void dump_varargs_get_socket_event( struct get_socket_event_request *req ) { - int i; - for (i = 0; i < FD_MAX_EVENTS; i++) - fprintf( stderr, "%c%d", i ? ',' : '{', req->errors[i] ); - fprintf( stderr, "}" ); + dump_ints( req->errors, FD_MAX_EVENTS ); +} + +static void dump_varargs_read_process_memory( struct read_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) ); } @@ -763,6 +787,19 @@ static void dump_debug_process_request( struct debug_process_request *req ) fprintf( stderr, " pid=%p", req->pid ); } +static void dump_read_process_memory_request( struct read_process_memory_request *req ) +{ + fprintf( stderr, " handle=%d,", req->handle ); + fprintf( stderr, " addr=%p,", req->addr ); + fprintf( stderr, " len=%d", req->len ); +} + +static void dump_read_process_memory_reply( struct read_process_memory_request *req ) +{ + fprintf( stderr, " data=" ); + dump_varargs_read_process_memory( req ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_new_thread_request, @@ -835,6 +872,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_send_debug_event_request, (dump_func)dump_continue_debug_event_request, (dump_func)dump_debug_process_request, + (dump_func)dump_read_process_memory_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -909,6 +947,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_send_debug_event_reply, (dump_func)0, (dump_func)0, + (dump_func)dump_read_process_memory_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -983,6 +1022,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "send_debug_event", "continue_debug_event", "debug_process", + "read_process_memory", }; /* ### make_requests end ### */